blob: 359cff938bf63bc4156173f8b8ff5d3968ddeba1 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
Shawn O. Pearce1dcb58a2009-07-02 12:45:47 -070016import re
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import subprocess
Mike Frysinger64477332023-08-21 21:20:32 -040018import sys
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import tempfile
20
21from error import EditorError
Renaud Paquay010fed72016-11-11 14:25:29 -080022import platform_utils
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023
David Pursehouse819827a2020-02-12 15:20:19 +090024
Mike Frysingerd4aee652023-10-19 05:13:32 -040025class Editor:
Gavin Makea2e3302023-03-11 06:46:20 +000026 """Manages the user's preferred text editor."""
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027
Gavin Makea2e3302023-03-11 06:46:20 +000028 _editor = None
29 globalConfig = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030
Gavin Makea2e3302023-03-11 06:46:20 +000031 @classmethod
32 def _GetEditor(cls):
33 if cls._editor is None:
34 cls._editor = cls._SelectEditor()
35 return cls._editor
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070036
Gavin Makea2e3302023-03-11 06:46:20 +000037 @classmethod
38 def _SelectEditor(cls):
39 e = os.getenv("GIT_EDITOR")
40 if e:
41 return e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Gavin Makea2e3302023-03-11 06:46:20 +000043 if cls.globalConfig:
44 e = cls.globalConfig.GetString("core.editor")
45 if e:
46 return e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070047
Gavin Makea2e3302023-03-11 06:46:20 +000048 e = os.getenv("VISUAL")
49 if e:
50 return e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070051
Gavin Makea2e3302023-03-11 06:46:20 +000052 e = os.getenv("EDITOR")
53 if e:
54 return e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070055
Gavin Makea2e3302023-03-11 06:46:20 +000056 if os.getenv("TERM") == "dumb":
57 print(
58 """No editor specified in GIT_EDITOR, core.editor, VISUAL or EDITOR.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070059Tried to fall back to vi but terminal is dumb. Please configure at
Gavin Makea2e3302023-03-11 06:46:20 +000060least one of these before using this command.""", # noqa: E501
61 file=sys.stderr,
62 )
63 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070064
Gavin Makea2e3302023-03-11 06:46:20 +000065 return "vi"
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070066
Gavin Makea2e3302023-03-11 06:46:20 +000067 @classmethod
68 def EditString(cls, data):
69 """Opens an editor to edit the given content.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070070
Gavin Makea2e3302023-03-11 06:46:20 +000071 Args:
72 data: The text to edit.
Sarah Owenscecd1d82012-11-01 22:59:27 -070073
Gavin Makea2e3302023-03-11 06:46:20 +000074 Returns:
75 New value of edited text.
Mike Frysinger70c54dc2019-11-15 01:19:03 -050076
Gavin Makea2e3302023-03-11 06:46:20 +000077 Raises:
78 EditorError: The editor failed to run.
79 """
80 editor = cls._GetEditor()
81 if editor == ":":
82 return data
Shawn O. Pearce1dcb58a2009-07-02 12:45:47 -070083
Gavin Makea2e3302023-03-11 06:46:20 +000084 fd, path = tempfile.mkstemp()
85 try:
86 os.write(fd, data.encode("utf-8"))
87 os.close(fd)
88 fd = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070089
Gavin Makea2e3302023-03-11 06:46:20 +000090 if platform_utils.isWindows():
91 # Split on spaces, respecting quoted strings
92 import shlex
Shawn O. Pearce1dcb58a2009-07-02 12:45:47 -070093
Gavin Makea2e3302023-03-11 06:46:20 +000094 args = shlex.split(editor)
95 shell = False
96 elif re.compile("^.*[$ \t'].*$").match(editor):
97 args = [editor + ' "$@"', "sh"]
98 shell = True
99 else:
100 args = [editor]
101 shell = False
102 args.append(path)
Shawn O. Pearce54fccd72009-06-24 07:09:51 -0700103
Gavin Makea2e3302023-03-11 06:46:20 +0000104 try:
105 rc = subprocess.Popen(args, shell=shell).wait()
106 except OSError as e:
Jason R. Coombsb32ccbb2023-09-29 11:04:49 -0400107 raise EditorError(f"editor failed, {str(e)}: {editor} {path}")
Gavin Makea2e3302023-03-11 06:46:20 +0000108 if rc != 0:
109 raise EditorError(
110 "editor failed with exit status %d: %s %s"
111 % (rc, editor, path)
112 )
113
114 with open(path, mode="rb") as fd2:
115 return fd2.read().decode("utf-8")
116 finally:
117 if fd:
118 os.close(fd)
119 platform_utils.remove(path)