From eeba468ca19d87980c294d3fa08b172fc0b88ae5 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 13:13:59 +0100 Subject: [PATCH 01/20] 3.7 version fix --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 790d8f9e..6e1b12c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] fail-fast: false runs-on: ${{ matrix.os }} From b4d8bdd31adb6bec01fcda97000554d9050fabe0 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 14:10:05 +0100 Subject: [PATCH 02/20] fixing CI tests, formatting --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 30656e08..af4147df 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ #!/usr/bin/env python -"""Builds the fortls Language Server -""" +"""Builds the fortls Language Server""" import setuptools setuptools.setup() From 0a00200cece9f6aeac31798e3f6f1006df87627f Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 14:15:53 +0100 Subject: [PATCH 03/20] ci tests pass, starting development --- fortls/langserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortls/langserver.py b/fortls/langserver.py index b6a1982e..01de4af1 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -234,7 +234,7 @@ def serve_initialize(self, request: dict): if self.enable_code_actions: server_capabilities["codeActionProvider"] = True if self.notify_init: - self.post_message("fortls initialization complete", Severity.info) + self.post_message("FoRtLs initialization complete", Severity.info) return {"capabilities": server_capabilities} def serve_workspace_symbol(self, request): From c15a1beb0ffb5e6939138b1fca331be34c55cd84 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 15:49:20 +0100 Subject: [PATCH 04/20] black formatting --- README.md | 1 + docs/options.rst | 2 + fortls/fortls.schema.json | 6 ++ fortls/interface.py | 7 +++ fortls/langserver.py | 55 ++++++++++++++++++ fortls/parsers/internal/ast.py | 4 ++ fortls/parsers/internal/parser.py | 50 ++++++++++++++++ fortls/parsers/internal/scope.py | 1 + fortls/regex_patterns.py | 2 + test/test_server.py | 1 + test/test_server_folding.py | 57 +++++++++++++++++++ test/test_source/f90_config.json | 1 + test/test_source/pp/.pp_conf.json | 1 + test/test_source/subdir/test_if_folding.f90 | 44 ++++++++++++++ .../subdir/test_mline_sub_folding.f90 | 16 ++++++ .../subdir/test_select_case_folding.f90 | 10 ++++ 16 files changed, 258 insertions(+) create mode 100644 test/test_server_folding.py create mode 100644 test/test_source/subdir/test_if_folding.f90 create mode 100644 test/test_source/subdir/test_mline_sub_folding.f90 create mode 100644 test/test_source/subdir/test_select_case_folding.f90 diff --git a/README.md b/README.md index 75e4514a..1af61bf6 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ An example for a Configuration file is given below "lowercase_intrinsics": true, "hover_signature": true, "use_signature_help": true, + "folding_range": true, "excl_paths": ["tests/**", "tools/**"], "excl_suffixes": ["_skip.f90"], "include_dirs": ["include/**"], diff --git a/docs/options.rst b/docs/options.rst index e68c4a0d..18efbf35 100644 --- a/docs/options.rst +++ b/docs/options.rst @@ -66,6 +66,8 @@ All the ``fortls`` settings with their default arguments can be found below "hover_signature": false, "hover_language": "fortran90", + "folding_range": false, + "max_line_length": -1, "max_comment_line_length": -1, "disable_diagnostics": false, diff --git a/fortls/fortls.schema.json b/fortls/fortls.schema.json index 62b434d7..78c6cca9 100644 --- a/fortls/fortls.schema.json +++ b/fortls/fortls.schema.json @@ -135,6 +135,12 @@ "title": "Hover Language", "type": "string" }, + "folding_range": { + "default": false, + "description": "Fold editor based on language keywords", + "title": "Folding Range", + "type": "boolean" + }, "max_line_length": { "default": -1, "description": "Maximum line length (default: -1)", diff --git a/fortls/interface.py b/fortls/interface.py index e16d0641..1d214ceb 100644 --- a/fortls/interface.py +++ b/fortls/interface.py @@ -210,6 +210,13 @@ def cli(name: str = "fortls") -> argparse.ArgumentParser: ), ) + # Folding range ------------------------------------------------------------ + group.add_argument( + "--folding_range", + action="store_true", + help="Fold editor based on language keywords", + ) + # Diagnostic options ------------------------------------------------------- group = parser.add_argument_group("Diagnostic options (error swigles)") group.add_argument( diff --git a/fortls/langserver.py b/fortls/langserver.py index 01de4af1..2d31b08e 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -151,6 +151,7 @@ def noop(request: dict): "textDocument/didClose": self.serve_onClose, "textDocument/didChange": self.serve_onChange, "textDocument/codeAction": self.serve_codeActions, + "textDocument/foldingRange": self.serve_folding_range, "initialized": noop, "workspace/didChangeWatchedFiles": noop, "workspace/didChangeConfiguration": noop, @@ -226,6 +227,7 @@ def serve_initialize(self, request: dict): "renameProvider": True, "workspaceSymbolProvider": True, "textDocumentSync": self.sync_type, + "foldingRangeProvider": True, } if self.use_signature_help: server_capabilities["signatureHelpProvider"] = { @@ -1250,6 +1252,56 @@ def serve_codeActions(self, request: dict): action["diagnostics"] = new_diags return action_list + def serve_folding_range(self, request: dict): + # Get parameters from request + params: dict = request["params"] + uri: str = params["textDocument"]["uri"] + path = path_from_uri(uri) + # Find object + file_obj = self.workspace.get(path) + if file_obj is None: + return None + if file_obj.ast is None: + return None + else: + folding_start = file_obj.ast.folding_start + folding_end = file_obj.ast.folding_end + if ( + folding_start is None + or folding_end is None + or len(folding_start) != len(folding_end) + ): + return None + # Construct folding_rage list + folding_ranges = [] + # First treating scope objects... + for scope in file_obj.ast.scope_list: + n_mlines = len(scope.mlines) + # ...with intermediate folding lines (if, select)... + if n_mlines > 0: + self.add_range(folding_ranges, scope.sline - 1, scope.mlines[0] - 2) + for i in range(1, n_mlines): + self.add_range( + folding_ranges, scope.mlines[i - 1] - 1, scope.mlines[i] - 2 + ) + self.add_range(folding_ranges, scope.mlines[-1] - 1, scope.eline - 2) + # ...and without + else: + self.add_range(folding_ranges, scope.sline - 1, scope.eline - 2) + # Then treat comment blocks + folds = len(folding_start) + for i in range(0, folds): + self.add_range(folding_ranges, folding_start[i] - 1, folding_end[i] - 1) + + return folding_ranges + + def add_range(self, folding_ranges: list, start: int, end: int): + folding_range = { + "startLine": start, + "endLine": end, + } + folding_ranges.append(folding_range) + def send_diagnostics(self, uri: str): diag_results, diag_exp = self.get_diagnostics(uri) if diag_results is not None: @@ -1621,6 +1673,9 @@ def _load_config_file_general(self, config_dict: dict) -> None: self.hover_signature = config_dict.get("hover_signature", self.hover_signature) self.hover_language = config_dict.get("hover_language", self.hover_language) + # Folding range -------------------------------------------------------- + self.folding_range = config_dict.get("folding_range", self.folding_range) + # Diagnostic options --------------------------------------------------- self.max_line_length = config_dict.get("max_line_length", self.max_line_length) self.max_comment_line_length = config_dict.get( diff --git a/fortls/parsers/internal/ast.py b/fortls/parsers/internal/ast.py index 94bf448f..61484bc4 100644 --- a/fortls/parsers/internal/ast.py +++ b/fortls/parsers/internal/ast.py @@ -34,6 +34,10 @@ def __init__(self, file_obj=None): self.inherit_objs: list = [] self.linkable_objs: list = [] self.external_objs: list = [] + self.folding_start: list = [] + self.folding_end: list = [] + self.comment_block_start = 0 + self.comment_block_end = 0 self.none_scope = None self.inc_scope = None self.current_scope = None diff --git a/fortls/parsers/internal/parser.py b/fortls/parsers/internal/parser.py index 5095a6d4..dc44120a 100644 --- a/fortls/parsers/internal/parser.py +++ b/fortls/parsers/internal/parser.py @@ -1305,6 +1305,20 @@ def parse( line = multi_lines.pop() get_full = False + # Add comment blocks to folding patterns + if FRegex.FREE_COMMENT.match(line) is not None: + if file_ast.comment_block_start == 0: + file_ast.comment_block_start = line_no + else: + file_ast.comment_block_end = line_no + elif file_ast.comment_block_start != 0: + # Only fold consecutive comment lines + if file_ast.comment_block_end > file_ast.comment_block_start + 1: + file_ast.folding_start.append(file_ast.comment_block_start) + file_ast.folding_end.append(line_no - 1) + file_ast.comment_block_end = 0 + file_ast.comment_block_start = 0 + if line == "": continue # Skip empty lines @@ -1335,6 +1349,10 @@ def parse( # Need to keep the line number for registering start of Scopes line_no_end += len(post_lines) line = "".join([line] + post_lines) + # Add multilines to folding blocks + if line_no != line_no_end: + file_ast.folding_start.append(line_no) + file_ast.folding_end.append(line_no_end) line, line_label = strip_line_label(line) line_stripped = strip_strings(line, maintain_len=True) # Find trailing comments @@ -1353,6 +1371,22 @@ def parse( line_no_comment = line # Test for scope end if file_ast.end_scope_regex is not None: + # treat intermediate folding lines in scopes if they exist + if ( + file_ast.end_scope_regex == FRegex.END_IF + and FRegex.ELSE_IF.match(line_no_comment) is not None + ): + self.update_scope_mlist(file_ast, "#IF", line_no) + elif ( + file_ast.end_scope_regex == FRegex.END_SELECT + and ( + FRegex.SELECT_CASE.match(line_no_comment) + or FRegex.SELECT_DEFAULT.match(line_no_comment) + ) + is not None + ): + self.update_scope_mlist(file_ast, "#SELECT", line_no) + match = FRegex.END_WORD.match(line_no_comment) # Handle end statement if self.parse_end_scope_word(line_no_comment, line_no, file_ast, match): @@ -1488,6 +1522,8 @@ def parse( keywords=keywords, ) file_ast.add_scope(new_sub, FRegex.END_SUB) + if line_no != line_no_end: + file_ast.scope_list[-1].mlines.append(line_no_end) log.debug("%s !!! SUBROUTINE - Ln:%d", line, line_no) elif obj_type == "fun": @@ -1684,6 +1720,20 @@ def parse( log.debug("%s: %s", error["range"], error["message"]) return file_ast + def update_scope_mlist( + self, file_ast: FortranAST, scope_name_prefix: str, line_no: int + ): + """Find the last unclosed scope (eline == sline) containing the + scope_name_prefix and add update its nb of intermediate lines (mlines)""" + + i = 1 + while True: + scope = file_ast.scope_list[-i] + if (scope_name_prefix in scope.name) and (scope.eline == scope.sline): + scope.mlines.append(line_no) + return + i += 1 + def parse_imp_dim(self, line: str): """Parse the implicit dimension of an array e.g. var(3,4), var_name(size(val,1)*10) diff --git a/fortls/parsers/internal/scope.py b/fortls/parsers/internal/scope.py index 7592729a..fc96746b 100644 --- a/fortls/parsers/internal/scope.py +++ b/fortls/parsers/internal/scope.py @@ -38,6 +38,7 @@ def __init__( self.file_ast: FortranAST = file_ast self.sline: int = line_number self.eline: int = line_number + self.mlines: list = [] self.name: str = name self.children: list[T[Scope]] = [] self.members: list = [] diff --git a/fortls/regex_patterns.py b/fortls/regex_patterns.py index 46cc5d28..207443d7 100644 --- a/fortls/regex_patterns.py +++ b/fortls/regex_patterns.py @@ -146,6 +146,8 @@ class FortranRegularExpressions: r" |MODULE|PROGRAM|SUBROUTINE|FUNCTION|PROCEDURE|TYPE|DO|IF|SELECT)?", I, ) + ELSE_IF: Pattern = compile(r"(^|.*\s)(ELSE$|ELSE(\s)|ELSEIF(\s*\())", I) + SELECT_CASE: Pattern = compile(r"((^|\s*\s)(CASE)(\s*\())", I) # Object regex patterns CLASS_VAR: Pattern = compile(r"(TYPE|CLASS)[ ]*\(", I) DEF_KIND: Pattern = compile(r"(\w*)[ ]*\((?:KIND|LEN)?[ =]*(\w*)", I) diff --git a/test/test_server.py b/test/test_server.py index 639ef427..c7e5d2af 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -181,6 +181,7 @@ def check_return(result_array): ["test_free", 2, 0], ["test_gen_type", 5, 1], ["test_generic", 2, 0], + ["test_if_folding", 2, 0], ["test_inherit", 2, 0], ["test_int", 2, 0], ["test_mod", 2, 0], diff --git a/test/test_server_folding.py b/test/test_server_folding.py new file mode 100644 index 00000000..1c03974b --- /dev/null +++ b/test/test_server_folding.py @@ -0,0 +1,57 @@ +from setup_tests import Path, run_request, test_dir, write_rpc_request + + +def folding_req(file_path: Path) -> str: + return write_rpc_request( + 1, + "textDocument/foldingRange", + {"textDocument": {"uri": str(file_path)}}, + ) + + +def validate_folding(results: list, ref: list): + assert len(results) == len(ref) + for i in range(0, len(results)): + assert results[i] == ref[i] + + +def test_if_folding(): + """Test the ranges for several blocks are correct""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_if_folding.f90" + string += folding_req(file_path) + errcode, results = run_request(string) + assert errcode == 0 + ref = [ + {"startLine": 0, "endLine": 42}, + {"startLine": 10, "endLine": 20}, + {"startLine": 21, "endLine": 22}, + {"startLine": 11, "endLine": 19}, + {"startLine": 13, "endLine": 14}, + {"startLine": 15, "endLine": 16}, + {"startLine": 17, "endLine": 18}, + {"startLine": 30, "endLine": 34}, + {"startLine": 35, "endLine": 38}, + {"startLine": 39, "endLine": 40}, + {"startLine": 2, "endLine": 5}, + {"startLine": 25, "endLine": 27}, + {"startLine": 31, "endLine": 33}, + ] + validate_folding(results[1], ref) + + +def test_mline_sub_folding(): + """Test the ranges for several blocks are correct""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_mline_sub_folding.f90" + string += folding_req(file_path) + errcode, results = run_request(string) + assert errcode == 0 + ref = [ + {"startLine": 0, "endLine": 3}, + {"startLine": 4, "endLine": 14}, + {"startLine": 0, "endLine": 4}, + {"startLine": 6, "endLine": 9}, + {"startLine": 12, "endLine": 13}, + ] + validate_folding(results[1], ref) diff --git a/test/test_source/f90_config.json b/test/test_source/f90_config.json index e7f9e0e9..00d8b7d1 100644 --- a/test/test_source/f90_config.json +++ b/test/test_source/f90_config.json @@ -20,6 +20,7 @@ "variable_hover": true, "hover_signature": true, "hover_language": "FortranFreeForm", + "folding_range": true, "max_line_length": 80, "max_comment_line_length": 80, diff --git a/test/test_source/pp/.pp_conf.json b/test/test_source/pp/.pp_conf.json index 0cf75a8a..30dd2844 100644 --- a/test/test_source/pp/.pp_conf.json +++ b/test/test_source/pp/.pp_conf.json @@ -4,6 +4,7 @@ "variable_hover": true, "hover_signature": true, "enable_code_actions": true, + "folding_range": true, "pp_suffixes": [".h", ".F90"], "incl_suffixes": [".h"], "include_dirs": ["include"], diff --git a/test/test_source/subdir/test_if_folding.f90 b/test/test_source/subdir/test_if_folding.f90 new file mode 100644 index 00000000..40e08766 --- /dev/null +++ b/test/test_source/subdir/test_if_folding.f90 @@ -0,0 +1,44 @@ +program test_if_folding + + ! adding some comment lines + ! to check is comment folding + ! works + ! as expected + + implicit none + integer :: i, j + + if (i > 0 .and. j > 0) then + if (i > j) then + j = j + 1 + if (mod(j,100) == 0) then + print*, "j = ", j + else if (mod(j,100) < 50) then + print*, "j = ", j + else + print*, "j = ", j + end if + end if + else + print*, i-j + end if + + if (i==0) & + i = 1; & + j = 2 + + + if (j == i) then + ! testing some + ! comment lines + ! right here + print*, "well done" + else if(.true.) then + print*, "missed something..." + print*, "something more" + ! random comment here + else + print*, "something else" + end if + + end program test_if_folding diff --git a/test/test_source/subdir/test_mline_sub_folding.f90 b/test/test_source/subdir/test_mline_sub_folding.f90 new file mode 100644 index 00000000..8b233710 --- /dev/null +++ b/test/test_source/subdir/test_mline_sub_folding.f90 @@ -0,0 +1,16 @@ +subroutine too_many_args(one, two, & + three, & + four, & + five, & + six ) + +integer, intent(in) :: one, two,& + three, & + four, & + five +integer, intent(out) :: six + +six = five + one + four - two + & +2*three + +end subroutine too_many_args diff --git a/test/test_source/subdir/test_select_case_folding.f90 b/test/test_source/subdir/test_select_case_folding.f90 new file mode 100644 index 00000000..fc0925c0 --- /dev/null +++ b/test/test_source/subdir/test_select_case_folding.f90 @@ -0,0 +1,10 @@ +select case (to_test) +case (1) + six = 6*to_test +case (2) + six = 3*to_test +case (3) + six = 2*to_test +case default + six = 6 +end select From 83572c9693067e363726b1bb5f2345f17b124668 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 16:33:13 +0100 Subject: [PATCH 05/20] formatting --- fortls/parsers/internal/parser.py | 2 +- fortls/regex_patterns.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fortls/parsers/internal/parser.py b/fortls/parsers/internal/parser.py index dc44120a..4a87571b 100644 --- a/fortls/parsers/internal/parser.py +++ b/fortls/parsers/internal/parser.py @@ -1381,7 +1381,7 @@ def parse( file_ast.end_scope_regex == FRegex.END_SELECT and ( FRegex.SELECT_CASE.match(line_no_comment) - or FRegex.SELECT_DEFAULT.match(line_no_comment) + or FRegex.CASE_DEFAULT.match(line_no_comment) ) is not None ): diff --git a/fortls/regex_patterns.py b/fortls/regex_patterns.py index 207443d7..8697df90 100644 --- a/fortls/regex_patterns.py +++ b/fortls/regex_patterns.py @@ -148,6 +148,7 @@ class FortranRegularExpressions: ) ELSE_IF: Pattern = compile(r"(^|.*\s)(ELSE$|ELSE(\s)|ELSEIF(\s*\())", I) SELECT_CASE: Pattern = compile(r"((^|\s*\s)(CASE)(\s*\())", I) + CASE_DEFAULT: Pattern = compile(r"[ ]*CASE[ ]+DEFAULT", I) # Object regex patterns CLASS_VAR: Pattern = compile(r"(TYPE|CLASS)[ ]*\(", I) DEF_KIND: Pattern = compile(r"(\w*)[ ]*\((?:KIND|LEN)?[ =]*(\w*)", I) From a18962cc6b849317e7b7fa5d85f36931395a05bf Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 17:11:51 +0100 Subject: [PATCH 06/20] updating tests --- test/test_server_folding.py | 64 ++++++++++++------- ...est_if_folding.f90 => test_folding_if.f90} | 9 +-- ...lding.f90 => test_folding_select_case.f90} | 6 ++ ...olding.f90 => test_folding_subroutine.f90} | 3 + 4 files changed, 56 insertions(+), 26 deletions(-) rename test/test_source/subdir/{test_if_folding.f90 => test_folding_if.f90} (89%) rename test/test_source/subdir/{test_select_case_folding.f90 => test_folding_select_case.f90} (67%) rename test/test_source/subdir/{test_mline_sub_folding.f90 => test_folding_subroutine.f90} (71%) diff --git a/test/test_server_folding.py b/test/test_server_folding.py index 1c03974b..05ee2460 100644 --- a/test/test_server_folding.py +++ b/test/test_server_folding.py @@ -15,43 +15,63 @@ def validate_folding(results: list, ref: list): assert results[i] == ref[i] -def test_if_folding(): +def test_folding_if(): """Test the ranges for several blocks are correct""" string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "subdir" / "test_if_folding.f90" + file_path = test_dir / "subdir" / "test_folding_if.f90" string += folding_req(file_path) errcode, results = run_request(string) assert errcode == 0 + print(results[1]) ref = [ - {"startLine": 0, "endLine": 42}, - {"startLine": 10, "endLine": 20}, - {"startLine": 21, "endLine": 22}, - {"startLine": 11, "endLine": 19}, - {"startLine": 13, "endLine": 14}, - {"startLine": 15, "endLine": 16}, - {"startLine": 17, "endLine": 18}, - {"startLine": 30, "endLine": 34}, - {"startLine": 35, "endLine": 38}, - {"startLine": 39, "endLine": 40}, - {"startLine": 2, "endLine": 5}, - {"startLine": 25, "endLine": 27}, - {"startLine": 31, "endLine": 33}, + {"startLine": 0, "endLine": 43}, + {"startLine": 11, "endLine": 21}, + {"startLine": 22, "endLine": 23}, + {"startLine": 12, "endLine": 20}, + {"startLine": 14, "endLine": 15}, + {"startLine": 16, "endLine": 17}, + {"startLine": 18, "endLine": 19}, + {"startLine": 31, "endLine": 35}, + {"startLine": 36, "endLine": 39}, + {"startLine": 40, "endLine": 41}, + {"startLine": 2, "endLine": 6}, + {"startLine": 26, "endLine": 28}, + {"startLine": 32, "endLine": 34}, + ] + validate_folding(results[1], ref) + + +def test_folding_select_case(): + """Test the ranges for several blocks are correct""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_folding_select_case.f90" + string += folding_req(file_path) + errcode, results = run_request(string) + assert errcode == 0 + ref = [ + {"startLine": 0, "endLine": 14}, + {"startLine": 4, "endLine": 4}, + {"startLine": 5, "endLine": 6}, + {"startLine": 7, "endLine": 8}, + {"startLine": 9, "endLine": 10}, + {"startLine": 11, "endLine": 12}, ] validate_folding(results[1], ref) -def test_mline_sub_folding(): +def test_folding_subroutine(): """Test the ranges for several blocks are correct""" string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "subdir" / "test_mline_sub_folding.f90" + file_path = test_dir / "subdir" / "test_folding_subroutine.f90" string += folding_req(file_path) errcode, results = run_request(string) assert errcode == 0 + print(results[1]) ref = [ - {"startLine": 0, "endLine": 3}, - {"startLine": 4, "endLine": 14}, - {"startLine": 0, "endLine": 4}, - {"startLine": 6, "endLine": 9}, - {"startLine": 12, "endLine": 13}, + {"startLine": 1, "endLine": 4}, + {"startLine": 5, "endLine": 17}, + {"startLine": 1, "endLine": 5}, + {"startLine": 8, "endLine": 11}, + {"startLine": 15, "endLine": 16}, ] validate_folding(results[1], ref) diff --git a/test/test_source/subdir/test_if_folding.f90 b/test/test_source/subdir/test_folding_if.f90 similarity index 89% rename from test/test_source/subdir/test_if_folding.f90 rename to test/test_source/subdir/test_folding_if.f90 index 40e08766..4059de17 100644 --- a/test/test_source/subdir/test_if_folding.f90 +++ b/test/test_source/subdir/test_folding_if.f90 @@ -1,12 +1,13 @@ -program test_if_folding +program if +!test comment folding ! adding some comment lines ! to check is comment folding ! works ! as expected - implicit none - integer :: i, j +implicit none +integer :: i, j if (i > 0 .and. j > 0) then if (i > j) then @@ -41,4 +42,4 @@ program test_if_folding print*, "something else" end if - end program test_if_folding + end program if diff --git a/test/test_source/subdir/test_select_case_folding.f90 b/test/test_source/subdir/test_folding_select_case.f90 similarity index 67% rename from test/test_source/subdir/test_select_case_folding.f90 rename to test/test_source/subdir/test_folding_select_case.f90 index fc0925c0..190680af 100644 --- a/test/test_source/subdir/test_select_case_folding.f90 +++ b/test/test_source/subdir/test_folding_select_case.f90 @@ -1,3 +1,7 @@ +program select_case + +integer :: to_test, six + select case (to_test) case (1) six = 6*to_test @@ -8,3 +12,5 @@ case default six = 6 end select + +end program select_case diff --git a/test/test_source/subdir/test_mline_sub_folding.f90 b/test/test_source/subdir/test_folding_subroutine.f90 similarity index 71% rename from test/test_source/subdir/test_mline_sub_folding.f90 rename to test/test_source/subdir/test_folding_subroutine.f90 index 8b233710..e2d07372 100644 --- a/test/test_source/subdir/test_mline_sub_folding.f90 +++ b/test/test_source/subdir/test_folding_subroutine.f90 @@ -1,15 +1,18 @@ +!test folding of subroutine arguments and of subroutine body subroutine too_many_args(one, two, & three, & four, & five, & six ) +!test multiline folding integer, intent(in) :: one, two,& three, & four, & five integer, intent(out) :: six +!test_multiline_folding six = five + one + four - two + & 2*three From 83085ca827eb09eb9bcca4a807752beeff396538 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 17:13:32 +0100 Subject: [PATCH 07/20] restore initialization message --- fortls/langserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortls/langserver.py b/fortls/langserver.py index 2d31b08e..47669dd3 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -236,7 +236,7 @@ def serve_initialize(self, request: dict): if self.enable_code_actions: server_capabilities["codeActionProvider"] = True if self.notify_init: - self.post_message("FoRtLs initialization complete", Severity.info) + self.post_message("fortls initialization complete", Severity.info) return {"capabilities": server_capabilities} def serve_workspace_symbol(self, request): From 1e359189515b3171e61e894064090d6b606e4f82 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 17:37:35 +0100 Subject: [PATCH 08/20] fixing tests --- test/test_server.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_server.py b/test/test_server.py index c7e5d2af..78ef2eb1 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -178,10 +178,12 @@ def check_return(result_array): ["test", 6, 7], ["test_abstract", 2, 0], ["test_associate_block", 2, 0], + ["test_folding_if", 2, 0], + ["test_folding_select_case", 2, 0], + ["test_folding_subroutine", 2, 0], ["test_free", 2, 0], ["test_gen_type", 5, 1], ["test_generic", 2, 0], - ["test_if_folding", 2, 0], ["test_inherit", 2, 0], ["test_int", 2, 0], ["test_mod", 2, 0], From df315f914ceb65345dfdba8e07d811679e4bcb4d Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 20:55:57 +0100 Subject: [PATCH 09/20] fix test_server --- test/test_server.py | 4 +--- test/test_server_folding.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/test/test_server.py b/test/test_server.py index 78ef2eb1..f19bb73c 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -178,9 +178,6 @@ def check_return(result_array): ["test", 6, 7], ["test_abstract", 2, 0], ["test_associate_block", 2, 0], - ["test_folding_if", 2, 0], - ["test_folding_select_case", 2, 0], - ["test_folding_subroutine", 2, 0], ["test_free", 2, 0], ["test_gen_type", 5, 1], ["test_generic", 2, 0], @@ -200,6 +197,7 @@ def check_return(result_array): ["test_str2", 13, 5], ["test_sub", 6, 8], ["test_vis_mod", 2, 0], + ["to_test", 13, 2], ) assert len(result_array) == len(objs) for i, obj in enumerate(objs): diff --git a/test/test_server_folding.py b/test/test_server_folding.py index 05ee2460..a95e3d73 100644 --- a/test/test_server_folding.py +++ b/test/test_server_folding.py @@ -22,7 +22,6 @@ def test_folding_if(): string += folding_req(file_path) errcode, results = run_request(string) assert errcode == 0 - print(results[1]) ref = [ {"startLine": 0, "endLine": 43}, {"startLine": 11, "endLine": 21}, From ddf77b8fc911ff04a1bdbdb84db79e5b782ae3d6 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 21:02:03 +0100 Subject: [PATCH 10/20] comments --- fortls/langserver.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fortls/langserver.py b/fortls/langserver.py index 47669dd3..62ffbc9a 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -1274,10 +1274,10 @@ def serve_folding_range(self, request: dict): return None # Construct folding_rage list folding_ranges = [] - # First treating scope objects... + # First treat scope objects ... for scope in file_obj.ast.scope_list: n_mlines = len(scope.mlines) - # ...with intermediate folding lines (if, select)... + # ...with intermediate folding lines (if, select case) ... if n_mlines > 0: self.add_range(folding_ranges, scope.sline - 1, scope.mlines[0] - 2) for i in range(1, n_mlines): @@ -1285,10 +1285,10 @@ def serve_folding_range(self, request: dict): folding_ranges, scope.mlines[i - 1] - 1, scope.mlines[i] - 2 ) self.add_range(folding_ranges, scope.mlines[-1] - 1, scope.eline - 2) - # ...and without + # ...and without, ... else: self.add_range(folding_ranges, scope.sline - 1, scope.eline - 2) - # Then treat comment blocks + # ...and finally treat comment blocks folds = len(folding_start) for i in range(0, folds): self.add_range(folding_ranges, folding_start[i] - 1, folding_end[i] - 1) From 6d14f4e7f968ef82a0ebf90f4410065d3d63470c Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 21:02:48 +0100 Subject: [PATCH 11/20] comment --- fortls/langserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fortls/langserver.py b/fortls/langserver.py index 62ffbc9a..54ee3778 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -1272,9 +1272,9 @@ def serve_folding_range(self, request: dict): or len(folding_start) != len(folding_end) ): return None - # Construct folding_rage list + # Construct folding_rage list: folding_ranges = [] - # First treat scope objects ... + # first treat scope objects ... for scope in file_obj.ast.scope_list: n_mlines = len(scope.mlines) # ...with intermediate folding lines (if, select case) ... From 1c90b1075accd0ee4be8bbc367c07dc5c0bea95d Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 22:02:55 +0100 Subject: [PATCH 12/20] reverting --- .github/workflows/docs_preview.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs_preview.yml b/.github/workflows/docs_preview.yml index a3e593ac..56a0aaa1 100644 --- a/.github/workflows/docs_preview.yml +++ b/.github/workflows/docs_preview.yml @@ -19,13 +19,16 @@ on: concurrency: preview-${{github.ref}} jobs: + job-name: + permissions: + contents: write deploy-preview: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Build docs run: | pip install -e .[dev,docs] @@ -36,3 +39,14 @@ jobs: source-dir: docs/_build/html preview-branch: gh-pages custom-url: fortls.fortran-lang.org + - name: Setup Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + - name: Build and Deploy + run: | + # Commands to build and deploy documentation + git checkout -b gh-pages + git add -A + git commit -m "Deploy preview for PR ${GITHUB_REF##*/} 🛫" + git push --force origin gh-pages From e3466edf585588afe00e5977128881fba54d4d02 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 22:11:13 +0100 Subject: [PATCH 13/20] restoring pre PR test fixes --- .github/workflows/docs_preview.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/docs_preview.yml b/.github/workflows/docs_preview.yml index 56a0aaa1..a3e593ac 100644 --- a/.github/workflows/docs_preview.yml +++ b/.github/workflows/docs_preview.yml @@ -19,16 +19,13 @@ on: concurrency: preview-${{github.ref}} jobs: - job-name: - permissions: - contents: write deploy-preview: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.11" - name: Build docs run: | pip install -e .[dev,docs] @@ -39,14 +36,3 @@ jobs: source-dir: docs/_build/html preview-branch: gh-pages custom-url: fortls.fortran-lang.org - - name: Setup Git - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - name: Build and Deploy - run: | - # Commands to build and deploy documentation - git checkout -b gh-pages - git add -A - git commit -m "Deploy preview for PR ${GITHUB_REF##*/} 🛫" - git push --force origin gh-pages From 903da1d3bd3a5d5c81e165cd16481081157b890b Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 22:17:08 +0100 Subject: [PATCH 14/20] allow write for docs_preview --- .github/workflows/docs_preview.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs_preview.yml b/.github/workflows/docs_preview.yml index a3e593ac..488f1a89 100644 --- a/.github/workflows/docs_preview.yml +++ b/.github/workflows/docs_preview.yml @@ -19,6 +19,9 @@ on: concurrency: preview-${{github.ref}} jobs: + job-name: + permissions: + contents: write deploy-preview: runs-on: ubuntu-latest steps: From 38d5f9cc13b3d118c1b5fc4d59d0d9951a22bb1c Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 22:49:26 +0100 Subject: [PATCH 15/20] testing if branch name is an issue --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 08439b38..810bcc6d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ master, dev ] + branches: [ master, dev, in_venv ] pull_request: # The branches below must be a subset of the branches above - branches: [ master, dev ] + branches: [ master, dev, in_venv ] schedule: - cron: '24 7 * * 2' From 4b1afac55f7c9f1dff2c5681d2bad89abfcdf07e Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 22:54:11 +0100 Subject: [PATCH 16/20] revert previous commit --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 810bcc6d..08439b38 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ master, dev, in_venv ] + branches: [ master, dev ] pull_request: # The branches below must be a subset of the branches above - branches: [ master, dev, in_venv ] + branches: [ master, dev ] schedule: - cron: '24 7 * * 2' From 366db838067daa11a22da6071fec7818c5d13166 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 23:12:24 +0100 Subject: [PATCH 17/20] n-th try --- .github/workflows/docs.yml | 2 +- .github/workflows/update-intrinsics.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7c68730f..ca7dfb19 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,5 +17,5 @@ jobs: uses: peaceiris/actions-gh-pages@v4 if: github.ref == 'refs/heads/master' with: - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.CODECOV_TOKEN }} publish_dir: docs/_build/html diff --git a/.github/workflows/update-intrinsics.yml b/.github/workflows/update-intrinsics.yml index 0c7a0e57..033c85ba 100644 --- a/.github/workflows/update-intrinsics.yml +++ b/.github/workflows/update-intrinsics.yml @@ -27,7 +27,7 @@ jobs: - name: Create Pull Request uses: peter-evans/create-pull-request@v7 with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.CODECOV_TOKEN }} commit-message: "docs: update M_intrinsics" title: Update M_intrinsics body: | From e35ec74175ba94533d38a2b97712c09f58daefcf Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 23:15:15 +0100 Subject: [PATCH 18/20] n+1-th try --- .github/workflows/docs.yml | 2 +- .github/workflows/main.yml | 2 +- .github/workflows/update-intrinsics.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ca7dfb19..7c68730f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,5 +17,5 @@ jobs: uses: peaceiris/actions-gh-pages@v4 if: github.ref == 'refs/heads/master' with: - github_token: ${{ secrets.CODECOV_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/_build/html diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6e1b12c5..aea23c0f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,4 +41,4 @@ jobs: fail_ci_if_error: true verbose: true env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + CODECOV_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update-intrinsics.yml b/.github/workflows/update-intrinsics.yml index 033c85ba..0c7a0e57 100644 --- a/.github/workflows/update-intrinsics.yml +++ b/.github/workflows/update-intrinsics.yml @@ -27,7 +27,7 @@ jobs: - name: Create Pull Request uses: peter-evans/create-pull-request@v7 with: - token: ${{ secrets.CODECOV_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} commit-message: "docs: update M_intrinsics" title: Update M_intrinsics body: | From b481d531a9e62f1a3ca7f1ee76da0747c33874e9 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Thu, 13 Feb 2025 23:18:30 +0100 Subject: [PATCH 19/20] revert last commit --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aea23c0f..6e1b12c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,4 +41,4 @@ jobs: fail_ci_if_error: true verbose: true env: - CODECOV_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 0529f0e1017823c5f780c780169b5a450393b935 Mon Sep 17 00:00:00 2001 From: Dionysios Grapsas Date: Mon, 10 Mar 2025 10:08:43 +0100 Subject: [PATCH 20/20] resovling conflicts --- .github/workflows/main.yml | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6e1b12c5..aadd659b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] fail-fast: false runs-on: ${{ matrix.os }} diff --git a/setup.py b/setup.py index af4147df..a7750973 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ #!/usr/bin/env python """Builds the fortls Language Server""" + import setuptools setuptools.setup()