use crate::{
LspDocumentOrigin, LspQueryReadView,
protocol::{file_label_from_uri, is_style_document_uri},
};
use serde_json::{Value, json};
const MAX_WORKSPACE_SYMBOLS: usize = 256;
pub(crate) fn resolve_lsp_workspace_symbols(
state: &dyn LspQueryReadView,
params: Option<&Value>,
) -> Value {
let query = params
.and_then(|params| params.pointer("/query"))
.and_then(Value::as_str)
.unwrap_or_default()
.to_lowercase();
let mut symbols = Vec::new();
'documents: for document in state.query_documents().values() {
if document.origin != LspDocumentOrigin::Local
|| !is_style_document_uri(document.uri.as_str())
{
continue;
}
for candidate in &document.style_candidates {
let Some((display_name, symbol_kind)) =
workspace_symbol_shape(candidate.kind, candidate.name.as_str())
else {
continue;
};
if !query.is_empty() && !display_name.to_lowercase().contains(query.as_str()) {
continue;
}
symbols.push(json!({
"name": display_name,
"kind": symbol_kind,
"location": {
"uri": document.uri,
"range": candidate.range,
},
"containerName": file_label_from_uri(document.uri.as_str()),
}));
if symbols.len() >= MAX_WORKSPACE_SYMBOLS {
break 'documents;
}
}
}
symbols.sort_by(|left, right| {
left.pointer("/name")
.and_then(Value::as_str)
.cmp(&right.pointer("/name").and_then(Value::as_str))
});
json!(symbols)
}
fn workspace_symbol_shape(candidate_kind: &str, name: &str) -> Option<(String, u8)> {
match candidate_kind {
"selector" => Some((format!(".{name}"), 5)),
"sassVariableDeclaration" => Some((format!("${name}"), 13)),
"sassMixinDeclaration" => Some((format!("@mixin {name}"), 6)),
"sassFunctionDeclaration" => Some((format!("@function {name}"), 12)),
"sassSymbolDeclaration" => Some((format!("${name}"), 13)),
"customPropertyDeclaration" => Some((name.to_string(), 7)),
_ => None,
}
}