Skip to content

Commit c7c3a76

Browse files
authored
Merge pull request #107 from gnikit/gnikit/issue62
Hover on associated blocks does not display types
2 parents 8565d4b + e624a9f commit c7c3a76

File tree

7 files changed

+91
-32
lines changed

7 files changed

+91
-32
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
### Added
88

99
- Added Code of Conduct
10+
- Added basic support for hovering over `ASSOCIATE` blocks
11+
([#62](https://github.com/gnikit/fortls/issues/62))
1012

1113
## 2.3.1
1214

fortls/objects.py

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import copy
44
import os
55
import re
6-
from dataclasses import replace
6+
from dataclasses import dataclass, replace
77
from typing import Pattern
88

99
from 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+
294301
class 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):
13651372
class 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

fortls/parse_fortran.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ def parse(
13571357
else:
13581358
name_raw = var_name.split("=")[0]
13591359
# Add dimension if specified
1360+
# TODO: turn into function and add support for co-arrays i.e. [*]
13601361
key_tmp = obj_info.keywords[:]
13611362
iparen = name_raw.find("(")
13621363
if iparen == 0:

test/test_server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ def check_return(result_array):
177177
objs = (
178178
["test", 6, 7],
179179
["test_abstract", 2, 0],
180+
["test_associate_block", 2, 0],
180181
["test_free", 2, 0],
181182
["test_gen_type", 5, 1],
182183
["test_generic", 2, 0],

test/test_server_hover.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,15 @@ def test_hover_interface_as_argument():
315315
REAL :: arg3""",
316316
)
317317
validate_hover(results, ref_results)
318+
319+
320+
def test_hover_block():
321+
string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "hover")})
322+
file_path = test_dir / "hover" / "associate_block.f90"
323+
string += hover_req(file_path, 4, 17)
324+
string += hover_req(file_path, 4, 20)
325+
# string += hover_req(file_path, 10, 11) # slice of array
326+
errcode, results = run_request(string, fortls_args=["--sort_keywords", "-n", "1"])
327+
assert errcode == 0
328+
ref_results = ["REAL, DIMENSION(5)", "REAL"]
329+
validate_hover(results, ref_results)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
PROGRAM test_associate_block
2+
IMPLICIT NONE
3+
REAL :: A(5), B(5,5), C, III = 1
4+
ASSOCIATE (X => A, Y => C)
5+
PRINT*, X, Y, III
6+
END ASSOCIATE
7+
ASSOCIATE (X => 1)
8+
PRINT*, X
9+
END ASSOCIATE
10+
ASSOCIATE (ARRAY => B(:,1))
11+
ARRAY (3) = ARRAY (1) + ARRAY (2)
12+
END ASSOCIATE
13+
END PROGRAM test_associate_block

test/test_source/tmp.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)