Skip to main content

shape_lsp/
trait_lookup.rs

1//! Shared trait lookup helpers for LSP hover/completion.
2//!
3//! Resolves trait definitions from:
4//! 1. Current file AST (fast path)
5//! 2. Importable modules via ModuleCache (stdlib/project modules)
6
7use crate::doc_render::render_doc_comment;
8use crate::module_cache::ModuleCache;
9use shape_ast::ast::{Item, Program, Span, TraitDef};
10use std::path::{Path, PathBuf};
11
12#[derive(Debug, Clone)]
13pub struct ResolvedTraitDef {
14    pub trait_def: TraitDef,
15    pub span: Span,
16    pub documentation: Option<String>,
17    pub source_path: Option<PathBuf>,
18    pub source_text: Option<String>,
19    pub import_path: Option<String>,
20}
21
22pub fn resolve_trait_definition(
23    program: &Program,
24    trait_name: &str,
25    module_cache: Option<&ModuleCache>,
26    current_file: Option<&Path>,
27    workspace_root: Option<&Path>,
28) -> Option<ResolvedTraitDef> {
29    for item in &program.items {
30        if let Item::Trait(trait_def, span) = item {
31            if trait_def.name == trait_name {
32                return Some(ResolvedTraitDef {
33                    trait_def: trait_def.clone(),
34                    span: *span,
35                    documentation: program
36                        .docs
37                        .comment_for_span(*span)
38                        .map(|comment| render_doc_comment(program, comment, None, None, None)),
39                    source_path: None,
40                    source_text: None,
41                    import_path: None,
42                });
43            }
44        }
45    }
46
47    let cache = module_cache?;
48    let current_file = current_file?;
49
50    let mut import_paths = cache.list_importable_modules_with_context(current_file, workspace_root);
51    import_paths.sort();
52
53    for import_path in import_paths {
54        let Some(resolved) = cache.resolve_import(&import_path, current_file, workspace_root)
55        else {
56            continue;
57        };
58        let Some(module_info) =
59            cache.load_module_with_context(&resolved, current_file, workspace_root)
60        else {
61            continue;
62        };
63
64        for item in &module_info.program.items {
65            if let Item::Trait(trait_def, span) = item {
66                if trait_def.name == trait_name {
67                    return Some(ResolvedTraitDef {
68                        trait_def: trait_def.clone(),
69                        span: *span,
70                        documentation: module_info.program.docs.comment_for_span(*span).map(
71                            |comment| {
72                                render_doc_comment(
73                                    &module_info.program,
74                                    comment,
75                                    Some(cache),
76                                    Some(&module_info.path),
77                                    workspace_root,
78                                )
79                            },
80                        ),
81                        source_text: std::fs::read_to_string(&module_info.path).ok(),
82                        source_path: Some(module_info.path.clone()),
83                        import_path: Some(import_path),
84                    });
85                }
86            }
87        }
88    }
89
90    None
91}