Skip to content

Commit 0e9348a

Browse files
authored
Merge pull request #10 from gnikit/split-file
Split files
2 parents a0620f6 + 9d3f42c commit 0e9348a

File tree

8 files changed

+367
-261
lines changed

8 files changed

+367
-261
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# CHANGELONG
22

3+
## 1.15.1
4+
5+
### Fixes
6+
7+
- Fixes premature end of scope with variables named `end`
8+
([gnikit/fortls#9](https://github.com/gnikit/fortls/issues/9))
9+
10+
## 1.15.0
11+
12+
### Adds
13+
14+
- Adds `--config` option which allows arbitrary named configuration files
15+
316
## 1.14.4
417

518
### Fixes

fortls/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import sys
2+
3+
PY3K = sys.version_info >= (3, 0)

fortls/helper_functions.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from fortls.regex_patterns import (
2+
DQ_STRING_REGEX,
3+
FIXED_COMMENT_LINE_MATCH,
4+
FREE_FORMAT_TEST,
5+
LINE_LABEL_REGEX,
6+
LOGICAL_REGEX,
7+
NAT_VAR_REGEX,
8+
NUMBER_REGEX,
9+
SQ_STRING_REGEX,
10+
WORD_REGEX,
11+
)
12+
13+
14+
def expand_name(line, char_poss):
15+
"""Get full word containing given cursor position"""
16+
# The order here is important.
17+
# WORD will capture substrings in logical and strings
18+
regexs = [LOGICAL_REGEX, SQ_STRING_REGEX, DQ_STRING_REGEX, WORD_REGEX, NUMBER_REGEX]
19+
for r in regexs:
20+
for num_match in r.finditer(line):
21+
if num_match.start(0) <= char_poss and num_match.end(0) >= char_poss:
22+
return num_match.group(0)
23+
return ""
24+
25+
26+
def detect_fixed_format(file_lines):
27+
"""Detect fixed/free format by looking for characters in label columns
28+
and variable declarations before column 6. Treat intersection format
29+
files as free format."""
30+
for line in file_lines:
31+
if FREE_FORMAT_TEST.match(line):
32+
return False
33+
tmp_match = NAT_VAR_REGEX.match(line)
34+
if tmp_match and tmp_match.start(1) < 6:
35+
return False
36+
# Trailing ampersand indicates free or intersection format
37+
if not FIXED_COMMENT_LINE_MATCH.match(line):
38+
line_end = line.split("!")[0].strip()
39+
if len(line_end) > 0 and line_end[-1] == "&":
40+
return False
41+
return True
42+
43+
44+
def strip_line_label(line):
45+
"""Strip leading numeric line label"""
46+
match = LINE_LABEL_REGEX.match(line)
47+
if match is None:
48+
return line, None
49+
else:
50+
line_label = match.group(1)
51+
out_str = line[: match.start(1)] + " " * len(line_label) + line[match.end(1) :]
52+
return out_str, line_label
53+
54+
55+
def strip_strings(in_line, maintain_len=False):
56+
"""String string literals from code line"""
57+
58+
def repl_sq(m):
59+
return "'{0}'".format(" " * (len(m.group()) - 2))
60+
61+
def repl_dq(m):
62+
return '"{0}"'.format(" " * (len(m.group()) - 2))
63+
64+
if maintain_len:
65+
out_line = SQ_STRING_REGEX.sub(repl_sq, in_line)
66+
out_line = DQ_STRING_REGEX.sub(repl_dq, out_line)
67+
else:
68+
out_line = SQ_STRING_REGEX.sub("", in_line)
69+
out_line = DQ_STRING_REGEX.sub("", out_line)
70+
return out_line
71+
72+
73+
def separate_def_list(test_str):
74+
"""Separate definition lists, skipping parenthesis and bracket groups
75+
76+
Examples:
77+
"var1, var2, var3" -> ["var1", "var2", "var3"]
78+
"var, init_var(3) = [1,2,3], array(3,3)" -> ["var", "init_var", "array"]
79+
"""
80+
stripped_str = strip_strings(test_str)
81+
paren_count = 0
82+
def_list = []
83+
curr_str = ""
84+
for char in stripped_str:
85+
if (char == "(") or (char == "["):
86+
paren_count += 1
87+
elif (char == ")") or (char == "]"):
88+
paren_count -= 1
89+
elif (char == ",") and (paren_count == 0):
90+
curr_str = curr_str.strip()
91+
if curr_str != "":
92+
def_list.append(curr_str)
93+
curr_str = ""
94+
elif (curr_str == "") and (len(def_list) == 0):
95+
return None
96+
continue
97+
curr_str += char
98+
curr_str = curr_str.strip()
99+
if curr_str != "":
100+
def_list.append(curr_str)
101+
return def_list
102+
103+
104+
def find_word_in_line(line, word):
105+
"""Find Fortran word in line"""
106+
i0 = -1
107+
for poss_name in WORD_REGEX.finditer(line):
108+
if poss_name.group() == word:
109+
i0 = poss_name.start()
110+
break
111+
return i0, i0 + len(word)
112+
113+
114+
def find_paren_match(test_str):
115+
"""Find matching closing parenthesis by searching forward,
116+
returns -1 if no match is found"""
117+
paren_count = 1
118+
ind = -1
119+
for (i, char) in enumerate(test_str):
120+
if char == "(":
121+
paren_count += 1
122+
elif char == ")":
123+
paren_count -= 1
124+
if paren_count == 0:
125+
return i
126+
return ind

fortls/langserver.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
from multiprocessing import Pool
1212
from pathlib import Path
1313

14+
# Local modules
15+
from fortls.helper_functions import expand_name
1416
from fortls.intrinsics import (
1517
get_intrinsic_keywords,
1618
load_intrinsics,
1719
set_lowercase_intrinsics,
1820
)
19-
20-
# Local modules
2121
from fortls.jsonrpc import path_from_uri, path_to_uri
2222
from fortls.objects import (
2323
CLASS_TYPE_ID,
@@ -31,30 +31,31 @@
3131
climb_type_tree,
3232
find_in_scope,
3333
find_in_workspace,
34+
fortran_ast,
3435
fortran_var,
36+
get_paren_level,
3537
get_use_tree,
3638
get_var_stack,
3739
set_keyword_ordering,
3840
)
39-
from fortls.parse_fortran import (
41+
from fortls.parse_fortran import fortran_file, get_line_context, process_file
42+
from fortls.regex_patterns import (
4043
DQ_STRING_REGEX,
4144
LOGICAL_REGEX,
4245
NUMBER_REGEX,
4346
SQ_STRING_REGEX,
44-
expand_name,
45-
fortran_ast,
46-
fortran_file,
47-
get_line_context,
48-
get_paren_level,
49-
process_file,
5047
)
5148

5249
log = logging.getLogger(__name__)
5350
# Global regexes
5451
FORTRAN_EXT_REGEX = re.compile(r"\.F(77|90|95|03|08|OR|PP)?$", re.I)
52+
# TODO: I think this can be replaced by fortls.regex_patterns
5553
INT_STMNT_REGEX = re.compile(r"^[ ]*[a-z]*$", re.I)
54+
# TODO: I think this can be replaced by fortls.regex_patterns type & class
5655
TYPE_DEF_REGEX = re.compile(r"[ ]*(TYPE|CLASS)[ ]*\([a-z0-9_ ]*$", re.I)
56+
# TODO: I think this can be replaced by fortls.regex_patterns
5757
SCOPE_DEF_REGEX = re.compile(r"[ ]*(MODULE|PROGRAM|SUBROUTINE|FUNCTION)[ ]+", re.I)
58+
# TODO: I think this can be replaced by fortls.regex_patterns END_REGEx
5859
END_REGEX = re.compile(
5960
r"[ ]*(END)( |MODULE|PROGRAM|SUBROUTINE|FUNCTION|TYPE|DO|IF|SELECT)?", re.I
6061
)

0 commit comments

Comments
 (0)