From 74f3c6ab60c93665f97ae65125be40ff731e8084 Mon Sep 17 00:00:00 2001 From: Albert Ziegenhagel Date: Thu, 29 Jun 2023 17:08:29 +0200 Subject: [PATCH 1/3] Ignore preprocessor lines when detecting fixed form fortran Currently a file with the content ``` #if defined(A) && !defined(B) c This is a fixed-form style comment #endif ``` is categorized as free-form Fortran because the preprocess if-clause contains ampersands and exclamation marks. This commit makes the detection algorithm ignore all lines starting with `#` if pre-processing is enabled for that file. Pre-processor lines are not valid Fortran code and should IMO not be taken into account when trying to determine the Fortran form. --- fortls/helper_functions.py | 12 +++++++++++- fortls/parsers/internal/parser.py | 4 ++-- test/test_helper.py | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/test_helper.py diff --git a/fortls/helper_functions.py b/fortls/helper_functions.py index b5e42167..195ba80c 100644 --- a/fortls/helper_functions.py +++ b/fortls/helper_functions.py @@ -38,7 +38,7 @@ def expand_name(line: str, char_pos: int) -> str: return "" -def detect_fixed_format(file_lines: list[str]) -> bool: +def detect_fixed_format(file_lines: list[str], preproc: bool = False) -> bool: """Detect fixed/free format by looking for characters in label columns and variable declarations before column 6. Treat intersection format files as free format. @@ -47,6 +47,9 @@ def detect_fixed_format(file_lines: list[str]) -> bool: ---------- file_lines : list[str] List of consecutive file lines + preproc : bool + If true, preprocessor directives (lines starting with '#') will be + ignored Returns ------- @@ -68,8 +71,15 @@ def detect_fixed_format(file_lines: list[str]) -> bool: Lines wih ampersands are not fixed format >>> detect_fixed_format(['trailing line & ! comment']) False + + But preprocessor lines might be ignored + >>> detect_fixed_format(['#if defined(A) && !defined(B)'], preproc=True) + True """ for line in file_lines: + # Ignore preprocessor lines + if preproc and line.startswith("#"): + continue if FRegex.FREE_FORMAT_TEST.match(line): return False tmp_match = FRegex.VAR.match(line) diff --git a/fortls/parsers/internal/parser.py b/fortls/parsers/internal/parser.py index ae5cfa1e..8d8ad19d 100644 --- a/fortls/parsers/internal/parser.py +++ b/fortls/parsers/internal/parser.py @@ -902,7 +902,7 @@ def load_from_disk(self) -> tuple[str | None, bool | None]: self.hash = hash self.contents_split = contents.splitlines() - self.fixed = detect_fixed_format(self.contents_split) + self.fixed = detect_fixed_format(self.contents_split, self.preproc) self.contents_pp = self.contents_split self.nLines = len(self.contents_split) return None, True @@ -1011,7 +1011,7 @@ def set_contents(self, contents_split: list, detect_format: bool = True): self.contents_pp = self.contents_split self.nLines = len(self.contents_split) if detect_format: - self.fixed = detect_fixed_format(self.contents_split) + self.fixed = detect_fixed_format(self.contents_split, self.preproc) def get_line(self, line_no: int, pp_content: bool = False) -> str: """Get single line from file""" diff --git a/test/test_helper.py b/test/test_helper.py new file mode 100644 index 00000000..fb208916 --- /dev/null +++ b/test/test_helper.py @@ -0,0 +1,20 @@ +from fortls.helper_functions import detect_fixed_format + + +def test_detect_fixed_format(): + assert detect_fixed_format([" free format"]) is False + assert detect_fixed_format([" INTEGER, PARAMETER :: N = 10"]) is False + assert detect_fixed_format(["C Fixed format"]) is True + assert detect_fixed_format(["trailing line & ! comment"]) is False + assert ( + detect_fixed_format( + ["#if defined(A) && !defined(B)", "C Fixed format", "#endif"], preproc=True + ) + is True + ) + assert ( + detect_fixed_format( + ["#if defined(A) && !defined(B)", " free format", "#endif"], preproc=True + ) + is False + ) From 65d0ca8f70fd7cefff4b8795bbe5ad8d46d0f7c3 Mon Sep 17 00:00:00 2001 From: Albert Ziegenhagel Date: Thu, 19 Oct 2023 13:49:39 +0200 Subject: [PATCH 2/3] Add changelog entry about changed fixed form detection --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a478156..1b78a65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ ([#219](https://github.com/fortran-lang/fortls/issues/219)) - Changed hover messages and signature help to use Markdown ([#45](https://github.com/fortran-lang/fortls/issues/45)) +- Changed automatic detection of fixed/free-form of files to ignore + preprocessor lines. + ([#302](https://github.com/fortran-lang/fortls/pull/302)) ### Fixed From f118412bd50f566609618638bc355c2fd11b4211 Mon Sep 17 00:00:00 2001 From: Albert Ziegenhagel Date: Tue, 26 Sep 2023 15:12:26 -0700 Subject: [PATCH 3/3] Remove `preproc` from`detect_fixed_format` and handle line continuation Additionally, `test_helper.py` has been removed since all the test cases in there are in the examples of the docstring and hence they are covered by doctest already. --- fortls/helper_functions.py | 27 ++++++++++++++++++++------- fortls/parsers/internal/parser.py | 4 ++-- test/test_helper.py | 20 -------------------- 3 files changed, 22 insertions(+), 29 deletions(-) delete mode 100644 test/test_helper.py diff --git a/fortls/helper_functions.py b/fortls/helper_functions.py index 195ba80c..fd1a9030 100644 --- a/fortls/helper_functions.py +++ b/fortls/helper_functions.py @@ -38,7 +38,7 @@ def expand_name(line: str, char_pos: int) -> str: return "" -def detect_fixed_format(file_lines: list[str], preproc: bool = False) -> bool: +def detect_fixed_format(file_lines: list[str]) -> bool: """Detect fixed/free format by looking for characters in label columns and variable declarations before column 6. Treat intersection format files as free format. @@ -47,9 +47,6 @@ def detect_fixed_format(file_lines: list[str], preproc: bool = False) -> bool: ---------- file_lines : list[str] List of consecutive file lines - preproc : bool - If true, preprocessor directives (lines starting with '#') will be - ignored Returns ------- @@ -72,13 +69,29 @@ def detect_fixed_format(file_lines: list[str], preproc: bool = False) -> bool: >>> detect_fixed_format(['trailing line & ! comment']) False - But preprocessor lines might be ignored - >>> detect_fixed_format(['#if defined(A) && !defined(B)'], preproc=True) + But preprocessor lines will be ignored + >>> detect_fixed_format( + ... ['#if defined(A) && !defined(B)', 'C Fixed format', '#endif']) True + + >>> detect_fixed_format( + ... ['#if defined(A) && !defined(B)', ' free format', '#endif']) + False + + And preprocessor line-continuation is taken into account + >>> detect_fixed_format( + ... ['#if defined(A) \\\\ ', ' && !defined(B)', 'C Fixed format', '#endif']) + True + + >>> detect_fixed_format( + ... ['#if defined(A) \\\\', '&& \\\\', '!defined(B)', ' free format', '#endif']) + False """ + pp_continue = False for line in file_lines: # Ignore preprocessor lines - if preproc and line.startswith("#"): + if line.startswith("#") or pp_continue: + pp_continue = line.rstrip().endswith("\\") continue if FRegex.FREE_FORMAT_TEST.match(line): return False diff --git a/fortls/parsers/internal/parser.py b/fortls/parsers/internal/parser.py index 8d8ad19d..ae5cfa1e 100644 --- a/fortls/parsers/internal/parser.py +++ b/fortls/parsers/internal/parser.py @@ -902,7 +902,7 @@ def load_from_disk(self) -> tuple[str | None, bool | None]: self.hash = hash self.contents_split = contents.splitlines() - self.fixed = detect_fixed_format(self.contents_split, self.preproc) + self.fixed = detect_fixed_format(self.contents_split) self.contents_pp = self.contents_split self.nLines = len(self.contents_split) return None, True @@ -1011,7 +1011,7 @@ def set_contents(self, contents_split: list, detect_format: bool = True): self.contents_pp = self.contents_split self.nLines = len(self.contents_split) if detect_format: - self.fixed = detect_fixed_format(self.contents_split, self.preproc) + self.fixed = detect_fixed_format(self.contents_split) def get_line(self, line_no: int, pp_content: bool = False) -> str: """Get single line from file""" diff --git a/test/test_helper.py b/test/test_helper.py deleted file mode 100644 index fb208916..00000000 --- a/test/test_helper.py +++ /dev/null @@ -1,20 +0,0 @@ -from fortls.helper_functions import detect_fixed_format - - -def test_detect_fixed_format(): - assert detect_fixed_format([" free format"]) is False - assert detect_fixed_format([" INTEGER, PARAMETER :: N = 10"]) is False - assert detect_fixed_format(["C Fixed format"]) is True - assert detect_fixed_format(["trailing line & ! comment"]) is False - assert ( - detect_fixed_format( - ["#if defined(A) && !defined(B)", "C Fixed format", "#endif"], preproc=True - ) - is True - ) - assert ( - detect_fixed_format( - ["#if defined(A) && !defined(B)", " free format", "#endif"], preproc=True - ) - is False - )