Skip to content

Commit 61466da

Browse files
authored
Merge pull request #53 from gnikit/feature/dataclasses
Feature/dataclasses
2 parents 443c933 + 7844dc7 commit 61466da

9 files changed

+385
-416
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# CHANGELONG
22

3+
## 2.2.4
4+
5+
### Fixed
6+
7+
- Fixed hovering value of literal and constant strings not displaying
8+
([#54](https://github.com/gnikit/fortls/issues/54))
9+
- Fixed hovering string length size is now matching the string
10+
([#55](https://github.com/gnikit/fortls/issues/55))
11+
312
## 2.2.3
413

514
### Changed

fortls/constants.py

+75
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import sys
55
from dataclasses import dataclass, field
66

7+
from fortls.regex_patterns import FortranRegularExpressions
8+
79
PY3K = sys.version_info >= (3, 0)
810

911
log = logging.getLogger(__name__)
@@ -63,6 +65,75 @@
6365
FORTRAN_LITERAL = "0^=__LITERAL_INTERNAL_DUMMY_VAR_"
6466

6567

68+
@dataclass
69+
class VAR_info:
70+
type_word: str
71+
keywords: list[str]
72+
var_names: list[str]
73+
74+
75+
@dataclass
76+
class SUB_info:
77+
name: str
78+
args: str
79+
mod_flag: bool
80+
keywords: list[str]
81+
82+
83+
@dataclass
84+
class SELECT_info:
85+
type: int
86+
binding: str
87+
desc: str
88+
89+
90+
@dataclass
91+
class CLASS_info:
92+
name: str
93+
parent: str
94+
keywords: str
95+
96+
97+
@dataclass
98+
class USE_info:
99+
mod_name: str
100+
only_list: set[str]
101+
rename_map: dict[str, str]
102+
103+
104+
@dataclass
105+
class GEN_info:
106+
bound_name: str
107+
pro_links: list[str]
108+
vis_flag: int
109+
110+
111+
@dataclass
112+
class SMOD_info:
113+
name: str
114+
parent: str
115+
116+
117+
@dataclass
118+
class INT_info:
119+
name: str
120+
abstract: bool
121+
122+
123+
@dataclass
124+
class VIS_info:
125+
type: int
126+
obj_names: list[str]
127+
128+
129+
@dataclass
130+
class INCLUDE_info:
131+
line_number: int
132+
path: str
133+
file: None # fortran_file
134+
scope_objs: list[str]
135+
136+
66137
@dataclass
67138
class RESULT_sig:
68139
name: str = field(default=None)
@@ -81,3 +152,7 @@ class FUN_sig:
81152
def __post_init__(self):
82153
if not self.result.name:
83154
self.result.name = self.name
155+
156+
157+
# Fortran Regular Expressions dataclass variable, immutable
158+
FRegex = FortranRegularExpressions()

fortls/helper_functions.py

+18-24
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,7 @@
33
import os
44
from pathlib import Path
55

6-
from fortls.constants import KEYWORD_ID_DICT, KEYWORD_LIST, log, sort_keywords
7-
from fortls.regex_patterns import (
8-
DQ_STRING_REGEX,
9-
FIXED_COMMENT_LINE_MATCH,
10-
FREE_FORMAT_TEST,
11-
LINE_LABEL_REGEX,
12-
LOGICAL_REGEX,
13-
NAT_VAR_REGEX,
14-
NUMBER_REGEX,
15-
OBJBREAK_REGEX,
16-
SQ_STRING_REGEX,
17-
WORD_REGEX,
18-
)
6+
from fortls.constants import KEYWORD_ID_DICT, KEYWORD_LIST, FRegex, log, sort_keywords
197

208

219
def expand_name(line: str, char_poss: int) -> str:
@@ -35,7 +23,13 @@ def expand_name(line: str, char_poss: int) -> str:
3523
"""
3624
# The order here is important.
3725
# WORD will capture substrings in logical and strings
38-
regexs = [LOGICAL_REGEX, SQ_STRING_REGEX, DQ_STRING_REGEX, WORD_REGEX, NUMBER_REGEX]
26+
regexs = [
27+
FRegex.LOGICAL,
28+
FRegex.SQ_STRING,
29+
FRegex.DQ_STRING,
30+
FRegex.WORD,
31+
FRegex.NUMBER,
32+
]
3933
for r in regexs:
4034
for num_match in r.finditer(line):
4135
if num_match.start(0) <= char_poss <= num_match.end(0):
@@ -59,13 +53,13 @@ def detect_fixed_format(file_lines: list[str]) -> bool:
5953
True if file_lines are of Fixed Fortran style
6054
"""
6155
for line in file_lines:
62-
if FREE_FORMAT_TEST.match(line):
56+
if FRegex.FREE_FORMAT_TEST.match(line):
6357
return False
64-
tmp_match = NAT_VAR_REGEX.match(line)
58+
tmp_match = FRegex.VAR.match(line)
6559
if tmp_match and tmp_match.start(1) < 6:
6660
return False
6761
# Trailing ampersand indicates free or intersection format
68-
if not FIXED_COMMENT_LINE_MATCH.match(line):
62+
if not FRegex.FIXED_COMMENT.match(line):
6963
line_end = line.split("!")[0].strip()
7064
if len(line_end) > 0 and line_end[-1] == "&":
7165
return False
@@ -86,7 +80,7 @@ def strip_line_label(line: str) -> tuple[str, str | None]:
8680
Output string, Line label returns None if no line label present
8781
"""
8882

89-
match = LINE_LABEL_REGEX.match(line)
83+
match = FRegex.LINE_LABEL.match(line)
9084
if match is None:
9185
return line, None
9286
else:
@@ -118,11 +112,11 @@ def repl_dq(m):
118112
return '"{0}"'.format(" " * (len(m.group()) - 2))
119113

120114
if maintain_len:
121-
out_line = SQ_STRING_REGEX.sub(repl_sq, in_line)
122-
out_line = DQ_STRING_REGEX.sub(repl_dq, out_line)
115+
out_line = FRegex.SQ_STRING.sub(repl_sq, in_line)
116+
out_line = FRegex.DQ_STRING.sub(repl_dq, out_line)
123117
else:
124-
out_line = SQ_STRING_REGEX.sub("", in_line)
125-
out_line = DQ_STRING_REGEX.sub("", out_line)
118+
out_line = FRegex.SQ_STRING.sub("", in_line)
119+
out_line = FRegex.DQ_STRING.sub("", out_line)
126120
return out_line
127121

128122

@@ -183,7 +177,7 @@ def find_word_in_line(line: str, word: str) -> tuple[int, int]:
183177
start and end positions (indices) of the word if not found it returns
184178
-1, len(word) -1"""
185179
i0 = -1
186-
for poss_name in WORD_REGEX.finditer(line):
180+
for poss_name in FRegex.WORD.finditer(line):
187181
if poss_name.group() == word:
188182
i0 = poss_name.start()
189183
break
@@ -417,7 +411,7 @@ def get_var_stack(line):
417411
final_var += line[section[0] : section[1]]
418412
#
419413
if final_var is not None:
420-
final_op_split = OBJBREAK_REGEX.split(final_var)
414+
final_op_split = FRegex.OBJBREAK.split(final_var)
421415
return final_op_split[-1].split("%")
422416
else:
423417
return None

fortls/langserver.py

+11-20
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
SELECT_TYPE_ID,
2222
SUBROUTINE_TYPE_ID,
2323
VAR_TYPE_ID,
24+
FRegex,
2425
log,
2526
)
2627
from fortls.helper_functions import (
@@ -47,25 +48,12 @@
4748
get_use_tree,
4849
)
4950
from fortls.parse_fortran import fortran_file, get_line_context, process_file
50-
from fortls.regex_patterns import (
51-
DQ_STRING_REGEX,
52-
INT_STMNT_REGEX,
53-
LOGICAL_REGEX,
54-
NUMBER_REGEX,
55-
SQ_STRING_REGEX,
56-
src_file_exts,
57-
)
51+
from fortls.regex_patterns import src_file_exts
5852
from fortls.version import __version__
5953

6054
# Global regexes
6155
# TODO: I think this can be replaced by fortls.regex_patterns type & class
6256
TYPE_DEF_REGEX = re.compile(r"[ ]*(TYPE|CLASS)[ ]*\([a-z0-9_ ]*$", re.I)
63-
# TODO: I think this can be replaced by fortls.regex_patterns
64-
SCOPE_DEF_REGEX = re.compile(r"[ ]*(MODULE|PROGRAM|SUBROUTINE|FUNCTION)[ ]+", re.I)
65-
# TODO: I think this can be replaced by fortls.regex_patterns END_REGEx
66-
END_REGEX = re.compile(
67-
r"[ ]*(END)( |MODULE|PROGRAM|SUBROUTINE|FUNCTION|TYPE|DO|IF|SELECT)?", re.I
68-
)
6957

7058

7159
def init_file(filepath, pp_defs, pp_suffixes, include_dirs, sort):
@@ -540,6 +528,7 @@ def build_comp(
540528
if self.autocomplete_no_prefix:
541529
var_prefix = ""
542530
# Suggestions for user-defined type members
531+
scope_list = []
543532
if is_member:
544533
curr_scope = file_obj.ast.get_inner_scope(ac_line + 1)
545534
type_scope = climb_type_tree(var_stack, curr_scope, self.obj_tree)
@@ -758,14 +747,16 @@ def get_definition(
758747
# Return a dummy object with the correct type & position in the doc
759748
if hover_req and curr_scope:
760749
var_type = None
761-
if NUMBER_REGEX.match(def_name):
750+
if FRegex.NUMBER.match(def_name):
762751
if any(s in def_name for s in [".", "e", "d"]):
763752
var_type = f"{FORTRAN_LITERAL}REAL"
764753
else:
765754
var_type = f"{FORTRAN_LITERAL}INTEGER"
766-
elif LOGICAL_REGEX.match(def_name):
755+
elif FRegex.LOGICAL.match(def_name):
767756
var_type = f"{FORTRAN_LITERAL}LOGICAL"
768-
elif SQ_STRING_REGEX.match(def_name) or DQ_STRING_REGEX.match(def_name):
757+
elif FRegex.SQ_STRING.match(def_name) or FRegex.DQ_STRING.match(
758+
def_name
759+
):
769760
var_type = f"{FORTRAN_LITERAL}STRING"
770761
if var_type:
771762
return fortran_var(
@@ -817,7 +808,7 @@ def check_optional(arg, params):
817808
if line_prefix is None:
818809
return None
819810
# Test if scope declaration or end statement
820-
if SCOPE_DEF_REGEX.match(curr_line) or END_REGEX.match(curr_line):
811+
if FRegex.SCOPE_DEF.match(curr_line) or FRegex.END.match(curr_line):
821812
return None
822813
is_member = False
823814
try:
@@ -853,7 +844,7 @@ def check_optional(arg, params):
853844
break
854845
# Check keywords
855846
if (var_obj is None) and (
856-
INT_STMNT_REGEX.match(line_prefix[:sub_end]) is not None
847+
FRegex.INT_STMNT.match(line_prefix[:sub_end]) is not None
857848
):
858849
key = sub_name.lower()
859850
for candidate in get_intrinsic_keywords(self.statements, self.keywords, 0):
@@ -1064,7 +1055,7 @@ def create_signature_hover():
10641055
elif var_obj.desc.endswith("LOGICAL"):
10651056
hover_array.append(create_hover("LOGICAL", True))
10661057
elif var_obj.desc.endswith("STRING"):
1067-
hover_str = f"CHARACTER(LEN={len(var_obj.name)})"
1058+
hover_str = f"CHARACTER(LEN={len(var_obj.name)-2})"
10681059
hover_array.append(create_hover(hover_str, True))
10691060

10701061
# Include the signature if one is present e.g. if in an argument list

0 commit comments

Comments
 (0)