Skip to content

Commit 2f4dfe4

Browse files
committed
New command 'append': Append results of another (notmuch) query to search buffer
1 parent 32b885d commit 2f4dfe4

File tree

4 files changed

+74
-26
lines changed

4 files changed

+74
-26
lines changed

alot/buffers/search.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ class SearchBuffer(Buffer):
2121
def __init__(self, ui, initialquery='', sort_order=None):
2222
self.dbman = ui.dbman
2323
self.ui = ui
24-
self.querystring = initialquery
24+
# We store a list of notmuch query strings and this buffer will
25+
# display the results for each query one after the other
26+
self.querystrings = [initialquery]
2527
default_order = settings.get('search_threads_sort_order')
2628
self.sort_order = sort_order or default_order
2729
self.result_count = 0
@@ -34,12 +36,13 @@ def __init__(self, ui, initialquery='', sort_order=None):
3436

3537
def __str__(self):
3638
formatstring = '[search] for "%s" (%d message%s)'
37-
return formatstring % (self.querystring, self.result_count,
39+
return formatstring % ('" + "'.join(self.querystrings),
40+
self.result_count,
3841
's' if self.result_count > 1 else '')
3942

4043
def get_info(self):
4144
info = {}
42-
info['querystring'] = self.querystring
45+
info['querystring'] = '" + "'.join(self.querystrings)
4346
info['result_count'] = self.result_count
4447
info['result_count_positive'] = 's' if self.result_count > 1 else ''
4548
return info
@@ -61,20 +64,27 @@ def rebuild(self, reverse=False):
6164
if exclude_tags:
6265
exclude_tags = [t for t in exclude_tags.split(';') if t]
6366

64-
try:
65-
self.result_count = self.dbman.count_messages(self.querystring)
66-
threads = self.dbman.get_threads(
67-
self.querystring, order, exclude_tags)
68-
except NotmuchError:
69-
self.ui.notify('malformed query string: %s' % self.querystring,
70-
'error')
71-
self.listbox = urwid.ListBox([])
72-
self.body = self.listbox
73-
return
74-
75-
self.threadlist = IterableWalker(threads, ThreadlineWidget,
76-
dbman=self.dbman,
77-
reverse=reverse)
67+
self.result_count = 0
68+
self.threadlist = None
69+
for query in self.querystrings:
70+
try:
71+
self.result_count += self.dbman.count_messages(query)
72+
threads = self.dbman.get_threads(
73+
query, order, exclude_tags)
74+
except NotmuchError:
75+
self.ui.notify('malformed query string: %s' % query,
76+
'error')
77+
self.listbox = urwid.ListBox([])
78+
self.body = self.listbox
79+
return
80+
81+
iterablewalker = IterableWalker(threads, ThreadlineWidget,
82+
dbman=self.dbman,
83+
reverse=reverse)
84+
if self.threadlist:
85+
self.threadlist.append(iterablewalker)
86+
else:
87+
self.threadlist = iterablewalker
7888

7989
self.listbox = urwid.ListBox(self.threadlist)
8090
self.body = self.listbox

alot/commands/globals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def apply(self, ui):
112112
open_searches = ui.get_buffers_of_type(buffers.SearchBuffer)
113113
to_be_focused = None
114114
for sb in open_searches:
115-
if sb.querystring == self.query:
115+
if sb.querystrings == [self.query]:
116116
to_be_focused = sb
117117
if to_be_focused:
118118
if ui.current_buffer != to_be_focused:

alot/commands/search.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ def apply(self, ui):
3535
if not self.thread:
3636
self.thread = ui.current_buffer.get_selected_thread()
3737
if self.thread:
38-
query = ui.current_buffer.querystring
38+
query_list = ui.current_buffer.querystrings
3939
logging.info('open thread view for %s', self.thread)
4040

4141
tb = buffers.ThreadBuffer(ui, self.thread)
4242
ui.buffer_open(tb)
43-
tb.unfold_matching(query)
43+
for query in query_list:
44+
tb.unfold_matching(query)
4445

4546

4647
@registerCommand(MODE, 'refine', help='refine query', arguments=[
@@ -70,9 +71,9 @@ def __init__(self, query=None, sort=None, **kwargs):
7071
def apply(self, ui):
7172
if self.querystring or self.sort_order:
7273
sbuffer = ui.current_buffer
73-
oldquery = sbuffer.querystring
74+
oldquery = sbuffer.querystrings[-1]
7475
if self.querystring not in [None, oldquery]:
75-
sbuffer.querystring = self.querystring
76+
sbuffer.querystrings[-1] = self.querystring
7677
sbuffer = ui.current_buffer
7778
if self.sort_order:
7879
sbuffer.sort_order = self.sort_order
@@ -90,13 +91,46 @@ class RefinePromptCommand(Command):
9091

9192
async def apply(self, ui):
9293
sbuffer = ui.current_buffer
93-
oldquery = sbuffer.querystring
94+
oldquery = sbuffer.querystrings[-1]
9495
return await ui.apply_command(PromptCommand('refine ' + oldquery))
9596

9697

9798
RetagPromptCommand = registerCommand(MODE, 'retagprompt')(RetagPromptCommand)
9899

99100

101+
@registerCommand(MODE, 'append', usage='append query', arguments=[
102+
(['--sort'], {'help': 'sort order', 'choices': [
103+
'oldest_first', 'newest_first', 'message_id', 'unsorted']}),
104+
(['query'], {'nargs': argparse.REMAINDER, 'help': 'search string'})])
105+
class AppendCommand(Command):
106+
107+
"""append the results of a new query to this buffer. Search obeys the notmuch
108+
:ref:`search.exclude_tags <search.exclude_tags>` setting."""
109+
repeatable = True
110+
111+
def __init__(self, query, sort=None, **kwargs):
112+
"""
113+
:param query: notmuch querystring
114+
:type query: str
115+
:param sort: how to order results. Must be one of
116+
'oldest_first', 'newest_first', 'message_id' or
117+
'unsorted'.
118+
:type sort: str
119+
"""
120+
self.query = ' '.join(query)
121+
self.order = sort
122+
Command.__init__(self, **kwargs)
123+
124+
def apply(self, ui):
125+
if self.query:
126+
sbuffer = ui.current_buffer
127+
sbuffer.querystrings.append(self.query)
128+
sbuffer.rebuild()
129+
ui.update()
130+
else:
131+
ui.notify('empty query string')
132+
133+
100134
@registerCommand(
101135
MODE, 'tag', forced={'action': 'add'},
102136
arguments=[
@@ -176,7 +210,7 @@ async def apply(self, ui):
176210
if threadline_widget is None:
177211
return
178212

179-
testquery = searchbuffer.querystring
213+
testquery = searchbuffer.querystrings[-1]
180214
thread = threadline_widget.get_thread()
181215
if not self.allm:
182216
testquery = "(%s) AND thread:%s" % (testquery,
@@ -198,7 +232,7 @@ def refresh():
198232
else:
199233
threadline_widget.rebuild()
200234
searchbuffer.result_count = searchbuffer.dbman.count_messages(
201-
searchbuffer.querystring)
235+
searchbuffer.querystrings[-1])
202236
else:
203237
searchbuffer.rebuild()
204238

@@ -265,5 +299,5 @@ class SaveQueryCommand(GlobalSaveQueryCommand):
265299
def apply(self, ui):
266300
searchbuffer = ui.current_buffer
267301
if not self.query:
268-
self.query = searchbuffer.querystring
302+
self.query = searchbuffer.querystrings[-1]
269303
GlobalSaveQueryCommand.apply(self, ui)

alot/walker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Copyright © 2018 Dylan Baker
33
# This file is released under the GNU GPL, version 3 or a later revision.
44
# For further details see the COPYING file
5+
import itertools
56
import logging
67
import urwid
78

@@ -92,3 +93,6 @@ def _get_next_item(self):
9293

9394
def get_lines(self):
9495
return self.lines
96+
97+
def append(self, iterableWalker):
98+
self.iterable = itertools.chain(self.iterable, iterableWalker.iterable)

0 commit comments

Comments
 (0)