From 79f795404845f71e1cd9d2888f0ad4e98e561ce7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Jun 2022 17:19:52 +0530 Subject: [PATCH] Cleanup previous PR --- docs/changelog.rst | 2 ++ kittens/diff/collect.py | 31 ++++++++++++++++++------------ kittens/diff/main.py | 2 +- kittens/diff/options/definition.py | 15 +++++++++++---- kittens/diff/options/parse.py | 10 ++++++---- kittens/diff/options/types.py | 5 +++-- kittens/diff/options/utils.py | 9 +++++---- 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9d34ed224..ab60a0217 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -45,6 +45,8 @@ Detailed list of changes - Fix a bug that caused :opt:`macos_colorspace` to always be ``default`` regardless of its actual value (:iss:`5129`) +- diff kitten: A new option :opt:`kitten-diff.ignore_name` to exclude files and directories from being scanned (:pull:`5171`) + - ssh kitten: Fix bash not being executed as a login shell since kitty 0.25.0 (:iss:`5130`) - macOS: When pasting text and the clipboard has a filesystem path, paste the diff --git a/kittens/diff/collect.py b/kittens/diff/collect.py index bda96cc1d..6a159e90e 100755 --- a/kittens/diff/collect.py +++ b/kittens/diff/collect.py @@ -7,9 +7,11 @@ from contextlib import suppress from fnmatch import fnmatch from functools import lru_cache from hashlib import md5 -from itertools import product +from typing import ( + TYPE_CHECKING, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Union +) + from kitty.guess_mime_type import guess_type -from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union if TYPE_CHECKING: from .highlight import DiffHighlight # noqa @@ -39,7 +41,7 @@ class Segment: class Collection: - ignore_paths: Tuple[str, ...] = () + ignore_names: Tuple[str, ...] = () def __init__(self) -> None: self.changes: Dict[str, str] = {} @@ -109,14 +111,19 @@ def resolve_remote_name(path: str, default: str) -> str: return default -def walk(base: str, names: Set[str], pmap: Dict[str, str], ignore_paths: Tuple[str, ...]) -> None: +def allowed_items(items: Sequence[str], ignore_patterns: Sequence[str]) -> Iterator[str]: + for name in items: + for pat in ignore_patterns: + if fnmatch(name, pat): + break + else: + yield name + + +def walk(base: str, names: Set[str], pmap: Dict[str, str], ignore_names: Tuple[str, ...]) -> None: for dirpath, dirnames, filenames in os.walk(base): - ignored = [_dir for _dir, pat in product(dirnames, ignore_paths) if fnmatch(_dir, pat)] - for _dir in ignored: - dirnames.pop(dirnames.index(_dir)) - for filename in filenames: - if any(fnmatch(filename, pat) for pat in ignore_paths if pat): - continue + dirnames[:] = allowed_items(dirnames, ignore_names) + for filename in allowed_items(filenames, ignore_names): path = os.path.abspath(os.path.join(dirpath, filename)) path_name_map[path] = name = os.path.relpath(path, base) names.add(name) @@ -129,8 +136,8 @@ def collect_files(collection: Collection, left: str, right: str) -> None: left_path_map: Dict[str, str] = {} right_path_map: Dict[str, str] = {} - walk(left, left_names, left_path_map, collection.ignore_paths) - walk(right, right_names, right_path_map, collection.ignore_paths) + walk(left, left_names, left_path_map, collection.ignore_names) + walk(right, right_names, right_path_map, collection.ignore_names) common_names = left_names & right_names changed_names = {n for n in common_names if data_for_path(left_path_map[n]) != data_for_path(right_path_map[n])} diff --git a/kittens/diff/main.py b/kittens/diff/main.py index f0e4676ba..271a36c31 100755 --- a/kittens/diff/main.py +++ b/kittens/diff/main.py @@ -655,7 +655,7 @@ def main(args: List[str]) -> None: opts = init_config(cli_opts) set_diff_command(opts.diff_cmd) lines_for_path.replace_tab_by = opts.replace_tab_by - Collection.ignore_paths = opts.ignore_paths + Collection.ignore_names = tuple(opts.ignore_name) left, right = map(get_remote_file, (left, right)) if os.path.isdir(left) != os.path.isdir(right): raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.') diff --git a/kittens/diff/options/definition.py b/kittens/diff/options/definition.py index 9a7d2a669..2378441d8 100755 --- a/kittens/diff/options/definition.py +++ b/kittens/diff/options/definition.py @@ -48,10 +48,17 @@ opt('replace_tab_by', '\\x20\\x20\\x20\\x20', long_text='The string to replace tabs with. Default is to use four spaces.' ) -opt('ignore_paths', '', - option_type='pattern_list', - long_text='''List of patterns that are matched against directory and file -names and ignored when directories are compared. +opt('+ignore_name', '', + option_type='store_multiple', + add_to_default=False, + long_text=''' +A glob pattern that is matched against only the filename of files and directories. Matching +files and directories are ignored when scanning the filesystem to look for files to diff. +Can be specified multiple times to use multiple patterns. For example:: + + ignore_name .git + ignore_name *~ + ignore_name *.pyc ''', ) diff --git a/kittens/diff/options/parse.py b/kittens/diff/options/parse.py index e4ffa26b1..b302fec61 100644 --- a/kittens/diff/options/parse.py +++ b/kittens/diff/options/parse.py @@ -1,7 +1,7 @@ # generated by gen-config.py DO NOT edit import typing -from kittens.diff.options.utils import parse_map, pattern_list, syntax_aliases +from kittens.diff.options.utils import parse_map, store_multiple, syntax_aliases from kitty.conf.utils import merge_dicts, positive_int, python_string, to_color, to_color_or_none @@ -37,6 +37,10 @@ class Parser: def hunk_margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['hunk_margin_bg'] = to_color(val) + def ignore_name(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + for k, v in store_multiple(val, ans["ignore_name"]): + ans["ignore_name"][k] = v + def margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['margin_bg'] = to_color(val) @@ -86,12 +90,10 @@ class Parser: for k in parse_map(val): ans['map'].append(k) - def ignore_paths(self, val: str, ans: typing.Dict[str, typing.Any]): - ans['ignore_paths'] = pattern_list(val) - def create_result_dict() -> typing.Dict[str, typing.Any]: return { + 'ignore_name': {}, 'map': [], } diff --git a/kittens/diff/options/types.py b/kittens/diff/options/types.py index f265c8a95..16b40bbe9 100644 --- a/kittens/diff/options/types.py +++ b/kittens/diff/options/types.py @@ -20,7 +20,7 @@ option_names = ( # {{{ 'highlight_removed_bg', 'hunk_bg', 'hunk_margin_bg', - 'ignore_paths', + 'ignore_name', 'map', 'margin_bg', 'margin_fg', @@ -50,7 +50,6 @@ class Options: highlight_removed_bg: Color = Color(253, 184, 192) hunk_bg: Color = Color(241, 248, 255) hunk_margin_bg: Color = Color(219, 237, 255) - ignore_paths: typing.Tuple[str, ...] = () margin_bg: Color = Color(250, 251, 252) margin_fg: Color = Color(170, 170, 170) margin_filler_bg: typing.Optional[kitty.fast_data_types.Color] = None @@ -66,6 +65,7 @@ class Options: syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'} title_bg: Color = Color(255, 255, 255) title_fg: Color = Color(0, 0, 0) + ignore_name: typing.Dict[str, str] = {} map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = [] key_definitions: KittensKeyMap = {} config_paths: typing.Tuple[str, ...] = () @@ -118,6 +118,7 @@ class Options: defaults = Options() +defaults.ignore_name = {} defaults.map = [ # quit (ParsedShortcut(mods=0, key_name='q'), KeyAction('quit')), # noqa diff --git a/kittens/diff/options/utils.py b/kittens/diff/options/utils.py index 4777306bf..16e7ffe20 100755 --- a/kittens/diff/options/utils.py +++ b/kittens/diff/options/utils.py @@ -3,8 +3,7 @@ # License: GPLv3 Copyright: 2021, Kovid Goyal -import shlex -from typing import Any, Dict, Iterable, List, Tuple, Union +from typing import Any, Container, Dict, Iterable, Tuple, Union from kitty.conf.utils import ( KeyFuncWrapper, KittensKeyDefinition, parse_kittens_key @@ -59,8 +58,10 @@ def syntax_aliases(raw: str) -> Dict[str, str]: return ans -def pattern_list(raw: str) -> Tuple[str, ...]: - return tuple(shlex.split(raw, comments=True)) +def store_multiple(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]: + val = val.strip() + if val not in current_val: + yield val, val def parse_map(val: str) -> Iterable[KittensKeyDefinition]: