Repository: kovidgoyal/kitty

Issue:
Regression re editor for preferences
My `EDITOR` var got set to nano for some reason, not sure why but that's beside the point. It did make me notice that kitty doesn't properly use the `VISUAL` variable to open up the preferences. This seems to be a regression because it used to open up in vim for me.

Current values:
```
VISUAL=vim
EDITOR=nano
```

`Cmd-;`, to open up preferences, opens up `kitty.conf` in a new kitty window inside *nano*.
This suggests to me that kitty's running `${EDITOR} kitty.conf` while accepted behavior is `${VISUAL:-${EDITOR}} kitty.conf`. Because no one uses non-visual editors (like ed) anymore anyway at least not to edit config files afaik.

Note that a "visual editor" is an editor like nano or vim. It has nothing to do with GUI editors which aren't guaranteed to work properly when set as either of `EDITOR` and `VISUAL`.


Discussion:
kitty will use the editor specified in `kitty.conf`. If the option is set to the default value `.`, kitty will try to load the environment variable `EDITOR`. Since by default it's not set on macOS, that won't work either. If that happens, kitty will start a shell and read the `EDITOR` environment variable from there. We try to avoid starting a shell because that makes kitty start quite a bit slower.
To work around kitty not respecting `VISUAL`, either set the editor in `kitty.conf`, see https://sw.kovidgoyal.net/kitty/conf.html#opt-kitty.editor or change `EDITOR` to `vim` in your `.bashrc` or similar.
@kovidgoyal should we try to read `VISUAL` as well before reading `EDITOR`?
The only reason to read `EDITOR` at all is that most people configure it wrong. It's a remnant from when line-based editors as opposed to visual editors were common.

I want to emphasise that this regressed for me because it used to open in vim without me having set `EDITOR`.
@Luflosi: Sure I dont mind. I never use VISUAL myself since the original
rationale for it was that EDITOR is supposed to work on dumb terminals,
while VISUAL works on full screen "modern" terminals. Since I have never
used a terminal not capable of running VISUAL in the last decade it
seems highly redundant and far less intuitive than EDITOR. But ok, I
wont refuse a PR to prefer VISUAL over EDITOR is both exist.


Resolving patch:
diff --git a/kitty/config_data.py b/kitty/config_data.py
--- a/kitty/config_data.py
+++ b/kitty/config_data.py
@@ -812,9 +812,9 @@ def selection_foreground(x):
 
 o('editor', '.', long_text=_('''
 The console editor to use when editing the kitty config file or similar tasks.
-A value of . means to use the environment variable EDITOR. Note that this
-environment variable has to be set not just in your shell startup scripts but
-system-wide, otherwise kitty will not see it.
+A value of . means to use the environment variables VISUAL and EDITOR in that
+order. Note that this environment variable has to be set not just in your shell
+startup scripts but system-wide, otherwise kitty will not see it.
 '''))
 
 o('close_on_child_death', False, long_text=_('''
diff --git a/kitty/main.py b/kitty/main.py
--- a/kitty/main.py
+++ b/kitty/main.py
@@ -247,19 +247,19 @@ def read_shell_environment(opts=None):
 def setup_environment(opts, args):
     extra_env = opts.env.copy()
     if opts.editor == '.':
-        if 'EDITOR' not in os.environ:
+        if 'VISUAL' not in os.environ and 'EDITOR' not in os.environ:
             shell_env = read_shell_environment(opts)
-            if 'EDITOR' in shell_env:
-                editor = shell_env['EDITOR']
-                if 'PATH' in shell_env:
-                    import shlex
-                    editor_cmd = shlex.split(editor)
-                    if not os.path.isabs(editor_cmd[0]):
-                        editor_cmd[0] = shutil.which(editor_cmd[0], path=shell_env['PATH'])
-                        if editor_cmd[0]:
-                            editor = ' '.join(map(shlex.quote, editor_cmd))
-                        else:
-                            editor = None
+            if 'VISUAL' in shell_env or 'EDITOR' in shell_env:
+                for editor in (shell_env['VISUAL'], shell_env['EDITOR']):
+                    if 'PATH' in shell_env:
+                        import shlex
+                        editor_cmd = shlex.split(editor)
+                        if not os.path.isabs(editor_cmd[0]):
+                            editor_cmd[0] = shutil.which(editor_cmd[0], path=shell_env['PATH'])
+                            if editor_cmd[0]:
+                                editor = ' '.join(map(shlex.quote, editor_cmd))
+                            else:
+                                editor = None
                 if editor:
                     os.environ['EDITOR'] = editor
     else:
diff --git a/kitty/utils.py b/kitty/utils.py
--- a/kitty/utils.py
+++ b/kitty/utils.py
@@ -415,15 +415,12 @@ def get_editor():
     ans = getattr(get_editor, 'ans', False)
     if ans is False:
         import shlex
-        ans = os.environ.get('EDITOR')
-        if not ans or not exe_exists(shlex.split(ans)[0]):
-            for q in ('vim', 'nvim', 'vi', 'emacs', 'kak', 'micro', 'nano', 'vis'):
-                r = exe_exists(q)
-                if r:
-                    ans = r
-                    break
-            else:
-                ans = 'vim'
+        for ans in (os.environ.get('VISUAL'), os.environ.get('EDITOR'), 'vim',
+                    'nvim', 'vi', 'emacs', 'kak', 'micro', 'nano', 'vis'):
+            if ans and exe_exists(shlex.split(ans)[0]):
+                break
+        else:
+            ans = 'vim'
         ans = shlex.split(ans)
         get_editor.ans = ans
     return ans


Test patch: