Skip to content

Commit 70dd1b7

Browse files
committed
feat(command): open external editor command in composing message
OPEN_EXTERNAL_EDITOR command paste the composing message in a python tempfile, run external editor over it and wait the exit before update the message. $ZULIP_EDITOR_COMMAND and fallback $EDITOR are use for the external editor command. Use shlex to split command.
1 parent bef2a8f commit 70dd1b7

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

docs/FAQ.md

+25
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,31 @@ If any of the hotkeys don't work for you, feel free to open an issue or discuss
428428
it on
429429
[#zulip-terminal](https://chat.zulip.org/#narrow/stream/206-zulip-terminal).
430430

431+
## Can I compose messages in another editor ?
432+
433+
In the main branch of zulip-terminal, you can now use an external editor to
434+
compose your message using `ctrl o` shortcut. If `ZULIP_EDITOR_COMMAND` or
435+
`EDITOR` environment variable is set, this command or program would be used
436+
to open the message by appending a temporary file filepath of the current message.
437+
438+
It will work directly for most terminal editors with only the program name `vim`,
439+
`nano`, `helix`, `kakoune`, `nvim`...
440+
441+
It can also be used for desktop editor with some constraint which needs to be
442+
address using `ZULIP_EDITOR_COMMAND` custom command. The program must not fork
443+
or detach from the running terminal and should open in a new window, some
444+
examples:
445+
446+
- [lapce](https://github.com/lapce/lapce) with `lapce -n -w`
447+
- [sublime-text](https://www.sublimetext.com/) with `subl -n -w`
448+
- [marker](https://github.com/fabiocolacio/Marker) with `marker`
449+
- [vim](https://github.com/vim/vim) with `vim -g -f` or `gvim -f`
450+
- [vscode](https://github.com/microsoft/vscode) with `code -n -w`
451+
452+
When the external editor process ends (closing the window or quitting terminal
453+
editor), the composing box will be updated with the new message content from
454+
the temporary file.
455+
431456
## Zulip-term crashed!
432457

433458
We hope this doesn't happen, but would love to hear about this in order to fix

docs/hotkeys.md

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
|Autocomplete @mentions, #stream_names, :emoji: and topics|<kbd>ctrl</kbd> + <kbd>f</kbd>|
8383
|Cycle through autocomplete suggestions in reverse|<kbd>ctrl</kbd> + <kbd>r</kbd>|
8484
|Narrow to compose box message recipient|<kbd>meta</kbd> + <kbd>.</kbd>|
85+
|Open the message in external editor|<kbd>ctrl</kbd> + <kbd>o</kbd>|
8586
|Jump to the beginning of line|<kbd>ctrl</kbd> + <kbd>a</kbd>|
8687
|Jump to the end of line|<kbd>ctrl</kbd> + <kbd>e</kbd>|
8788
|Jump backward one word|<kbd>meta</kbd> + <kbd>b</kbd>|

zulipterminal/config/keys.py

+5
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,11 @@ class KeyBinding(TypedDict):
315315
'help_text': 'View user information (From Users list)',
316316
'key_category': 'general',
317317
},
318+
'OPEN_EXTERNAL_EDITOR': {
319+
'keys': ['ctrl o'],
320+
'help_text': 'Open the message in external editor',
321+
'key_category': 'msg_compose',
322+
},
318323
'BEGINNING_OF_LINE': {
319324
'keys': ['ctrl a'],
320325
'help_text': 'Jump to the beginning of line',

zulipterminal/ui_tools/boxes.py

+31
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
UI boxes for entering text: WriteBox, MessageSearchBox, PanelSearchBox
33
"""
44

5+
import os
56
import re
7+
import shlex
8+
import shutil
9+
import subprocess
610
import unicodedata
711
from collections import Counter
812
from datetime import datetime, timedelta
13+
from tempfile import NamedTemporaryFile
914
from time import sleep
1015
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple
1116

@@ -808,6 +813,32 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
808813
elif is_command_key("MARKDOWN_HELP", key):
809814
self.view.controller.show_markdown_help()
810815
return key
816+
elif is_command_key("OPEN_EXTERNAL_EDITOR", key):
817+
editor = os.environ.get("ZULIP_EDITOR_COMMAND", os.environ.get("EDITOR"))
818+
if editor is None:
819+
self.view.controller.report_error(
820+
"Configure $EDITOR or $ZULIP_EDITOR_COMMAND shell environment."
821+
)
822+
return key
823+
editor_splits = shlex.split(editor)
824+
fullpath_program = shutil.which(editor_splits[0])
825+
if fullpath_program is None:
826+
self.view.controller.report_error(
827+
"Editor program not found, check $EDITOR "
828+
"or $ZULIP_EDITOR_COMMAND."
829+
)
830+
return key
831+
editor_splits[0] = fullpath_program
832+
with NamedTemporaryFile(suffix=".md") as edit_tempfile:
833+
with open(edit_tempfile.name, mode="w") as edit_writer:
834+
edit_writer.write(self.msg_write_box.edit_text)
835+
self.view.controller.loop.screen.stop()
836+
editor_splits.append(edit_tempfile.name)
837+
subprocess.call(editor_splits)
838+
with open(edit_tempfile.name, mode="r") as edit_reader:
839+
self.msg_write_box.edit_text = edit_reader.read().rstrip()
840+
self.view.controller.loop.screen.start()
841+
return key
811842
elif is_command_key("SAVE_AS_DRAFT", key):
812843
if self.msg_edit_state is None:
813844
if self.compose_box_status == "open_with_private":

0 commit comments

Comments
 (0)