diff --git a/AUTHORS.txt b/AUTHORS.txt index 891fe947f..789a765d3 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -57,3 +57,4 @@ Julian Wollrath Mattori Birnbaum - me [at] mattori [dot] com - https://mattori.com Pi R Alnoman Kamil - noman [at] kamil [dot] gr - https://kamil.gr +Johan XU - xujohan [at] outlook [dot] fr diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 47cb66302..8bbb79e8c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,29 @@ Package maintainers and users who have to manually update their installation may want to subscribe to `GitHub's tag feed `_. +#################################### +Added Multi-Keyword Search Capability + +Support for multi-keyword search: +The search in command cli.py has been updated to accept a list of search terms +(SEARCH_STRINGS) instead of a single term (SEARCH_STRING). This allows users +to search for events matching one or more specified keywords. + +Key Changes: + +Replaced search_string argument: +The single search_string argument has been replaced with search_strings, +enabling variable-length keyword input (nargs=-1). +Each search term is processed in a loop, and matching results are added to a +unified set of events. + +Consolidation of search results: +Events matching each keyword are combined using a set (all_events) to eliminate +duplicates. +A new event_set is introduced to ensure that events with identical descriptions +are displayed only once. +The final results are sorted and displayed. + 0.11.4 ====== not released yet diff --git a/khal/cli.py b/khal/cli.py index 51ad78b92..2566f1c1b 100644 --- a/khal/cli.py +++ b/khal/cli.py @@ -445,9 +445,9 @@ def printics(ctx, ics, format): @click.option('--format', '-f', help=('The format of the events.')) @click.option('--json', help=("Fields to output in json"), multiple=True) -@click.argument('search_string') +@click.argument('search_strings', nargs=-1) @click.pass_context -def search(ctx, format, json, search_string, include_calendar, exclude_calendar): +def search(ctx, format, json, search_strings, include_calendar, exclude_calendar): '''Search for events matching SEARCH_STRING. For recurring events, only the master event and different overwritten @@ -461,7 +461,10 @@ def search(ctx, format, json, search_string, include_calendar, exclude_calendar) ctx.obj['conf'], multi_calendar_select(ctx, include_calendar, exclude_calendar) ) - events = sorted(collection.search(search_string)) + all_events = set() + for term in search_strings: + all_events.update(collection.search(term)) + events = sorted(all_events) event_column = [] term_width, _ = get_terminal_size() now = dt.datetime.now() @@ -470,14 +473,18 @@ def search(ctx, format, json, search_string, include_calendar, exclude_calendar) formatter = human_formatter(format) else: formatter = json_formatter(json) + events_set = set() for event in events: desc = textwrap.wrap(formatter( event.attributes(relative_to=now, env=env)), term_width) - event_column.extend( - [colored(d, event.color, - bold_for_light_color=ctx.obj['conf']['view']['bold_for_light_color']) - for d in desc] - ) + event_key = ' '.join(desc) + if event_key not in events_set: + events_set.add(event_key) + event_column.extend( + [colored(d, event.color, + bold_for_light_color=ctx.obj['conf']['view']['bold_for_light_color']) + for d in desc] + ) if event_column: click.echo('\n'.join(event_column)) else: diff --git a/tests/cli_test.py b/tests/cli_test.py index 5f90dbaaf..0fdabaeae 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -1018,3 +1018,29 @@ def test_list_now(runner, tmpdir): result = runner.invoke(main_khal, ['list', 'now']) assert not result.exception + +def test_multi_keyword_search(runner): + """ + Test the multi-keyword search functionality with event deduplication. + """ + runner = runner() + + result = runner.invoke(main_khal, "new 14.12.2024 10:00-11:00 meeting conference".split()) + assert not result.exception + result = runner.invoke(main_khal, "new 14.12.2024 12:00-13:00 birthday".split()) + assert not result.exception + result = runner.invoke(main_khal, "new 14.12.2024 10:00-11:00 duplicate meeting".split()) + assert not result.exception + + search_args = ['search', 'meeting', 'conference'] + result = runner.invoke(main_khal, search_args) + + expected_output = [ + "14.12.2024 10:00-11:00: meeting conference", + "14.12.2024 10:00-11:00 duplicate meeting" + ] + output_lines = result.output.strip().split('\n') + assert sorted(output_lines) == sorted(expected_output), ( + f"Expected: {expected_output}, Got: {output_lines}" + ) + assert not result.exception