diff --git a/docs/hotkeys.md b/docs/hotkeys.md
index 1272bfc519..d1cd722db5 100644
--- a/docs/hotkeys.md
+++ b/docs/hotkeys.md
@@ -8,7 +8,6 @@
|Show/hide Help Menu|?|
|Show/hide Markdown Help Menu|Meta + m|
|Show/hide About Menu|Meta + ?|
-|Go back|Esc|
|Open draft message saved in this session|d|
|Copy information from About Menu to clipboard|c|
|Redraw screen|Ctrl + l|
@@ -18,6 +17,7 @@
## Navigation
|Command|Key Combination|
| :--- | :---: |
+|Close popup|Esc|
|Go up / Previous message|Up / k|
|Go down / Next message|Down / j|
|Go left|Left / h|
@@ -42,6 +42,7 @@
|Search topics in a stream|q|
|Search emojis from emoji picker|p|
|Submit search and browse results|Enter|
+|Clear search in current panel|Esc|
## Message actions
|Command|Key Combination|
@@ -85,6 +86,7 @@
|Autocomplete @mentions, #stream_names, :emoji: and topics|Ctrl + f|
|Cycle through autocomplete suggestions in reverse|Ctrl + r|
|Narrow to compose box message recipient|Meta + .|
+|Exit message compose box|Esc|
|Insert new line|Enter|
## Editor: Navigation
diff --git a/tests/ui/test_ui_tools.py b/tests/ui/test_ui_tools.py
index 18d2ef27b3..0a4d9ec030 100644
--- a/tests/ui/test_ui_tools.py
+++ b/tests/ui/test_ui_tools.py
@@ -559,8 +559,8 @@ def test_keypress_SEARCH_STREAMS(self, mocker, stream_view, key, widget_size):
stream_view.stream_search_box
)
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
- def test_keypress_GO_BACK(self, mocker, stream_view, key, widget_size):
+ @pytest.mark.parametrize("key", keys_for_command("CLEAR_SEARCH"))
+ def test_keypress_CLEAR_SEARCH(self, mocker, stream_view, key, widget_size):
size = widget_size(stream_view)
mocker.patch.object(stream_view, "set_focus")
mocker.patch(VIEWS + ".urwid.Frame.keypress")
@@ -725,8 +725,8 @@ def test_keypress_SEARCH_TOPICS(self, mocker, topic_view, key, widget_size):
topic_view.topic_search_box
)
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
- def test_keypress_GO_BACK(self, mocker, topic_view, key, widget_size):
+ @pytest.mark.parametrize("key", keys_for_command("CLEAR_SEARCH"))
+ def test_keypress_CLEAR_SEARCH(self, mocker, topic_view, key, widget_size):
size = widget_size(topic_view)
mocker.patch(VIEWS + ".TopicsView.set_focus")
mocker.patch(VIEWS + ".urwid.Frame.keypress")
@@ -1105,8 +1105,8 @@ def test_keypress_SEARCH_PEOPLE(self, right_col_view, mocker, key, widget_size):
right_col_view.user_search
)
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
- def test_keypress_GO_BACK(self, right_col_view, mocker, key, widget_size):
+ @pytest.mark.parametrize("key", keys_for_command("CLEAR_SEARCH"))
+ def test_keypress_CLEAR_SEARCH(self, right_col_view, mocker, key, widget_size):
size = widget_size(right_col_view)
mocker.patch(VIEWS + ".UsersView")
mocker.patch(VIEWS + ".RightColumnView.set_focus")
diff --git a/tests/ui_tools/test_boxes.py b/tests/ui_tools/test_boxes.py
index b6c763c0cb..4b28a1e02b 100644
--- a/tests/ui_tools/test_boxes.py
+++ b/tests/ui_tools/test_boxes.py
@@ -235,7 +235,7 @@ def test_not_calling_send_private_message_without_recipients(
assert not write_box.model.send_private_message.called
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
+ @pytest.mark.parametrize("key", keys_for_command("EXIT_COMPOSE"))
def test__compose_attributes_reset_for_private_compose(
self,
key: str,
@@ -256,7 +256,7 @@ def test__compose_attributes_reset_for_private_compose(
assert write_box.msg_write_box.edit_text == ""
assert write_box.compose_box_status == "closed"
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
+ @pytest.mark.parametrize("key", keys_for_command("EXIT_COMPOSE"))
def test__compose_attributes_reset_for_stream_compose(
self,
key: str,
@@ -1516,7 +1516,7 @@ def test_keypress_SEND_MESSAGE_no_topic(
(primary_key_for_command("AUTOCOMPLETE"), True, True, False),
(primary_key_for_command("AUTOCOMPLETE_REVERSE"), True, True, False),
# footer resets
- (primary_key_for_command("GO_BACK"), True, False, True),
+ (primary_key_for_command("EXIT_COMPOSE"), True, False, True),
("space", True, False, True),
("k", True, False, True),
],
@@ -1858,8 +1858,8 @@ def test_keypress_ENTER(
panel_view.set_focus.assert_not_called()
panel_view.body.set_focus.assert_not_called()
- @pytest.mark.parametrize("back_key", keys_for_command("GO_BACK"))
- def test_keypress_GO_BACK(
+ @pytest.mark.parametrize("back_key", keys_for_command("CLEAR_SEARCH"))
+ def test_keypress_CLEAR_SEARCH(
self,
panel_search_box: PanelSearchBox,
back_key: str,
diff --git a/tests/ui_tools/test_popups.py b/tests/ui_tools/test_popups.py
index ee412f65a1..d58b6c3353 100644
--- a/tests/ui_tools/test_popups.py
+++ b/tests/ui_tools/test_popups.py
@@ -78,8 +78,8 @@ def test_exit_popup_no(
self.callback.assert_not_called()
assert self.controller.exit_popup.called
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
- def test_exit_popup_GO_BACK(
+ @pytest.mark.parametrize("key", keys_for_command("EXIT_POPUP"))
+ def test_exit_popup_EXIT_POPUP(
self,
popup_view: PopUpConfirmationView,
key: str,
@@ -133,8 +133,8 @@ def test_init(self, mocker: MockerFixture) -> None:
self.pop_up_view.body, header=mocker.ANY, footer=mocker.ANY
)
- @pytest.mark.parametrize("key", keys_for_command("GO_BACK"))
- def test_keypress_GO_BACK(
+ @pytest.mark.parametrize("key", keys_for_command("EXIT_POPUP"))
+ def test_keypress_EXIT_POPUP(
self,
key: str,
widget_size: Callable[[Widget], urwid_Size],
@@ -210,7 +210,7 @@ def mock_external_classes(self, mocker: MockerFixture) -> None:
)
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("ABOUT")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("ABOUT")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -467,7 +467,7 @@ def test__fetch_user_data_USER_NOT_FOUND(self, mocker: MockerFixture) -> None:
assert custom_profile_data == {}
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("USER_INFO")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("USER_INFO")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -540,7 +540,7 @@ def test_keypress_exit_popup_invalid_key(
"key",
{
*keys_for_command("FULL_RENDERED_MESSAGE"),
- *keys_for_command("GO_BACK"),
+ *keys_for_command("EXIT_POPUP"),
},
)
def test_keypress_show_msg_info(
@@ -616,7 +616,7 @@ def test_keypress_exit_popup_invalid_key(
"key",
{
*keys_for_command("FULL_RAW_MESSAGE"),
- *keys_for_command("GO_BACK"),
+ *keys_for_command("EXIT_POPUP"),
},
)
def test_keypress_show_msg_info(
@@ -688,7 +688,7 @@ def test_keypress_exit_popup_invalid_key(
assert not self.controller.exit_popup.called
@pytest.mark.parametrize(
- "key", {*keys_for_command("EDIT_HISTORY"), *keys_for_command("GO_BACK")}
+ "key", {*keys_for_command("EDIT_HISTORY"), *keys_for_command("EXIT_POPUP")}
)
def test_keypress_show_msg_info(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -911,7 +911,7 @@ def test_keypress_any_key(
assert not self.controller.exit_popup.called
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("MARKDOWN_HELP")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("MARKDOWN_HELP")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -942,7 +942,7 @@ def test_keypress_any_key(
assert not self.controller.exit_popup.called
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("HELP")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("HELP")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -1113,7 +1113,7 @@ def test_keypress_full_raw_message(
)
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("MSG_INFO")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("MSG_INFO")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -1550,7 +1550,7 @@ def test_footlinks(
assert footlinks_width == expected_footlinks_width
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("STREAM_INFO")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("STREAM_INFO")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -1615,7 +1615,7 @@ def mock_external_classes(self, mocker: MockerFixture) -> None:
self.stream_members_view = StreamMembersView(self.controller, stream_id)
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("STREAM_MEMBERS")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("STREAM_MEMBERS")}
)
def test_keypress_exit_popup(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
@@ -1730,7 +1730,7 @@ def test_keypress_search_emoji(
assert self.emoji_picker_view.get_focus() == "header"
@pytest.mark.parametrize(
- "key", {*keys_for_command("GO_BACK"), *keys_for_command("ADD_REACTION")}
+ "key", {*keys_for_command("EXIT_POPUP"), *keys_for_command("ADD_REACTION")}
)
def test_keypress_exit_called(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
diff --git a/tools/lint-hotkeys b/tools/lint-hotkeys
index 503a024e98..83a7db068e 100755
--- a/tools/lint-hotkeys
+++ b/tools/lint-hotkeys
@@ -23,7 +23,7 @@ SCRIPT_NAME = PurePath(__file__).name
HELP_TEXT_STYLE = re.compile(r"^[a-zA-Z /()',&@#:_-]*$")
# Exclude keys from duplicate keys checking
-KEYS_TO_EXCLUDE = ["q", "e", "m", "r"]
+KEYS_TO_EXCLUDE = ["q", "e", "m", "r", "Esc"]
def main(fix: bool) -> None:
diff --git a/zulipterminal/config/keys.py b/zulipterminal/config/keys.py
index 3306f682de..254778a12b 100644
--- a/zulipterminal/config/keys.py
+++ b/zulipterminal/config/keys.py
@@ -46,12 +46,6 @@ class KeyBinding(TypedDict):
'help_text': 'Show/hide About Menu',
'key_category': 'general',
},
- 'GO_BACK': {
- 'keys': ['esc'],
- 'help_text': 'Go back',
- 'excluded_from_random_tips': False,
- 'key_category': 'general',
- },
'OPEN_DRAFT': {
'keys': ['d'],
'help_text': 'Open draft message saved in this session',
@@ -62,6 +56,11 @@ class KeyBinding(TypedDict):
'help_text': 'Copy information from About Menu to clipboard',
'key_category': 'general',
},
+ 'EXIT_POPUP': {
+ 'keys': ['esc'],
+ 'help_text': 'Close popup',
+ 'key_category': 'navigation',
+ },
'GO_UP': {
'keys': ['up', 'k'],
'help_text': 'Go up / Previous message',
@@ -183,6 +182,11 @@ class KeyBinding(TypedDict):
'help_text': 'Narrow to compose box message recipient',
'key_category': 'msg_compose',
},
+ 'EXIT_COMPOSE': {
+ 'keys': ['esc'],
+ 'help_text': 'Exit message compose box',
+ 'key_category': 'msg_compose',
+ },
'TOGGLE_NARROW': {
'keys': ['z'],
'help_text':
@@ -260,6 +264,11 @@ class KeyBinding(TypedDict):
'help_text': 'Submit search and browse results',
'key_category': 'searching',
},
+ 'CLEAR_SEARCH': {
+ 'keys': ['esc'],
+ 'help_text': 'Clear search in current panel',
+ 'key_category': 'searching',
+ },
'TOGGLE_MUTE_STREAM': {
'keys': ['m'],
'help_text': 'Mute/unmute streams',
diff --git a/zulipterminal/model.py b/zulipterminal/model.py
index f917a56254..1d7688cdeb 100644
--- a/zulipterminal/model.py
+++ b/zulipterminal/model.py
@@ -1714,7 +1714,7 @@ def _handle_message_event(self, event: Event) -> None:
"Press '{}' to close this window."
)
notice = notice_template.format(
- failed_command, primary_display_key_for_command("GO_BACK")
+ failed_command, primary_display_key_for_command("EXIT_POPUP")
)
self.controller.popup_with_message(notice, width=50)
self.controller.update_screen()
diff --git a/zulipterminal/ui_tools/boxes.py b/zulipterminal/ui_tools/boxes.py
index 91e2f80966..fa3dc1ffd6 100644
--- a/zulipterminal/ui_tools/boxes.py
+++ b/zulipterminal/ui_tools/boxes.py
@@ -775,7 +775,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if success:
self.msg_write_box.edit_text = ""
if self.msg_edit_state is not None:
- self.keypress(size, primary_key_for_command("GO_BACK"))
+ self.keypress(size, primary_key_for_command("EXIT_COMPOSE"))
assert self.msg_edit_state is None
elif is_command_key("NARROW_MESSAGE_RECIPIENT", key):
if self.compose_box_status == "open_with_stream":
@@ -798,7 +798,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.view.controller.report_error(
"Cannot narrow to message without specifying recipients."
)
- elif is_command_key("GO_BACK", key):
+ elif is_command_key("EXIT_COMPOSE", key):
self.send_stop_typing_status()
self._set_compose_attributes_to_defaults()
self.view.controller.exit_editor_mode()
@@ -948,7 +948,7 @@ def main_view(self) -> Any:
def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if (
is_command_key("EXECUTE_SEARCH", key) and self.text_box.edit_text == ""
- ) or is_command_key("GO_BACK", key):
+ ) or is_command_key("CLEAR_SEARCH", key):
self.text_box.set_edit_text("")
self.controller.exit_editor_mode()
self.controller.view.middle_column.set_focus("body")
@@ -1004,13 +1004,13 @@ def valid_char(self, ch: str) -> bool:
def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if (
is_command_key("EXECUTE_SEARCH", key) and self.get_edit_text() == ""
- ) or is_command_key("GO_BACK", key):
+ ) or is_command_key("CLEAR_SEARCH", key):
self.panel_view.view.controller.exit_editor_mode()
self.reset_search_text()
self.panel_view.set_focus("body")
# Don't call 'Esc' when inside a popup search-box.
if not self.panel_view.view.controller.is_any_popup_open():
- self.panel_view.keypress(size, primary_key_for_command("GO_BACK"))
+ self.panel_view.keypress(size, primary_key_for_command("CLEAR_SEARCH"))
elif is_command_key("EXECUTE_SEARCH", key) and not self.panel_view.empty_search:
self.panel_view.view.controller.exit_editor_mode()
self.set_caption([("filter_results", " Search Results "), " "])
diff --git a/zulipterminal/ui_tools/views.py b/zulipterminal/ui_tools/views.py
index ddb8e8d01a..c2034b3ef7 100644
--- a/zulipterminal/ui_tools/views.py
+++ b/zulipterminal/ui_tools/views.py
@@ -398,7 +398,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.stream_search_box.set_caption(" ")
self.view.controller.enter_editor_mode_with(self.stream_search_box)
return key
- elif is_command_key("GO_BACK", key):
+ elif is_command_key("CLEAR_SEARCH", key):
self.stream_search_box.reset_search_text()
self.log.clear()
self.log.extend(self.streams_btn_list)
@@ -518,7 +518,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.topic_search_box.set_caption(" ")
self.view.controller.enter_editor_mode_with(self.topic_search_box)
return key
- elif is_command_key("GO_BACK", key):
+ elif is_command_key("CLEAR_SEARCH", key):
self.topic_search_box.reset_search_text()
self.log.clear()
self.log.extend(self.topics_btn_list)
@@ -759,7 +759,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.user_search.set_caption(" ")
self.view.controller.enter_editor_mode_with(self.user_search)
return key
- elif is_command_key("GO_BACK", key):
+ elif is_command_key("CLEAR_SEARCH", key):
self.user_search.reset_search_text()
self.allow_update_user_list = True
self.body = UsersView(self.view.controller, self.users_btn_list)
@@ -1058,7 +1058,7 @@ def make_table_with_categories(
return widgets
def keypress(self, size: urwid_Size, key: str) -> str:
- if is_command_key("GO_BACK", key) or is_command_key(self.command, key):
+ if is_command_key("EXIT_POPUP", key) or is_command_key(self.command, key):
self.controller.exit_popup()
return super().keypress(size, key)
@@ -1073,7 +1073,7 @@ def __init__(
urwid.Padding(urwid.Text(notice_text), left=1, right=1),
urwid.Divider(),
]
- super().__init__(controller, widgets, "GO_BACK", width, title)
+ super().__init__(controller, widgets, "EXIT_POPUP", width, title)
class AboutView(PopUpView):
@@ -1351,7 +1351,7 @@ def exit_popup_no(self, args: Any) -> None:
self.controller.exit_popup()
def keypress(self, size: urwid_Size, key: str) -> str:
- if is_command_key("GO_BACK", key):
+ if is_command_key("EXIT_POPUP", key):
self.controller.exit_popup()
return super().keypress(size, key)
@@ -1561,7 +1561,7 @@ def __init__(self, controller: Any, stream_id: int) -> None:
super().__init__(controller, widgets, "STREAM_INFO", popup_width, title)
def keypress(self, size: urwid_Size, key: str) -> str:
- if is_command_key("GO_BACK", key) or is_command_key("STREAM_MEMBERS", key):
+ if is_command_key("EXIT_POPUP", key) or is_command_key("STREAM_MEMBERS", key):
self.controller.show_stream_info(stream_id=self.stream_id)
return key
return super().keypress(size, key)
@@ -1895,7 +1895,7 @@ def _get_author_prefix(snapshot: Dict[str, Any], tag: EditHistoryTag) -> str:
return author_prefix
def keypress(self, size: urwid_Size, key: str) -> str:
- if is_command_key("GO_BACK", key) or is_command_key("EDIT_HISTORY", key):
+ if is_command_key("EXIT_POPUP", key) or is_command_key("EDIT_HISTORY", key):
self.controller.show_msg_info(
msg=self.message,
topic_links=self.topic_links,
@@ -1937,7 +1937,7 @@ def __init__(
)
def keypress(self, size: urwid_Size, key: str) -> str:
- if is_command_key("GO_BACK", key) or is_command_key(
+ if is_command_key("EXIT_POPUP", key) or is_command_key(
"FULL_RENDERED_MESSAGE", key
):
self.controller.show_msg_info(
@@ -1989,7 +1989,7 @@ def __init__(
)
def keypress(self, size: urwid_Size, key: str) -> str:
- if is_command_key("GO_BACK", key) or is_command_key("FULL_RAW_MESSAGE", key):
+ if is_command_key("EXIT_POPUP", key) or is_command_key("FULL_RAW_MESSAGE", key):
self.controller.show_msg_info(
msg=self.message,
topic_links=self.topic_links,
@@ -2144,7 +2144,7 @@ def keypress(self, size: urwid_Size, key: str) -> str:
self.emoji_search.set_caption(" ")
self.controller.enter_editor_mode_with(self.emoji_search)
return key
- elif is_command_key("GO_BACK", key) or is_command_key("ADD_REACTION", key):
+ elif is_command_key("EXIT_POPUP", key) or is_command_key("ADD_REACTION", key):
for emoji_code, emoji_name in self.selected_emojis.items():
self.controller.model.toggle_message_reaction(self.message, emoji_name)
self.emoji_search.reset_search_text()