Skip to content

Commit a5fc5ed

Browse files
authored
Merge pull request #396 from fortran-lang/feat/debug-preproc
feat/debug preproc
2 parents e82ce61 + c964f7c commit a5fc5ed

File tree

4 files changed

+115
-46
lines changed

4 files changed

+115
-46
lines changed

docs/options.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ Options for debugging language server
219219
- ``--debug_filepath DEBUG_FILEPATH`` File path for language server tests
220220
- ``--debug_rootpath DEBUG_ROOTPATH`` Root path for language server tests
221221
- ``--debug_parser`` Test source code parser on specified file
222+
- ``--debug_preproc`` Test preprocessor on specified file
222223
- ``--debug_hover`` Test `textDocument/hover` request for specified file and position
223224
- ``--debug_rename RENAME_STRING`` Test `textDocument/rename` request for specified file and position
224225
- ``--debug_actions`` Test `textDocument/codeAction` request for specified file and position

fortls/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
import sys
44
from multiprocessing import freeze_support
55

6-
from .debug import DebugError, debug_lsp, debug_parser, is_debug_mode
6+
from .debug import (
7+
DebugError,
8+
debug_lsp,
9+
debug_parser,
10+
debug_preprocessor,
11+
is_debug_mode,
12+
)
713
from .interface import cli
814
from .jsonrpc import JSONRPC2Connection, ReadWriter
915
from .langserver import LangServer
@@ -20,6 +26,9 @@ def main():
2026
if args.debug_parser:
2127
debug_parser(args)
2228

29+
elif args.debug_preproc:
30+
debug_preprocessor(args)
31+
2332
elif is_debug_mode(args):
2433
debug_lsp(args, vars(args))
2534

fortls/debug.py

Lines changed: 99 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
from __future__ import annotations
22

3+
import logging
34
import os
45
import pprint
6+
import sys
57

68
import json5
79

810
from .helper_functions import only_dirs, resolve_globs
911
from .jsonrpc import JSONRPC2Connection, ReadWriter, path_from_uri
1012
from .langserver import LangServer
11-
from .parsers.internal.parser import FortranFile
13+
from .parsers.internal.parser import FortranFile, preprocess_file
1214

1315

1416
class DebugError(Exception):
@@ -415,54 +417,11 @@ def debug_parser(args):
415417
The arguments parsed from the `ArgumentParser`
416418
"""
417419

418-
def locate_config(root: str) -> str | None:
419-
default_conf_files = [args.config, ".fortlsrc", ".fortls.json5", ".fortls"]
420-
present_conf_files = [
421-
os.path.isfile(os.path.join(root, f)) for f in default_conf_files
422-
]
423-
if not any(present_conf_files):
424-
return None
425-
426-
# Load the first config file found
427-
for f, present in zip(default_conf_files, present_conf_files):
428-
if not present:
429-
continue
430-
config_path = os.path.join(root, f)
431-
return config_path
432-
433-
def read_config(root: str | None):
434-
pp_suffixes = None
435-
pp_defs = {}
436-
include_dirs = set()
437-
if root is None:
438-
return pp_suffixes, pp_defs, include_dirs
439-
440-
# Check for config files
441-
config_path = locate_config(root)
442-
print(f" Config file = {config_path}")
443-
if config_path is None or not os.path.isfile(config_path):
444-
return pp_suffixes, pp_defs, include_dirs
445-
446-
try:
447-
with open(config_path, encoding="utf-8") as fhandle:
448-
config_dict = json5.load(fhandle)
449-
pp_suffixes = config_dict.get("pp_suffixes", None)
450-
pp_defs = config_dict.get("pp_defs", {})
451-
for path in config_dict.get("include_dirs", set()):
452-
include_dirs.update(only_dirs(resolve_globs(path, root)))
453-
454-
if isinstance(pp_defs, list):
455-
pp_defs = {key: "" for key in pp_defs}
456-
except ValueError as e:
457-
print(f"Error {e} while parsing '{config_path}' settings file")
458-
459-
return pp_suffixes, pp_defs, include_dirs
460-
461420
print("\nTesting parser")
462421
separator()
463422

464423
ensure_file_accessible(args.debug_filepath)
465-
pp_suffixes, pp_defs, include_dirs = read_config(args.debug_rootpath)
424+
pp_suffixes, pp_defs, include_dirs = read_config(args.debug_rootpath, args.config)
466425

467426
print(f' File = "{args.debug_filepath}"')
468427
file_obj = FortranFile(args.debug_filepath, pp_suffixes)
@@ -482,6 +441,56 @@ def read_config(root: str | None):
482441
separator()
483442

484443

444+
def debug_preprocessor(args):
445+
"""Debug the preprocessor of the Language Server
446+
Triggered by `--debug_preprocessor` option.
447+
448+
Parameters
449+
----------
450+
args : Namespace
451+
The arguments parsed from the `ArgumentParser`
452+
"""
453+
454+
def sep_lvl2(heading: str):
455+
print("\n" + "=" * 75 + f"\n{heading}\n" + "=" * 75)
456+
457+
print("\nTesting preprocessor")
458+
separator()
459+
460+
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout, format="%(message)s")
461+
462+
file = args.debug_filepath
463+
ensure_file_accessible(file)
464+
with open(file, encoding="utf-8") as f:
465+
lines = f.readlines()
466+
467+
root = args.debug_rootpath if args.debug_rootpath else os.path.dirname(file)
468+
_, pp_defs, include_dirs = read_config(root, args.config)
469+
470+
sep_lvl2("Preprocessor Pass:")
471+
output, skips, defines, defs = preprocess_file(
472+
lines, file, pp_defs, include_dirs, debug=True
473+
)
474+
475+
sep_lvl2("Preprocessor Skipped Lines:")
476+
for line in skips:
477+
print(f" {line}")
478+
479+
sep_lvl2("Preprocessor Macros:")
480+
for key, value in defs.items():
481+
print(f" {key} = {value}")
482+
483+
sep_lvl2("Preprocessor Defines (#define):")
484+
for line in defines:
485+
print(f" {line}")
486+
487+
sep_lvl2("Preprocessor Final Output:")
488+
for line in output:
489+
print(rf" {line.rstrip()}")
490+
491+
separator()
492+
493+
485494
def ensure_file_accessible(filepath: str):
486495
"""Ensure the file exists and is accessible, raising an error if not."""
487496
if not os.path.isfile(filepath):
@@ -500,6 +509,51 @@ def check_request_params(args, loc_needed=True):
500509
print(f" Char = {args.debug_char}\n")
501510

502511

512+
def locate_config(root: str, input_config: str) -> str | None:
513+
default_conf_files = [input_config, ".fortlsrc", ".fortls.json5", ".fortls"]
514+
present_conf_files = [
515+
os.path.isfile(os.path.join(root, f)) for f in default_conf_files
516+
]
517+
if not any(present_conf_files):
518+
return None
519+
520+
# Load the first config file found
521+
for f, present in zip(default_conf_files, present_conf_files):
522+
if not present:
523+
continue
524+
config_path = os.path.join(root, f)
525+
return config_path
526+
527+
528+
def read_config(root: str | None, input_config: str):
529+
pp_suffixes = None
530+
pp_defs = {}
531+
include_dirs = set()
532+
if root is None:
533+
return pp_suffixes, pp_defs, include_dirs
534+
535+
# Check for config files
536+
config_path = locate_config(root, input_config)
537+
print(f" Config file = {config_path}")
538+
if config_path is None or not os.path.isfile(config_path):
539+
return pp_suffixes, pp_defs, include_dirs
540+
541+
try:
542+
with open(config_path, encoding="utf-8") as fhandle:
543+
config_dict = json5.load(fhandle)
544+
pp_suffixes = config_dict.get("pp_suffixes", None)
545+
pp_defs = config_dict.get("pp_defs", {})
546+
for path in config_dict.get("include_dirs", set()):
547+
include_dirs.update(only_dirs(resolve_globs(path, root)))
548+
549+
if isinstance(pp_defs, list):
550+
pp_defs = {key: "" for key in pp_defs}
551+
except ValueError as e:
552+
print(f"Error {e} while parsing '{config_path}' settings file")
553+
554+
return pp_suffixes, pp_defs, include_dirs
555+
556+
503557
def debug_generic(args, test_label, lsp_request, format_results, loc_needed=True):
504558
print(f'\nTesting "{test_label}" request:')
505559
check_request_params(args, loc_needed)

fortls/interface.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ def hide_opt(help: str) -> str:
333333
action="store_true",
334334
help=hide_opt("Test source code parser on specified file"),
335335
)
336+
group.add_argument(
337+
"--debug_preproc",
338+
action="store_true",
339+
help=hide_opt("Test source code preprocessor parser on specified file"),
340+
)
336341
group.add_argument(
337342
"--debug_hover",
338343
action="store_true",

0 commit comments

Comments
 (0)