use erg_common::traits::Locational; use erg_compiler::artifact::BuildRunnable; use erg_compiler::erg_parser::ast::DefKind; use erg_compiler::erg_parser::parse::Parsable; use erg_compiler::hir::Expr; use erg_compiler::ty::{HasType, Type}; use erg_compiler::varinfo::VarInfo; use lsp_types::{ DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, SymbolInformation, SymbolKind, WorkspaceSymbolParams, }; use crate::_log; use crate::server::{ELSResult, RedirectableStdout, Server}; use crate::util::{abs_loc_to_lsp_loc, loc_to_range, NormalizedUrl}; pub(crate) fn symbol_kind(vi: &VarInfo) -> SymbolKind { match &vi.t { Type::Subr(subr) if subr.self_t().is_some() => SymbolKind::METHOD, Type::Quantified(quant) if quant.self_t().is_some() => SymbolKind::METHOD, Type::Subr(_) | Type::Quantified(_) => SymbolKind::FUNCTION, Type::ClassType => SymbolKind::CLASS, Type::TraitType => SymbolKind::INTERFACE, t if matches!(&t.qual_name()[..], "Module" | "PyModule" | "GenericModule") => { SymbolKind::MODULE } _ if vi.muty.is_const() => SymbolKind::CONSTANT, _ => SymbolKind::VARIABLE, } } impl Server { pub(crate) fn handle_workspace_symbol( &mut self, params: WorkspaceSymbolParams, ) -> ELSResult>> { _log!(self, "workspace symbol requested: {params:?}"); let mut res = vec![]; for context in self.get_workspace_ctxs() { for (name, vi) in context.local_dir() { if name.inspect().starts_with(['%']) { continue; } if vi .alias_of .as_ref() .is_some_and(|alias| &alias.name == name.inspect()) { continue; } let Some(location) = abs_loc_to_lsp_loc(&vi.def_loc) else { continue; }; #[allow(deprecated)] let info = SymbolInformation { name: name.to_string(), location, kind: symbol_kind(vi), container_name: None, tags: None, deprecated: None, }; res.push(info); } } Ok(Some(res)) } pub(crate) fn handle_document_symbol( &mut self, params: DocumentSymbolParams, ) -> ELSResult> { _log!(self, "document symbol requested: {params:?}"); let uri = NormalizedUrl::new(params.text_document.uri); if let Some(hir) = self.get_hir(&uri) { let mut res = vec![]; for chunk in hir.module.iter() { let symbol = self.symbol(chunk); res.extend(symbol); } return Ok(Some(DocumentSymbolResponse::Nested(res))); } Ok(None) } fn symbol(&self, chunk: &Expr) -> Option { match chunk { Expr::Def(def) => { if def.sig.inspect().starts_with(['%']) { return None; } let range = loc_to_range(def.loc())?; let selection_range = loc_to_range(def.sig.loc())?; #[allow(deprecated)] Some(DocumentSymbol { name: def.sig.name().to_string(), detail: Some(def.sig.ident().ref_t().to_string()), kind: symbol_kind(&def.sig.ident().vi), tags: None, deprecated: None, range, selection_range, children: Some(self.child_symbols(chunk)), }) } Expr::ClassDef(def) => { let range = loc_to_range(def.loc())?; let selection_range = loc_to_range(def.sig.loc())?; #[allow(deprecated)] Some(DocumentSymbol { name: def.sig.name().to_string(), detail: Some(def.sig.ident().ref_t().to_string()), kind: symbol_kind(&def.sig.ident().vi), tags: None, deprecated: None, range, selection_range, children: Some(self.child_symbols(chunk)), }) } Expr::PatchDef(def) => { let range = loc_to_range(def.loc())?; let selection_range = loc_to_range(def.sig.loc())?; #[allow(deprecated)] Some(DocumentSymbol { name: def.sig.name().to_string(), detail: Some(def.sig.ident().ref_t().to_string()), kind: symbol_kind(&def.sig.ident().vi), tags: None, deprecated: None, range, selection_range, children: Some(self.child_symbols(chunk)), }) } _ => None, } } fn child_symbols(&self, chunk: &Expr) -> Vec { match chunk { Expr::Def(def) => match def.def_kind() { DefKind::Class | DefKind::Trait => { if let Some(base) = def.get_base() { let mut res = vec![]; for member in base.attrs.iter() { let symbol = self.symbol(&Expr::Def(member.clone())); res.extend(symbol); } res } else { vec![] } } _ => vec![], }, Expr::ClassDef(def) => { let mut res = vec![]; if let Some(Expr::Record(rec)) = def.require_or_sup.as_deref() { for member in rec.attrs.iter() { let symbol = self.symbol(&Expr::Def(member.clone())); res.extend(symbol); } } for method in def.all_methods() { let symbol = self.symbol(method); res.extend(symbol); } res } Expr::PatchDef(def) => { let mut res = vec![]; for method in def.methods.iter() { let symbol = self.symbol(method); res.extend(symbol); } res } _ => vec![], } } }