@@ -212,6 +212,28 @@ impl Backend {
212212 if offset < import_decl. span . start || offset >= import_decl. span . end {
213213 continue ;
214214 }
215+
216+ // Check if cursor is on the module name
217+ if offset >= import_decl. module_span . start && offset < import_decl. module_span . end {
218+ let module_name = interner:: resolve_module_name ( & import_decl. module . parts ) ;
219+ let docs = self . get_imported_module_doc ( & module_name) . await ;
220+ let mut markdown = format ! ( "```purescript\n module {module_name}\n ```" ) ;
221+ if !docs. is_empty ( ) {
222+ markdown. push_str ( "\n \n ---\n \n " ) ;
223+ for doc in & docs {
224+ markdown. push_str ( doc. trim ( ) ) ;
225+ markdown. push ( '\n' ) ;
226+ }
227+ }
228+ return Some ( Hover {
229+ contents : HoverContents :: Markup ( MarkupContent {
230+ kind : MarkupKind :: Markdown ,
231+ value : markdown,
232+ } ) ,
233+ range : None ,
234+ } ) ;
235+ }
236+
215237 let items = match & import_decl. imports {
216238 Some ( ImportList :: Explicit ( items) ) | Some ( ImportList :: Hiding ( items) ) => items,
217239 None => continue ,
@@ -300,6 +322,43 @@ impl Backend {
300322 . collect ( )
301323 }
302324
325+ async fn get_imported_module_doc ( & self , module_name : & str ) -> Vec < String > {
326+ // Try registry first (has module_doc from typechecking)
327+ {
328+ let module_parts: Vec < interner:: Symbol > = module_name
329+ . split ( '.' )
330+ . map ( |s| interner:: intern ( s) )
331+ . collect ( ) ;
332+ let registry = self . registry . read ( ) . await ;
333+ if let Some ( mod_exports) = registry. lookup ( & module_parts) {
334+ if !mod_exports. module_doc . is_empty ( ) {
335+ return mod_exports. module_doc . clone ( ) ;
336+ }
337+ }
338+ }
339+
340+ // Fall back to parsing the source file
341+ let target_uri = {
342+ let mf = self . module_file_map . read ( ) . await ;
343+ mf. get ( module_name) . cloned ( )
344+ } ;
345+ let target_uri = match target_uri {
346+ Some ( u) => u,
347+ None => return Vec :: new ( ) ,
348+ } ;
349+ let target_source = match self . get_source_for_uri ( & target_uri) . await {
350+ Some ( s) => s,
351+ None => return Vec :: new ( ) ,
352+ } ;
353+ let target_module = match crate :: parser:: parse ( & target_source) {
354+ Ok ( m) => m,
355+ Err ( _) => return Vec :: new ( ) ,
356+ } ;
357+ target_module. doc_comments . iter ( ) . filter_map ( |c| {
358+ if let cst:: Comment :: Doc ( text) = c { Some ( text. clone ( ) ) } else { None }
359+ } ) . collect ( )
360+ }
361+
303362 async fn get_local_kind ( & self , module : & cst:: Module , symbol : interner:: Symbol ) -> Option < String > {
304363 let registry = self . registry . read ( ) . await ;
305364 let check_result = crate :: typechecker:: check_module_with_registry ( module, & registry) ;
0 commit comments