33import copy
44import os
55import re
6- from dataclasses import replace
6+ from dataclasses import dataclass , replace
77from typing import Pattern
88
99from fortls .constants import (
@@ -291,6 +291,13 @@ def __init__(
291291 }
292292
293293
294+ @dataclass
295+ class AssociateMap :
296+ var : fortran_var
297+ bind_name : str
298+ link_name : str
299+
300+
294301class fortran_diagnostic :
295302 def __init__ (
296303 self , sline : int , message : str , severity : int = 1 , find_word : str = None
@@ -1365,33 +1372,65 @@ def get_desc(self):
13651372class fortran_associate (fortran_block ):
13661373 def __init__ (self , file_ast : fortran_ast , line_number : int , name : str ):
13671374 super ().__init__ (file_ast , line_number , name )
1368- self .assoc_links = []
1375+ self .links : list [ AssociateMap ] = [] # holds the info to associate variables
13691376
13701377 def get_type (self , no_link = False ):
13711378 return ASSOC_TYPE_ID
13721379
13731380 def get_desc (self ):
13741381 return "ASSOCIATE"
13751382
1376- def create_binding_variable (self , file_ast , line_number , bound_name , link_var ):
1377- new_var = fortran_var (file_ast , line_number , bound_name , "UNKNOWN" , [])
1378- self .assoc_links .append ([new_var , bound_name , link_var ])
1383+ def create_binding_variable (
1384+ self , file_ast : fortran_ast , line_number : int , bind_name : str , link_name : str
1385+ ) -> fortran_var :
1386+ """Create a new variable to be linked upon resolution to the real variable
1387+ that contains the information of the mapping from the parent scope to the
1388+ ASSOCIATE block scope.
1389+
1390+ Parameters
1391+ ----------
1392+ file_ast : fortran_ast
1393+ AST file
1394+ line_number : int
1395+ Line number
1396+ bind_name : str
1397+ Name of the ASSOCIATE block variable
1398+ link_name : str
1399+ Name of the parent scope variable
1400+
1401+ Returns
1402+ -------
1403+ fortran_var
1404+ Variable object holding the ASSOCIATE block variable, pending resolution
1405+ """
1406+ new_var = fortran_var (file_ast , line_number , bind_name , "UNKNOWN" , [])
1407+ self .links .append (AssociateMap (new_var , bind_name , link_name ))
13791408 return new_var
13801409
13811410 def resolve_link (self , obj_tree ):
1382- for assoc_link in self .assoc_links :
1383- var_stack = get_var_stack (assoc_link [2 ])
1384- if len (var_stack ) > 1 :
1411+ # Loop through the list of the associated variables map and resolve the links
1412+ # find the AST node that that corresponds to the variable with link_name
1413+ for assoc in self .links :
1414+ # TODO: extract the dimensions component from the link_name
1415+ # re.sub(r'\(.*\)', '', link_name) removes the dimensions component
1416+ # keywords = re.match(r'(.*)\((.*)\)', link_name).groups()
1417+ # now pass the keywords through the dimension_parser and set the keywords
1418+ # in the associate object. Hover should now pick the local keywords
1419+ # over the linked_object keywords
1420+ assoc .link_name = re .sub (r"\(.*\)" , "" , assoc .link_name )
1421+ var_stack = get_var_stack (assoc .link_name )
1422+ is_member = len (var_stack ) > 1
1423+ if is_member :
13851424 type_scope = climb_type_tree (var_stack , self , obj_tree )
13861425 if type_scope is None :
13871426 continue
13881427 var_obj = find_in_scope (type_scope , var_stack [- 1 ], obj_tree )
13891428 if var_obj is not None :
1390- assoc_link [ 0 ] .link_obj = var_obj
1429+ assoc . var .link_obj = var_obj
13911430 else :
1392- var_obj = find_in_scope (self , assoc_link [ 2 ] , obj_tree )
1431+ var_obj = find_in_scope (self , assoc . link_name , obj_tree )
13931432 if var_obj is not None :
1394- assoc_link [ 0 ] .link_obj = var_obj
1433+ assoc . var .link_obj = var_obj
13951434
13961435 def require_link (self ):
13971436 return True
@@ -1601,6 +1640,7 @@ def get_type_obj(self, obj_tree):
16011640 self .type_obj = type_obj
16021641 return self .type_obj
16031642
1643+ # XXX: unused delete or use for associate blocks
16041644 def set_dim (self , dim_str ):
16051645 if KEYWORD_ID_DICT ["dimension" ] not in self .keywords :
16061646 self .keywords .append (KEYWORD_ID_DICT ["dimension" ])
@@ -1618,9 +1658,9 @@ def get_snippet(self, name_replace=None, drop_arg=-1):
16181658
16191659 def get_hover (self , long = False , include_doc = True , drop_arg = - 1 ):
16201660 doc_str = self .get_documentation ()
1621- hover_str = ", " . join (
1622- [ self . desc ] + get_keywords ( self . keywords , self . keyword_info )
1623- )
1661+ # In associated blocks we need to fetch the desc and keywords of the
1662+ # linked object
1663+ hover_str = ", " . join ([ self . get_desc ()] + self . get_keywords () )
16241664 # TODO: at this stage we can mae this lowercase
16251665 # Add parameter value in the output
16261666 if self .is_parameter () and self .param_val :
@@ -1629,6 +1669,14 @@ def get_hover(self, long=False, include_doc=True, drop_arg=-1):
16291669 hover_str += "\n {0}" .format ("\n " .join (doc_str .splitlines ()))
16301670 return hover_str , True
16311671
1672+ def get_keywords (self ):
1673+ # TODO: if local keywords are set they should take precedence over link_obj
1674+ # Alternatively, I could do a dictionary merge with local variables
1675+ # having precedence by default and use a flag to override?
1676+ if self .link_obj is not None :
1677+ return get_keywords (self .link_obj .keywords , self .link_obj .keyword_info )
1678+ return get_keywords (self .keywords , self .keyword_info )
1679+
16321680 def is_optional (self ):
16331681 if self .keywords .count (KEYWORD_ID_DICT ["optional" ]) > 0 :
16341682 return True
0 commit comments