Skip to content

Commit 027406f

Browse files
committed
improved heuristic to find project root
We're now trying to look at a few directories, instead of just the git root. Fixes #49
1 parent 04fc216 commit 027406f

File tree

4 files changed

+81
-37
lines changed

4 files changed

+81
-37
lines changed

src/bacon.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,13 @@ impl Bacon {
344344
.to_file_path()
345345
.expect("the workspace folder sent by the editor is not a file path");
346346
if let Some(git_root) = BaconLs::find_git_root_directory(&folder_path).await {
347-
tracing::debug!(
348-
"found git root directory {}, using it for files base path",
349-
git_root.display()
350-
);
351-
folder_path = Cow::Owned(git_root);
347+
if git_root.join("Cargo.toml").exists() {
348+
tracing::debug!(
349+
"found git root directory {}, using it for files base path",
350+
git_root.display()
351+
);
352+
folder_path = Cow::Owned(git_root);
353+
}
352354
}
353355
let mut bacon_locations = Vec::new();
354356
if let Err(e) = Bacon::find_bacon_locations(&folder_path, locations_file_name, &mut bacon_locations) {
@@ -437,10 +439,11 @@ impl Bacon {
437439
tracing::info!("starting background task in charge of syncronizing diagnostics for all open files");
438440
let (tx, rx) = flume::unbounded::<DebounceEventResult>();
439441

440-
let (locations_file, wait_time, cancel_token) = {
442+
let (locations_file, proj_root, wait_time, cancel_token) = {
441443
let state = state.read().await;
442444
(
443445
state.locations_file.clone(),
446+
state.project_root.clone(),
444447
state.syncronize_all_open_files_wait_millis,
445448
state.cancel_token.clone(),
446449
)
@@ -452,12 +455,20 @@ impl Bacon {
452455
})
453456
.expect("failed to create file watcher");
454457

458+
let locations_file_path =
459+
proj_root.map_or_else(|| PathBuf::from(&locations_file), |root| root.join(&locations_file));
455460
loop {
456-
match watcher.watch(PathBuf::from(&locations_file), notify::RecursiveMode::Recursive) {
457-
Ok(_) => break,
461+
match watcher.watch(PathBuf::from(&locations_file_path), notify::RecursiveMode::Recursive) {
462+
Ok(_) => {
463+
tracing::info!("watching '{}' for changes...", locations_file_path.display());
464+
break;
465+
}
458466
Err(e) => {
459-
tracing::info!("unable to start .bacon_locations file watcher, retrying in 1 second");
460-
tracing::debug!(".bacon_locations watcher: {e}");
467+
tracing::warn!(
468+
"unable to watch '{}', retrying in 1 second",
469+
locations_file_path.display()
470+
);
471+
tracing::error!(".bacon_locations watcher error: {e}");
461472
tokio::time::sleep(Duration::from_secs(1)).await;
462473
}
463474
}
@@ -485,7 +496,6 @@ impl Bacon {
485496

486497
let loop_state = state.read().await;
487498
let open_files = loop_state.open_files.clone();
488-
let locations_file = loop_state.locations_file.clone();
489499
let workspace_folders = loop_state.workspace_folders.clone();
490500
drop(loop_state);
491501
tracing::debug!(

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ enum Backend {
4848

4949
#[derive(Debug)]
5050
struct State {
51+
project_root: Option<PathBuf>,
5152
workspace_folders: Option<Vec<WorkspaceFolder>>,
5253
locations_file: String,
5354
update_on_save: bool,
@@ -75,6 +76,7 @@ struct State {
7576
impl Default for State {
7677
fn default() -> Self {
7778
Self {
79+
project_root: None,
7880
workspace_folders: None,
7981
locations_file: LOCATIONS_FILE.to_string(),
8082
update_on_save: true,

src/lsp.rs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::HashMap, env, path::PathBuf, time::Duration};
1+
use std::{collections::HashMap, env, time::Duration};
22

33
use tokio::{fs, time::Instant};
44
use tower_lsp_server::{
@@ -19,6 +19,8 @@ impl LanguageServer for BaconLs {
1919
async fn initialize(&self, params: InitializeParams) -> jsonrpc::Result<InitializeResult> {
2020
tracing::info!("initializing {PKG_NAME} v{PKG_VERSION}",);
2121
tracing::debug!("initializing with input parameters: {params:#?}");
22+
let project_root = Cargo::find_project_root(&params).await;
23+
tracing::debug!("Found project root: {project_root:?}");
2224

2325
if let Some(TextDocumentClientCapabilities {
2426
publish_diagnostics: Some(PublishDiagnosticsClientCapabilities { .. }),
@@ -48,6 +50,7 @@ impl LanguageServer for BaconLs {
4850
}
4951

5052
let mut state = self.state.write().await;
53+
state.project_root = project_root;
5154
state.workspace_folders = params.workspace_folders;
5255
state.diagnostics_data_supported = diagnostics_data_supported;
5356

@@ -145,30 +148,8 @@ impl LanguageServer for BaconLs {
145148
state.update_on_save = true;
146149
state.update_on_save_wait_millis = Duration::ZERO;
147150
if !state.update_on_change {
148-
if let Some(git_root) = Cargo::find_git_root_directory().await {
149-
// We only chose the git root as our workspace root if a
150-
// `Cargo.toml` actually exists in the git root.
151-
if git_root.join("Cargo.toml").exists() {
152-
state.build_folder = git_root;
153-
} else {
154-
#[allow(deprecated)]
155-
if let Some(root_path) = params.root_path {
156-
state.build_folder = PathBuf::from(root_path);
157-
} else if let Some(root_uri) = params.root_uri {
158-
state.build_folder = PathBuf::from(root_uri.path().as_str());
159-
} else if let Some(workspace_folders) = &state.workspace_folders {
160-
if let Some(first) = workspace_folders.get(0) {
161-
state.build_folder = PathBuf::from(first.uri.path().as_str());
162-
} else {
163-
// This duplication can be avoided when let chains are finally
164-
// stabilized.
165-
state.build_folder = git_root;
166-
}
167-
} else {
168-
// We know this is broken, but it's our best guess.
169-
state.build_folder = git_root;
170-
}
171-
}
151+
if let Some(root) = &state.project_root {
152+
state.build_folder = root.clone();
172153
}
173154
}
174155
}
@@ -198,6 +179,7 @@ impl LanguageServer for BaconLs {
198179

199180
async fn initialized(&self, _: InitializedParams) {
200181
let state = self.state.read().await;
182+
let proj_root = state.project_root.clone();
201183
let run_bacon = state.run_bacon_in_background;
202184
let bacon_command_args = state.run_bacon_in_background_command_args.clone();
203185
let create_bacon_prefs = state.create_bacon_preferences_file;
@@ -253,7 +235,15 @@ impl LanguageServer for BaconLs {
253235
let mut current_dir = None;
254236
if let Ok(cwd) = env::current_dir() {
255237
current_dir = Self::find_git_root_directory(&cwd).await;
238+
if let Some(dir) = &current_dir {
239+
if !dir.join("Cargo.toml").exists() {
240+
current_dir = proj_root;
241+
}
242+
} else {
243+
current_dir = proj_root;
244+
}
256245
}
246+
257247
match Bacon::run_in_background("bacon", &bacon_command_args, current_dir.as_ref(), cancel_token).await {
258248
Ok(command) => {
259249
tracing::info!("bacon was started successfully and is running in the background");

src/native.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99

1010
use serde::{Deserialize, Deserializer};
1111
use tokio::{fs, process::Command};
12-
use tower_lsp_server::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Uri};
12+
use tower_lsp_server::lsp_types::{Diagnostic, DiagnosticSeverity, InitializeParams, Position, Range, Uri};
1313

1414
use crate::{DiagnosticData, PKG_NAME};
1515

@@ -210,6 +210,48 @@ impl Cargo {
210210
}
211211
}
212212

213+
pub(crate) async fn find_project_root(params: &InitializeParams) -> Option<PathBuf> {
214+
let git_root = Self::find_git_root_directory().await?;
215+
216+
// We only chose the git root as our workspace root if a
217+
// `Cargo.toml` actually exists in the git root.
218+
if git_root.join("Cargo.toml").exists() {
219+
return Some(git_root);
220+
}
221+
222+
if let Some(workspace_folders) = &params.workspace_folders {
223+
for folder in workspace_folders {
224+
let root_path = PathBuf::from(folder.uri.path().as_str());
225+
if root_path.join("Cargo.toml").exists() {
226+
return Some(root_path);
227+
}
228+
}
229+
}
230+
231+
#[allow(deprecated)]
232+
if let Some(root_uri) = &params.root_uri {
233+
let root_path = PathBuf::from(root_uri.path().as_str());
234+
if root_path.join("Cargo.toml").exists() {
235+
return Some(root_path);
236+
}
237+
}
238+
239+
#[allow(deprecated)]
240+
if let Some(root_path) = &params.root_path {
241+
let root_path = PathBuf::from(root_path);
242+
if root_path.join("Cargo.toml").exists() {
243+
return Some(root_path);
244+
}
245+
}
246+
247+
let cwd = std::env::current_dir().ok()?;
248+
if cwd.join("Cargo.toml").exists() {
249+
return Some(cwd);
250+
}
251+
252+
None
253+
}
254+
213255
pub(crate) async fn copy_source_code(destination_folder: &Path) -> Result<(), io::Error> {
214256
let source_repo = Self::find_git_root_directory()
215257
.await

0 commit comments

Comments
 (0)