Rust LSP文本处理库lsp-textdocument的使用,实现高效语言服务器协议文本操作与解析
Rust LSP文本处理库lsp-textdocument的使用,实现高效语言服务器协议文本操作与解析
简介
在开发LSP服务时,可能无法舒适地管理文本文档。开发困难的原因主要有两个:
- 通常只提供URL变量,需要自己读取文件内容
- 需要将偏移量从字符串索引映射到文本维度坐标
通过监听来自LSP客户端的通知,lsp-textdocument
可以帮助您自动管理文本文档。
这个crate基于vscode-languageserver-textdocument。
示例用法
基本用法
use lsp_textdocument::TextDocuments;
fn main() {
let text_documents = TextDocument::new();
...
let text = text_documents.get_document_content(&url, None);
}
完整示例
下面是一个更完整的示例,展示如何使用lsp-textdocument库来管理文档内容:
use lsp_textdocument::{TextDocuments, DocumentUri};
use lsp_types::{TextDocumentItem, TextDocumentContentChangeEvent};
fn main() {
// 创建文档管理器
let mut text_documents = TextDocuments::new();
// 文档URI
let uri = DocumentUri::from("file:///example.rs");
// 创建新文档
let doc_item = TextDocumentItem {
uri: uri.clone(),
language_id: "rust".to_string(),
version: 1,
text: "fn main() {\n println!(\"Hello, world!\");\n}".to_string(),
};
// 添加文档到管理器
text_documents.add_document(doc_item);
// 获取文档内容
if let Some(content) = text_documents.get_document_content(&uri, None) {
println!("Document content: {}", content);
}
// 更新文档内容
let change = TextDocumentContentChangeEvent {
range: None, // 全文档替换
range_length: None,
text: "fn main() {\n println!(\\"Hello, LSP!\\");\n}".to_string(),
};
text_documents.update_document(&uri, 2, vec![change]);
// 再次获取更新后的内容
if let Some(content) = text_documents.get_document_content(&uri, None) {
println!("Updated content: {}", content);
}
// 获取文档位置信息
if let Some(doc) = text_documents.get_document(&uri) {
// 将LSP位置转换为文档偏移量
let position = lsp_types::Position {
line: 1,
character: 8,
};
let offset = doc.offset_of(position).unwrap();
println!("Position offset: {}", offset);
// 将偏移量转回位置
let pos = doc.position_of(offset).unwrap();
println!("Offset position: line {}, char {}", pos.line, pos.character);
}
}
注意事项
- 文本文档的position-encoding仅支持UTF-16编码
安装
在您的项目目录中运行以下Cargo命令:
cargo add lsp-textdocument
或者在Cargo.toml中添加以下行:
lsp-textdocument = "0.4.2"
文档
更多详细信息请参考官方文档
完整示例demo
基于上述内容,这里提供一个更完整的示例,展示如何在实际项目中使用lsp-textdocument库:
use lsp_textdocument::{TextDocuments, DocumentUri};
use lsp_types::{
TextDocumentItem,
TextDocumentContentChangeEvent,
Position,
VersionedTextDocumentIdentifier
};
fn main() {
// 初始化文档管理器
let mut docs = TextDocuments::new();
// 创建文档URI
let uri = DocumentUri::from("file:///project/main.rs");
// 准备初始文档内容
let initial_content = r#"fn main() {
// 欢迎消息
println!("Welcome to LSP!");
}"#.to_string();
// 创建文档项
let document = TextDocumentItem {
uri: uri.clone(),
language_id: "rust".to_string(),
version: 1,
text: initial_content,
};
// 添加文档到管理器
docs.add_document(document);
// 示例1: 获取文档内容
if let Some(content) = docs.get_document_content(&uri, None) {
println!("Initial content:\n{}", content);
}
// 示例2: 更新文档内容
let changes = vec![
TextDocumentContentChangeEvent {
range: None, // 全文档替换
range_length: None,
text: r#"fn main() {
// 新的欢迎消息
println!("Hello from LSP!");
}"#.to_string()
}
];
// 更新文档版本号为2
docs.update_document(&uri, 2, changes);
// 示例3: 位置转换
if let Some(doc) = docs.get_document(&uri) {
// 定义位置(第2行,第4个字符)
let pos = Position { line: 1, character: 4 };
// 转换为偏移量
if let Ok(offset) = doc.offset_of(pos) {
println!("Position {:?} → offset {}", pos, offset);
// 从偏移量转回位置
let restored_pos = doc.position_of(offset).unwrap();
println!("Offset {} → position {:?}", offset, restored_pos);
}
}
// 示例4: 获取文档元数据
if let Some(doc) = docs.get_document(&uri) {
let versioned_id = VersionedTextDocumentIdentifier {
uri: uri.clone(),
version: doc.version(),
};
println!("Document version: {}", versioned_id.version);
}
}
这个完整示例展示了:
- 文档的初始化和添加
- 内容的获取和更新
- 位置与偏移量的相互转换
- 文档元数据的获取
- 版本控制的基本操作
所有操作都遵循LSP协议规范,确保与各种LSP客户端的兼容性。
1 回复
Rust LSP文本处理库lsp-textdocument使用指南
lsp-textdocument
是一个用于处理语言服务器协议(LSP)中文本文档操作的Rust库,它提供了高效的方式来处理文本内容、位置转换和范围操作。
主要功能
- 文本内容管理
- 位置(行/列)与偏移量之间的转换
- 文本范围操作
- 变更事件处理
- 支持增量更新
基本使用方法
添加依赖
首先在Cargo.toml
中添加依赖:
[dependencies]
lsp-textdocument = "0.3"
lsp-types = "0.94" # 用于LSP相关类型
创建文本文档
use lsp_textdocument::{FullTextDocument, TextDocumentContentChangeEvent};
use lsp_types::TextDocumentItem;
let doc_item = TextDocumentItem {
uri: Url::parse("file:///example.rs").unwrap(),
language_id: "rust".to_string(),
version: 1,
text: "fn main() {\n println!(\"Hello\");\n}".to_string(),
};
let doc = FullTextDocument::new(doc_item);
基本操作示例
// 获取文档内容
let content = doc.get_text();
// 获取总行数
let line_count = doc.line_count();
// 位置到偏移量转换
let pos = lsp_types::Position {
line: 1,
character: 4,
};
let offset = doc.offset_at_position(pos).unwrap();
println!("偏移量: {}", offset); // 输出: 16
// 偏移量到位置转换
let position = doc.position_at_offset(16).unwrap();
println!("位置: {:?}", position); // 输出: Position { line: 1, character: 4 }
// 获取特定行内容
let line_text = doc.line(1).unwrap();
println!("第2行: {}", line_text); // 输出: " println!(\"Hello\");"
处理文本变更
// 创建变更事件
let change = TextDocumentContentChangeEvent {
range: Some(lsp_types::Range {
start: lsp_types::Position { line: 1, character: 4 },
end: lsp_types::Position { line: 1, character: 9 },
}),
range_length: None,
text: "writeln!".to_string(),
};
// 应用变更
doc.apply_content_changes(vec![change]).unwrap();
println!("变更后内容:\n{}", doc.get_text());
// 输出:
// fn main() {
// writeln!("Hello");
// }
范围操作
// 获取文本范围
let range = lsp_types::Range {
start: lsp_types::Position { line: 0, character: 3 },
end: lsp_types::Position { line: 1, character: 4 },
};
let range_text = doc.get_text_range(range).unwrap();
println!("范围文本: {}", range_text); // 输出: "main() {\n w"
高级用法
增量更新处理
let mut doc = FullTextDocument::new(doc_item);
// 模拟一系列增量变更
let changes = vec![
TextDocumentContentChangeEvent {
range: Some(lsp_types::Range {
start: lsp_types::Position { line: 1, character: 15 },
end: lsp_types::Position { line: 1, character: 16 },
}),
range_length: None,
text: "\", world".to_string(),
},
TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: "fn main() {\n println!(\"Hello, world\");\n}\n".to_string(),
},
];
doc.apply_content_changes(changes).unwrap();
版本控制
// 更新文档版本
doc.update_version(2);
// 获取当前版本
println!("当前版本: {}", doc.version());
完整示例代码
use lsp_textdocument::FullTextDocument;
use lsp_types::{TextDocumentItem, Position, Range, TextDocumentContentChangeEvent};
use url::Url;
fn main() {
// 创建新的文本文档
let doc_item = TextDocumentItem {
uri: Url::parse("file:///demo.rs").unwrap(),
language_id: "rust".to_string(),
version: 1,
text: "fn greet() {\n println!(\"Hi\");\n}".to_string(),
};
let mut doc = FullTextDocument::new(doc_item);
// 基本操作
println!("初始内容:\n{}", doc.get_text());
println!("总行数: {}", doc.line_count());
// 位置转换
let pos = Position { line: 1, character: 4 };
let offset = doc.offset_at_position(pos).unwrap();
println!("位置 {:?} 的偏移量: {}", pos, offset);
// 获取行内容
println!("第2行内容: {}", doc.line(1).unwrap());
// 应用文本变更
let change = TextDocumentContentChangeEvent {
range: Some(Range {
start: Position { line: 1, character: 15 },
end: Position { line: 1, character: 16 },
}),
range_length: None,
text: "\", Rust开发者".to_string(),
};
doc.apply_content_changes(vec![change]).unwrap();
println!("变更后内容:\n{}", doc.get_text());
// 范围操作
let range = Range {
start: Position { line: 0, character: 3 },
end: Position { line: 1, character: 5 },
};
println!("范围文本: {}", doc.get_text_range(range).unwrap());
// 版本控制
doc.update_version(2);
println!("当前文档版本: {}", doc.version());
}
性能提示
- 对于大型文件,优先使用增量更新而非全量替换
- 批量处理多个变更事件比单独处理更高效
- 频繁的位置转换操作可以缓存结果
lsp-textdocument
库为Rust语言服务器开发提供了强大的文本处理基础,能够高效地处理LSP协议中的各种文本操作需求。