summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormathieui <mathieui@mathieui.net>2016-08-21 15:27:53 +0200
committermathieui <mathieui@mathieui.net>2016-08-21 15:39:30 +0200
commit84e59b05ff0a17178da9ecdb6c5d084e48b42763 (patch)
tree224948d7c3d49a0005bff9390260357b3ec9c60e
parent6c270b363ac018dfd4d66b23af95efcc35610da0 (diff)
downloadpoezio-84e59b05ff0a17178da9ecdb6c5d084e48b42763.tar.gz
poezio-84e59b05ff0a17178da9ecdb6c5d084e48b42763.tar.bz2
poezio-84e59b05ff0a17178da9ecdb6c5d084e48b42763.tar.xz
poezio-84e59b05ff0a17178da9ecdb6c5d084e48b42763.zip
Don’t call input completion() functions inside completion methods
Use a placeholder object that can run it afterwards, so that we don’t have side effects inside those functions.
-rw-r--r--doc/source/dev/overview.rst10
-rw-r--r--plugins/admin.py3
-rw-r--r--plugins/alias.py3
-rw-r--r--plugins/bob.py3
-rw-r--r--plugins/irc.py5
-rw-r--r--plugins/mpd_client.py3
-rw-r--r--plugins/otr.py5
-rw-r--r--plugins/ping.py5
-rw-r--r--plugins/quote.py3
-rw-r--r--plugins/reminder.py9
-rw-r--r--plugins/send_delayed.py3
-rw-r--r--plugins/tell.py5
-rw-r--r--poezio/core/completions.py76
-rw-r--r--poezio/core/structs.py16
-rw-r--r--poezio/plugin_manager.py6
-rw-r--r--poezio/tabs/basetabs.py9
-rw-r--r--poezio/tabs/muctab.py31
-rw-r--r--poezio/tabs/rostertab.py31
18 files changed, 129 insertions, 97 deletions
diff --git a/doc/source/dev/overview.rst b/doc/source/dev/overview.rst
index fb880073..8711cbcd 100644
--- a/doc/source/dev/overview.rst
+++ b/doc/source/dev/overview.rst
@@ -88,9 +88,13 @@ structured as key (command name) -> tuple(command function, help string, complet
Completions are a bit tricky, but it’s easy once you get used to it:
They take an **Input** (a _windows_ class) as a parameter, named the_input
-everywhere in the sources. To effectively have a completion, you have to call
-**the_input.auto_completion()** or **the_input.new_completion()** with the relevant
-parameters before returning from the function.
+everywhere in the sources. To effectively have a completion, you have to create
+a :py:class:`poezio.core.structs.Completion` object initialized with the
+completion you want to call
+(**the_input.auto_completion()** or **the_input.new_completion()**) with the
+relevant parameters and return it with the function. Previously you would call
+the function directly from the completion method, but having side effects
+inside it makes it harder to test.
.. code-block:: python
diff --git a/plugins/admin.py b/plugins/admin.py
index 01672014..8c632532 100644
--- a/plugins/admin.py
+++ b/plugins/admin.py
@@ -53,6 +53,7 @@ For affiliations
from poezio.plugin import BasePlugin
from poezio.tabs import MucTab
+from poezio.core.structs import Completion
class Plugin(BasePlugin):
"""
@@ -113,7 +114,7 @@ class Plugin(BasePlugin):
compare_users = lambda x: x.last_talked
word_list = [user.nick for user in sorted(tab.users, key=compare_users, reverse=True)\
if user.nick != tab.own_nick]
- return the_input.auto_completion(word_list, '')
+ return Completion(the_input.auto_completion, word_list, '')
diff --git a/plugins/alias.py b/plugins/alias.py
index 839fa3d8..108fde54 100644
--- a/plugins/alias.py
+++ b/plugins/alias.py
@@ -66,6 +66,7 @@ Example of the syntax:
from poezio.plugin import BasePlugin
from poezio.common import shell_split
+from poezio.core.structs import Completion
class Plugin(BasePlugin):
@@ -140,7 +141,7 @@ class Plugin(BasePlugin):
"Completion for /unalias"
aliases = [alias for alias in self.commands]
aliases.sort()
- return the_input.auto_completion(aliases, '', quotify=False)
+ return Completion(the_input.auto_completion, aliases, '', quotify=False)
def get_command(self, name):
"""Returns the function associated with a command"""
diff --git a/plugins/bob.py b/plugins/bob.py
index b2a6441e..527692b3 100644
--- a/plugins/bob.py
+++ b/plugins/bob.py
@@ -22,6 +22,7 @@ Configuration options
The time during which the file should stay in cache on the receiving side.
"""
+from poezio.core.structs import Completion
from poezio.plugin import BasePlugin
from poezio import tabs
@@ -72,4 +73,4 @@ class Plugin(BasePlugin):
mime_type = guess_type(filename)[0]
if mime_type is not None and mime_type.startswith('image/'):
images.append(filename)
- return the_input.auto_completion(images, quotify=False)
+ return Completion(the_input.auto_completion, images, quotify=False)
diff --git a/plugins/irc.py b/plugins/irc.py
index 56ce5c9c..263d7d22 100644
--- a/plugins/irc.py
+++ b/plugins/irc.py
@@ -131,6 +131,7 @@ Example configuration
from poezio.plugin import BasePlugin
from poezio.decorators import command_args_parser
+from poezio.core.structs import Completion
from poezio import common
from poezio import tabs
@@ -270,7 +271,7 @@ class Plugin(BasePlugin):
sections.remove(section)
except:
pass
- return the_input.new_completion(sections, pos)
+ return Completion(the_input.new_completion, sections, pos)
@command_args_parser.quoted(1, 1)
def command_irc_join(self, args):
@@ -375,6 +376,6 @@ class Plugin(BasePlugin):
sections = self.config.sections()
if 'irc' in sections:
sections.remove('irc')
- return the_input.new_completion(sections, 1)
+ return Completion(the_input.new_completion, sections, 1)
diff --git a/plugins/mpd_client.py b/plugins/mpd_client.py
index 82e94ad1..6115c0da 100644
--- a/plugins/mpd_client.py
+++ b/plugins/mpd_client.py
@@ -49,6 +49,7 @@ Usage
from poezio.plugin import BasePlugin
from poezio.common import shell_split
+from poezio.core.structs import Completion
from os.path import basename as base
from poezio import tabs
import mpd
@@ -84,4 +85,4 @@ class Plugin(BasePlugin):
self.api.information('Cannot send result (%s)' % s, 'Error')
def completion_mpd(self, the_input):
- return the_input.auto_completion(['full'], quotify=False)
+ return Completion(the_input.auto_completion, ['full'], quotify=False)
diff --git a/plugins/otr.py b/plugins/otr.py
index 562bc197..1f41cf71 100644
--- a/plugins/otr.py
+++ b/plugins/otr.py
@@ -194,6 +194,7 @@ from poezio.plugin import BasePlugin
from poezio.tabs import ConversationTab, DynamicConversationTab, PrivateTab
from poezio.theming import get_theme, dump_tuple
from poezio.decorators import command_args_parser
+from poezio.core.structs import Completion
OTR_DIR = os.path.join(os.getenv('XDG_DATA_HOME') or
'~/.local/share', 'poezio', 'otr')
@@ -912,7 +913,7 @@ class Plugin(BasePlugin):
Completion for /otr
"""
comp = ['start', 'fpr', 'ourfpr', 'refresh', 'end', 'trust', 'untrust']
- return the_input.new_completion(comp, 1, quotify=False)
+ return Completion(the_input.new_completion, comp, 1, quotify=False)
@command_args_parser.quoted(1, 2)
def command_smp(self, args):
@@ -972,7 +973,7 @@ class Plugin(BasePlugin):
def completion_smp(the_input):
"""Completion for /otrsmp"""
if the_input.get_argument_position() == 1:
- return the_input.new_completion(['ask', 'answer', 'abort'], 1, quotify=False)
+ return Completion(the_input.new_completion, ['ask', 'answer', 'abort'], 1, quotify=False)
def get_tlv(tlvs, cls):
"""Find the instance of a class in a list"""
diff --git a/plugins/ping.py b/plugins/ping.py
index 066ad1f6..533053b7 100644
--- a/plugins/ping.py
+++ b/plugins/ping.py
@@ -27,6 +27,7 @@ from poezio.plugin import BasePlugin
from poezio.roster import roster
from poezio.common import safeJID
from poezio.contact import Contact, Resource
+from poezio.core.structs import Completion
from poezio import tabs
import time
@@ -74,7 +75,7 @@ class Plugin(BasePlugin):
users = [user.nick for user in self.api.current_tab().users]
l = self.resources()
users.extend(l)
- return the_input.auto_completion(users, '', quotify=False)
+ return Completion(the_input.auto_completion, users, '', quotify=False)
@command_args_parser.raw
def command_private_ping(self, arg):
@@ -115,5 +116,5 @@ class Plugin(BasePlugin):
return l
def completion_ping(self, the_input):
- return the_input.auto_completion(self.resources(), '', quotify=False)
+ return Completion(the_input.auto_completion, self.resources(), '', quotify=False)
diff --git a/plugins/quote.py b/plugins/quote.py
index ba7f4ea3..a5ffd193 100644
--- a/plugins/quote.py
+++ b/plugins/quote.py
@@ -44,6 +44,7 @@ Options
time of the message.
"""
+from poezio.core.structs import Completion
from poezio.plugin import BasePlugin
from poezio.xhtml import clean_text
from poezio import common
@@ -101,5 +102,5 @@ class Plugin(BasePlugin):
messages = list(filter(message_match, messages))
elif len(args) > 1:
return False
- return the_input.auto_completion([clean_text(msg.txt) for msg in messages[::-1]], '')
+ return Completion(the_input.auto_completion, [clean_text(msg.txt) for msg in messages[::-1]], '')
diff --git a/plugins/reminder.py b/plugins/reminder.py
index a5ab1f00..f8a69f70 100644
--- a/plugins/reminder.py
+++ b/plugins/reminder.py
@@ -47,10 +47,11 @@ Will remind you to get up every 1 hour 23 minutes.
"""
+from poezio.core.structs import Completion
from poezio.plugin import BasePlugin
-import curses
-from poezio import common
from poezio import timed_events
+from poezio import common
+import curses
class Plugin(BasePlugin):
@@ -107,10 +108,10 @@ class Plugin(BasePlugin):
if txt.endswith(' '):
n += 1
if n == 2:
- return the_input.auto_completion(["60", "5m", "15m", "30m", "1h", "10h", "1d"], '')
+ return Completion(the_input.auto_completion, ["60", "5m", "15m", "30m", "1h", "10h", "1d"], '')
def completion_done(self, the_input):
- return the_input.auto_completion(["%s" % key for key in self.tasks], '')
+ return Completion(the_input.auto_completion, ["%s" % key for key in self.tasks], '')
def command_done(self, arg="0"):
try:
diff --git a/plugins/send_delayed.py b/plugins/send_delayed.py
index 8c536ea8..299de6e2 100644
--- a/plugins/send_delayed.py
+++ b/plugins/send_delayed.py
@@ -19,6 +19,7 @@ This plugin adds a command to the chat tabs.
"""
from poezio.plugin import BasePlugin
+from poezio.core.structs import Completion
from poezio.decorators import command_args_parser
from poezio import tabs
from poezio import common
@@ -54,7 +55,7 @@ class Plugin(BasePlugin):
if txt.endswith(' '):
n += 1
if n == 2:
- return the_input.auto_completion(["60", "5m", "15m", "30m", "1h", "10h", "1d"], '')
+ return Completion(the_input.auto_completion, ["60", "5m", "15m", "30m", "1h", "10h", "1d"], '')
def say(self, args=None):
if not args:
diff --git a/plugins/tell.py b/plugins/tell.py
index 2b3eb0ce..ba638e82 100644
--- a/plugins/tell.py
+++ b/plugins/tell.py
@@ -20,6 +20,7 @@ This plugin defines two new commands for MUC tabs: :term:`/tell` and :term:`/unt
"""
from poezio.plugin import BasePlugin
+from poezio.core.structs import Completion
from poezio.decorators import command_args_parser
from poezio import tabs
@@ -77,6 +78,6 @@ class Plugin(BasePlugin):
def completion_untell(self, the_input):
tab = self.api.current_tab()
if not tab in self.tabs:
- return the_input.auto_completion([], '')
- return the_input.auto_completion(list(self.tabs[tab]), '', quotify=False)
+ return Completion(the_input.auto_completion, [], '')
+ return Completion(the_input.auto_completion, list(self.tabs[tab]), '', quotify=False)
diff --git a/poezio/core/completions.py b/poezio/core/completions.py
index 7dd2c970..25c23dd6 100644
--- a/poezio/core/completions.py
+++ b/poezio/core/completions.py
@@ -15,8 +15,7 @@ from poezio.common import safeJID
from poezio.config import config
from poezio.roster import roster
-from poezio.core.structs import POSSIBLE_SHOW
-
+from poezio.core.structs import POSSIBLE_SHOW, Completion
class CompletionCore:
def __init__(self, core):
@@ -25,15 +24,14 @@ class CompletionCore:
def help(self, the_input):
"""Completion for /help."""
commands = sorted(self.core.commands.keys()) + sorted(self.core.current_tab().commands.keys())
- return the_input.new_completion(commands, 1, quotify=False)
-
+ return Completion(the_input.new_completion, commands, 1, quotify=False)
def status(self, the_input):
"""
Completion of /status
"""
if the_input.get_argument_position() == 1:
- return the_input.new_completion([status for status in POSSIBLE_SHOW], 1, ' ', quotify=False)
+ return Completion(the_input.new_completion, [status for status in POSSIBLE_SHOW], 1, ' ', quotify=False)
def presence(self, the_input):
@@ -42,9 +40,9 @@ class CompletionCore:
"""
arg = the_input.get_argument_position()
if arg == 1:
- return the_input.auto_completion([jid for jid in roster.jids()], '', quotify=True)
+ return Completion(the_input.auto_completion, [jid for jid in roster.jids()], '', quotify=True)
elif arg == 2:
- return the_input.auto_completion([status for status in POSSIBLE_SHOW], '', quotify=True)
+ return Completion(the_input.auto_completion, [status for status in POSSIBLE_SHOW], '', quotify=True)
def theme(self, the_input):
@@ -63,7 +61,7 @@ class CompletionCore:
theme_files = [name[:-3] for name in names if name.endswith('.py') and name != '__init__.py']
if 'default' not in theme_files:
theme_files.append('default')
- return the_input.new_completion(theme_files, 1, '', quotify=False)
+ return Completion(the_input.new_completion, theme_files, 1, '', quotify=False)
def win(self, the_input):
@@ -72,7 +70,7 @@ class CompletionCore:
for tab in self.core.tabs:
l.extend(tab.matching_names())
l = [i[1] for i in l]
- return the_input.new_completion(l, 1, '', quotify=False)
+ return Completion(the_input.new_completion, l, 1, '', quotify=False)
def join(self, the_input):
@@ -107,7 +105,7 @@ class CompletionCore:
relevant_rooms.extend(sorted(room[0] for room in bookmarks.items() if room[1]))
if the_input.last_completion:
- return the_input.new_completion([], 1, quotify=True)
+ return Completion(the_input.new_completion, [], 1, quotify=True)
if jid.user:
# we are writing the server: complete the server
@@ -116,18 +114,18 @@ class CompletionCore:
if tab.joined:
serv_list.append('%s@%s' % (jid.user, safeJID(tab.name).host))
serv_list.extend(relevant_rooms)
- return the_input.new_completion(serv_list, 1, quotify=True)
+ return Completion(the_input.new_completion, serv_list, 1, quotify=True)
elif args[1].startswith('/'):
# we completing only a resource
- return the_input.new_completion(['/%s' % self.core.own_nick], 1, quotify=True)
+ return Completion(the_input.new_completion, ['/%s' % self.core.own_nick], 1, quotify=True)
else:
- return the_input.new_completion(relevant_rooms, 1, quotify=True)
+ return Completion(the_input.new_completion, relevant_rooms, 1, quotify=True)
def version(self, the_input):
"""Completion for /version"""
comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), [])
- return the_input.new_completion(sorted(comp), 1, quotify=False)
+ return Completion(the_input.new_completion, sorted(comp), 1, quotify=False)
def list(self, the_input):
@@ -137,7 +135,7 @@ class CompletionCore:
if tab.name not in muc_serv_list:
muc_serv_list.append(safeJID(tab.name).server)
if muc_serv_list:
- return the_input.new_completion(muc_serv_list, 1, quotify=False)
+ return Completion(the_input.new_completion, muc_serv_list, 1, quotify=False)
def move_tab(self, the_input):
@@ -146,7 +144,7 @@ class CompletionCore:
if n == 1:
nodes = [tab.name for tab in self.core.tabs if tab]
nodes.remove('Roster')
- return the_input.new_completion(nodes, 1, ' ', quotify=True)
+ return Completion(the_input.new_completion, nodes, 1, ' ', quotify=True)
def runkey(self, the_input):
@@ -156,7 +154,7 @@ class CompletionCore:
list_ = []
list_.extend(self.core.key_func.keys())
list_.extend(self.core.current_tab().key_func.keys())
- return the_input.new_completion(list_, 1, quotify=False)
+ return Completion(the_input.new_completion, list_, 1, quotify=False)
def bookmark(self, the_input):
@@ -165,7 +163,7 @@ class CompletionCore:
n = the_input.get_argument_position(quoted=True)
if n == 2:
- return the_input.new_completion(['true', 'false'], 2, quotify=True)
+ return Completion(the_input.new_completion, ['true', 'false'], 2, quotify=True)
if n >= 3:
return False
@@ -185,23 +183,23 @@ class CompletionCore:
if nick not in nicks:
nicks.append(nick)
jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
- return the_input.new_completion(jids_list, 1, quotify=True)
+ return Completion(the_input.new_completion, jids_list, 1, quotify=True)
muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)]
muc_list.sort()
muc_list.append('*')
- return the_input.new_completion(muc_list, 1, quotify=True)
+ return Completion(the_input.new_completion, muc_list, 1, quotify=True)
def remove_bookmark(self, the_input):
"""Completion for /remove_bookmark"""
- return the_input.new_completion([bm.jid for bm in self.core.bookmarks], 1, quotify=False)
+ return Completion(the_input.new_completion, [bm.jid for bm in self.core.bookmarks], 1, quotify=False)
def decline(self, the_input):
"""Completion for /decline"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- return the_input.auto_completion(sorted(self.core.pending_invites.keys()), 1, '', quotify=True)
+ return Completion(the_input.auto_completion, sorted(self.core.pending_invites.keys()), 1, '', quotify=True)
def bind(self, the_input):
@@ -213,7 +211,7 @@ class CompletionCore:
else:
return False
- return the_input.new_completion(args, n, '', quotify=False)
+ return Completion(the_input.new_completion, args, n, '', quotify=False)
def message(self, the_input):
@@ -230,7 +228,7 @@ class CompletionCore:
for jid in roster.jids():
if not len(roster[jid]):
l.append(jid)
- return the_input.new_completion(l, 1, '', quotify=True)
+ return Completion(the_input.new_completion, l, 1, '', quotify=True)
def invite(self, the_input):
@@ -242,14 +240,14 @@ class CompletionCore:
bares = sorted(roster[contact].bare_jid for contact in roster.jids() if len(roster[contact]))
off = sorted(jid for jid in roster.jids() if jid not in bares)
comp = comp + bares + off
- return the_input.new_completion(comp, n, quotify=True)
+ return Completion(the_input.new_completion, comp, n, quotify=True)
elif n == 2:
rooms = []
for tab in self.core.get_tabs(tabs.MucTab):
if tab.joined:
rooms.append(tab.name)
rooms.sort()
- return the_input.new_completion(rooms, n, '', quotify=True)
+ return Completion(the_input.new_completion, rooms, n, '', quotify=True)
def activity(self, the_input):
@@ -257,20 +255,20 @@ class CompletionCore:
n = the_input.get_argument_position(quoted=True)
args = common.shell_split(the_input.text)
if n == 1:
- return the_input.new_completion(sorted(pep.ACTIVITIES.keys()), n, quotify=True)
+ return Completion(the_input.new_completion, sorted(pep.ACTIVITIES.keys()), n, quotify=True)
elif n == 2:
if args[1] in pep.ACTIVITIES:
l = list(pep.ACTIVITIES[args[1]])
l.remove('category')
l.sort()
- return the_input.new_completion(l, n, quotify=True)
+ return Completion(the_input.new_completion, l, n, quotify=True)
def mood(self, the_input):
"""Completion for /mood"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- return the_input.new_completion(sorted(pep.MOODS.keys()), 1, quotify=True)
+ return Completion(the_input.new_completion, sorted(pep.MOODS.keys()), 1, quotify=True)
def last_activity(self, the_input):
@@ -281,7 +279,7 @@ class CompletionCore:
if n >= 2:
return False
comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), [])
- return the_input.new_completion(sorted(comp), 1, '', quotify=False)
+ return Completion(the_input.new_completion, sorted(comp), 1, '', quotify=False)
def server_cycle(self, the_input):
@@ -290,7 +288,7 @@ class CompletionCore:
for tab in self.core.get_tabs(tabs.MucTab):
serv = safeJID(tab.name).server
serv_list.add(serv)
- return the_input.new_completion(sorted(serv_list), 1, ' ')
+ return Completion(the_input.new_completion, sorted(serv_list), 1, ' ')
def set(self, the_input):
@@ -303,7 +301,7 @@ class CompletionCore:
if '|' in args[1]:
plugin_name, section = args[1].split('|')[:2]
if plugin_name not in self.core.plugin_manager.plugins:
- return the_input.new_completion([], n, quotify=True)
+ return Completion(the_input.new_completion, [], n, quotify=True)
plugin = self.core.plugin_manager.plugins[plugin_name]
end_list = ['%s|%s' % (plugin_name, section) for section in plugin.config.sections()]
else:
@@ -315,7 +313,7 @@ class CompletionCore:
if '|' in args[1]:
plugin_name, section = args[1].split('|')[:2]
if plugin_name not in self.core.plugin_manager.plugins:
- return the_input.new_completion([''], n, quotify=True)
+ return Completion(the_input.new_completion, [''], n, quotify=True)
plugin = self.core.plugin_manager.plugins[plugin_name]
end_list = set(plugin.config.options(section or plugin_name))
if plugin.config.default:
@@ -334,7 +332,7 @@ class CompletionCore:
if '|' in args[1]:
plugin_name, section = args[1].split('|')[:2]
if plugin_name not in self.core.plugin_manager.plugins:
- return the_input.new_completion([''], n, quotify=True)
+ return Completion(the_input.new_completion, [''], n, quotify=True)
plugin = self.core.plugin_manager.plugins[plugin_name]
end_list = [str(plugin.config.get(args[2], '', section or plugin_name)), '']
else:
@@ -344,7 +342,7 @@ class CompletionCore:
end_list = [str(config.get(args[2], '', args[1])), '']
else:
return False
- return the_input.new_completion(end_list, n, quotify=True)
+ return Completion(the_input.new_completion, end_list, n, quotify=True)
def set_default(self, the_input):
@@ -355,13 +353,13 @@ class CompletionCore:
if n >= len(args):
args.append('')
if n == 1 or (n == 2 and config.has_section(args[1])):
- return self.set(the_input)
+ return Completion(self.set, the_input)
return False
def toggle(self, the_input):
"Completion for /toggle"
- return the_input.new_completion(config.options('Poezio'), 1, quotify=False)
+ return Completion(the_input.new_completion, config.options('Poezio'), 1, quotify=False)
def bookmark_local(self, the_input):
@@ -387,8 +385,8 @@ class CompletionCore:
if nick not in nicks:
nicks.append(nick)
jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
- return the_input.new_completion(jids_list, 1, quotify=True)
+ return Completion(the_input.new_completion, jids_list, 1, quotify=True)
muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)]
muc_list.append('*')
- return the_input.new_completion(muc_list, 1, quotify=True)
+ return Completion(the_input.new_completion, muc_list, 1, quotify=True)
diff --git a/poezio/core/structs.py b/poezio/core/structs.py
index 82effd48..7d568f04 100644
--- a/poezio/core/structs.py
+++ b/poezio/core/structs.py
@@ -3,7 +3,7 @@ Module defining structures useful to the core class and related methods
"""
__all__ = ['ERROR_AND_STATUS_CODES', 'DEPRECATED_ERRORS', 'POSSIBLE_SHOW',
- 'Status', 'Command']
+ 'Status', 'Command', 'Completion']
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
@@ -62,3 +62,17 @@ class Command:
self.comp = comp
self.short_desc = short_desc
self.usage = usage
+
+class Completion:
+ """
+ A completion result essentially currying the input completion call.
+ """
+ __slots__ = ['func', 'args', 'kwargs', 'comp_list']
+ def __init__(self, func, comp_list, *args, **kwargs):
+ self.func = func
+ self.comp_list = comp_list
+ self.args = args
+ self.kwargs = kwargs
+
+ def run(self):
+ return self.func(self.comp_list, *self.args, **self.kwargs)
diff --git a/poezio/plugin_manager.py b/poezio/plugin_manager.py
index 8e08432b..11584b23 100644
--- a/poezio/plugin_manager.py
+++ b/poezio/plugin_manager.py
@@ -10,7 +10,7 @@ from os import path
import logging
from poezio import tabs
-from poezio.core.structs import Command
+from poezio.core.structs import Command, Completion
from poezio.plugin import PluginAPI
from poezio.config import config
@@ -284,7 +284,7 @@ class PluginManager(object):
and name != '__init__.py' and not name.startswith('.')]
plugins_files.sort()
position = the_input.get_argument_position(quoted=False)
- return the_input.new_completion(plugins_files, position, '',
+ return Completion(the_input.new_completion, plugins_files, position, '',
quotify=False)
def completion_unload(self, the_input):
@@ -292,7 +292,7 @@ class PluginManager(object):
completion function that completes the name of loaded plugins
"""
position = the_input.get_argument_position(quoted=False)
- return the_input.new_completion(sorted(self.plugins.keys()), position,
+ return Completion(the_input.new_completion, sorted(self.plugins.keys()), position,
'', quotify=False)
def on_plugins_dir_change(self, new_value):
diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py
index dbd57aa4..5e753643 100644
--- a/poezio/tabs/basetabs.py
+++ b/poezio/tabs/basetabs.py
@@ -20,7 +20,7 @@ import string
import time
from xml.etree import cElementTree as ET
-from poezio.core.structs import Command
+from poezio.core.structs import Command, Completion
from poezio import timed_events
from poezio import windows
from poezio import xhtml
@@ -230,7 +230,10 @@ class Tab(object):
if command.comp is None:
return False # There's no completion function
else:
- return command.comp(the_input)
+ comp = command.comp(the_input)
+ if comp:
+ return comp.run()
+ return comp
return False
def execute_command(self, provided_text):
@@ -633,7 +636,7 @@ class ChatTab(Tab):
def completion_correct(self, the_input):
if self.last_sent_message and the_input.get_argument_position() == 1:
- return the_input.auto_completion([self.last_sent_message['body']], '', quotify=False)
+ return Completion(the_input.auto_completion, [self.last_sent_message['body']], '', quotify=False)
@property
def inactive(self):
diff --git a/poezio/tabs/muctab.py b/poezio/tabs/muctab.py
index 281237ad..e17ab18b 100644
--- a/poezio/tabs/muctab.py
+++ b/poezio/tabs/muctab.py
@@ -32,6 +32,7 @@ from poezio.logger import logger
from poezio.roster import roster
from poezio.theming import get_theme, dump_tuple
from poezio.user import User
+from poezio.core.structs import Completion
SHOW_NAME = {
@@ -243,7 +244,7 @@ class MucTab(ChatTab):
comp.sort()
userlist.extend(comp)
- return the_input.auto_completion(userlist, quotify=False)
+ return Completion(the_input.auto_completion, userlist, quotify=False)
def completion_info(self, the_input):
"""Completion for /info"""
@@ -251,7 +252,7 @@ class MucTab(ChatTab):
userlist = []
for user in sorted(self.users, key=compare_users, reverse=True):
userlist.append(user.nick)
- return the_input.auto_completion(userlist, quotify=False)
+ return Completion(the_input.auto_completion, userlist, quotify=False)
def completion_nick(self, the_input):
"""Completion for /nick"""
@@ -259,11 +260,11 @@ class MucTab(ChatTab):
config.get('default_nick'),
self.core.get_bookmark_nickname(self.name)]
nicks = [i for i in nicks if i]
- return the_input.auto_completion(nicks, '', quotify=False)
+ return Completion(the_input.auto_completion, nicks, '', quotify=False)
def completion_recolor(self, the_input):
if the_input.get_argument_position() == 1:
- return the_input.new_completion(['random'], 1, '', quotify=False)
+ return Completion(the_input.new_completion, ['random'], 1, '', quotify=False)
return True
def completion_color(self, the_input):
@@ -273,13 +274,13 @@ class MucTab(ChatTab):
userlist = [user.nick for user in self.users]
if self.own_nick in userlist:
userlist.remove(self.own_nick)
- return the_input.new_completion(userlist, 1, '', quotify=True)
+ return Completion(the_input.new_completion, userlist, 1, '', quotify=True)
elif n == 2:
colors = [i for i in xhtml.colors if i]
colors.sort()
colors.append('unset')
colors.append('random')
- return the_input.new_completion(colors, 2, '', quotify=False)
+ return Completion(the_input.new_completion, colors, 2, '', quotify=False)
def completion_ignore(self, the_input):
"""Completion for /ignore"""
@@ -287,7 +288,7 @@ class MucTab(ChatTab):
if self.own_nick in userlist:
userlist.remove(self.own_nick)
userlist.sort()
- return the_input.auto_completion(userlist, quotify=False)
+ return Completion(the_input.auto_completion, userlist, quotify=False)
def completion_role(self, the_input):
"""Completion for /role"""
@@ -296,10 +297,10 @@ class MucTab(ChatTab):
userlist = [user.nick for user in self.users]
if self.own_nick in userlist:
userlist.remove(self.own_nick)
- return the_input.new_completion(userlist, 1, '', quotify=True)
+ return Completion(the_input.new_completion, userlist, 1, '', quotify=True)
elif n == 2:
possible_roles = ['none', 'visitor', 'participant', 'moderator']
- return the_input.new_completion(possible_roles, 2, '',
+ return Completion(the_input.new_completion, possible_roles, 2, '',
quotify=True)
def completion_affiliation(self, the_input):
@@ -313,11 +314,11 @@ class MucTab(ChatTab):
if self.core.xmpp.boundjid.bare in jidlist:
jidlist.remove(self.core.xmpp.boundjid.bare)
userlist.extend(jidlist)
- return the_input.new_completion(userlist, 1, '', quotify=True)
+ return Completion(the_input.new_completion, userlist, 1, '', quotify=True)
elif n == 2:
possible_affiliations = ['none', 'member', 'admin',
'owner', 'outcast']
- return the_input.new_completion(possible_affiliations, 2, '',
+ return Completion(the_input.new_completion, possible_affiliations, 2, '',
quotify=True)
@command_args_parser.quoted(1, 1, [''])
@@ -332,7 +333,7 @@ class MucTab(ChatTab):
"""Completion for /invite"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- return the_input.new_completion(roster.jids(), 1, quotify=True)
+ return Completion(the_input.new_completion, roster.jids(), 1, quotify=True)
def scroll_user_list_up(self):
self.user_win.scroll_up()
@@ -714,7 +715,7 @@ class MucTab(ChatTab):
def completion_topic(self, the_input):
if the_input.get_argument_position() == 1:
- return the_input.auto_completion([self.topic], '', quotify=False)
+ return Completion(the_input.auto_completion, [self.topic], '', quotify=False)
def completion_quoted(self, the_input):
"""Nick completion, but with quotes"""
@@ -725,7 +726,7 @@ class MucTab(ChatTab):
if user.nick != self.own_nick:
word_list.append(user.nick)
- return the_input.new_completion(word_list, 1, quotify=True)
+ return Completion(the_input.new_completion, word_list, 1, quotify=True)
@command_args_parser.quoted(1, 1)
def command_kick(self, args):
@@ -914,7 +915,7 @@ class MucTab(ChatTab):
def completion_unignore(self, the_input):
if the_input.get_argument_position() == 1:
users = [user.nick for user in self.ignores]
- return the_input.auto_completion(users, quotify=False)
+ return Completion(the_input.auto_completion, users, quotify=False)
def resize(self):
"""
diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py
index f8b3e906..64aae731 100644
--- a/poezio/tabs/rostertab.py
+++ b/poezio/tabs/rostertab.py
@@ -25,6 +25,7 @@ from poezio.decorators import refresh_wrapper
from poezio.roster import RosterGroup, roster
from poezio.theming import get_theme, dump_tuple
from poezio.decorators import command_args_parser
+from poezio.core.structs import Completion
from poezio.tabs import Tab
@@ -277,7 +278,7 @@ class RosterInfoTab(Tab):
elif n == 2:
return self.completion_file(2, the_input)
elif n == 3:
- return the_input.new_completion(['true', 'false'], n)
+ return Completion(the_input.new_completion, ['true', 'false'], n)
@command_args_parser.quoted(1)
def command_cert_disable(self, args):
@@ -399,7 +400,7 @@ class RosterInfoTab(Tab):
"""
if the_input.get_argument_position() == 1:
jids = roster.jids()
- return the_input.new_completion(jids, 1, '', quotify=False)
+ return Completion(the_input.new_completion, jids, 1, '', quotify=False)
@command_args_parser.quoted(0, 1)
def command_unblock(self, args):
@@ -429,7 +430,7 @@ class RosterInfoTab(Tab):
if iq['type'] == 'error':
return
l = sorted(str(item) for item in iq['blocklist']['items'])
- return the_input.new_completion(l, 1, quotify=False)
+ return Completion(the_input.new_completion, l, 1, quotify=False)
if the_input.get_argument_position():
self.core.xmpp.plugin['xep_0191'].get_blocked(callback=on_result)
@@ -544,7 +545,7 @@ class RosterInfoTab(Tab):
if n == complete_number:
if args[n-1] == '' or len(args) < n+1:
home = os.getenv('HOME') or '/'
- return the_input.new_completion([home, '/tmp'], n, quotify=True)
+ return Completion(the_input.new_completion, [home, '/tmp'], n, quotify=True)
path_ = args[n]
if path.isdir(path_):
dir_ = path_
@@ -567,7 +568,7 @@ class RosterInfoTab(Tab):
if not name.startswith('.'):
end_list.append(value)
- return the_input.new_completion(end_list, n, quotify=True)
+ return Completion(the_input.new_completion, end_list, n, quotify=True)
@command_args_parser.ignored
def command_clear(self):
@@ -872,24 +873,24 @@ class RosterInfoTab(Tab):
Completion for /remove
"""
jids = [jid for jid in roster.jids()]
- return the_input.auto_completion(jids, '', quotify=False)
+ return Completion(the_input.auto_completion, jids, '', quotify=False)
def completion_name(self, the_input):
"""Completion for /name"""
n = the_input.get_argument_position()
if n == 1:
jids = [jid for jid in roster.jids()]
- return the_input.new_completion(jids, n, quotify=True)
+ return Completion(the_input.new_completion, jids, n, quotify=True)
return False
def completion_groupadd(self, the_input):
n = the_input.get_argument_position()
if n == 1:
jids = sorted(jid for jid in roster.jids())
- return the_input.new_completion(jids, n, '', quotify=True)
+ return Completion(the_input.new_completion, jids, n, '', quotify=True)
elif n == 2:
groups = sorted(group for group in roster.groups if group != 'none')
- return the_input.new_completion(groups, n, '', quotify=True)
+ return Completion(the_input.new_completion, groups, n, '', quotify=True)
return False
def completion_groupmove(self, the_input):
@@ -897,7 +898,7 @@ class RosterInfoTab(Tab):
n = the_input.get_argument_position()
if n == 1:
jids = sorted(jid for jid in roster.jids())
- return the_input.new_completion(jids, n, '', quotify=True)
+ return Completion(the_input.new_completion, jids, n, '', quotify=True)
elif n == 2:
contact = roster[args[1]]
if not contact:
@@ -905,10 +906,10 @@ class RosterInfoTab(Tab):
groups = list(contact.groups)
if 'none' in groups:
groups.remove('none')
- return the_input.new_completion(groups, n, '', quotify=True)
+ return Completion(the_input.new_completion, groups, n, '', quotify=True)
elif n == 3:
groups = sorted(group for group in roster.groups)
- return the_input.new_completion(groups, n, '', quotify=True)
+ return Completion(the_input.new_completion, groups, n, '', quotify=True)
return False
def completion_groupremove(self, the_input):
@@ -916,7 +917,7 @@ class RosterInfoTab(Tab):
n = the_input.get_argument_position()
if n == 1:
jids = sorted(jid for jid in roster.jids())
- return the_input.new_completion(jids, n, '', quotify=True)
+ return Completion(the_input.new_completion, jids, n, '', quotify=True)
elif n == 2:
contact = roster[args[1]]
if contact is None:
@@ -926,7 +927,7 @@ class RosterInfoTab(Tab):
groups.remove('none')
except ValueError:
pass
- return the_input.new_completion(groups, n, '', quotify=True)
+ return Completion(the_input.new_completion, groups, n, '', quotify=True)
return False
def completion_deny(self, the_input):
@@ -936,7 +937,7 @@ class RosterInfoTab(Tab):
"""
jids = sorted(str(contact.bare_jid) for contact in roster.contacts.values()
if contact.pending_in)
- return the_input.new_completion(jids, 1, '', quotify=False)
+ return Completion(the_input.new_completion, jids, 1, '', quotify=False)
@command_args_parser.quoted(0, 1)
def command_accept(self, args):