use crate::{editor, server::ServerState};
use anyhow::Context;
use lsp_types::{
DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Position, Range,
SymbolKind as LspSymbolKind,
};
pub fn handle(
state: &ServerState,
params: DocumentSymbolParams,
) -> anyhow::Result<Option<DocumentSymbolResponse>> {
if let Some(document) = state.documents.get(params.text_document.uri.as_str()) {
let editor_buffer = editor::from_source(&document.text).with_context(|| {
format!(
"failed to analyze document `{:?}`",
params.text_document.uri
)
})?;
let symbols = editor_buffer
.symbols()
.iter()
.map(to_lsp_document_symbol)
.collect::<Vec<_>>();
Ok(Some(DocumentSymbolResponse::Nested(symbols)))
} else {
Ok(None)
}
}
#[allow(deprecated)]
fn to_lsp_document_symbol(symbol: &editor::Symbol) -> DocumentSymbol {
DocumentSymbol {
name: symbol.name().to_owned(),
detail: symbol.detail().map(str::to_owned),
kind: match symbol.kind() {
editor::SymbolKind::Blueprint => LspSymbolKind::MODULE,
editor::SymbolKind::Prompt => LspSymbolKind::FIELD,
editor::SymbolKind::Validate => LspSymbolKind::OBJECT,
},
tags: None,
deprecated: None,
range: to_lsp_range(symbol.range()),
selection_range: to_lsp_range(symbol.selection_range()),
children: Some(
symbol
.children()
.iter()
.map(to_lsp_document_symbol)
.collect(),
),
}
}
fn to_lsp_range(range: achitekfile::TextRange) -> Range {
Range {
start: to_lsp_position(range.start),
end: to_lsp_position(range.end),
}
}
fn to_lsp_position(position: achitekfile::TextPosition) -> Position {
Position {
line: u32::try_from(position.line).expect("line should fit into u32"),
character: u32::try_from(position.byte).expect("column should fit into u32"),
}
}