Skip to content

Refactoring Fortran Objects #324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 21, 2023
121 changes: 76 additions & 45 deletions fortls/helper_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ def strip_line_label(line: str) -> tuple[str, str | None]:
match = FRegex.LINE_LABEL.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
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: str, maintain_len: bool = False) -> str:
Expand Down Expand Up @@ -172,7 +171,7 @@ def separate_def_list(test_str: str) -> list[str] | None:
if curr_str != "":
def_list.append(curr_str)
curr_str = ""
elif (curr_str == "") and (len(def_list) == 0):
elif not def_list:
return None
continue
curr_str += char
Expand All @@ -198,17 +197,20 @@ def find_word_in_line(line: str, word: str) -> Range:
start and end positions (indices) of the word if not found it returns
-1, len(word) -1
"""
i = -1
for poss_name in FRegex.WORD.finditer(line):
if poss_name.group() == word:
i = poss_name.start()
break
i = next(
(
poss_name.start()
for poss_name in FRegex.WORD.finditer(line)
if poss_name.group() == word
),
-1,
)
# TODO: if i == -1: return None makes more sense
return Range(i, i + len(word))


def find_paren_match(string: str) -> int:
"""Find matching closing parenthesis **from an already open parenthesis scope**
"""Find matching closing parenthesis from an already open parenthesis scope
by forward search of the string, returns -1 if no match is found

Parameters
Expand Down Expand Up @@ -237,15 +239,14 @@ def find_paren_match(string: str) -> int:
-1
"""
paren_count = 1
ind = -1
for i, char in enumerate(string):
if char == "(":
paren_count += 1
elif char == ")":
paren_count -= 1
if paren_count == 0:
return i
return ind
return -1


def get_line_prefix(
Expand Down Expand Up @@ -282,17 +283,16 @@ def get_line_prefix(
col += len(prepend_string)
line_prefix = curr_line[:col].lower()
# Ignore string literals
if qs:
if (line_prefix.find("'") > -1) or (line_prefix.find('"') > -1):
sq_count = 0
dq_count = 0
for char in line_prefix:
if (char == "'") and (dq_count % 2 == 0):
sq_count += 1
elif (char == '"') and (sq_count % 2 == 0):
dq_count += 1
if (dq_count % 2 == 1) or (sq_count % 2 == 1):
return None
if qs and ((line_prefix.find("'") > -1) or (line_prefix.find('"') > -1)):
sq_count = 0
dq_count = 0
for char in line_prefix:
if (char == "'") and (dq_count % 2 == 0):
sq_count += 1
elif (char == '"') and (sq_count % 2 == 0):
dq_count += 1
if (dq_count % 2 == 1) or (sq_count % 2 == 1):
return None
return line_prefix


Expand Down Expand Up @@ -329,14 +329,12 @@ def resolve_globs(glob_path: str, root_path: str = None) -> list[str]:
>>> resolve_globs('test') == [str(pathlib.Path(os.getcwd()) / 'test')]
True
"""
# Resolve absolute paths i.e. not in our root_path
if os.path.isabs(glob_path) or not root_path:
p = Path(glob_path).resolve()
root = p.anchor # drive letter + root path
rel = str(p.relative_to(root)) # contains glob pattern
return [str(p.resolve()) for p in Path(root).glob(rel)]
else:
if not os.path.isabs(glob_path) and root_path:
return [str(p.resolve()) for p in Path(root_path).resolve().glob(glob_path)]
p = Path(glob_path).resolve()
root = p.anchor # drive letter + root path
rel = str(p.relative_to(root)) # contains glob pattern
return [str(p.resolve()) for p in Path(root).glob(rel)]


def only_dirs(paths: list[str]) -> list[str]:
Expand Down Expand Up @@ -406,7 +404,9 @@ def map_keywords(keywords: list[str]):
return mapped_keywords, keyword_info


def get_keywords(keywords: list, keyword_info: dict = {}):
def get_keywords(keywords: list, keyword_info: dict = None):
if keyword_info is None:
keyword_info = {}
keyword_strings = []
for keyword_id in keywords:
string_rep = KEYWORD_LIST[keyword_id]
Expand Down Expand Up @@ -461,10 +461,7 @@ def get_paren_substring(string: str) -> str | None:
"""
i1 = string.find("(")
i2 = string.rfind(")")
if -1 < i1 < i2:
return string[i1 + 1 : i2]
else:
return None
return string[i1 + 1 : i2] if -1 < i1 < i2 else None


def get_paren_level(line: str) -> tuple[str, list[Range]]:
Expand Down Expand Up @@ -496,7 +493,7 @@ def get_paren_level(line: str) -> tuple[str, list[Range]]:
('', [Range(start=0, end=0)])

"""
if line == "":
if not line:
return "", [Range(0, 0)]
level = 0
in_string = False
Expand Down Expand Up @@ -526,9 +523,7 @@ def get_paren_level(line: str) -> tuple[str, list[Range]]:
if level == 0:
sections.append(Range(i, i1))
sections.reverse()
out_string = ""
for section in sections:
out_string += line[section.start : section.end]
out_string = "".join(line[section.start : section.end] for section in sections)
return out_string, sections


Expand Down Expand Up @@ -564,7 +559,7 @@ def get_var_stack(line: str) -> list[str]:
>>> get_var_stack('')
['']
"""
if len(line) == 0:
if not line:
return [""]
final_var, sections = get_paren_level(line)
if final_var == "":
Expand All @@ -574,10 +569,9 @@ def get_var_stack(line: str) -> list[str]:
for i, section in enumerate(sections):
if not line[section.start : section.end].strip().startswith("%"):
iLast = i
final_var = ""
for section in sections[iLast:]:
final_var += line[section.start : section.end]

final_var = "".join(
line[section.start : section.end] for section in sections[iLast:]
)
if final_var is not None:
final_var = "%".join([i.strip() for i in final_var.split("%")])
final_op_split: list[str] = FRegex.OBJBREAK.split(final_var)
Expand All @@ -586,6 +580,43 @@ def get_var_stack(line: str) -> list[str]:
return None


def get_placeholders(arg_list: list[str]) -> tuple[str, str]:
"""
Function used to generate placeholders for snippets

Parameters
----------
arg_list : list[str]
Method arguments list

Returns
-------
Tuple[str, str]
Tuple of arguments as a string and snippet string

Examples
--------
>>> get_placeholders(['x', 'y'])
('(x, y)', '(${1:x}, ${2:y})')

>>> get_placeholders(['x=1', 'y=2'])
('(x=1, y=2)', '(x=${1:1}, y=${2:2})')

>>> get_placeholders(['x', 'y=2', 'z'])
('(x, y=2, z)', '(${1:x}, y=${2:2}, ${3:z})')
"""
place_holders = []
for i, arg in enumerate(arg_list):
opt_split = arg.split("=")
if len(opt_split) > 1:
place_holders.append(f"{opt_split[0]}=${{{i+1}:{opt_split[1]}}}")
else:
place_holders.append(f"${{{i+1}:{arg}}}")
arg_str = f"({', '.join(arg_list)})"
arg_snip = f"({', '.join(place_holders)})"
return arg_str, arg_snip


def fortran_md(code: str, docs: str | None):
"""Convert Fortran code to markdown

Expand Down
30 changes: 10 additions & 20 deletions fortls/intrinsics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import glob
import json
import os
import pathlib

from fortls.helper_functions import fortran_md, map_keywords
from fortls.helper_functions import fortran_md, get_placeholders, map_keywords
from fortls.objects import (
FortranAST,
FortranObj,
Expand All @@ -26,9 +27,7 @@ def set_lowercase_intrinsics():


def intrinsics_case(name: str, args: str):
if lowercase_intrinsics:
return name.lower(), args.lower()
return name, args
return (name.lower(), args.lower()) if lowercase_intrinsics else (name, args)


class Intrinsic(FortranObj):
Expand Down Expand Up @@ -68,19 +67,13 @@ def get_snippet(self, name_replace=None, drop_arg=-1):
arg_snip = None
else:
arg_list = self.args.split(",")
arg_str, arg_snip = self.get_placeholders(arg_list)
name = self.name
if name_replace is not None:
name = name_replace
snippet = None
if arg_snip is not None:
snippet = name + arg_snip
arg_str, arg_snip = get_placeholders(arg_list)
name = name_replace if name_replace is not None else self.name
snippet = name + arg_snip if arg_snip is not None else None
return name + arg_str, snippet

def get_signature(self):
arg_sigs = []
for arg in self.args.split(","):
arg_sigs.append({"label": arg})
arg_sigs = [{"label": arg} for arg in self.args.split(",")]
call_sig, _ = self.get_snippet()
return call_sig, self.doc_str, arg_sigs

Expand All @@ -89,13 +82,11 @@ def get_hover(self, long=False):

def get_hover_md(self, long=False):
msg, docs = self.get_hover(long)
msg = msg if msg else ""
msg = msg or ""
return fortran_md(msg, docs)

def is_callable(self):
if self.type == 2:
return True
return False
return self.type == 2


def load_intrinsics():
Expand Down Expand Up @@ -281,8 +272,7 @@ def update_m_intrinsics():
for f in sorted(files):
key = f.replace("M_intrinsics/md/", "")
key = key.replace(".md", "").upper() # remove md extension
with open(f) as md_f:
val = md_f.read()
val = pathlib.Path(f).read_text()
# remove manpage tag
val = val.replace(f"**{key.lower()}**(3)", f"**{key.lower()}**")
val = val.replace(f"**{key.upper()}**(3)", f"**{key.upper()}**")
Expand Down
2 changes: 1 addition & 1 deletion fortls/json_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def range_json(sln: int, sch: int, eln: int = None, ech: int = None):
}


def diagnostic_json(sln: int, sch: int, eln: int, ech: int, msg: str, sev: str):
def diagnostic_json(sln: int, sch: int, eln: int, ech: int, msg: str, sev: int):
return {**range_json(sln, sch, eln, ech), "message": msg, "severity": sev}


Expand Down
Loading