实现 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 execute_command;
|
||||
|
||||
// 测试模块
|
||||
pub mod test;
|
@ -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)
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
||||
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<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user