Light refactoring + typing
This commit is contained in:
parent
38f0cd1c32
commit
ec05954420
4 changed files with 197 additions and 204 deletions
|
@ -6,19 +6,19 @@ handlers but those are defined in submodules in order to avoir cluttering
|
|||
this file.
|
||||
"""
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
import asyncio
|
||||
import shutil
|
||||
import curses
|
||||
import os
|
||||
import pipes
|
||||
import sys
|
||||
import shutil
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from typing import Callable, Dict, List, Optional, Tuple, Type
|
||||
|
||||
from slixmpp.xmlstream.handler import Callback
|
||||
from slixmpp import JID
|
||||
from slixmpp.util import FileSystemPerJidCache
|
||||
from slixmpp.xmlstream.handler import Callback
|
||||
|
||||
from poezio import connection
|
||||
from poezio import decorators
|
||||
|
@ -39,6 +39,7 @@ from poezio.logger import logger
|
|||
from poezio.plugin_manager import PluginManager
|
||||
from poezio.roster import roster
|
||||
from poezio.size_manager import SizeManager
|
||||
from poezio.user import User
|
||||
from poezio.text_buffer import TextBuffer
|
||||
from poezio.theming import get_theme
|
||||
from poezio import keyboard, xdg
|
||||
|
@ -50,6 +51,8 @@ from poezio.core.handlers import HandlerCore
|
|||
from poezio.core.structs import POSSIBLE_SHOW, DEPRECATED_ERRORS, \
|
||||
ERROR_AND_STATUS_CODES, Command, Status
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Core:
|
||||
"""
|
||||
|
@ -98,8 +101,7 @@ class Core:
|
|||
self.plugins_autoloaded = False
|
||||
self.plugin_manager = PluginManager(self)
|
||||
self.events = events.EventHandler()
|
||||
self.events.add_event_handler('tab_change',
|
||||
self.on_tab_change)
|
||||
self.events.add_event_handler('tab_change', self.on_tab_change)
|
||||
|
||||
self.tabs = Tabs(self.events)
|
||||
self.previous_tab_nb = 0
|
||||
|
@ -205,78 +207,55 @@ class Core:
|
|||
self.key_func.update(key_func)
|
||||
|
||||
# Add handlers
|
||||
self.xmpp.add_event_handler('connected', self.handler.on_connected)
|
||||
self.xmpp.add_event_handler('connection_failed',
|
||||
self.handler.on_failed_connection)
|
||||
self.xmpp.add_event_handler('disconnected',
|
||||
self.handler.on_disconnected)
|
||||
self.xmpp.add_event_handler('stream_error',
|
||||
self.handler.on_stream_error)
|
||||
self.xmpp.add_event_handler('failed_all_auth',
|
||||
self.handler.on_failed_all_auth)
|
||||
self.xmpp.add_event_handler('no_auth', self.handler.on_no_auth)
|
||||
self.xmpp.add_event_handler("session_start",
|
||||
self.handler.on_session_start)
|
||||
self.xmpp.add_event_handler("session_start",
|
||||
self.handler.on_session_start_features)
|
||||
self.xmpp.add_event_handler("groupchat_presence",
|
||||
self.handler.on_groupchat_presence)
|
||||
self.xmpp.add_event_handler("groupchat_message",
|
||||
self.handler.on_groupchat_message)
|
||||
self.xmpp.add_event_handler("groupchat_invite",
|
||||
self.handler.on_groupchat_invitation)
|
||||
self.xmpp.add_event_handler(
|
||||
"groupchat_direct_invite",
|
||||
self.handler.on_groupchat_direct_invitation)
|
||||
self.xmpp.add_event_handler("groupchat_decline",
|
||||
self.handler.on_groupchat_decline)
|
||||
self.xmpp.add_event_handler("groupchat_config_status",
|
||||
self.handler.on_status_codes)
|
||||
self.xmpp.add_event_handler("groupchat_subject",
|
||||
self.handler.on_groupchat_subject)
|
||||
self.xmpp.add_event_handler("message", self.handler.on_message)
|
||||
self.xmpp.add_event_handler("message_error",
|
||||
self.handler.on_error_message)
|
||||
self.xmpp.add_event_handler("receipt_received",
|
||||
self.handler.on_receipt)
|
||||
self.xmpp.add_event_handler("got_online", self.handler.on_got_online)
|
||||
self.xmpp.add_event_handler("got_offline", self.handler.on_got_offline)
|
||||
self.xmpp.add_event_handler("roster_update",
|
||||
self.handler.on_roster_update)
|
||||
self.xmpp.add_event_handler("changed_status", self.handler.on_presence)
|
||||
self.xmpp.add_event_handler("presence_error",
|
||||
self.handler.on_presence_error)
|
||||
self.xmpp.add_event_handler("roster_subscription_request",
|
||||
self.handler.on_subscription_request)
|
||||
self.xmpp.add_event_handler("roster_subscription_authorized",
|
||||
self.handler.on_subscription_authorized)
|
||||
self.xmpp.add_event_handler("roster_subscription_remove",
|
||||
self.handler.on_subscription_remove)
|
||||
self.xmpp.add_event_handler("roster_subscription_removed",
|
||||
self.handler.on_subscription_removed)
|
||||
self.xmpp.add_event_handler("message_xform", self.handler.on_data_form)
|
||||
self.xmpp.add_event_handler("chatstate_active",
|
||||
self.handler.on_chatstate_active)
|
||||
self.xmpp.add_event_handler("chatstate_composing",
|
||||
self.handler.on_chatstate_composing)
|
||||
self.xmpp.add_event_handler("chatstate_paused",
|
||||
self.handler.on_chatstate_paused)
|
||||
self.xmpp.add_event_handler("chatstate_gone",
|
||||
self.handler.on_chatstate_gone)
|
||||
self.xmpp.add_event_handler("chatstate_inactive",
|
||||
self.handler.on_chatstate_inactive)
|
||||
self.xmpp.add_event_handler("attention", self.handler.on_attention)
|
||||
self.xmpp.add_event_handler("ssl_cert", self.handler.validate_ssl)
|
||||
self.xmpp.add_event_handler("ssl_invalid_chain",
|
||||
self.handler.ssl_invalid_chain)
|
||||
self.xmpp.add_event_handler('carbon_received',
|
||||
self.handler.on_carbon_received)
|
||||
self.xmpp.add_event_handler('carbon_sent', self.handler.on_carbon_sent)
|
||||
self.xmpp.add_event_handler('http_confirm', self.handler.http_confirm)
|
||||
xmpp_event_handlers = [
|
||||
('attention', self.handler.on_attention),
|
||||
('carbon_received', self.handler.on_carbon_received),
|
||||
('carbon_sent', self.handler.on_carbon_sent),
|
||||
('changed_status', self.handler.on_presence),
|
||||
('chatstate_active', self.handler.on_chatstate_active),
|
||||
('chatstate_composing', self.handler.on_chatstate_composing),
|
||||
('chatstate_gone', self.handler.on_chatstate_gone),
|
||||
('chatstate_inactive', self.handler.on_chatstate_inactive),
|
||||
('chatstate_paused', self.handler.on_chatstate_paused),
|
||||
('connected', self.handler.on_connected),
|
||||
('connection_failed', self.handler.on_failed_connection),
|
||||
('disconnected', self.handler.on_disconnected),
|
||||
('failed_all_auth', self.handler.on_failed_all_auth),
|
||||
('got_offline', self.handler.on_got_offline),
|
||||
('got_online', self.handler.on_got_online),
|
||||
('groupchat_config_status', self.handler.on_status_codes),
|
||||
('groupchat_decline', self.handler.on_groupchat_decline),
|
||||
('groupchat_direct_invite',
|
||||
self.handler.on_groupchat_direct_invitation),
|
||||
('groupchat_invite', self.handler.on_groupchat_invitation),
|
||||
('groupchat_message', self.handler.on_groupchat_message),
|
||||
('groupchat_presence', self.handler.on_groupchat_presence),
|
||||
('groupchat_subject', self.handler.on_groupchat_subject),
|
||||
('http_confirm', self.handler.http_confirm),
|
||||
('message', self.handler.on_message),
|
||||
('message_error', self.handler.on_error_message),
|
||||
('message_xform', self.handler.on_data_form),
|
||||
('no_auth', self.handler.on_no_auth),
|
||||
('presence_error', self.handler.on_presence_error),
|
||||
('receipt_received', self.handler.on_receipt),
|
||||
('roster_subscription_authorized',
|
||||
self.handler.on_subscription_authorized),
|
||||
('roster_subscription_remove',
|
||||
self.handler.on_subscription_remove),
|
||||
('roster_subscription_removed',
|
||||
self.handler.on_subscription_removed),
|
||||
('roster_subscription_request',
|
||||
self.handler.on_subscription_request),
|
||||
('roster_update', self.handler.on_roster_update),
|
||||
('session_start', self.handler.on_session_start),
|
||||
('session_start', self.handler.on_session_start_features),
|
||||
('ssl_cert', self.handler.validate_ssl),
|
||||
('ssl_invalid_chain', self.handler.ssl_invalid_chain),
|
||||
('stream_error', self.handler.on_stream_error),
|
||||
]
|
||||
for name, handler in xmpp_event_handlers:
|
||||
self.xmpp.add_event_handler(name, handler)
|
||||
|
||||
all_stanzas = Callback('custom matcher', connection.MatchAll(None),
|
||||
self.handler.incoming_stanza)
|
||||
self.xmpp.register_handler(all_stanzas)
|
||||
if config.get('enable_avatars'):
|
||||
self.xmpp.add_event_handler("vcard_avatar_update",
|
||||
self.handler.on_vcard_avatar)
|
||||
|
@ -298,6 +277,10 @@ class Core:
|
|||
self.xmpp.add_event_handler("user_gaming_publish",
|
||||
self.handler.on_gaming_event)
|
||||
|
||||
all_stanzas = Callback('custom matcher', connection.MatchAll(None),
|
||||
self.handler.incoming_stanza)
|
||||
self.xmpp.register_handler(all_stanzas)
|
||||
|
||||
self.initial_joins = []
|
||||
|
||||
self.connected_events = {}
|
||||
|
@ -316,40 +299,34 @@ class Core:
|
|||
# string option (""), they will be called for every option change
|
||||
# The callback takes two argument: the config option, and the new
|
||||
# value
|
||||
self.configuration_change_handlers = {"": []}
|
||||
self.add_configuration_handler("create_gaps",
|
||||
self.on_gaps_config_change)
|
||||
self.add_configuration_handler("request_message_receipts",
|
||||
self.on_request_receipts_config_change)
|
||||
self.add_configuration_handler("ack_message_receipts",
|
||||
self.on_ack_receipts_config_change)
|
||||
self.add_configuration_handler(
|
||||
"plugins_dir", self.plugin_manager.on_plugins_dir_change)
|
||||
self.add_configuration_handler(
|
||||
"plugins_conf_dir", self.plugin_manager.on_plugins_conf_dir_change)
|
||||
self.add_configuration_handler("connection_timeout_delay",
|
||||
self.xmpp.set_keepalive_values)
|
||||
self.add_configuration_handler("connection_check_interval",
|
||||
self.xmpp.set_keepalive_values)
|
||||
self.add_configuration_handler("themes_dir", theming.update_themes_dir)
|
||||
self.add_configuration_handler("theme", self.on_theme_config_change)
|
||||
self.add_configuration_handler("use_bookmarks_method",
|
||||
self.on_bookmarks_method_config_change)
|
||||
self.add_configuration_handler("password", self.on_password_change)
|
||||
self.add_configuration_handler("enable_vertical_tab_list",
|
||||
self.on_vertical_tab_list_config_change)
|
||||
self.add_configuration_handler("vertical_tab_list_size",
|
||||
self.on_vertical_tab_list_config_change)
|
||||
self.add_configuration_handler("deterministic_nick_colors",
|
||||
self.on_nick_determinism_changed)
|
||||
self.add_configuration_handler("enable_carbons",
|
||||
self.on_carbons_switch)
|
||||
self.add_configuration_handler("hide_user_list",
|
||||
self.on_hide_user_list_change)
|
||||
self.configuration_change_handlers = defaultdict(list)
|
||||
config_handlers = [
|
||||
('', self.on_any_config_change),
|
||||
('ack_message_receipts', self.on_ack_receipts_config_change),
|
||||
('connection_check_interval', self.xmpp.set_keepalive_values),
|
||||
('connection_timeout_delay', self.xmpp.set_keepalive_values),
|
||||
('create_gaps', self.on_gaps_config_change),
|
||||
('deterministic_nick_colors', self.on_nick_determinism_changed),
|
||||
('enable_carbons', self.on_carbons_switch),
|
||||
('enable_vertical_tab_list',
|
||||
self.on_vertical_tab_list_config_change),
|
||||
('hide_user_list', self.on_hide_user_list_change),
|
||||
('password', self.on_password_change),
|
||||
('plugins_conf_dir',
|
||||
self.plugin_manager.on_plugins_conf_dir_change),
|
||||
('plugins_dir', self.plugin_manager.on_plugins_dir_change),
|
||||
('request_message_receipts',
|
||||
self.on_request_receipts_config_change),
|
||||
('theme', self.on_theme_config_change),
|
||||
('themes_dir', theming.update_themes_dir),
|
||||
('use_bookmarks_method', self.on_bookmarks_method_config_change),
|
||||
('vertical_tab_list_size',
|
||||
self.on_vertical_tab_list_config_change),
|
||||
]
|
||||
for option, handler in config_handlers:
|
||||
self.add_configuration_handler(option, handler)
|
||||
|
||||
self.add_configuration_handler("", self.on_any_config_change)
|
||||
|
||||
def on_tab_change(self, old_tab, new_tab):
|
||||
def on_tab_change(self, old_tab: tabs.Tab, new_tab: tabs.Tab):
|
||||
"""Whenever the current tab changes, change focus and refresh"""
|
||||
old_tab.on_lose_focus()
|
||||
new_tab.on_gain_focus()
|
||||
|
@ -361,14 +338,12 @@ class Core:
|
|||
"""
|
||||
roster.modified()
|
||||
|
||||
def add_configuration_handler(self, option, callback):
|
||||
def add_configuration_handler(self, option: str, callback: Callable):
|
||||
"""
|
||||
Add a callback, associated with the given option. It will be called
|
||||
each time the configuration option is changed using /set or by
|
||||
reloading the configuration with a signal
|
||||
"""
|
||||
if option not in self.configuration_change_handlers:
|
||||
self.configuration_change_handlers[option] = []
|
||||
self.configuration_change_handlers[option].append(callback)
|
||||
|
||||
def trigger_configuration_change(self, option, value):
|
||||
|
@ -599,50 +574,6 @@ class Core:
|
|||
main loop waiting for the user to press a key
|
||||
"""
|
||||
|
||||
def replace_line_breaks(key):
|
||||
"replace ^J with \n"
|
||||
if key == '^J':
|
||||
return '\n'
|
||||
return key
|
||||
|
||||
def separate_chars_from_bindings(char_list):
|
||||
"""
|
||||
returns a list of lists. For example if you give
|
||||
['a', 'b', 'KEY_BACKSPACE', 'n', 'u'], this function returns
|
||||
[['a', 'b'], ['KEY_BACKSPACE'], ['n', 'u']]
|
||||
|
||||
This way, in case of lag (for example), we handle the typed text
|
||||
by “batch” as much as possible (instead of one char at a time,
|
||||
which implies a refresh after each char, which is very slow),
|
||||
but we still handle the special chars (backspaces, arrows,
|
||||
ctrl+x ou alt+x, etc) one by one, which avoids the issue of
|
||||
printing them OR ignoring them in that case. This should
|
||||
resolve the “my ^W are ignored when I lag ;(”.
|
||||
"""
|
||||
res = []
|
||||
current = []
|
||||
for char in char_list:
|
||||
assert char
|
||||
# Transform that stupid char into what we actually meant
|
||||
if char == '\x1f':
|
||||
char = '^/'
|
||||
if len(char) == 1:
|
||||
current.append(char)
|
||||
else:
|
||||
# special case for the ^I key, it’s considered as \t
|
||||
# only when pasting some text, otherwise that’s the ^I
|
||||
# (or M-i) key, which stands for completion by default.
|
||||
if char == '^I' and len(char_list) != 1:
|
||||
current.append('\t')
|
||||
continue
|
||||
if current:
|
||||
res.append(current)
|
||||
current = []
|
||||
res.append([char])
|
||||
if current:
|
||||
res.append(current)
|
||||
return res
|
||||
|
||||
log.debug("Input is readable.")
|
||||
big_char_list = [replace_key_with_bound(key)\
|
||||
for key in self.read_keyboard()]
|
||||
|
@ -705,7 +636,7 @@ class Core:
|
|||
self.focus_tab_named(roster_row.jid)
|
||||
self.refresh_window()
|
||||
|
||||
def get_conversation_messages(self):
|
||||
def get_conversation_messages(self) -> Optional[List[Tuple]]:
|
||||
"""
|
||||
Returns a list of all the messages in the current chat.
|
||||
If the current tab is not a ChatTab, returns None.
|
||||
|
@ -717,7 +648,7 @@ class Core:
|
|||
return None
|
||||
return self.tabs.current_tab.get_conversation_messages()
|
||||
|
||||
def insert_input_text(self, text):
|
||||
def insert_input_text(self, text: str):
|
||||
"""
|
||||
Insert the given text into the current input
|
||||
"""
|
||||
|
@ -725,7 +656,7 @@ class Core:
|
|||
|
||||
##################### Anything related to command execution ###################
|
||||
|
||||
def execute(self, line):
|
||||
def execute(self, line: str):
|
||||
"""
|
||||
Execute the /command or just send the line on the current room
|
||||
"""
|
||||
|
@ -811,7 +742,7 @@ class Core:
|
|||
exc_info=True)
|
||||
self.information(str(exc), 'Error')
|
||||
|
||||
def do_command(self, key, raw):
|
||||
def do_command(self, key: str, raw: bool):
|
||||
"""
|
||||
Execute the action associated with a key
|
||||
|
||||
|
@ -830,7 +761,7 @@ class Core:
|
|||
else:
|
||||
self.tabs.current_tab.on_input(key, raw)
|
||||
|
||||
def try_execute(self, line):
|
||||
def try_execute(self, line: str):
|
||||
"""
|
||||
Try to execute a command in the current tab
|
||||
"""
|
||||
|
@ -859,7 +790,7 @@ class Core:
|
|||
"""
|
||||
return self.status
|
||||
|
||||
def set_status(self, pres, msg):
|
||||
def set_status(self, pres: str, msg: str):
|
||||
"""
|
||||
Set our current status so we can remember
|
||||
it and use it back when needed (for example to display it
|
||||
|
@ -875,7 +806,7 @@ class Core:
|
|||
'Unable to save the status in '
|
||||
'the config file', 'Error')
|
||||
|
||||
def get_bookmark_nickname(self, room_name):
|
||||
def get_bookmark_nickname(self, room_name: str) -> str:
|
||||
"""
|
||||
Returns the nickname associated with a bookmark
|
||||
or the default nickname
|
||||
|
@ -903,7 +834,7 @@ class Core:
|
|||
lambda event: self.xmpp.connect(),
|
||||
disposable=True)
|
||||
|
||||
def send_message(self, msg):
|
||||
def send_message(self, msg: str) -> bool:
|
||||
"""
|
||||
Function to use in plugins to send a message in the current
|
||||
conversation.
|
||||
|
@ -914,7 +845,7 @@ class Core:
|
|||
self.tabs.current_tab.command_say(msg)
|
||||
return True
|
||||
|
||||
def invite(self, jid, room, reason=None):
|
||||
def invite(self, jid: JID, room: JID, reason: Optional[str] = None):
|
||||
"""
|
||||
Checks if the sender supports XEP-0249, then send an invitation,
|
||||
or a mediated one if it does not.
|
||||
|
@ -974,13 +905,16 @@ class Core:
|
|||
|
||||
### Tab getters ###
|
||||
|
||||
def get_tabs(self, cls=None):
|
||||
def get_tabs(self, cls: Optional[Type[tabs.Tab]] = None):
|
||||
"Get all the tabs of a type"
|
||||
if cls is None:
|
||||
return self.tabs.get_tabs()
|
||||
return self.tabs.by_class(cls)
|
||||
|
||||
def get_conversation_by_jid(self, jid, create=True, fallback_barejid=True):
|
||||
def get_conversation_by_jid(self,
|
||||
jid: JID,
|
||||
create=True,
|
||||
fallback_barejid=True) -> tabs.ChatTab:
|
||||
"""
|
||||
From a JID, get the tab containing the conversation with it.
|
||||
If none already exist, and create is "True", we create it
|
||||
|
@ -1013,7 +947,7 @@ class Core:
|
|||
conversation = None
|
||||
return conversation
|
||||
|
||||
def add_tab(self, new_tab, focus=False):
|
||||
def add_tab(self, new_tab: tabs.Tab, focus=False):
|
||||
"""
|
||||
Appends the new_tab in the tab list and
|
||||
focus it if focus==True
|
||||
|
@ -1022,7 +956,7 @@ class Core:
|
|||
if focus:
|
||||
self.tabs.set_current_tab(new_tab)
|
||||
|
||||
def insert_tab(self, old_pos, new_pos=99999):
|
||||
def insert_tab(self, old_pos: int, new_pos=99999) -> bool:
|
||||
"""
|
||||
Insert a tab at a position, changing the number of the following tabs
|
||||
returns False if it could not move the tab, True otherwise
|
||||
|
@ -1108,7 +1042,9 @@ class Core:
|
|||
return
|
||||
return
|
||||
|
||||
def focus_tab_named(self, tab_name, type_=None):
|
||||
def focus_tab_named(self,
|
||||
tab_name: str,
|
||||
type_: Optional[Type[tabs.Tab]] = None) -> bool:
|
||||
"""Returns True if it found a tab to focus on"""
|
||||
if type_ is None:
|
||||
tab = self.tabs.by_name(tab_name)
|
||||
|
@ -1121,7 +1057,8 @@ class Core:
|
|||
|
||||
### Opening actions ###
|
||||
|
||||
def open_conversation_window(self, jid, focus=True):
|
||||
def open_conversation_window(self, jid: JID,
|
||||
focus=True) -> tabs.ConversationTab:
|
||||
"""
|
||||
Open a new conversation tab and focus it if needed. If a resource is
|
||||
provided, we open a StaticConversationTab, else a
|
||||
|
@ -1137,7 +1074,8 @@ class Core:
|
|||
self.refresh_window()
|
||||
return new_tab
|
||||
|
||||
def open_private_window(self, room_name, user_nick, focus=True):
|
||||
def open_private_window(self, room_name: str, user_nick: str,
|
||||
focus=True) -> tabs.PrivateTab:
|
||||
"""
|
||||
Open a Private conversation in a MUC and focus if needed.
|
||||
"""
|
||||
|
@ -1162,7 +1100,12 @@ class Core:
|
|||
tab.privates.append(new_tab)
|
||||
return new_tab
|
||||
|
||||
def open_new_room(self, room, nick, *, password=None, focus=True):
|
||||
def open_new_room(self,
|
||||
room: str,
|
||||
nick: str,
|
||||
*,
|
||||
password: Optional[str] = None,
|
||||
focus=True) -> tabs.MucTab:
|
||||
"""
|
||||
Open a new tab.MucTab containing a muc Room, using the specified nick
|
||||
"""
|
||||
|
@ -1171,7 +1114,8 @@ class Core:
|
|||
self.refresh_window()
|
||||
return new_tab
|
||||
|
||||
def open_new_form(self, form, on_cancel, on_send, **kwargs):
|
||||
def open_new_form(self, form, on_cancel: Callable, on_send: Callable,
|
||||
**kwargs):
|
||||
"""
|
||||
Open a new tab containing the form
|
||||
The callback are called with the completed form as parameter in
|
||||
|
@ -1182,7 +1126,7 @@ class Core:
|
|||
|
||||
### Modifying actions ###
|
||||
|
||||
def rename_private_tabs(self, room_name, old_nick, user):
|
||||
def rename_private_tabs(self, room_name: str, old_nick: str, user: User):
|
||||
"""
|
||||
Call this method when someone changes his/her nick in a MUC,
|
||||
this updates the name of all the opened private conversations
|
||||
|
@ -1193,8 +1137,8 @@ class Core:
|
|||
if tab:
|
||||
tab.rename_user(old_nick, user)
|
||||
|
||||
def on_user_left_private_conversation(self, room_name, user,
|
||||
status_message):
|
||||
def on_user_left_private_conversation(self, room_name: str, user: User,
|
||||
status_message: str):
|
||||
"""
|
||||
The user left the MUC: add a message in the associated
|
||||
private conversation
|
||||
|
@ -1204,7 +1148,7 @@ class Core:
|
|||
if tab:
|
||||
tab.user_left(status_message, user)
|
||||
|
||||
def on_user_rejoined_private_conversation(self, room_name, nick):
|
||||
def on_user_rejoined_private_conversation(self, room_name: str, nick: str):
|
||||
"""
|
||||
The user joined a MUC: add a message in the associated
|
||||
private conversation
|
||||
|
@ -1214,7 +1158,9 @@ class Core:
|
|||
if tab:
|
||||
tab.user_rejoined(nick)
|
||||
|
||||
def disable_private_tabs(self, room_name, reason=None):
|
||||
def disable_private_tabs(self,
|
||||
room_name: str,
|
||||
reason: Optional[str] = None):
|
||||
"""
|
||||
Disable private tabs when leaving a room
|
||||
"""
|
||||
|
@ -1224,7 +1170,8 @@ class Core:
|
|||
if tab.name.startswith(room_name):
|
||||
tab.deactivate(reason=reason)
|
||||
|
||||
def enable_private_tabs(self, room_name, reason=None):
|
||||
def enable_private_tabs(self, room_name: str,
|
||||
reason: Optional[str] = None):
|
||||
"""
|
||||
Enable private tabs when joining a room
|
||||
"""
|
||||
|
@ -1234,12 +1181,12 @@ class Core:
|
|||
if tab.name.startswith(room_name):
|
||||
tab.activate(reason=reason)
|
||||
|
||||
def on_user_changed_status_in_private(self, jid, status):
|
||||
def on_user_changed_status_in_private(self, jid: JID, status: str):
|
||||
tab = self.tabs.by_name_and_class(jid, tabs.ChatTab)
|
||||
if tab is not None: # display the message in private
|
||||
tab.update_status(status)
|
||||
|
||||
def close_tab(self, tab=None):
|
||||
def close_tab(self, tab: tabs.Tab = None):
|
||||
"""
|
||||
Close the given tab. If None, close the current one
|
||||
"""
|
||||
|
@ -1262,7 +1209,7 @@ class Core:
|
|||
gc.get_referrers(tab))
|
||||
del tab
|
||||
|
||||
def add_information_message_to_conversation_tab(self, jid, msg):
|
||||
def add_information_message_to_conversation_tab(self, jid: JID, msg: str):
|
||||
"""
|
||||
Search for a ConversationTab with the given jid (full or bare),
|
||||
if yes, add the given message to it
|
||||
|
@ -1281,7 +1228,7 @@ class Core:
|
|||
return
|
||||
curses.doupdate()
|
||||
|
||||
def information(self, msg, typ=''):
|
||||
def information(self, msg: str, typ=''):
|
||||
"""
|
||||
Displays an informational message in the "Info" buffer
|
||||
"""
|
||||
|
@ -1987,24 +1934,67 @@ class Core:
|
|||
self.refresh_window()
|
||||
|
||||
|
||||
class KeyDict(dict):
|
||||
class KeyDict(Dict[str, Callable]):
|
||||
"""
|
||||
A dict, with a wrapper for get() that will return a custom value
|
||||
if the key starts with _exc_
|
||||
"""
|
||||
|
||||
def get(self, k, d=None):
|
||||
if isinstance(k, str) and k.startswith('_exc_') and len(k) > 5:
|
||||
return lambda: dict.get(self, '_exc_')(k[5:])
|
||||
return dict.get(self, k, d)
|
||||
def get(self, key: str, default: Optional[Callable] = None) -> Callable:
|
||||
if isinstance(key, str) and key.startswith('_exc_') and len(key) > 5:
|
||||
return lambda: dict.get(self, '_exc_')(key[5:])
|
||||
return dict.get(self, key, default)
|
||||
|
||||
|
||||
def replace_key_with_bound(key):
|
||||
def replace_key_with_bound(key: str) -> str:
|
||||
"""
|
||||
Replace an inputted key with the one defined as its replacement
|
||||
in the config
|
||||
"""
|
||||
bind = config.get(key, default=key, section='bindings')
|
||||
if not bind:
|
||||
bind = key
|
||||
return bind
|
||||
return config.get(key, default=key, section='bindings') or key
|
||||
|
||||
|
||||
def replace_line_breaks(key: str) -> str:
|
||||
"replace ^J with \n"
|
||||
if key == '^J':
|
||||
return '\n'
|
||||
return key
|
||||
|
||||
|
||||
def separate_chars_from_bindings(char_list: List[str]) -> List[List[str]]:
|
||||
"""
|
||||
returns a list of lists. For example if you give
|
||||
['a', 'b', 'KEY_BACKSPACE', 'n', 'u'], this function returns
|
||||
[['a', 'b'], ['KEY_BACKSPACE'], ['n', 'u']]
|
||||
|
||||
This way, in case of lag (for example), we handle the typed text
|
||||
by “batch” as much as possible (instead of one char at a time,
|
||||
which implies a refresh after each char, which is very slow),
|
||||
but we still handle the special chars (backspaces, arrows,
|
||||
ctrl+x ou alt+x, etc) one by one, which avoids the issue of
|
||||
printing them OR ignoring them in that case. This should
|
||||
resolve the “my ^W are ignored when I lag ;(”.
|
||||
"""
|
||||
res = []
|
||||
current = []
|
||||
for char in char_list:
|
||||
assert char
|
||||
# Transform that stupid char into what we actually meant
|
||||
if char == '\x1f':
|
||||
char = '^/'
|
||||
if len(char) == 1:
|
||||
current.append(char)
|
||||
else:
|
||||
# special case for the ^I key, it’s considered as \t
|
||||
# only when pasting some text, otherwise that’s the ^I
|
||||
# (or M-i) key, which stands for completion by default.
|
||||
if char == '^I' and len(char_list) != 1:
|
||||
current.append('\t')
|
||||
continue
|
||||
if current:
|
||||
res.append(current)
|
||||
current = []
|
||||
res.append([char])
|
||||
if current:
|
||||
res.append(current)
|
||||
return res
|
||||
|
|
|
@ -15,6 +15,8 @@ shortcut, like ^A, M-a or KEY_RESIZE)
|
|||
import curses
|
||||
import curses.ascii
|
||||
import logging
|
||||
from typing import Callable, List, Optional, Tuple
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# A callback that will handle the next key entered by the user. For
|
||||
|
@ -24,10 +26,10 @@ log = logging.getLogger(__name__)
|
|||
# shortcuts or inserting text in the current output. The callback
|
||||
# is always reset to None afterwards (to resume the normal
|
||||
# processing of keys)
|
||||
continuation_keys_callback = None
|
||||
continuation_keys_callback = None # type: Optional[Callable]
|
||||
|
||||
|
||||
def get_next_byte(s):
|
||||
def get_next_byte(s) -> Tuple[Optional[int], Optional[bytes]]:
|
||||
"""
|
||||
Read the next byte of the utf-8 char
|
||||
ncurses seems to return a string of the byte
|
||||
|
@ -43,8 +45,8 @@ def get_next_byte(s):
|
|||
return (ord(c), c.encode('latin-1')) # returns a number and a bytes object
|
||||
|
||||
|
||||
def get_char_list(s):
|
||||
ret_list = []
|
||||
def get_char_list(s) -> List[str]:
|
||||
ret_list = [] # type: List[str]
|
||||
while True:
|
||||
try:
|
||||
key = s.get_wch()
|
||||
|
@ -109,7 +111,7 @@ class Keyboard:
|
|||
"""
|
||||
self.escape = True
|
||||
|
||||
def get_user_input(self, s):
|
||||
def get_user_input(self, s) -> List[str]:
|
||||
"""
|
||||
Returns a list of all the available characters to read (for example it
|
||||
may contain a whole text if there’s some lag, or the user pasted text,
|
||||
|
|
|
@ -355,4 +355,4 @@ def create_logger() -> None:
|
|||
logger = Logger()
|
||||
|
||||
|
||||
logger = None
|
||||
logger = None # type: Optional[Logger]
|
||||
|
|
|
@ -13,7 +13,7 @@ Once created, they must be added to the list of checked events with
|
|||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Callable
|
||||
from typing import Callable, Union
|
||||
|
||||
|
||||
class DelayedEvent:
|
||||
|
@ -22,7 +22,8 @@ class DelayedEvent:
|
|||
Use it if you want an event to happen in, e.g. 6 seconds.
|
||||
"""
|
||||
|
||||
def __init__(self, delay: int, callback: Callable, *args) -> None:
|
||||
def __init__(self, delay: Union[int, float], callback: Callable,
|
||||
*args) -> None:
|
||||
"""
|
||||
Create a new DelayedEvent.
|
||||
|
||||
|
|
Loading…
Reference in a new issue