mirror of
https://github.com/kbwo/testing-language-server.git
synced 2025-08-03 15:18:16 +00:00
Merge pull request #43 from kbwo/fix/data-structure
Change adapterCommand schema
This commit is contained in:
commit
7da5b98850
6 changed files with 237 additions and 221 deletions
|
@ -2,15 +2,13 @@
|
|||
"testing.enable": true,
|
||||
"testing.fileTypes": ["rust"],
|
||||
"testing.adapterCommand": {
|
||||
"rust": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test", "--workspace"],
|
||||
"include": ["/**/*.rs"],
|
||||
"exclude": ["/demo/**/*"],
|
||||
"workspace_dir": "."
|
||||
}
|
||||
]
|
||||
"rust": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test", "--workspace"],
|
||||
"include": ["/**/*.rs"],
|
||||
"exclude": ["/demo/**/*"],
|
||||
"workspace_dir": "."
|
||||
}
|
||||
},
|
||||
"testing.enableWorkspaceDiagnostics": true,
|
||||
"testing.server.path": "testing-language-server",
|
||||
|
|
111
README.md
111
README.md
|
@ -26,51 +26,98 @@ cargo install testing-ls-adapter
|
|||
|
||||
- [x] Realtime testing diagnostics
|
||||
- [x] [VSCode extension](https://github.com/kbwo/vscode-testing-ls)
|
||||
- [x] [Coc.nvim extension](https://github.com/kbwo/coc-testing-ls)
|
||||
- [x] [coc.nvim extension](https://github.com/kbwo/coc-testing-ls)
|
||||
- [x] For Neovim builtin LSP, see [demo/README.md](./demo/README.md)
|
||||
- [ ] More efficient checking of diagnostics
|
||||
- [ ] Useful commands in each extension
|
||||
|
||||
## Configuration
|
||||
|
||||
language server config:
|
||||
### VSCode
|
||||
|
||||
```
|
||||
"languageserver": {
|
||||
"testing": {
|
||||
"command": "<server path>/testing-language-server",
|
||||
"trace.server": "verbose",
|
||||
"filetypes": [
|
||||
"rust",
|
||||
"javascript"
|
||||
],
|
||||
"initializationOptions": {
|
||||
"initializationOptions": {
|
||||
"adapterCommand": {
|
||||
"cargo test": [
|
||||
{
|
||||
"path": "<adapter path>/testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test"],
|
||||
"include": ["**/*.rs"],
|
||||
"exclude": ["**/target/**"]
|
||||
}
|
||||
],
|
||||
"jest": [
|
||||
{
|
||||
"path": "<adapter path>/testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=jest"],
|
||||
"include": ["/**/*.js"],
|
||||
"exclude": ["/node_modules/**/*"]
|
||||
}
|
||||
]
|
||||
Install from [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=kbwo.testing-language-server).
|
||||
You should set `adapterCommand` in `initializationOptions` for each project.
|
||||
You can see the example in [settings.json](./demo/.vscode/settings.json).
|
||||
|
||||
|
||||
### coc.nvim
|
||||
Install from `:CocInstall coc-testing-ls`.
|
||||
You should set `adapterCommand` in `initializationOptions` for each project.
|
||||
You can see the example in [See more example](./demo/.vim/coc-settings.json)
|
||||
|
||||
### Neovim (nvim-lspconfig)
|
||||
|
||||
```lua
|
||||
local lspconfig = require('lspconfig')
|
||||
local configs = require('lspconfig.configs')
|
||||
local util = require "lspconfig/util"
|
||||
|
||||
configs.testing_ls = {
|
||||
default_config = {
|
||||
cmd = { "testing-language-server" },
|
||||
filetypes = { "rust" },
|
||||
root_dir = util.root_pattern(".git", "Cargo.toml"),
|
||||
init_options = {
|
||||
enable = true,
|
||||
fileTypes = {"rust"},
|
||||
adapterCommand = {
|
||||
-- See test execution settings for each project
|
||||
-- This configuration assumes a Rust project
|
||||
rust = {
|
||||
path = "testing-ls-adapter",
|
||||
extra_arg = { "--test-kind=cargo-test", "--workspace" },
|
||||
include = { "/demo/**/src/**/*.rs"},
|
||||
exclude = { "/**/target/**"},
|
||||
}
|
||||
},
|
||||
enableWorkspaceDiagnostics = true,
|
||||
trace = {
|
||||
server = "verbose"
|
||||
}
|
||||
}
|
||||
},
|
||||
docs = {
|
||||
description = [[
|
||||
https://github.com/kbwo/testing-language-server
|
||||
|
||||
Language Server for real-time testing.
|
||||
]],
|
||||
},
|
||||
}
|
||||
|
||||
lspconfig.testing_ls.setup{}
|
||||
```
|
||||
|
||||
|
||||
## ⚠️ Breaking Changes (2024-10-25)
|
||||
|
||||
The configuration structure for adapter commands has been changed:
|
||||
|
||||
**Before:**
|
||||
```json
|
||||
"adapterCommand": {
|
||||
"rust": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test"]
|
||||
// ...
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```json
|
||||
"adapterCommand": {
|
||||
"rust": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test"]
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[See more example](./demo/.vim/coc-settings.json)
|
||||
The array wrapper has been removed to simplify the configuration structure. Please update your settings accordingly.
|
||||
|
||||
## Adapter
|
||||
- [x] `cargo test`
|
||||
|
@ -85,4 +132,4 @@ language server config:
|
|||
### Writing custom adapter
|
||||
⚠ The specification of adapter CLI is not stabilized yet.
|
||||
|
||||
See [ADAPTER_SPEC.md](./doc/ADAPTER_SPEC.md) and [spec.rs](./src/spec.rs).
|
||||
See [ADAPTER_SPEC.md](./doc/ADAPTER_SPEC.md) and [spec.rs](./src/spec.rs).
|
3
demo/.helix/config.toml
Normal file
3
demo/.helix/config.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[editor.soft-wrap]
|
||||
enable = true
|
||||
max-wrap = 25 # increase value to reduce forced mid-word wrapping
|
|
@ -6,70 +6,54 @@
|
|||
"filetypes": ["rust", "javascript", "go", "typescript", "php"],
|
||||
"initializationOptions": {
|
||||
"adapterCommand": {
|
||||
"cargo-test": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test"],
|
||||
"include": ["/**/src/**/*.rs"],
|
||||
"exclude": ["/**/target/**"]
|
||||
}
|
||||
],
|
||||
"cargo-nextest": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-nextest"],
|
||||
"include": ["/**/src/**/*.rs"],
|
||||
"exclude": ["/**/target/**"]
|
||||
}
|
||||
],
|
||||
"jest": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=jest"],
|
||||
"include": ["/jest/*.js"],
|
||||
"exclude": ["/jest/**/node_modules/**/*"]
|
||||
}
|
||||
],
|
||||
"vitest": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=vitest"],
|
||||
"include": ["/vitest/*.test.ts", "/vitest/config/**/*.test.ts"],
|
||||
"exclude": ["/vitest/**/node_modules/**/*"]
|
||||
}
|
||||
],
|
||||
"deno": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=deno"],
|
||||
"include": ["/deno/*.ts"],
|
||||
"exclude": []
|
||||
}
|
||||
],
|
||||
"go": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=go-test"],
|
||||
"include": ["/**/*.go"],
|
||||
"exclude": []
|
||||
}
|
||||
],
|
||||
"node-test": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=node-test"],
|
||||
"include": ["/node-test/*.test.js"],
|
||||
"exclude": []
|
||||
}
|
||||
],
|
||||
"phpunit": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=phpunit"],
|
||||
"include": ["/**/*Test.php"],
|
||||
"exclude": ["/phpunit/vendor/**/*.php"]
|
||||
}
|
||||
]
|
||||
"cargo-test": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-test"],
|
||||
"include": ["/**/src/**/*.rs"],
|
||||
"exclude": ["/**/target/**"]
|
||||
},
|
||||
"cargo-nextest": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-nextest"],
|
||||
"include": ["/**/src/**/*.rs"],
|
||||
"exclude": ["/**/target/**"]
|
||||
},
|
||||
"jest": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=jest"],
|
||||
"include": ["/jest/*.js"],
|
||||
"exclude": ["/jest/**/node_modules/**/*"]
|
||||
},
|
||||
"vitest": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=vitest"],
|
||||
"include": ["/vitest/*.test.ts", "/vitest/config/**/*.test.ts"],
|
||||
"exclude": ["/vitest/**/node_modules/**/*"]
|
||||
},
|
||||
"deno": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=deno"],
|
||||
"include": ["/deno/*.ts"],
|
||||
"exclude": []
|
||||
},
|
||||
"go": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=go-test"],
|
||||
"include": ["/**/*.go"],
|
||||
"exclude": []
|
||||
},
|
||||
"node-test": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=node-test"],
|
||||
"include": ["/node-test/*.test.js"],
|
||||
"exclude": []
|
||||
},
|
||||
"phpunit": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=phpunit"],
|
||||
"include": ["/**/*Test.php"],
|
||||
"exclude": ["/phpunit/vendor/**/*.php"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
98
demo/.vscode/settings.json
vendored
98
demo/.vscode/settings.json
vendored
|
@ -2,62 +2,48 @@
|
|||
"testing.enable": true,
|
||||
"filetypes": ["rust", "javascript", "go", "typescript", "php"],
|
||||
"testing.adapterCommand": {
|
||||
"cargo-nextest": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-nextest"],
|
||||
"include": ["/**/src/**/*.rs"],
|
||||
"exclude": ["/**/target/**"]
|
||||
}
|
||||
],
|
||||
"jest": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=jest"],
|
||||
"include": ["/jest/*.js"],
|
||||
"exclude": ["/jest/**/node_modules/**/*"]
|
||||
}
|
||||
],
|
||||
"vitest": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=vitest"],
|
||||
"include": ["/vitest/*.test.ts", "/vitest/config/**/*.test.ts"],
|
||||
"exclude": ["/vitest/**/node_modules/**/*"]
|
||||
}
|
||||
],
|
||||
"deno": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=deno"],
|
||||
"include": ["/deno/*.ts"],
|
||||
"exclude": []
|
||||
}
|
||||
],
|
||||
"go": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=go-test"],
|
||||
"include": ["/**/*.go"],
|
||||
"exclude": []
|
||||
}
|
||||
],
|
||||
"node-test": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=node-test"],
|
||||
"include": ["/node-test/*.test.js"],
|
||||
"exclude": []
|
||||
}
|
||||
],
|
||||
"phpunit": [
|
||||
{
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=phpunit"],
|
||||
"include": ["/**/*Test.php"],
|
||||
"exclude": ["/phpunit/vendor/**/*.php"]
|
||||
}
|
||||
]
|
||||
"cargo-nextest": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=cargo-nextest"],
|
||||
"include": ["/**/src/**/*.rs"],
|
||||
"exclude": ["/**/target/**"]
|
||||
},
|
||||
"jest": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=jest"],
|
||||
"include": ["/jest/*.js"],
|
||||
"exclude": ["/jest/**/node_modules/**/*"]
|
||||
},
|
||||
"vitest": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=vitest"],
|
||||
"include": ["/vitest/*.test.ts", "/vitest/config/**/*.test.ts"],
|
||||
"exclude": ["/vitest/**/node_modules/**/*"]
|
||||
},
|
||||
"deno": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=deno"],
|
||||
"include": ["/deno/*.ts"],
|
||||
"exclude": []
|
||||
},
|
||||
"go": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=go-test"],
|
||||
"include": ["/**/*.go"],
|
||||
"exclude": []
|
||||
},
|
||||
"node-test": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=node-test"],
|
||||
"include": ["/node-test/*.test.js"],
|
||||
"exclude": []
|
||||
},
|
||||
"phpunit": {
|
||||
"path": "testing-ls-adapter",
|
||||
"extra_arg": ["--test-kind=phpunit"],
|
||||
"include": ["/**/*Test.php"],
|
||||
"exclude": ["/phpunit/vendor/**/*.php"]
|
||||
}
|
||||
},
|
||||
"testing.enableWorkspaceDiagnostics": true,
|
||||
"testing.server.path": "testing-language-server",
|
||||
|
|
118
src/server.rs
118
src/server.rs
|
@ -17,7 +17,7 @@ use std::process::Output;
|
|||
#[derive(Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InitializedOptions {
|
||||
adapter_command: HashMap<AdapterId, Vec<AdapterConfiguration>>,
|
||||
adapter_command: HashMap<AdapterId, AdapterConfiguration>,
|
||||
enable_workspace_diagnostics: Option<bool>,
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ impl TestingLS {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn adapter_commands(&self) -> HashMap<AdapterId, Vec<AdapterConfiguration>> {
|
||||
fn adapter_commands(&self) -> HashMap<AdapterId, AdapterConfiguration> {
|
||||
self.options.adapter_command.clone()
|
||||
}
|
||||
|
||||
|
@ -150,62 +150,60 @@ impl TestingLS {
|
|||
let project_dir = self.project_dir()?;
|
||||
self.workspaces_cache = vec![];
|
||||
// Nested and multiple loops, but each count is small
|
||||
for adapter_commands in adapter_commands.into_values() {
|
||||
for adapter in adapter_commands.into_iter() {
|
||||
let AdapterConfiguration {
|
||||
path,
|
||||
extra_arg,
|
||||
env,
|
||||
include,
|
||||
exclude,
|
||||
workspace_dir,
|
||||
..
|
||||
} = &adapter;
|
||||
let file_paths = Self::project_files(&project_dir, include, exclude);
|
||||
if file_paths.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut adapter_command = Command::new(path);
|
||||
let mut args_file_path: Vec<&str> = vec![];
|
||||
file_paths.iter().for_each(|file_path| {
|
||||
args_file_path.push("--file-paths");
|
||||
args_file_path.push(file_path);
|
||||
});
|
||||
let output = adapter_command
|
||||
.arg("detect-workspace")
|
||||
.args(args_file_path)
|
||||
.arg("--")
|
||||
.args(extra_arg)
|
||||
.envs(env)
|
||||
.output()
|
||||
.map_err(|err| LSError::Adapter(err.to_string()))?;
|
||||
let adapter_result = String::from_utf8(output.stdout)
|
||||
.map_err(|err| LSError::Adapter(err.to_string()))?;
|
||||
let workspace: DetectWorkspaceResult = match serde_json::from_str(&adapter_result) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
let stderr = String::from_utf8(output.stderr);
|
||||
tracing::error!("Failed to parse adapter result: {:?}", err);
|
||||
tracing::error!("Error: {:?}", stderr);
|
||||
return Err(LSError::Adapter(err.to_string()));
|
||||
}
|
||||
};
|
||||
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, target_paths)])
|
||||
} else {
|
||||
workspace
|
||||
};
|
||||
self.workspaces_cache
|
||||
.push(WorkspaceAnalysis::new(adapter, workspace))
|
||||
for adapter in adapter_commands.into_values() {
|
||||
let AdapterConfiguration {
|
||||
path,
|
||||
extra_arg,
|
||||
env,
|
||||
include,
|
||||
exclude,
|
||||
workspace_dir,
|
||||
..
|
||||
} = &adapter;
|
||||
let file_paths = Self::project_files(&project_dir, include, exclude);
|
||||
if file_paths.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut adapter_command = Command::new(path);
|
||||
let mut args_file_path: Vec<&str> = vec![];
|
||||
file_paths.iter().for_each(|file_path| {
|
||||
args_file_path.push("--file-paths");
|
||||
args_file_path.push(file_path);
|
||||
});
|
||||
let output = adapter_command
|
||||
.arg("detect-workspace")
|
||||
.args(args_file_path)
|
||||
.arg("--")
|
||||
.args(extra_arg)
|
||||
.envs(env)
|
||||
.output()
|
||||
.map_err(|err| LSError::Adapter(err.to_string()))?;
|
||||
let adapter_result = String::from_utf8(output.stdout)
|
||||
.map_err(|err| LSError::Adapter(err.to_string()))?;
|
||||
let workspace: DetectWorkspaceResult = match serde_json::from_str(&adapter_result) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
let stderr = String::from_utf8(output.stderr);
|
||||
tracing::error!("Failed to parse adapter result: {:?}", err);
|
||||
tracing::error!("Error: {:?}", stderr);
|
||||
return Err(LSError::Adapter(err.to_string()));
|
||||
}
|
||||
};
|
||||
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, target_paths)])
|
||||
} else {
|
||||
workspace
|
||||
};
|
||||
self.workspaces_cache
|
||||
.push(WorkspaceAnalysis::new(adapter, workspace))
|
||||
}
|
||||
tracing::info!("workspaces_cache={:#?}", self.workspaces_cache);
|
||||
send_stdout(&json!({
|
||||
|
@ -497,7 +495,7 @@ mod tests {
|
|||
name: "demo".to_string(),
|
||||
}]),
|
||||
options: InitializedOptions {
|
||||
adapter_command: HashMap::from([(String::from(".rs"), vec![])]),
|
||||
adapter_command: HashMap::new(),
|
||||
enable_workspace_diagnostics: Some(true),
|
||||
},
|
||||
workspaces_cache: Vec::new(),
|
||||
|
@ -527,7 +525,7 @@ mod tests {
|
|||
name: "demo".to_string(),
|
||||
}]),
|
||||
options: InitializedOptions {
|
||||
adapter_command: HashMap::from([(String::from(".rs"), vec![adapter_conf])]),
|
||||
adapter_command: HashMap::from([(String::from(".rs"), adapter_conf)]),
|
||||
enable_workspace_diagnostics: Some(true),
|
||||
},
|
||||
workspaces_cache: Vec::new(),
|
||||
|
@ -592,7 +590,7 @@ mod tests {
|
|||
name: "demo".to_string(),
|
||||
}]),
|
||||
options: InitializedOptions {
|
||||
adapter_command: HashMap::from([(String::from(".rs"), vec![adapter_conf.clone()])]),
|
||||
adapter_command: HashMap::from([(String::from(".rs"), adapter_conf.clone())]),
|
||||
enable_workspace_diagnostics: Some(true),
|
||||
},
|
||||
workspaces_cache: Vec::new(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue