use crate::world::{DocumentState, World};
use jsona::{
dom::{self, DomNode, Node},
rowan::TextRange,
};
use jsona_util::environment::Environment;
use lsp_async_stub::{
rpc::Error,
util::{LspExt, Mapper},
Context, Params,
};
use lsp_types::{DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, SymbolKind};
#[tracing::instrument(skip_all)]
pub(crate) async fn document_symbols<E: Environment>(
context: Context<World<E>>,
params: Params<DocumentSymbolParams>,
) -> Result<Option<DocumentSymbolResponse>, Error> {
let p = params.required()?;
let workspaces = context.workspaces.read().await;
let document_uri = &p.text_document.uri;
let (ws, doc) = workspaces.try_get_document(document_uri)?;
if !ws.lsp_config.schema.enabled {
return Ok(None);
}
Ok(Some(DocumentSymbolResponse::Nested(create_symbols(doc))))
}
pub(crate) fn create_symbols(doc: &DocumentState) -> Vec<DocumentSymbol> {
let mapper = &doc.mapper;
let mut symbols: Vec<DocumentSymbol> = Vec::new();
let dom = doc.dom.clone();
symbols_for_annotations(&dom, mapper, &mut symbols);
match dom {
Node::Object(obj) => {
symbols.extend(symbols_for_object(&obj, mapper));
}
Node::Array(arr) => {
symbols.extend(symbols_for_array(&arr, mapper));
}
_ => {}
}
symbols
}
#[allow(deprecated)]
fn symbols_for_value(
name: String,
key_range: Option<TextRange>,
node: &Node,
mapper: &Mapper,
symbols: &mut Vec<DocumentSymbol>,
) {
let (selection_range, range) = match (key_range, node.node_text_range()) {
(None, None) => return,
(None, Some(range)) | (Some(range), None) => (range, range),
(Some(range1), Some(range2)) => (range1, range1.cover(range2)),
};
let range = mapper.range(range).unwrap();
let selection_range = mapper.range(selection_range).unwrap();
match node {
Node::Null(_) => symbols.push(DocumentSymbol {
name,
kind: SymbolKind::NULL,
range: range.into_lsp(),
selection_range: selection_range.into_lsp(),
detail: None,
deprecated: None,
tags: Default::default(),
children: None,
}),
Node::Bool(_) => symbols.push(DocumentSymbol {
name,
kind: SymbolKind::BOOLEAN,
range: range.into_lsp(),
selection_range: selection_range.into_lsp(),
detail: None,
deprecated: None,
tags: Default::default(),
children: None,
}),
Node::String(_) => symbols.push(DocumentSymbol {
name,
kind: SymbolKind::STRING,
range: range.into_lsp(),
selection_range: selection_range.into_lsp(),
detail: None,
deprecated: None,
tags: Default::default(),
children: None,
}),
Node::Number(_) => symbols.push(DocumentSymbol {
name,
kind: SymbolKind::NUMBER,
range: range.into_lsp(),
selection_range: selection_range.into_lsp(),
detail: None,
deprecated: None,
tags: Default::default(),
children: None,
}),
Node::Array(arr) => symbols.push(DocumentSymbol {
name,
kind: SymbolKind::ARRAY,
range: range.into_lsp(),
selection_range: selection_range.into_lsp(),
detail: None,
deprecated: None,
tags: Default::default(),
children: Some(symbols_for_array(arr, mapper)),
}),
Node::Object(obj) => {
symbols.push(DocumentSymbol {
name,
kind: SymbolKind::OBJECT,
range: range.into_lsp(),
selection_range: selection_range.into_lsp(),
detail: None,
deprecated: None,
tags: Default::default(),
children: Some(symbols_for_object(obj, mapper)),
});
}
}
}
fn symbols_for_annotations(node: &Node, mapper: &Mapper, symbols: &mut Vec<DocumentSymbol>) {
if let Some(annotations) = node.annotations() {
for (key, value) in annotations.value().read().iter() {
if !key.value().is_empty() {
symbols_for_value(
key.value().to_string(),
key.text_range(),
value,
mapper,
symbols,
);
}
}
}
}
fn symbols_for_array(arr: &dom::Array, mapper: &Mapper) -> Vec<DocumentSymbol> {
let items = arr.value().read();
let mut symbols = vec![];
for (index, value) in items.iter().enumerate() {
symbols_for_annotations(value, mapper, &mut symbols);
symbols_for_value(index.to_string(), None, value, mapper, &mut symbols);
}
symbols
}
fn symbols_for_object(obj: &dom::Object, mapper: &Mapper) -> Vec<DocumentSymbol> {
let properties = obj.value().read();
let mut symbols = vec![];
for (key, value) in properties.iter() {
symbols_for_annotations(value, mapper, &mut symbols);
if !key.value().is_empty() {
symbols_for_value(
key.to_string(),
key.text_range(),
value,
mapper,
&mut symbols,
);
}
}
symbols
}