use crate::query_engine::{SymbolInfo, SymbolKind};
use crate::types::{CodeLocation, CodeRange, Layer3Result};
use async_trait::async_trait;
use std::path::PathBuf;
#[async_trait]
pub trait LspClient: Send + Sync {
async fn initialize(&self, root_uri: String) -> Layer3Result<LspCapabilities>;
async fn shutdown(&self) -> Layer3Result<bool>;
async fn open_document(
&self,
uri: String,
language_id: String,
content: String,
) -> Layer3Result<bool>;
async fn close_document(&self, uri: String) -> Layer3Result<bool>;
async fn change_document(&self, uri: String, changes: Vec<TextChange>) -> Layer3Result<bool>;
async fn save_document(&self, uri: String) -> Layer3Result<bool>;
fn is_ready(&self) -> bool;
fn supported_languages(&self) -> Vec<String>;
}
#[derive(Debug, Clone)]
pub struct TextChange {
pub range: CodeRange,
pub range_length: u32,
pub text: String,
}
#[derive(Debug, Clone)]
pub struct LspCapabilities {
pub text_document_sync: bool,
pub hover_provider: bool,
pub definition_provider: bool,
pub references_provider: bool,
pub implementation_provider: bool,
pub type_definition_provider: bool,
pub document_symbol_provider: bool,
pub workspace_symbol_provider: bool,
pub rename_provider: bool,
pub completion_provider: Option<CompletionOptions>,
pub signature_help_provider: Option<SignatureHelpOptions>,
}
impl Default for LspCapabilities {
fn default() -> Self {
Self {
text_document_sync: true,
hover_provider: true,
definition_provider: true,
references_provider: true,
implementation_provider: false,
type_definition_provider: false,
document_symbol_provider: true,
workspace_symbol_provider: false,
rename_provider: false,
completion_provider: None,
signature_help_provider: None,
}
}
}
#[derive(Debug, Clone)]
pub struct CompletionOptions {
pub trigger_characters: Vec<String>,
pub all_commit_characters: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct SignatureHelpOptions {
pub trigger_characters: Vec<String>,
pub retrigger_characters: Vec<String>,
}
#[async_trait]
pub trait LspRequester: LspClient {
async fn request_definition(
&self,
uri: String,
position: Position,
) -> Layer3Result<Option<LocationLink>>;
async fn request_references(
&self,
uri: String,
position: Position,
include_declaration: bool,
) -> Layer3Result<Vec<LocationLink>>;
async fn request_hover(
&self,
uri: String,
position: Position,
) -> Layer3Result<Option<HoverResult>>;
async fn request_document_symbols(&self, uri: String) -> Layer3Result<Vec<DocumentSymbol>>;
async fn request_workspace_symbols(&self, query: String) -> Layer3Result<Vec<SymbolInfo>>;
}
#[derive(Debug, Clone, Copy)]
pub struct Position {
pub line: u32,
pub character: u32,
}
impl From<CodeLocation> for Position {
fn from(loc: CodeLocation) -> Self {
Self {
line: loc.line - 1, character: loc.column - 1,
}
}
}
#[derive(Debug, Clone)]
pub struct LocationLink {
pub target_uri: String,
pub target_range: Range,
pub target_selection_range: Range,
}
#[derive(Debug, Clone, Copy)]
pub struct Range {
pub start: Position,
pub end: Position,
}
#[derive(Debug, Clone)]
pub struct HoverResult {
pub contents: MarkupContent,
pub range: Option<Range>,
}
#[derive(Debug, Clone)]
pub struct MarkupContent {
pub kind: MarkupKind,
pub value: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MarkupKind {
PlainText,
Markdown,
}
#[derive(Debug, Clone)]
pub struct DocumentSymbol {
pub name: String,
pub detail: Option<String>,
pub kind: SymbolKind,
pub deprecated: bool,
pub range: Range,
pub selection_range: Range,
pub children: Vec<DocumentSymbol>,
}
#[async_trait]
pub trait LspServerManager: Send + Sync {
async fn start_server(&self, language: String, root_path: PathBuf) -> Layer3Result<String>;
async fn stop_server(&self, server_id: &str) -> Layer3Result<bool>;
fn get_client(&self, server_id: &str) -> Option<&dyn LspClient>;
fn list_servers(&self) -> Vec<(String, String)>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_position_from_code_location() {
let loc = CodeLocation {
file: PathBuf::from("test.rs"),
line: 10,
column: 5,
};
let pos: Position = loc.into();
assert_eq!(pos.line, 9);
assert_eq!(pos.character, 4);
}
#[test]
fn test_lsp_capabilities_default() {
let caps = LspCapabilities::default();
assert!(caps.hover_provider);
assert!(caps.definition_provider);
}
}