Rust语言服务器协议库codespan-lsp的使用,实现高效源代码分析与错误诊断
Rust语言服务器协议库codespan-lsp的使用,实现高效源代码分析与错误诊断
安装
在项目目录中运行以下Cargo命令:
cargo add codespan-lsp
或者在Cargo.toml中添加以下行:
codespan-lsp = "0.11.1"
基本使用示例
以下是使用codespan-lsp进行源代码分析和错误诊断的基本示例:
use codespan_lsp::{Diagnostic, DiagnosticSeverity, Position, Range};
use codespan::{FileId, Files};
use codespan_reporting::diagnostic::{Label, LabelStyle};
fn main() {
// 创建源代码文件容器
let mut files = Files::new();
let file_id = files.add("example.rs", r#"
fn main() {
let x = 42;
println!("{}", y);
}
"#);
// 创建诊断信息
let diagnostic = Diagnostic {
range: Range {
start: Position {
line: 3,
character: 20,
},
end: Position {
line: 3,
character: 21,
},
},
severity: Some(DiagnosticSeverity::Error),
code: Some("E0425".into()),
code_description: None,
source: Some("rustc".into()),
message: "cannot find value `y` in this scope".into(),
related_information: None,
tags: None,
data: None,
};
// 生成详细的错误标签
let label = Label {
style: LabelStyle::Primary,
file_id,
range: 70..71, // y的位置
message: "not found in this scope".into(),
};
// 这里可以添加将诊断信息发送到LSP客户端的逻辑
println!("Generated diagnostic: {:?}", diagnostic);
println!("Generated label: {:?}", label);
}
完整示例
以下是一个更完整的示例,展示了如何设置LSP服务器并使用codespan-lsp进行源代码分析:
use codespan_lsp::{Server, Diagnostic, DiagnosticSeverity, Position, Range};
use codespan::{FileId, Files};
use lsp_types::{
InitializeParams, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
};
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// 创建LSP服务器
let (service, messages) = Server::new(|client| async move {
// 服务器能力配置
let capabilities = serde_json::to_value(ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind(
TextDocumentSyncKind::FULL,
)),
..Default::default()
})
.unwrap();
// 处理初始化请求
client
.on_request::<lsp_types::request::Initialize, _>(move |params: InitializeParams| {
Ok(serde_json::json!({
"capabilities": capabilities,
"serverInfo": {
"name": "codespan-lsp-example",
"version": "0.1.0"
}
}))
});
// 处理文本文档诊断
client.on_notification::<lsp_types::notification::DidOpenTextDocument>(|params| {
let uri = params.text_document.uri;
let text = params.text_document.text;
// 创建源代码文件容器
let mut files = Files::new();
let file_id = files.add(uri.path(), &text);
// 这里可以添加实际的代码分析逻辑
// 示例: 创建一个假想的诊断信息
let diagnostic = Diagnostic {
range: Range {
start: Position { line: 0, character: 0 },
end: Position { line: 0, character: 5 },
},
severity: Some(DiagnosticSeverity::Warning),
code: Some("EXAMPLE".into()),
message: "This is an example diagnostic".into(),
..Default::default()
};
// 在实际应用中,这里会发送诊断信息给客户端
println!("Generated diagnostic for {}: {:?}", uri, diagnostic);
Ok(())
});
});
// 运行服务器
Server::run(service, messages).await?;
Ok(())
}
关键功能
- 源代码定位:codespan-lsp提供了精确的源代码位置跟踪能力
- 诊断信息生成:可以生成符合LSP标准的诊断信息
- 错误报告:支持创建详细的错误标签和消息
- 与codespan集成:无缝对接codespan的源代码管理功能
1 回复
我将根据您提供的内容,整理关于codespan-lsp的完整示例demo。首先展示内容中已有的示例,然后提供一个更完整的实现。
已有示例代码回顾
基本语言服务器示例
use codespan_lsp::{
server::{LanguageServer, Server},
PositionEncoding,
};
use tower::Service;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (service, messages) = LanguageServer::new(|client| Server {
client,
position_encoding: PositionEncoding::Utf8,
});
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
codespan_lsp::stdio::run(&mut service, &messages, stdin, stdout).await?;
Ok(())
}
Rust语法检查器示例
use codespan_lsp::{
server::{LanguageServer, Server},
lsp_types::{
Url, Diagnostic, DiagnosticSeverity, Position, Range,
PublishDiagnosticsParams, DidOpenTextDocumentParams,
},
};
use tower::Service;
struct RustLinter {
documents: std::collections::HashMap<Url, String>,
}
impl Server for RustLinter {
fn on_open_text_document(
&mut self,
params: DidOpenTextDocumentParams,
) -> Option<codespan_lsp::server::Request> {
let uri = params.text_document.uri;
let text = params.text_document.text;
self.documents.insert(uri.clone(), text.clone());
let diagnostics = if !text.contains("fn main()") {
vec![Diagnostic {
range: Range {
start: Position { line: 0, character: 0 },
end: Position { line: 0, character: 0 },
},
severity: Some(DiagnosticSeverity::Warning),
message: "Rust file should contain a main function".to_string(),
..Default::default()
}]
} else {
vec![]
};
Some(codespan_lsp::server::Request::PublishDiagnostics(
PublishDiagnosticsParams {
uri,
diagnostics,
version: None,
}
))
}
}
完整示例:增强型Rust语言服务器
下面是一个更完整的Rust语言服务器实现,包含语法检查、错误诊断和简单的代码补全功能:
use codespan_lsp::{
server::{LanguageServer, Server},
lsp_types::{
Url, Diagnostic, DiagnosticSeverity, Position, Range,
PublishDiagnosticsParams, DidOpenTextDocumentParams,
DidChangeTextDocumentParams, CompletionParams, CompletionItem,
CompletionResponse, InitializeParams,
},
PositionEncoding,
};
use tower::Service;
use std::collections::HashMap;
struct EnhancedRustServer {
documents: HashMap<Url, String>,
position_encoding: PositionEncoding,
}
impl Server for EnhancedRustServer {
fn initialize(&mut self, params: InitializeParams) -> Option<codespan_lsp::server::Request> {
// 设置位置编码
self.position_encoding = params.capabilities.position_encoding
.unwrap_or(PositionEncoding::Utf8);
None
}
fn on_open_text_document(
&mut self,
params: DidOpenTextDocumentParams,
) -> Option<codespan_lsp::server::Request> {
let uri = params.text_document.uri;
let text = params.text_document.text;
self.documents.insert(uri.clone(), text.clone());
// 检查基本的Rust语法
let mut diagnostics = Vec::new();
if !text.contains("fn main()") {
diagnostics.push(Diagnostic {
range: Range {
start: Position { line: 0, character: 0 },
end: Position { line: 0, character: 0 },
},
severity: Some(DiagnosticSeverity::Warning),
message: "Rust文件应该包含main函数".to_string(),
..Default::default()
});
}
// 检查常见的语法错误
if text.contains("let mut") && !text.contains("=") {
diagnostics.push(Diagnostic {
range: Range {
start: Position { line: 0, character: 0 },
end: Position { line: 0, character: 0 },
},
severity: Some(DiagnosticSeverity::Error),
message: "可变绑定必须初始化".to_string(),
..Default::default()
});
}
Some(codespan_lsp::server::Request::PublishDiagnostics(
PublishDiagnosticsParams {
uri,
diagnostics,
version: None,
}
))
}
fn on_change_text_document(
&mut self,
params: DidChangeTextDocumentParams,
) -> Option<codespan_lsp::server::Request> {
let uri = params.text_document.uri;
if let Some(text) = params.content_changes.into_iter().next() {
self.documents.insert(uri.clone(), text.text);
}
None
}
fn on_completion(
&mut self,
_params: CompletionParams,
) -> Option<codespan_lsp::server::Request> {
// 提供简单的自动补全
let items = vec![
CompletionItem {
label: "fn".to_string(),
kind: Some(lsp_types::CompletionItemKind::KEYWORD),
detail: Some("函数定义".to_string()),
..Default::default()
},
CompletionItem {
label: "let".to_string(),
kind: Some(lsp_types::CompletionItemKind::KEYWORD),
detail: Some("变量绑定".to_string()),
..Default::default()
},
CompletionItem {
label: "println!".to_string(),
kind: Some(lsp_types::CompletionItemKind::FUNCTION),
detail: Some("打印宏".to_string()),
..Default::default()
},
];
Some(codespan_lsp::server::Request::CompletionResponse(
CompletionResponse::Array(items)
))
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 创建语言服务器实例
let (mut service, messages) = LanguageServer::new(|client| EnhancedRustServer {
documents: HashMap::new(),
position_encoding: PositionEncoding::Utf8,
});
// 运行标准IO服务
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
codespan_lsp::stdio::run(&mut service, &messages, stdin, stdout).await?;
Ok(())
}
关键功能说明
- 初始化处理:通过
initialize
方法设置位置编码 - 文档管理:使用
HashMap
跟踪打开的文档内容 - 语法检查:
- 检查是否包含
main
函数 - 验证可变绑定是否初始化
- 检查是否包含
- 变更通知:处理文档内容变更
- 代码补全:提供基本的Rust关键字和宏补全
扩展建议
- 集成codespan诊断:可以结合codespan库提供更精确的错误定位
- 添加更多分析功能:如类型检查、借用检查等
- 支持更多LSP特性:如代码格式化、重命名等
这个完整示例展示了如何使用codespan-lsp构建一个功能相对完整的Rust语言服务器,可以作为开发更复杂语言工具的基础。