summaryrefslogtreecommitdiff
path: root/alembic/util/editor.py
blob: 51041489c8b702d2a85034424fb0d7723663880c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import os
from os.path import exists
from os.path import join
from os.path import splitext
from subprocess import check_call

from .compat import is_posix
from .compat import raise_
from .exc import CommandError


def open_in_editor(filename, environ=None):
    """
    Opens the given file in a text editor. If the environment variable
    ``EDITOR`` is set, this is taken as preference.

    Otherwise, a list of commonly installed editors is tried.

    If no editor matches, an :py:exc:`OSError` is raised.

    :param filename: The filename to open. Will be passed  verbatim to the
        editor command.
    :param environ: An optional drop-in replacement for ``os.environ``. Used
        mainly for testing.
    """

    try:
        editor = _find_editor(environ)
        check_call([editor, filename])
    except Exception as exc:
        raise_(CommandError("Error executing editor (%s)" % (exc,)), from_=exc)


def _find_editor(environ=None):
    candidates = _default_editors()
    for i, var in enumerate(("EDITOR", "VISUAL")):
        if var in environ:
            user_choice = environ[var]
            if exists(user_choice):
                return user_choice
            if os.sep not in user_choice:
                candidates.insert(i, user_choice)

    for candidate in candidates:
        path = _find_executable(candidate, environ)
        if path is not None:
            return path
    raise OSError(
        "No suitable editor found. Please set the "
        '"EDITOR" or "VISUAL" environment variables'
    )


def _find_executable(candidate, environ):
    # Assuming this is on the PATH, we need to determine it's absolute
    # location. Otherwise, ``check_call`` will fail
    if not is_posix and splitext(candidate)[1] != ".exe":
        candidate += ".exe"
    for path in environ.get("PATH", "").split(os.pathsep):
        value = join(path, candidate)
        if exists(value):
            return value
    return None


def _default_editors():
    # Look for an editor. Prefer the user's choice by env-var, fall back to
    # most commonly installed editor (nano/vim)
    if is_posix:
        return ["sensible-editor", "editor", "nano", "vim", "code"]
    else:
        return ["code.exe", "notepad++.exe", "notepad.exe"]