add feature to configure workspace directory

close #16
This commit is contained in:
kbwo 2024-07-13 12:06:38 +09:00
parent 1b74a7ad4f
commit 0ae8988cc5
4 changed files with 133 additions and 23 deletions

17
.vim/coc-settings.json Normal file
View file

@ -0,0 +1,17 @@
{
"testing.enable": true,
"testing.fileTypes": ["rust"],
"testing.adapterCommand": {
"rust": [
{
"path": "testing-ls-adapter",
"extra_args": ["--test-kind=cargo-test", "--workspace"],
"include_patterns": ["/**/*.rs"],
"exclude_patterns": ["/test_proj/**/*"],
"workspace_dir": "."
}
]
},
"testing.server.path": "testing-language-server",
"testing.trace.server": "verbose"
}

View file

@ -7,6 +7,7 @@ use crate::spec::RunFileTestResult;
use crate::spec::RunFileTestResultItem;
use crate::spec::WorkspaceAnalysis;
use crate::util::format_uri;
use crate::util::resolve_path;
use crate::util::send_stdout;
use glob::Pattern;
use lsp_types::Diagnostic;
@ -35,6 +36,7 @@ use serde::Deserialize;
use serde_json::json;
use serde_json::Value;
use std::collections::HashMap;
use std::env::current_dir;
use std::io::BufRead;
use std::io::{self, Read};
use std::path::Path;
@ -46,7 +48,6 @@ use std::process::Output;
#[serde(rename_all = "camelCase")]
pub struct InitializedOptions {
adapter_command: HashMap<AdapterId, Vec<AdapterConfiguration>>,
project_dir: Option<PathBuf>,
}
pub struct TestingLS {
@ -71,18 +72,18 @@ impl TestingLS {
}
fn project_dir(&self) -> Result<PathBuf, LSError> {
let default_project_dir = self
.initialize_params
.clone()
.workspace_folders
.ok_or(LSError::Any(anyhow::anyhow!("No workspace folders found")))?;
let default_workspace_uri = default_project_dir[0].uri.clone();
let project_dir = self
.options
.project_dir
.clone()
.unwrap_or(default_workspace_uri.to_file_path().unwrap());
Ok(project_dir)
let cwd = current_dir();
if let Ok(cwd) = cwd {
Ok(cwd)
} else {
let default_project_dir = self
.initialize_params
.clone()
.workspace_folders
.ok_or(LSError::Any(anyhow::anyhow!("No workspace folders found")))?;
let default_workspace_uri = default_project_dir[0].uri.clone();
Ok(default_workspace_uri.to_file_path().unwrap())
}
}
pub fn main_loop(&mut self) -> Result<(), LSError> {
@ -278,6 +279,8 @@ impl TestingLS {
envs,
include_patterns,
exclude_patterns,
workspace_dir,
..
} = &adapter;
let file_paths =
Self::project_files(&project_dir, include_patterns, exclude_patterns);
@ -301,6 +304,19 @@ impl TestingLS {
let adapter_result = String::from_utf8(output.stdout)
.map_err(|err| LSError::Adapter(err.to_string()))?;
let workspace: DetectWorkspaceResult = serde_json::from_str(&adapter_result)?;
let workspace = if let Some(workspace_dir) = workspace_dir {
let workspace_dir = resolve_path(&project_dir, workspace_dir)
.to_str()
.unwrap()
.to_string();
let target_paths = workspace
.into_iter()
.flat_map(|kv| kv.1)
.collect::<Vec<_>>();
HashMap::from([(workspace_dir.clone(), target_paths)])
} else {
workspace
};
self.workspaces_cache
.push(WorkspaceAnalysis::new(adapter.clone(), workspace))
}
@ -572,7 +588,6 @@ mod tests {
},
options: InitializedOptions {
adapter_command: HashMap::from([(String::from(".rs"), vec![])]),
project_dir: None,
},
workspaces_cache: Vec::new(),
};
@ -593,9 +608,7 @@ mod tests {
let adapter_conf = AdapterConfiguration {
path: abs_path_of_rust_adapter,
extra_args: vec!["--test-kind=cargo-test".to_string()],
envs: HashMap::new(),
include_patterns: vec![],
exclude_patterns: vec![],
..Default::default()
};
let mut server = TestingLS {
initialize_params: InitializeParams {
@ -607,7 +620,6 @@ mod tests {
},
options: InitializedOptions {
adapter_command: HashMap::from([(String::from(".rs"), vec![adapter_conf])]),
project_dir: None,
},
workspaces_cache: Vec::new(),
};
@ -659,9 +671,7 @@ mod tests {
.unwrap()
.to_string(),
extra_args: vec!["--invalid-arg".to_string()],
envs: HashMap::new(),
include_patterns: vec![],
exclude_patterns: vec![],
..Default::default()
};
let abs_path_of_test_proj = std::env::current_dir().unwrap().join("test_proj/rust");
let files = TestingLS::project_files(
@ -680,7 +690,6 @@ mod tests {
},
options: InitializedOptions {
adapter_command: HashMap::from([(String::from(".rs"), vec![adapter_conf.clone()])]),
project_dir: None,
},
workspaces_cache: Vec::new(),
};

View file

@ -62,7 +62,7 @@ impl WorkspaceAnalysis {
}
}
#[derive(Debug, Deserialize, Clone, Serialize)]
#[derive(Debug, Deserialize, Clone, Serialize, Default)]
pub struct AdapterConfiguration {
pub path: String,
#[serde(default)]
@ -71,6 +71,7 @@ pub struct AdapterConfiguration {
pub envs: HashMap<String, String>,
pub include_patterns: Vec<String>,
pub exclude_patterns: Vec<String>,
pub workspace_dir: Option<String>,
}
pub type DetectWorkspaceResult = HashMap<WorkspaceFilePath, Vec<FilePath>>;

View file

@ -2,6 +2,8 @@ use crate::error::LSError;
use serde::Serialize;
use std::io::stdout;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
pub fn send_stdout<T>(message: &T) -> Result<(), LSError>
where
@ -18,3 +20,84 @@ where
pub fn format_uri(uri: &str) -> String {
uri.replace("file://", "")
}
pub fn resolve_path(base_dir: &Path, relative_path: &str) -> PathBuf {
let absolute = if Path::new(relative_path).is_absolute() {
PathBuf::from(relative_path)
} else {
base_dir.join(relative_path)
};
let mut components = Vec::new();
for component in absolute.components() {
match component {
std::path::Component::ParentDir => {
components.pop();
}
std::path::Component::Normal(_) | std::path::Component::RootDir => {
components.push(component);
}
_ => {}
}
}
PathBuf::from_iter(components)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resolve_path() {
let base_dir = PathBuf::from("/Users/test/projects");
// relative path
assert_eq!(
resolve_path(&base_dir, "github.com/hoge/fuga"),
PathBuf::from("/Users/test/projects/github.com/hoge/fuga")
);
// current directory
assert_eq!(
resolve_path(&base_dir, "./github.com/hoge/fuga"),
PathBuf::from("/Users/test/projects/github.com/hoge/fuga")
);
// parent directory
assert_eq!(
resolve_path(&base_dir, "../other/project"),
PathBuf::from("/Users/test/other/project")
);
// multiple ..
assert_eq!(
resolve_path(&base_dir, "foo/bar/../../../baz"),
PathBuf::from("/Users/test/baz")
);
// absolute path
assert_eq!(
resolve_path(&base_dir, "/absolute/path"),
PathBuf::from("/absolute/path")
);
// empty relative path
assert_eq!(
resolve_path(&base_dir, ""),
PathBuf::from("/Users/test/projects")
);
// ending /
assert_eq!(
resolve_path(&base_dir, "github.com/hoge/fuga/"),
PathBuf::from("/Users/test/projects/github.com/hoge/fuga")
);
// complex path
assert_eq!(
resolve_path(&base_dir, "./foo/../bar/./baz/../qux/"),
PathBuf::from("/Users/test/projects/bar/qux")
);
}
}