实现 server 的 execute_command 管线,并实现前端主动发起 lint 请求
This commit is contained in:
parent
574c50325e
commit
826d62dfbd
45
src/execute_command/diagnostics.rs
Normal file
45
src/execute_command/diagnostics.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use std::{path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
use tower_lsp::lsp_types::{Diagnostic, Url};
|
||||||
|
|
||||||
|
use crate::{diagnostics::provide_diagnostics, server::Backend, utils::{from_uri_to_escape_path_string, open_doc_as_rope}};
|
||||||
|
|
||||||
|
/// 前端请求,发布诊断结果,仅在初始化和修改配置时触发
|
||||||
|
/// 参数为 [file_path: string]
|
||||||
|
pub async fn publish_diagnostics(
|
||||||
|
backend: &Backend,
|
||||||
|
arguments: Vec<Value>
|
||||||
|
) -> tower_lsp::jsonrpc::Result<Option<Value>> {
|
||||||
|
let path_string = arguments.get(0).unwrap().to_string();
|
||||||
|
let uri = Url::from_file_path(path_string).unwrap();
|
||||||
|
let path_string = from_uri_to_escape_path_string(&uri).unwrap();
|
||||||
|
let pathbuf = PathBuf::from_str(&path_string).unwrap();
|
||||||
|
|
||||||
|
// 考虑到性能,如果后端文本缓冲器内存在当前路径的 文本备份,则使用它作为 rope
|
||||||
|
// 否则,进行 IO 后再转换
|
||||||
|
let rope = open_doc_as_rope(&pathbuf).unwrap();
|
||||||
|
let diagnostics_params = provide_diagnostics(uri, &rope, &backend.server);
|
||||||
|
backend.client.publish_diagnostics(
|
||||||
|
diagnostics_params.uri,
|
||||||
|
diagnostics_params.diagnostics,
|
||||||
|
None
|
||||||
|
).await;
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 前端请求,清除诊断结果,仅在初始化和修改配置时触发
|
||||||
|
/// 参数为 [file_path: string]
|
||||||
|
pub async fn clear_diagnostics(
|
||||||
|
backend: &Backend,
|
||||||
|
arguments: Vec<Value>
|
||||||
|
) -> tower_lsp::jsonrpc::Result<Option<Value>> {
|
||||||
|
let path_string = arguments.get(0).unwrap().to_string();
|
||||||
|
let uri = Url::from_file_path(path_string).unwrap();
|
||||||
|
|
||||||
|
let diagnostics = Vec::<Diagnostic>::new();
|
||||||
|
backend.client.publish_diagnostics(uri, diagnostics, None).await;
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
25
src/execute_command/mod.rs
Normal file
25
src/execute_command/mod.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
use tower_lsp::lsp_types::*;
|
||||||
|
|
||||||
|
use crate::server::Backend;
|
||||||
|
mod diagnostics;
|
||||||
|
|
||||||
|
pub async fn execute_command(
|
||||||
|
backend: &Backend,
|
||||||
|
params: ExecuteCommandParams
|
||||||
|
) -> tower_lsp::jsonrpc::Result<Option<Value>> {
|
||||||
|
match params.command.as_str() {
|
||||||
|
"publish-diagnostics" => {
|
||||||
|
diagnostics::publish_diagnostics(backend, params.arguments).await
|
||||||
|
}
|
||||||
|
|
||||||
|
"clear-diagnostics" => {
|
||||||
|
diagnostics::clear_diagnostics(backend, params.arguments).await
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,5 +43,8 @@ pub mod sources;
|
|||||||
// 自定义发送请求
|
// 自定义发送请求
|
||||||
pub mod request;
|
pub mod request;
|
||||||
|
|
||||||
|
// 自定义异步命令
|
||||||
|
pub mod execute_command;
|
||||||
|
|
||||||
// 测试模块
|
// 测试模块
|
||||||
pub mod test;
|
pub mod test;
|
@ -7,8 +7,7 @@ use request::{
|
|||||||
fast::SyncFastApi,
|
fast::SyncFastApi,
|
||||||
config::UpdateConfigurationApi,
|
config::UpdateConfigurationApi,
|
||||||
primitives::DoPrimitivesJudgeApi,
|
primitives::DoPrimitivesJudgeApi,
|
||||||
linter::LinterStatusApi,
|
linter::LinterStatusApi
|
||||||
linter::DoLintApi
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
@ -30,6 +29,7 @@ mod format;
|
|||||||
mod server;
|
mod server;
|
||||||
mod sources;
|
mod sources;
|
||||||
mod request;
|
mod request;
|
||||||
|
mod execute_command;
|
||||||
|
|
||||||
use server::Backend;
|
use server::Backend;
|
||||||
|
|
||||||
@ -60,7 +60,6 @@ async fn main() {
|
|||||||
.custom_method("api/update-configuration", UpdateConfigurationApi)
|
.custom_method("api/update-configuration", UpdateConfigurationApi)
|
||||||
.custom_method("api/sync-fast", SyncFastApi)
|
.custom_method("api/sync-fast", SyncFastApi)
|
||||||
.custom_method("api/linter-status", LinterStatusApi)
|
.custom_method("api/linter-status", LinterStatusApi)
|
||||||
.custom_method("api/do-lint", DoLintApi)
|
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
Server::new(stdin, stdout, socket)
|
Server::new(stdin, stdout, socket)
|
||||||
|
@ -4,11 +4,9 @@ use std::future;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tower_lsp::jsonrpc::Result;
|
use tower_lsp::jsonrpc::Result;
|
||||||
use tower_lsp::lsp_types::{Diagnostic, Url};
|
|
||||||
|
|
||||||
use crate::diagnostics::{provide_diagnostics, AbstractLinterConfiguration, LinterStatus};
|
use crate::diagnostics::{AbstractLinterConfiguration, LinterStatus};
|
||||||
use crate::server::Backend;
|
use crate::server::Backend;
|
||||||
use crate::utils::from_uri_to_escape_path_string;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LinterStatusApi;
|
pub struct LinterStatusApi;
|
||||||
@ -95,40 +93,3 @@ fn get_linter_status(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct DoLintApi;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct DoLintParams {
|
|
||||||
path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (DoLintParams, ), Result<Vec<Diagnostic>>> for DoLintApi {
|
|
||||||
type Future = future::Ready<Result<Vec<Diagnostic>>>;
|
|
||||||
|
|
||||||
fn invoke(&self, _server: &'a Arc<Backend>, _params: (DoLintParams, )) -> Self::Future {
|
|
||||||
let path = _params.0.path;
|
|
||||||
let uri = Url::from_file_path(&path).unwrap();
|
|
||||||
let path_string = from_uri_to_escape_path_string(&uri).unwrap();
|
|
||||||
let server = &_server.server;
|
|
||||||
|
|
||||||
let result = if let Some(source) = server.srcs.get_source(&path_string) {
|
|
||||||
let source = source.read().unwrap();
|
|
||||||
let diags_param = provide_diagnostics(uri, &source.text, server);
|
|
||||||
Ok(diags_param.diagnostics)
|
|
||||||
} else {
|
|
||||||
Err(tower_lsp::jsonrpc::Error {
|
|
||||||
code: tower_lsp::jsonrpc::ErrorCode::InvalidRequest,
|
|
||||||
message: Cow::Owned(format!("文件尚未初始化 {}", path)),
|
|
||||||
data: None
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
future::ready(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -230,6 +230,10 @@ impl LanguageServer for Backend {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn execute_command(&self, params: ExecuteCommandParams) -> Result<Option<serde_json::Value>> {
|
||||||
|
crate::execute_command::execute_command(self, params).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn shutdown(&self) -> Result<()> {
|
async fn shutdown(&self) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -321,13 +321,10 @@ impl Sources {
|
|||||||
// 对于当前的文件增加一个解析线程,不断进行解析和同步
|
// 对于当前的文件增加一个解析线程,不断进行解析和同步
|
||||||
#[allow(clippy::mutex_atomic)] // https://github.com/rust-lang/rust-clippy/issues/1516
|
#[allow(clippy::mutex_atomic)] // https://github.com/rust-lang/rust-clippy/issues/1516
|
||||||
let valid_parse = Arc::new((Mutex::new(false), Condvar::new()));
|
let valid_parse = Arc::new((Mutex::new(false), Condvar::new()));
|
||||||
let valid_parse2 = valid_parse.clone();
|
let valid_parse_handle = valid_parse.clone();
|
||||||
|
|
||||||
let mut sources = self.sources.write().unwrap();
|
let mut sources = self.sources.write().unwrap();
|
||||||
|
|
||||||
// uri 转换为标准文件路径
|
|
||||||
let path_string = from_uri_to_escape_path_string(&doc.uri).unwrap();
|
|
||||||
|
|
||||||
// 为新加入的文件构建后端文本缓冲器副本
|
// 为新加入的文件构建后端文本缓冲器副本
|
||||||
let source_handle = Arc::new(RwLock::new(Source {
|
let source_handle = Arc::new(RwLock::new(Source {
|
||||||
uri: doc.uri.clone(),
|
uri: doc.uri.clone(),
|
||||||
@ -362,7 +359,9 @@ impl Sources {
|
|||||||
|
|
||||||
// 创建一个解析线程
|
// 创建一个解析线程
|
||||||
let parse_handle = thread::spawn(move || {
|
let parse_handle = thread::spawn(move || {
|
||||||
let (lock, cvar) = &*valid_parse2;
|
// TODO: 检查这里
|
||||||
|
let (ref lock, ref cvar) = *valid_parse_handle;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
info!("do parse in {:?}, language_id: {:?}", uri.to_string(), language_id);
|
info!("do parse in {:?}, language_id: {:?}", uri.to_string(), language_id);
|
||||||
|
|
||||||
@ -404,9 +403,11 @@ impl Sources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过条件锁进行控制
|
// 下方基于消费者-生产者模型进行控制
|
||||||
|
// lock 会尝试去获取互斥锁,如果当前持有互斥锁的线程发生了 panic,则当前 unwrap 操作会失败
|
||||||
let mut valid = lock.lock().unwrap();
|
let mut valid = lock.lock().unwrap();
|
||||||
*valid = true;
|
*valid = true;
|
||||||
|
// 唤醒所有正在等待条件变量的线程,即所有被下方 cvar.wait 阻塞的线程将被激活
|
||||||
cvar.notify_all();
|
cvar.notify_all();
|
||||||
while *valid {
|
while *valid {
|
||||||
valid = cvar.wait(valid).unwrap();
|
valid = cvar.wait(valid).unwrap();
|
||||||
@ -455,6 +456,7 @@ impl Sources {
|
|||||||
let source_status_handle = self.get_source_status(path_string).unwrap(); // status 和 source 是同步创建的,要有一起有
|
let source_status_handle = self.get_source_status(path_string).unwrap(); // status 和 source 是同步创建的,要有一起有
|
||||||
let (lock, cvar) = &*source_status_handle.read().unwrap().valid_parse;
|
let (lock, cvar) = &*source_status_handle.read().unwrap().valid_parse;
|
||||||
let mut valid = lock.lock().unwrap();
|
let mut valid = lock.lock().unwrap();
|
||||||
|
// 解析线程一轮解析结束后, valid 会变为 true,结束循环
|
||||||
while !*valid {
|
while !*valid {
|
||||||
valid = cvar.wait(valid).unwrap();
|
valid = cvar.wait(valid).unwrap();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user