├── .python-version ├── .no-sublime-package ├── tox.ini ├── messages.json ├── pyrightconfig.json ├── .gitignore ├── PS.bat ├── Context.sublime-menu ├── Side Bar.sublime-menu ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── messages └── 2.0.0.txt ├── PS.ps1 ├── hyper.sh ├── TerminalSwitch.sh ├── Terminal.sh ├── TerminalReuse.sh ├── .github └── workflows │ └── lint.yml ├── Terminal.sublime-settings ├── LICENSE ├── Default.sublime-commands ├── iTerm.sh ├── iTerm2-v3.sh ├── Main.sublime-menu ├── readme.md └── Terminal.py /.python-version: -------------------------------------------------------------------------------- 1 | 3.8 -------------------------------------------------------------------------------- /.no-sublime-package: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "2.0.2": "messages/2.0.0.txt" 3 | } 4 | -------------------------------------------------------------------------------- /pyrightconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "pythonPlatform": "All" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sublime-project 3 | *.sublime-workspace 4 | -------------------------------------------------------------------------------- /PS.bat: -------------------------------------------------------------------------------- 1 | start powershell -noexit -ExecutionPolicy RemoteSigned "%sublime_terminal_path%\PS.ps1" -------------------------------------------------------------------------------- /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { "caption": "-", "id": "file" }, 3 | { "command": "open_terminal", "caption": "Open Terminal Here" } 4 | ] 5 | -------------------------------------------------------------------------------- /Side Bar.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Open Terminal Here", 4 | "command": "open_terminal", 5 | "args": {"paths": []} 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // { "keys": ["ctrl+shift+t"], "command": "open_terminal" }, 3 | // { "keys": ["ctrl+shift+alt+t"], "command": "open_terminal_project_folder" } 4 | ] 5 | -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // { "keys": ["super+shift+t"], "command": "open_terminal" }, 3 | // { "keys": ["super+shift+alt+t"], "command": "open_terminal_project_folder" } 4 | ] 5 | -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // { "keys": ["ctrl+shift+t"], "command": "open_terminal" }, 3 | // { "keys": ["ctrl+shift+alt+t"], "command": "open_terminal_project_folder" } 4 | ] 5 | -------------------------------------------------------------------------------- /messages/2.0.0.txt: -------------------------------------------------------------------------------- 1 | Terminal 2.0 2 | 3 | The terminal package has seen a little make-over. 4 | Nothing major you should notice, except: 5 | 6 | Key bindings are no longer enabled by default. 7 | 8 | The original key bindings were masking built-in binding of Sublime Text itself. 9 | To restore the bindings, or create different ones, edit your bindings file: 10 | Preferences > Package Settings > Terminal > Key Bindings 11 | -------------------------------------------------------------------------------- /PS.ps1: -------------------------------------------------------------------------------- 1 | $pshost = get-host 2 | $pswindow = $pshost.ui.rawui 3 | 4 | $newsize = $pswindow.buffersize 5 | $newsize.height = 3000 6 | $newsize.width = 120 7 | $pswindow.buffersize = $newsize 8 | 9 | $newsize = $pswindow.windowsize 10 | $newsize.height = 50 11 | $newsize.width = 120 12 | $pswindow.windowsize = $newsize 13 | 14 | $pswindow.windowtitle = "Windows Powershell" 15 | $pswindow.foregroundcolor = "DarkYellow" 16 | $pswindow.backgroundcolor = "DarkMagenta" 17 | 18 | cls -------------------------------------------------------------------------------- /hyper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CD_CMD="cd "\\\"$(pwd)\\\"" && clear" 4 | 5 | osascript &>/dev/null < 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences: Terminal Settings", 4 | "command": "edit_settings", 5 | "args": { 6 | "base_file": "${packages}/Terminal/Terminal.sublime-settings", 7 | "default": "// Terminal Settings - User\n{\n\t$0\n}\n", 8 | } 9 | }, 10 | { 11 | "caption": "Preferences: Terminal OS Specific Settings", 12 | "command": "edit_settings", 13 | "args": { 14 | "base_file": "${packages}/Terminal/Terminal.sublime-settings", 15 | "user_file": "${packages}/User/Terminal ($platform).sublime-settings", 16 | "default": "// Terminal OS Specific Settings - User\n{\n\t$0\n}\n" 17 | } 18 | }, 19 | { 20 | "caption": "Preferences: Terminal Key Bindings", 21 | "command": "edit_settings", 22 | "args": { 23 | "base_file": "${packages}/Terminal/Default ($platform).sublime-keymap", 24 | "default": "[\n\t$0\n]\n" 25 | } 26 | }, 27 | { 28 | "caption": "Terminal: Open", 29 | "command": "open_terminal" 30 | }, 31 | { 32 | "caption": "Terminal: Open in project folder", 33 | "command": "open_terminal_project_folder" 34 | }, 35 | { 36 | "caption": "Terminal: Switch to application", 37 | "command": "switch_to_terminal" 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /iTerm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CD_CMD="cd "\\\"$(pwd)\\\"" && clear" 4 | if echo "$SHELL" | grep -E "/fish$" &> /dev/null; then 5 | CD_CMD="cd "\\\"$(pwd)\\\""; and clear" 6 | fi 7 | VERSION=$(sw_vers -productVersion) 8 | OPEN_IN_TAB=0 9 | 10 | while [ "$1" != "" ]; do 11 | PARAM="$1" 12 | VALUE="$2" 13 | case "$PARAM" in 14 | --open-in-tab) 15 | OPEN_IN_TAB=1 16 | ;; 17 | esac 18 | shift 19 | done 20 | 21 | if (( $(expr $VERSION '<' 10.7) )); then 22 | RUNNING=$(osascript</dev/null </dev/null < /dev/null; then 5 | CD_CMD="cd "\\\"$(pwd)\\\""; and clear" 6 | fi 7 | VERSION=$(sw_vers -productVersion) 8 | OPEN_IN_TAB=0 9 | 10 | while [ "$1" != "" ]; do 11 | PARAM="$1" 12 | VALUE="$2" 13 | case "$PARAM" in 14 | --open-in-tab) 15 | OPEN_IN_TAB=1 16 | ;; 17 | esac 18 | shift 19 | done 20 | 21 | RUNNING=$(osascript</dev/null </dev/null < Package Settings > Terminal > Key Bindings* menu entry. Our suggested key bindings are on the left, you can copy these over to your personal bindings on the right and tweak them to your liking. Example: 21 | 22 | ```json 23 | [ 24 | { "keys": ["super+shift+t"], "command": "open_terminal" }, 25 | { "keys": ["super+shift+alt+t"], "command": "open_terminal_project_folder" } 26 | ] 27 | ``` 28 | 29 | Note that in version 2 of this package, we stopped enabling these bindings by default. They conflicted with built-in bindings of Sublime Text, and users might have different preferences. 30 | 31 | ## Package Settings 32 | 33 | The settings can be viewed and edited by accessing the *Preferences > Package Settings > Terminal > Settings* menu entry. 34 | 35 | - **terminal** 36 | - The terminal to execute, will default to the OS default if blank. 37 | - Default: `""` 38 | - **parameters** 39 | - The parameters to pass to the terminal. These parameters will be used if no [custom parameters](#custom-parameters) are passed. 40 | - Default: `[]` 41 | - **env** 42 | - The environment variables changeset. Default environment variables used when invoking the terminal are inherited from Sublime Text. 43 | - The changeset may be used to overwrite/unset environment variables. Use `null` to indicate that the environment variable should be unset. 44 | - Default: `{}` 45 | 46 | ## Custom Parameters 47 | 48 | By passing parameters argument to the `open_terminal` or `open_terminal_project_folder` commands, it is possible to construct custom terminal environments. You can do so by creating custom [key bindings](https://www.sublimetext.com/docs/key_bindings.html) that call these commands with the arguments you want, as we'll document here, or by adding custom [command palette](https://docs.sublimetext.io/reference/command_palette.html) or [menu entries](https://docs.sublimetext.io/reference/menus.html). 49 | 50 | The following is an example, of passing the parameters `-T 'Custom Window Title'`` to an XFCE terminal. 51 | 52 | ```json 53 | { 54 | "keys": ["ctrl+alt+t"], 55 | "command": "open_terminal", 56 | "args": { 57 | "parameters": ["-T", "Custom Window Title"] 58 | } 59 | } 60 | ``` 61 | 62 | A parameter may also contain the *%CWD%* placeholder, which will be substituted with the current working directory the terminal was opened to. 63 | 64 | ```json 65 | { 66 | "keys": ["ctrl+alt+t"], 67 | "command": "open_terminal", 68 | "args": { 69 | "parameters": ["-T", "Working in directory %CWD%"] 70 | } 71 | } 72 | ``` 73 | 74 | ## Example configurations 75 | 76 | Here are some example configurations calling different terminals. Note that paths to executables might differ on your machine. 77 | 78 | ### Cmder on Windows 79 | 80 | ```json 81 | { 82 | "terminal": "C:\\Program Files\\cmder_mini\\cmder.exe", 83 | "parameters": ["/START", "%CWD%"] 84 | } 85 | ``` 86 | 87 | ### xterm on GNU/Linux 88 | 89 | ```json 90 | { 91 | "terminal": "xterm" 92 | } 93 | ``` 94 | 95 | ### gnome-terminal for CJK users on GNU/Linux 96 | 97 | We unset LD_PRELOAD, as it may cause problems for Sublime Text with imfix. 98 | 99 | ```json 100 | { 101 | "terminal": "gnome-terminal", 102 | "env": {"LD_PRELOAD": null} 103 | } 104 | ``` 105 | ### iTerm on MacOS. 106 | 107 | ```json 108 | { 109 | "terminal": "iTerm.sh" 110 | } 111 | ``` 112 | 113 | ### iTerm on MacOS. with tabs 114 | 115 | ```json 116 | { 117 | "terminal": "iTerm.sh", 118 | "parameters": ["--open-in-tab"] 119 | } 120 | ``` 121 | 122 | ### iTerm2 v3 on MacOS. 123 | 124 | ```json 125 | { 126 | "terminal": "iTerm2-v3.sh" 127 | } 128 | ``` 129 | 130 | ### Hyper on MacOS. 131 | 132 | ```json 133 | { 134 | "terminal": "hyper.sh" 135 | } 136 | ``` 137 | 138 | ### Kitty on OS X 139 | 140 | ```json 141 | { 142 | "terminal": "/opt/homebrew/bin/kitty", 143 | "parameters": ["-d", "%CWD%"] 144 | } 145 | ``` 146 | 147 | ### [Windows Terminal](https://github.com/microsoft/terminal) 148 | 149 | ```json 150 | { 151 | "terminal": "C:/Users/yourusername/AppData/Local/Microsoft/WindowsApps/wt.exe", 152 | "parameters": ["-d", "."] 153 | } 154 | ``` 155 | -------------------------------------------------------------------------------- /Terminal.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | if os.name == 'nt': 8 | try: 9 | import _winreg 10 | except (ImportError): 11 | import winreg as _winreg 12 | from ctypes import windll, create_unicode_buffer 13 | 14 | 15 | class NotFoundError(Exception): 16 | pass 17 | 18 | 19 | INSTALLED_DIR = __name__.split('.')[0] 20 | 21 | 22 | def get_setting(key, default=None): 23 | settings = sublime.load_settings('Terminal.sublime-settings') 24 | os_specific_settings = {} 25 | if os.name == 'nt': 26 | os_specific_settings = sublime.load_settings('Terminal (Windows).sublime-settings') 27 | elif sys.platform == 'darwin': 28 | os_specific_settings = sublime.load_settings('Terminal (OSX).sublime-settings') 29 | else: 30 | os_specific_settings = sublime.load_settings('Terminal (Linux).sublime-settings') 31 | return os_specific_settings.get(key, settings.get(key, default)) 32 | 33 | 34 | def powershell(package_dir): 35 | # This mimics the default powershell colors since calling 36 | # subprocess.POpen() ends up acting like launching powershell 37 | # from cmd.exe. Normally the size and color are inherited 38 | # from cmd.exe, but this creates a custom mapping, and then 39 | # the LaunchPowerShell.bat file adjusts some other settings. 40 | key_string = 'Console\\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe' 41 | try: 42 | key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, key_string) 43 | except (WindowsError): 44 | key = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER, key_string) 45 | _winreg.SetValueEx(key, 'ColorTable05', 0, _winreg.REG_DWORD, 5645313) 46 | _winreg.SetValueEx(key, 'ColorTable06', 0, _winreg.REG_DWORD, 15789550) 47 | default = os.path.join(package_dir, 'PS.bat') 48 | sublime_terminal_path = os.path.join( 49 | sublime.packages_path(), INSTALLED_DIR) 50 | # This should turn the path into an 8.3-style path, 51 | # getting around unicode issues and spaces 52 | buf = create_unicode_buffer(512) 53 | if windll.kernel32.GetShortPathNameW(sublime_terminal_path, buf, len(buf)): 54 | sublime_terminal_path = buf.value 55 | os.environ['sublime_terminal_path'] = sublime_terminal_path.replace(' ', '` ') 56 | 57 | return default 58 | 59 | 60 | def linux_terminal(): 61 | ps = 'ps -eo comm,args | grep -E "^(gnome-session|ksmserver|xfce4-session|lxsession|mate-panel|cinnamon-sessio)" | grep -v grep' # noqa: E501 62 | wm = [x.replace("\n", '') for x in os.popen(ps)] 63 | if wm: 64 | # elementary OS: `/usr/lib/gnome-session/gnome-session-binary --session=pantheon` 65 | # Gnome: `gnome-session` or `gnome-session-binary` 66 | # Linux Mint Cinnamon: `cinnamon-sessio cinnamon-session --session cinnamon` 67 | if wm[0].startswith('gnome-session') or wm[0].startswith('cinnamon-sessio'): 68 | if 'pantheon' in wm[0]: 69 | return 'pantheon-terminal' 70 | return 'gnome-terminal' 71 | if wm[0].startswith('xfce4-session'): 72 | return 'xfce4-terminal' 73 | if wm[0].startswith('ksmserver'): 74 | return 'konsole' 75 | if wm[0].startswith('lxsession'): 76 | return 'lxterminal' 77 | if wm[0].startswith('mate-panel'): 78 | return 'mate-terminal' 79 | 80 | # nothing specific found, return a default 81 | return 'xterm' 82 | 83 | 84 | class TerminalSelector(): 85 | default = None 86 | 87 | @staticmethod 88 | def get(terminal_key): 89 | package_dir = os.path.join(sublime.packages_path(), INSTALLED_DIR) 90 | terminal = get_setting(terminal_key) 91 | if terminal: 92 | path, executable = os.path.split(terminal) 93 | if not path: 94 | joined_terminal = os.path.join(package_dir, executable) 95 | if os.path.exists(joined_terminal): 96 | terminal = joined_terminal 97 | if not os.access(terminal, os.X_OK): 98 | os.chmod(terminal, 0o755) 99 | return terminal 100 | 101 | if TerminalSelector.default: 102 | return TerminalSelector.default 103 | 104 | default = None 105 | 106 | if os.name == 'nt': 107 | if os.path.exists(os.environ['SYSTEMROOT'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'): 108 | default = powershell(package_dir) 109 | else: 110 | default = os.environ['SYSTEMROOT'] + '\\System32\\cmd.exe' 111 | 112 | elif sys.platform == 'darwin': 113 | script = 'Terminal.sh' 114 | if get_setting('reuse_window', False): 115 | script = 'TerminalReuse.sh' 116 | 117 | default = os.path.join(package_dir, script) 118 | if not os.access(default, os.X_OK): 119 | os.chmod(default, 0o755) 120 | 121 | else: 122 | default = linux_terminal() 123 | 124 | TerminalSelector.default = default 125 | return default 126 | 127 | 128 | class TerminalCommand(): 129 | def get_path(self, paths): 130 | view = self.window.active_view() 131 | 132 | if paths: 133 | # a path has been passed to the command (ie. a context) 134 | return paths[0] 135 | 136 | if view and view.file_name(): 137 | # check that the file actually exists on disk 138 | return view.file_name() 139 | 140 | if self.window.folders(): 141 | # default to the first project directory, if it exists 142 | return self.window.folders()[0] 143 | 144 | # finally fall back to the user home directory 145 | sublime.status_message('Terminal: opening at home directory') 146 | return os.path.expanduser('~') 147 | 148 | def open_terminal(self, location, terminal, parameters): 149 | try: 150 | for k, v in enumerate(parameters): 151 | parameters[k] = v.replace('%CWD%', location) 152 | args = [TerminalSelector.get(terminal)] 153 | args.extend(parameters) 154 | 155 | # Copy over environment settings onto parent environment 156 | env_setting = get_setting('env', {}) 157 | env = os.environ.copy() 158 | for k in env_setting: 159 | if env_setting[k] is None: 160 | env.pop(k, None) 161 | else: 162 | env[k] = env_setting[k] 163 | 164 | # Run our process 165 | subprocess.Popen(args, cwd=location, env=env) 166 | 167 | except (OSError) as exception: 168 | print(str(exception)) 169 | sublime.error_message('Terminal: The terminal ' + TerminalSelector.get(terminal) + ' was not found') 170 | except (Exception) as exception: 171 | sublime.error_message('Terminal: ' + str(exception)) 172 | 173 | 174 | class OpenTerminalCommand(sublime_plugin.WindowCommand, TerminalCommand): 175 | def is_visible(self, paths=[]): 176 | # remove the command if the view doesn't have a path to open at 177 | # taking is_visible over is_enabled to remove it from the context menu, 178 | # instead of simply disabling the entry 179 | view = self.window.active_view() 180 | return bool(view and view.file_name() or paths) 181 | 182 | def run(self, paths=[], parameters=None, terminal=None): 183 | path = self.get_path(paths) 184 | 185 | if terminal is None: 186 | terminal = 'terminal' 187 | 188 | if parameters is None: 189 | parameters = get_setting('parameters', []) 190 | 191 | if os.path.isfile(path): 192 | path = os.path.dirname(path) 193 | 194 | self.open_terminal(path, terminal, parameters) 195 | 196 | 197 | class OpenTerminalProjectFolderCommand(sublime_plugin.WindowCommand, TerminalCommand): 198 | def is_visible(self): 199 | # remove the command if the current window doesn't have directories 200 | # i.e. it's a single file (use the other command) 201 | # is_visible and is_enabled effectively do the same thing here 202 | return bool(self.window.folders()) 203 | 204 | def run(self, paths=[], parameters=None): 205 | path = self.get_path(paths) 206 | if not path: 207 | return 208 | 209 | # We require separator to be appended since /hello and /hello-world 210 | # would both match a file in `/hello` without it 211 | # See https://github.com/wbond/sublime_terminal/issues/86 212 | folders = [x for x in self.window.folders() if path.find(x + os.sep) == 0][0:1] 213 | 214 | command = OpenTerminalCommand(self.window) 215 | command.run(folders, parameters=parameters) 216 | 217 | 218 | class SwitchToTerminalCommand(sublime_plugin.WindowCommand, TerminalCommand): 219 | def is_visible(self): 220 | # only have an applescript to do this 221 | return sys.platform == 'darwin' 222 | 223 | def run(self, paths=[], parameters=None): 224 | package_dir = os.path.join(sublime.packages_path(), INSTALLED_DIR) 225 | subprocess.Popen(os.path.join(package_dir, 'TerminalSwitch.sh')) 226 | --------------------------------------------------------------------------------