Skip to content

Commit

Permalink
Merge pull request #10 from gnikit/split-file
Browse files Browse the repository at this point in the history
Split files
  • Loading branch information
gnikit authored Jan 1, 2022
2 parents a0620f6 + 9d3f42c commit 0e9348a
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 261 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# CHANGELONG

## 1.15.1

### Fixes

- Fixes premature end of scope with variables named `end`
([gnikit/fortls#9](https://github.com/gnikit/fortls/issues/9))

## 1.15.0

### Adds

- Adds `--config` option which allows arbitrary named configuration files

## 1.14.4

### Fixes
Expand Down
3 changes: 3 additions & 0 deletions fortls/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sys

PY3K = sys.version_info >= (3, 0)
126 changes: 126 additions & 0 deletions fortls/helper_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from fortls.regex_patterns import (
DQ_STRING_REGEX,
FIXED_COMMENT_LINE_MATCH,
FREE_FORMAT_TEST,
LINE_LABEL_REGEX,
LOGICAL_REGEX,
NAT_VAR_REGEX,
NUMBER_REGEX,
SQ_STRING_REGEX,
WORD_REGEX,
)


def expand_name(line, char_poss):
"""Get full word containing given cursor position"""
# The order here is important.
# WORD will capture substrings in logical and strings
regexs = [LOGICAL_REGEX, SQ_STRING_REGEX, DQ_STRING_REGEX, WORD_REGEX, NUMBER_REGEX]
for r in regexs:
for num_match in r.finditer(line):
if num_match.start(0) <= char_poss and num_match.end(0) >= char_poss:
return num_match.group(0)
return ""


def detect_fixed_format(file_lines):
"""Detect fixed/free format by looking for characters in label columns
and variable declarations before column 6. Treat intersection format
files as free format."""
for line in file_lines:
if FREE_FORMAT_TEST.match(line):
return False
tmp_match = NAT_VAR_REGEX.match(line)
if tmp_match and tmp_match.start(1) < 6:
return False
# Trailing ampersand indicates free or intersection format
if not FIXED_COMMENT_LINE_MATCH.match(line):
line_end = line.split("!")[0].strip()
if len(line_end) > 0 and line_end[-1] == "&":
return False
return True


def strip_line_label(line):
"""Strip leading numeric line label"""
match = LINE_LABEL_REGEX.match(line)
if match is None:
return line, None
else:
line_label = match.group(1)
out_str = line[: match.start(1)] + " " * len(line_label) + line[match.end(1) :]
return out_str, line_label


def strip_strings(in_line, maintain_len=False):
"""String string literals from code line"""

def repl_sq(m):
return "'{0}'".format(" " * (len(m.group()) - 2))

def repl_dq(m):
return '"{0}"'.format(" " * (len(m.group()) - 2))

if maintain_len:
out_line = SQ_STRING_REGEX.sub(repl_sq, in_line)
out_line = DQ_STRING_REGEX.sub(repl_dq, out_line)
else:
out_line = SQ_STRING_REGEX.sub("", in_line)
out_line = DQ_STRING_REGEX.sub("", out_line)
return out_line


def separate_def_list(test_str):
"""Separate definition lists, skipping parenthesis and bracket groups
Examples:
"var1, var2, var3" -> ["var1", "var2", "var3"]
"var, init_var(3) = [1,2,3], array(3,3)" -> ["var", "init_var", "array"]
"""
stripped_str = strip_strings(test_str)
paren_count = 0
def_list = []
curr_str = ""
for char in stripped_str:
if (char == "(") or (char == "["):
paren_count += 1
elif (char == ")") or (char == "]"):
paren_count -= 1
elif (char == ",") and (paren_count == 0):
curr_str = curr_str.strip()
if curr_str != "":
def_list.append(curr_str)
curr_str = ""
elif (curr_str == "") and (len(def_list) == 0):
return None
continue
curr_str += char
curr_str = curr_str.strip()
if curr_str != "":
def_list.append(curr_str)
return def_list


def find_word_in_line(line, word):
"""Find Fortran word in line"""
i0 = -1
for poss_name in WORD_REGEX.finditer(line):
if poss_name.group() == word:
i0 = poss_name.start()
break
return i0, i0 + len(word)


def find_paren_match(test_str):
"""Find matching closing parenthesis by searching forward,
returns -1 if no match is found"""
paren_count = 1
ind = -1
for (i, char) in enumerate(test_str):
if char == "(":
paren_count += 1
elif char == ")":
paren_count -= 1
if paren_count == 0:
return i
return ind
19 changes: 10 additions & 9 deletions fortls/langserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
from multiprocessing import Pool
from pathlib import Path

# Local modules
from fortls.helper_functions import expand_name
from fortls.intrinsics import (
get_intrinsic_keywords,
load_intrinsics,
set_lowercase_intrinsics,
)

# Local modules
from fortls.jsonrpc import path_from_uri, path_to_uri
from fortls.objects import (
CLASS_TYPE_ID,
Expand All @@ -31,30 +31,31 @@
climb_type_tree,
find_in_scope,
find_in_workspace,
fortran_ast,
fortran_var,
get_paren_level,
get_use_tree,
get_var_stack,
set_keyword_ordering,
)
from fortls.parse_fortran import (
from fortls.parse_fortran import fortran_file, get_line_context, process_file
from fortls.regex_patterns import (
DQ_STRING_REGEX,
LOGICAL_REGEX,
NUMBER_REGEX,
SQ_STRING_REGEX,
expand_name,
fortran_ast,
fortran_file,
get_line_context,
get_paren_level,
process_file,
)

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

0 comments on commit 0e9348a

Please sign in to comment.