diff --git a/gdtoolkit/formatter/__init__.py b/gdtoolkit/formatter/__init__.py index 3de8a221..1516bcf6 100644 --- a/gdtoolkit/formatter/__init__.py +++ b/gdtoolkit/formatter/__init__.py @@ -1,5 +1,6 @@ from typing import Optional +from types import MappingProxyType from lark import Tree from ..parser import parser @@ -11,6 +12,12 @@ LoosenTreeTransformer, ) +DEFAULT_CONFIG = MappingProxyType( + { + "excluded_directories": {".git"}, + } +) + # pylint: disable-next=too-many-arguments def check_formatting_safety( diff --git a/gdtoolkit/formatter/__main__.py b/gdtoolkit/formatter/__main__.py index fe9397c5..6e5aca7b 100644 --- a/gdtoolkit/formatter/__main__.py +++ b/gdtoolkit/formatter/__main__.py @@ -6,6 +6,7 @@ Usage: gdformat ... [options] + gdformat --dump-default-config Options: -c --check Don't write the files back, @@ -19,19 +20,25 @@ -s --use-spaces= Use spaces for indent instead of tabs. -h --help Show this screen. --version Show version. + --dump-default-config Dump default config to 'gdformatrc' file. Examples: echo 'pass' | gdformat - # reads from STDIN """ import sys +import os +import logging +import pathlib import difflib from typing import List, Tuple, Optional +from types import MappingProxyType import pkg_resources from docopt import docopt import lark +import yaml -from gdtoolkit.formatter import format_code, check_formatting_safety +from gdtoolkit.formatter import format_code, check_formatting_safety, DEFAULT_CONFIG from gdtoolkit.formatter.exceptions import ( TreeInvariantViolation, FormattingStabilityViolation, @@ -44,6 +51,8 @@ lark_unexpected_input_to_str, ) +CONFIG_FILE_NAME = "gdformatrc" + def main(): sys.stdout.reconfigure(encoding="utf-8") @@ -54,6 +63,9 @@ def main(): ), ) + if arguments["--dump-default-config"]: + _dump_default_config() + if arguments["--diff"]: arguments["--check"] = True @@ -64,8 +76,14 @@ def main(): else None ) safety_checks = not arguments["--fast"] + + config_file_path = _find_config_file() + config = _load_config_file_or_default(config_file_path) + _log_config_entries(config) + _update_config_with_missing_entries_inplace(config) + files: List[str] = find_gd_files_from_paths( - arguments[""], excluded_directories=set(".git") + arguments[""], excluded_directories=set(config["excluded_directories"]) ) if files == ["-"]: @@ -78,6 +96,56 @@ def main(): _format_files(files, line_length, spaces_for_indent, safety_checks) +def _dump_default_config() -> None: + # TODO: error handling + assert not os.path.isfile(CONFIG_FILE_NAME) + with open(CONFIG_FILE_NAME, "w", encoding="utf-8") as handle: + handle.write(yaml.dump(DEFAULT_CONFIG.copy())) + sys.exit(0) + + +def _find_config_file() -> Optional[str]: + search_dir = pathlib.Path(os.getcwd()) + config_file_path = None + while search_dir != pathlib.Path(os.path.abspath(os.sep)): + file_path = os.path.join(search_dir, CONFIG_FILE_NAME) + if os.path.isfile(file_path): + config_file_path = file_path + break + file_path = os.path.join(search_dir, ".{}".format(CONFIG_FILE_NAME)) + if os.path.isfile(file_path): + config_file_path = file_path + break + search_dir = search_dir.parent + return config_file_path + + +def _load_config_file_or_default(config_file_path: Optional[str]) -> MappingProxyType: + # TODO: error handling + if config_file_path is not None: + logging.info("Config file found: '%s'", config_file_path) + with open(config_file_path, "r", encoding="utf-8") as handle: + return yaml.load(handle.read(), Loader=yaml.Loader) + + logging.info("""No 'gdformatrc' nor '.gdformatrc' found. Using default config...""") + return DEFAULT_CONFIG + + +def _log_config_entries(config: MappingProxyType) -> None: + logging.info("Loaded config:") + for entry in config.items(): + logging.info(entry) + + +def _update_config_with_missing_entries_inplace(config: dict) -> None: + for key in DEFAULT_CONFIG: + if key not in config: + logging.info( + "Adding missing entry from defaults: %s", (key, DEFAULT_CONFIG[key]) + ) + config[key] = DEFAULT_CONFIG[key] + + def _format_stdin( line_length: int, spaces_for_indent: Optional[int], safety_checks: bool ) -> None: