From 826d62dfbdd5ef15ff89421b8e5eed8fca31a453 Mon Sep 17 00:00:00 2001 From: LSTM-Kirigaya <1193466151@qq.com> Date: Tue, 17 Dec 2024 18:20:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20server=20=E7=9A=84=20execu?= =?UTF-8?q?te=5Fcommand=20=E7=AE=A1=E7=BA=BF=EF=BC=8C=E5=B9=B6=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=89=8D=E7=AB=AF=E4=B8=BB=E5=8A=A8=E5=8F=91=E8=B5=B7?= =?UTF-8?q?=20lint=20=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/execute_command/diagnostics.rs | 45 ++++++++++++++++++++++++++++++ src/execute_command/mod.rs | 25 +++++++++++++++++ src/lib.rs | 3 ++ src/main.rs | 5 ++-- src/request/linter.rs | 41 +-------------------------- src/server.rs | 4 +++ src/sources.rs | 14 ++++++---- 7 files changed, 88 insertions(+), 49 deletions(-) create mode 100644 src/execute_command/diagnostics.rs create mode 100644 src/execute_command/mod.rs diff --git a/src/execute_command/diagnostics.rs b/src/execute_command/diagnostics.rs new file mode 100644 index 0000000..5cfb137 --- /dev/null +++ b/src/execute_command/diagnostics.rs @@ -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 +) -> tower_lsp::jsonrpc::Result> { + 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 +) -> tower_lsp::jsonrpc::Result> { + let path_string = arguments.get(0).unwrap().to_string(); + let uri = Url::from_file_path(path_string).unwrap(); + + let diagnostics = Vec::::new(); + backend.client.publish_diagnostics(uri, diagnostics, None).await; + + Ok(None) +} \ No newline at end of file diff --git a/src/execute_command/mod.rs b/src/execute_command/mod.rs new file mode 100644 index 0000000..bf5628d --- /dev/null +++ b/src/execute_command/mod.rs @@ -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> { + 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) + } + } +} + diff --git a/src/lib.rs b/src/lib.rs index fd2ac23..95be780 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,5 +43,8 @@ pub mod sources; // 自定义发送请求 pub mod request; +// 自定义异步命令 +pub mod execute_command; + // 测试模块 pub mod test; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6160bf1..dc9ae0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,7 @@ use request::{ fast::SyncFastApi, config::UpdateConfigurationApi, primitives::DoPrimitivesJudgeApi, - linter::LinterStatusApi, - linter::DoLintApi + linter::LinterStatusApi }; use log::info; @@ -30,6 +29,7 @@ mod format; mod server; mod sources; mod request; +mod execute_command; use server::Backend; @@ -60,7 +60,6 @@ async fn main() { .custom_method("api/update-configuration", UpdateConfigurationApi) .custom_method("api/sync-fast", SyncFastApi) .custom_method("api/linter-status", LinterStatusApi) - .custom_method("api/do-lint", DoLintApi) .finish(); Server::new(stdin, stdout, socket) diff --git a/src/request/linter.rs b/src/request/linter.rs index 7fb84b0..b3fd003 100644 --- a/src/request/linter.rs +++ b/src/request/linter.rs @@ -4,11 +4,9 @@ use std::future; use log::info; use serde::{Deserialize, Serialize}; 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::utils::from_uri_to_escape_path_string; #[derive(Clone)] 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, (DoLintParams, ), Result>> for DoLintApi { - type Future = future::Ready>>; - - fn invoke(&self, _server: &'a Arc, _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) - } -} diff --git a/src/server.rs b/src/server.rs index 9d0c5c8..19782f9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -230,6 +230,10 @@ impl LanguageServer for Backend { .await; } + async fn execute_command(&self, params: ExecuteCommandParams) -> Result> { + crate::execute_command::execute_command(self, params).await + } + async fn shutdown(&self) -> Result<()> { Ok(()) } diff --git a/src/sources.rs b/src/sources.rs index 3a319c0..b031fdb 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -321,13 +321,10 @@ impl Sources { // 对于当前的文件增加一个解析线程,不断进行解析和同步 #[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_parse2 = valid_parse.clone(); + let valid_parse_handle = valid_parse.clone(); 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 { uri: doc.uri.clone(), @@ -362,7 +359,9 @@ impl Sources { // 创建一个解析线程 let parse_handle = thread::spawn(move || { - let (lock, cvar) = &*valid_parse2; + // TODO: 检查这里 + let (ref lock, ref cvar) = *valid_parse_handle; + loop { 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(); *valid = true; + // 唤醒所有正在等待条件变量的线程,即所有被下方 cvar.wait 阻塞的线程将被激活 cvar.notify_all(); while *valid { 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 (lock, cvar) = &*source_status_handle.read().unwrap().valid_parse; let mut valid = lock.lock().unwrap(); + // 解析线程一轮解析结束后, valid 会变为 true,结束循环 while !*valid { valid = cvar.wait(valid).unwrap(); }