Skip to content

Commit 68f0805

Browse files
committed
Add multi-keyword search
1 parent 106e4d0 commit 68f0805

File tree

4 files changed

+65
-8
lines changed

4 files changed

+65
-8
lines changed

AUTHORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ Julian Wollrath
5757
Mattori Birnbaum - me [at] mattori [dot] com - https://mattori.com
5858
Pi R
5959
Alnoman Kamil - noman [at] kamil [dot] gr - https://kamil.gr
60+
Johan XU - xujohan [at] outlook [dot] fr

CHANGELOG.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@ Package maintainers and users who have to manually update their installation
77
may want to subscribe to `GitHub's tag feed
88
<https://github.com/geier/khal/tags.atom>`_.
99

10+
####################################
11+
Added Multi-Keyword Search Capability
12+
13+
Support for multi-keyword search:
14+
The search in command cli.py has been updated to accept a list of search terms
15+
(SEARCH_STRINGS) instead of a single term (SEARCH_STRING). This allows users
16+
to search for events matching one or more specified keywords.
17+
18+
Key Changes:
19+
20+
Replaced search_string argument:
21+
The single search_string argument has been replaced with search_strings,
22+
enabling variable-length keyword input (nargs=-1).
23+
Each search term is processed in a loop, and matching results are added to a
24+
unified set of events.
25+
26+
Consolidation of search results:
27+
Events matching each keyword are combined using a set (all_events) to eliminate
28+
duplicates.
29+
A new event_set is introduced to ensure that events with identical descriptions
30+
are displayed only once.
31+
The final results are sorted and displayed.
32+
1033
0.11.4
1134
======
1235
not released yet

khal/cli.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,9 @@ def printics(ctx, ics, format):
445445
@click.option('--format', '-f',
446446
help=('The format of the events.'))
447447
@click.option('--json', help=("Fields to output in json"), multiple=True)
448-
@click.argument('search_string')
448+
@click.argument('search_strings', nargs=-1)
449449
@click.pass_context
450-
def search(ctx, format, json, search_string, include_calendar, exclude_calendar):
450+
def search(ctx, format, json, search_strings, include_calendar, exclude_calendar):
451451
'''Search for events matching SEARCH_STRING.
452452
453453
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)
461461
ctx.obj['conf'],
462462
multi_calendar_select(ctx, include_calendar, exclude_calendar)
463463
)
464-
events = sorted(collection.search(search_string))
464+
all_events = set()
465+
for term in search_strings:
466+
all_events.update(collection.search(term))
467+
events = sorted(all_events)
465468
event_column = []
466469
term_width, _ = get_terminal_size()
467470
now = dt.datetime.now()
@@ -470,14 +473,18 @@ def search(ctx, format, json, search_string, include_calendar, exclude_calendar)
470473
formatter = human_formatter(format)
471474
else:
472475
formatter = json_formatter(json)
476+
events_set = set()
473477
for event in events:
474478
desc = textwrap.wrap(formatter(
475479
event.attributes(relative_to=now, env=env)), term_width)
476-
event_column.extend(
477-
[colored(d, event.color,
478-
bold_for_light_color=ctx.obj['conf']['view']['bold_for_light_color'])
479-
for d in desc]
480-
)
480+
event_key = ' '.join(desc)
481+
if event_key not in events_set:
482+
events_set.add(event_key)
483+
event_column.extend(
484+
[colored(d, event.color,
485+
bold_for_light_color=ctx.obj['conf']['view']['bold_for_light_color'])
486+
for d in desc]
487+
)
481488
if event_column:
482489
click.echo('\n'.join(event_column))
483490
else:

tests/cli_test.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,3 +1018,29 @@ def test_list_now(runner, tmpdir):
10181018

10191019
result = runner.invoke(main_khal, ['list', 'now'])
10201020
assert not result.exception
1021+
1022+
def test_multi_keyword_search(runner):
1023+
"""
1024+
Test the multi-keyword search functionality with event deduplication.
1025+
"""
1026+
runner = runner()
1027+
1028+
result = runner.invoke(main_khal, "new 14.12.2024 10:00-11:00 meeting conference".split())
1029+
assert not result.exception
1030+
result = runner.invoke(main_khal, "new 14.12.2024 12:00-13:00 birthday".split())
1031+
assert not result.exception
1032+
result = runner.invoke(main_khal, "new 14.12.2024 10:00-11:00 duplicate meeting".split())
1033+
assert not result.exception
1034+
1035+
search_args = ['search', 'meeting', 'conference']
1036+
result = runner.invoke(main_khal, search_args)
1037+
1038+
expected_output = [
1039+
"14.12.2024 10:00-11:00: meeting conference",
1040+
"14.12.2024 10:00-11:00 duplicate meeting"
1041+
]
1042+
output_lines = result.output.strip().split('\n')
1043+
assert sorted(output_lines) == sorted(expected_output), (
1044+
f"Expected: {expected_output}, Got: {output_lines}"
1045+
)
1046+
assert not result.exception

0 commit comments

Comments
 (0)