├── README.md ├── company-term.el ├── docs └── todolist.org ├── images └── find.png ├── multi-term-config.el ├── multi-term-plus.el ├── multi-term.el └── scripts └── install.sh /README.md: -------------------------------------------------------------------------------- 1 | # multi-term-plus 2 | File [multi-term.el](./multi-term.el) fork from [multi-term](https://www.emacswiki.org/emacs/download/multi-term.el). 3 | And I do some extensions in [multi-term-plus.el](./multi-term-plus.el). 4 | 5 | ## Why for this? 6 | The [multi-term](https://www.emacswiki.org/emacs/download/multi-term.el) 7 | only provides basic operations in term-mode. And *C-a C-e C-k C-d* do not act as normal 8 | terminal. So, [multi-term-plus.el](./multi-term-plus.el) implements those features. 9 | What's more, it can recover previous term when emacs reopen and fast switch between terms. 10 | 11 | ## How to use it? 12 | * Download multi-term-plus files via curl: 13 | ```shell 14 | $ sh -c "$(curl -fsSL https://raw.github.com/aborn/multi-term-plus/master/scripts/install.sh)" 15 | ``` 16 | * Add following code to your emacs init file: 17 | ```elisp 18 | (add-to-list 'load-path "~/multi-term-plus") 19 | (require 'multi-term-config) 20 | ``` 21 | * For detail configuration, please ref [multi-term-config.el](./multi-term-config.el). Modify it as you need. ```M-x get-term``` run the shell. 22 | 23 | * For windows user, pls install [koopa-mode](https://github.com/sch0lars/koopa-mode) firt, and ```M-x get-term``` will run koopa-run-powershell. 24 | 25 | ## Extensions 26 | * Fast term switch when you open multi terms (key binding: **C-{**). 27 | ![](images/find.png "multi-term-find.") 28 | ```elisp 29 | multi-term-find 30 | ``` 31 | * Act as normal terminal using kill-line in term-mode (key binding: **C-k**). 32 | ```elisp 33 | multi-term-kill-line 34 | ``` 35 | * Auto recover previous term buffers when emacs reopen. Add follow code to your 36 | emacs init file if you don't need the recovery feature. 37 | ```elisp 38 | (setq multi-term-recovery-p nil) 39 | ``` 40 | 41 | English version readme ends here. Chinese readme provided as follows. 42 | 43 | -------------------------------------------------------------------------------- 44 | # multi-term-plus 45 | multi-term.el这个文件是从[multi-term](https://www.emacswiki.org/emacs/download/multi-term.el)fork而来。 46 | 在这个基础上,我自己添加了一些新的功能在[multi-term-plus.el](./multi-term-plus.el)里。 47 | 48 | ## 为什么要做这个工作? 49 | 原生的multi-term.el功能不是很完善,像*C-e C-a C-k C-d*这些功能表现得跟正常的terminal不一样。 50 | 所以才有了[multi-term-plus.el](./multi-term-plus.el)这个扩展。它不仅提供了在不同terms里快速 51 | 切换的能力,同时当emacs再次启动的时候,也能恢复上一次打开的terminals。 52 | 53 | ## 如何安装? 54 | * 通过curl的方式下载需要的文件(机器需要安装git): 55 | ```shell 56 | $ sh -c "$(curl -fsSL https://raw.github.com/aborn/multi-term-plus/master/scripts/install.sh)" 57 | ``` 58 | * 将下列代码添加到你的配置文件中(~/.emacs, ~/.emacs.d/init.el, ~/.spacemacs, ~/.spacemacs.d/init.el) 59 | ```elisp 60 | (add-to-list 'load-path "~/multi-term-plus") 61 | (require 'multi-term-config) 62 | ``` 63 | * 更详细的配置请参考我的配置 [multi-term-config.el](./multi-term-config.el),请按自己需要进行相应修改! 64 | 65 | ## 功能点 66 | * 快速切换到不同的multi-terms(绑定到**C-{**这个快捷键里) 67 | ![](images/find.png "multi-term-find.") 68 | ```elisp 69 | multi-term-find 70 | ``` 71 | * 智能的kill-line操作,绑定到**C-k**,像在普通的terminal里操作一样 72 | ```elisp 73 | multi-term-kill-line 74 | ``` 75 | * 再次打开emacs时,会自动恢复上一次的几个multi-term。如果不需要这个功能可以通过设置**multi-term-recovery-p**值为nil来关闭该功能。 76 | ```elisp 77 | (setq multi-term-recovery-p nil) 78 | ``` 79 | 80 | ## 详情 81 | 更多详情,请参考我写的这篇博客[emacs 使用multi-term](http://www.jianshu.com/p/2c1ac913d2cb)。 82 | 83 | -------------------------------------------------------------------------------- /company-term.el: -------------------------------------------------------------------------------- 1 | ;;; company-term.el --- An company plug for multi-term -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016 Aborn Jiang 4 | 5 | ;;; Code: 6 | ;; 7 | ;; TODO on development. 8 | 9 | (require 'cl-lib) 10 | (require 'company) 11 | 12 | (defconst term-completions 13 | '("ls" "cp" "mv" "rm" "ls -a" "df -h" "df" "ls -ltrh")) 14 | 15 | (defun company-term--prefix () 16 | "Prefix-command handler for the company backend." 17 | (and (eq major-mode 'term-mode) 18 | (company-grab-symbol))) 19 | 20 | (defun company-term--post-completion (candidate) 21 | "Insert function arguments after completion for CANDIDATE." 22 | ;;(term-send-raw-string candidate) 23 | ;;(term-char-mode) 24 | (message "%s" candidate)) 25 | 26 | (defun company-term (command &optional arg &rest ignored) 27 | "company-term." 28 | (interactive (list 'interactive)) 29 | (cl-case command 30 | (interactive (company-begin-backend 'company-term)) 31 | (prefix (company-term--prefix)) 32 | ;;(prefix (and (eq major-mode 'term-mode) 33 | ;;(company-grab-symbol))) 34 | (candidates 35 | (cl-remove-if-not 36 | (lambda (c) (string-prefix-p arg c)) 37 | term-completions)) 38 | (meta (format "%s" arg)) 39 | (post-completion (company-term--post-completion arg)) 40 | )) 41 | 42 | ;;;###autoload 43 | (defun company-term-setup () 44 | "add to list" 45 | (add-to-list 'company-backends 'company-term)) 46 | 47 | (provide 'company-term) 48 | ;;; company-term.el ends here 49 | -------------------------------------------------------------------------------- /docs/todolist.org: -------------------------------------------------------------------------------- 1 | #+TITLE: multi-term-plus todolist 2 | #+AUTHOR: Aborn Jiang 3 | #+EMAIL: (concat "aborn.jiang" at-sign "gmail.com") 4 | #+DATE: 2016-10-25 5 | #+TIME: 2016-10-25 ~ 6 | 7 | #+SETUPFILE: ~/github/org-html-themes/setup/theme-readtheorg.setup 8 | 9 | ----- 10 | * to-do-list 11 | ** TODO 能在elisp中调用其中一个term,执行shll脚本 12 | -------------------------------------------------------------------------------- /images/find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aborn/multi-term-plus/d0e35c514d7513e55aa0b7020d3eedf9b8d36873/images/find.png -------------------------------------------------------------------------------- /multi-term-config.el: -------------------------------------------------------------------------------- 1 | ;;; Configurations for multi-term and multi-term-plus, modified it as your need. 2 | 3 | ;; Copyright (C) 2016 Aborn Jiang 4 | 5 | ;;; Notes: 6 | ;; First of all, add multi-term.el and multi-term-plus.el to your load-path 7 | ;; 8 | ;; Others: 9 | ;; multi-term major mode is term-mode, which has two sub-mode. 10 | ;; one (term-char-mode) it likes common shell. 11 | ;; another (term-line-mode) it likes common buffer. 12 | ;; Refs. http://www.gnu.org/software/emacs/manual/html_node/emacs/Term-Mode.html 13 | 14 | ;;; Code: 15 | (require 'multi-term) 16 | (require 'multi-term-plus) 17 | 18 | ;; Some basic configuration 19 | (setq multi-term-program "/bin/zsh") ;; shell-type setting, default bash. 20 | (setq multi-term-buffer-name "mterm") ;; term buffer name setting. 21 | 22 | ;; Use emacs terminfo, not system terminfo, for macOS 4m 23 | (setq system-uses-terminfo nil) 24 | 25 | (defun last-term-buffer (l) 26 | "Return most recently used term buffer." 27 | (when l 28 | (if (or (eq 'term-mode (with-current-buffer (car l) major-mode)) 29 | (eq 'comint-mode (with-current-buffer (car l) major-mode))) 30 | (car l) (last-term-buffer (cdr l))))) 31 | 32 | (defun multi-term-use-right-botton-window () 33 | "Get the right botton window." 34 | (window-at (- (frame-width) 2) (- (frame-height) 6))) 35 | 36 | (defun get-term () 37 | "Switch to the term buffer last used, or create a new one if 38 | none exists, or if the current buffer is already a term." 39 | (interactive) 40 | ;; use right-botton window, modify as you need. 41 | (select-window (multi-term-use-right-botton-window)) 42 | (let ((b (last-term-buffer (buffer-list)))) 43 | (if (string= system-type "windows-nt") 44 | (progn 45 | (if (or (not b) (eq 'comint-mode major-mode)) 46 | (progn (multi-term-create-powershell) 47 | (message "create a new powershell-term!"))) 48 | (progn (switch-to-buffer b) 49 | (message "switch a exist powershell-term!"))) 50 | (if (or (not b) (eq 'term-mode major-mode)) 51 | (progn (multi-term) 52 | (message "create a new multi-term!")) 53 | (progn (switch-to-buffer b) 54 | (message "switch a exist multi-term!")))) 55 | (define-key term-raw-map (kbd "M-n") 'ace-jump-mode))) 56 | 57 | ;; Reserved key-binding, not used in multi-term mode. 58 | (add-to-list 'term-bind-key-alist '("C-j")) 59 | (add-to-list 'term-bind-key-alist '("C-o")) 60 | 61 | ;; for fast switch to multi-term sessions. 62 | (global-set-key (kbd "C-{") 'multi-term-find) 63 | 64 | ;; Some hot-key im term-mode 65 | (add-hook 'term-mode-hook 66 | (lambda () 67 | (setq term-buffer-maximum-size 0) ;; means no limitation. 68 | (add-to-list 'term-bind-key-alist '("M-n" . ace-jump-mode)) 69 | (add-to-list 'term-bind-key-alist '("M-[" . multi-term-prev)) 70 | (add-to-list 'term-bind-key-alist '("M-]" . multi-term-next)) 71 | (add-to-list 'term-bind-key-alist '("C-a" . multi-term-move-beginning-of-line)) 72 | (add-to-list 'term-bind-key-alist '("C-e" . multi-term-move-end-of-line)) 73 | (add-to-list 'term-bind-key-alist '("C-k" . multi-term-kill-line)) 74 | (add-to-list 'term-bind-key-alist '("C-j" . multi-term-find)) 75 | (add-to-list 'term-bind-key-alist '("C-d" . multi-term-delete-char)) 76 | (add-to-list 'term-bind-key-alist '("C-b" . multi-term-backward-char)) 77 | (add-to-list 'term-bind-key-alist '("C-f" . multi-term-forward-char)) 78 | (add-to-list 'term-bind-key-alist '("M-l" . multi-term-expand-region)) 79 | (setq show-trailing-whitespace nil))) 80 | 81 | ;; init multi-term-plus 82 | (multi-term-plus-init) 83 | 84 | ;; if you use ivy's ivy-completing-read for completing-read-function, add following code. 85 | ;; (add-to-list 'ivy-sort-functions-alist '(t . nil)) ;; use it as you need 86 | ;; 87 | ;; or defun your owner sort function. 88 | ;; (defun aborn/multi-term-find-sort (x y) 89 | ;; "customize multi-term-find sort function." 90 | ;; (message "customize the sort function.") 91 | ;; (string< x y)) 92 | ;; (add-to-list 'ivy-sort-functions-alist 93 | ;; '(multi-term-find . aborn/multi-term-find-sort)) 94 | 95 | ;; company-term not finished. 96 | ;; (require 'company-term) 97 | ;; (company-term-setup) 98 | ;; (add-hook 'after-init-hook 'global-company-mode) 99 | ;; (add-hook 'multi-term-recover-hook 100 | ;; #'(lambda () 101 | ;; (company-mode) 102 | ;; (add-to-list 'company-backends 'company-term))) 103 | 104 | ;; ace-jump-mode key-binding 105 | (define-key term-raw-map (kbd "M-n") 'ace-jump-mode) 106 | (provide 'multi-term-config) 107 | ;;; multi-term-config.el ends here 108 | -------------------------------------------------------------------------------- /multi-term-plus.el: -------------------------------------------------------------------------------- 1 | ;;; multi-term-plus.el --- An extensions plug for multi-term -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016 Aborn Jiang 4 | 5 | ;; Author: Aborn Jiang 6 | ;; Version: 0.1.0 7 | ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5") (let-alist "1.0.3") (s "1.10.0") (multi-term "1.3")) 8 | ;; Keywords: term, multi-term, multi-term-plus 9 | ;; Homepage: https://github.com/aborn/multi-term-plus 10 | ;; URL: https://github.com/aborn/multi-term-plus 11 | 12 | ;; This file is NOT part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; The multi-term-plus package is used as extension for multi-term mode. 30 | ;; 31 | ;; The leanote package provides follwoing features: 32 | ;; * multi-term-find Fast switch when you open multi-terms. 33 | ;; * multi-term-kill-line Smart kill-line in multi-term mode. 34 | ;; * Auto recover previous term buffers 35 | ;; 36 | ;; Usage: 37 | ;; see multi-term-config.el 38 | ;; 39 | ;; Optional 40 | ;; 41 | 42 | ;;; Code: 43 | (require 'multi-term) 44 | (require 'cl-lib) 45 | 46 | (defvar multi-term-recover-alist '()) 47 | 48 | (defcustom multi-term-recovery-p t 49 | "Is need to recover previous term buffers when emacs bootup." 50 | :type 'boolean 51 | :group 'multi-term) 52 | 53 | (defcustom multi-term-recover-alist-file "~/.multi-term-recover-alist" 54 | "Multi term alist save file." 55 | :type 'string 56 | :group 'multi-term) 57 | 58 | (defcustom multi-term-recover-hook '() 59 | "Called when each term recover." 60 | :type 'hook 61 | :group 'multi-term) 62 | 63 | (defun multi-term-is-at-end-line () 64 | "Cursor is in last line." 65 | (equal (line-number-at-pos) (count-lines (point-min) (point-max)))) 66 | 67 | (defun multi-term-is-term-mode () 68 | "Current is term-mode" 69 | (eq major-mode 'term-mode)) 70 | 71 | (defun multi-term-backward-char () 72 | "Backward-char in term-mode. " 73 | (interactive) 74 | (if (not (multi-term-is-term-mode)) 75 | (backward-char) 76 | (progn (if (not (multi-term-is-at-end-line)) 77 | (backward-char) 78 | (progn (term-send-left) 79 | ;; (message "term-send-left") 80 | ))))) 81 | 82 | (defun multi-term-forward-char () 83 | "Forward-char in term-mode." 84 | (interactive) 85 | (if (not (multi-term-is-term-mode)) 86 | (forward-char) 87 | (progn (if (not (multi-term-is-at-end-line)) 88 | (forward-char) 89 | (progn (term-send-right) 90 | ;; (message "term-send-right") 91 | ))))) 92 | 93 | (defun multi-term-move-beginning-of-line () 94 | "Smart version of move-beginning-of-line in term-mode." 95 | (interactive) 96 | (if (not (multi-term-is-term-mode)) 97 | (beginning-of-line) 98 | (if (not (multi-term-is-at-end-line)) 99 | (beginning-of-line) 100 | (term-send-raw)))) 101 | 102 | (defun multi-term-move-end-of-line () 103 | "Smart version of move-end-of-line in term-mode." 104 | (interactive) 105 | (if (not (multi-term-is-term-mode)) 106 | (move-end-of-line nil) 107 | (if (not (multi-term-is-at-end-line)) 108 | (progn 109 | (move-end-of-line nil)) 110 | (term-send-raw-string "\C-e")))) 111 | 112 | (defun multi-term-kill-line () 113 | "Smart kill-line in multi-term mode." 114 | (interactive) 115 | (if (and (eq 'term-mode major-mode) 116 | (multi-term-is-at-end-line)) 117 | (term-send-raw-string "\C-k") 118 | (kill-line))) 119 | 120 | (defun multi-term-delete-char () 121 | "Delete char in term-mode" 122 | (interactive) 123 | (if (multi-term-is-at-end-line) 124 | (term-send-raw) 125 | (delete-char 1))) 126 | 127 | (defun multi-term-expand-region () 128 | "Wrap er/expand-region function in term-mode." 129 | (interactive) 130 | (er/expand-region 1)) 131 | 132 | (defun multi-term--buffer-name-list () 133 | "Multi-term session list." 134 | (let* ((multi-term-filter-buffer-list 135 | (cl-remove-if-not 136 | #'(lambda (x) 137 | (and (if (string= system-type "windows-nt") 138 | (eq 'comint-mode (buffer-mode x)) ;; windows system powershell 139 | (member x multi-term-buffer-list)) 140 | (not (eq (current-buffer) x)))) 141 | (buffer-list)))) 142 | (mapcar (lambda (elt) 143 | (save-current-buffer 144 | (set-buffer elt) 145 | (let (name) 146 | (setq ab/debug3 default-directory) 147 | (setq name (format "%s@%s" 148 | ( 149 | decode-coding-string (buffer-name elt) 'utf-8) 150 | (decode-coding-string default-directory 'utf-8))) 151 | (list name elt)))) 152 | multi-term-filter-buffer-list))) 153 | 154 | (defun multi-term-find () 155 | "Find multi-term by name, and switch it!" 156 | (interactive) 157 | (let* (collection key) 158 | (setq collection (multi-term--buffer-name-list)) 159 | ;; (setq ab/debug this-command) ;; only for debug 160 | (setq key (completing-read "find multi-term by name: " 161 | collection)) 162 | (let* ((buf (car (assoc-default key collection))) 163 | (bufwind (get-buffer-window buf))) 164 | (if (and bufwind (window-valid-p bufwind)) 165 | (progn 166 | (message "switch to window %s" (buffer-name buf)) 167 | (select-window bufwind)) 168 | (when (bufferp buf) 169 | (message "switch to buffer %s" (buffer-name buf)) 170 | (switch-to-buffer buf)))))) 171 | 172 | (defun multi-term-create-powershell () 173 | "Create a new power shell" 174 | (interactive) 175 | (let* (name) 176 | (setq name (multi-term-generate-powershell-buffer-name)) 177 | (message "name = %s " name) 178 | (setq koopa-powershell-buffer-name (multi-term-generate-powershell-buffer-name)) 179 | (koopa-run-powershell))) 180 | 181 | (defun aborn/getname () 182 | "get" 183 | (interactive) 184 | (message "%s" (multi-term-generate-powershell-buffer-name))) 185 | 186 | (defun multi-term-generate-powershell-buffer-name () 187 | "Auto create power shell buffer name" 188 | (let* (blist name) 189 | (setq blist (multi-term--buffer-name-list)) 190 | (setq name (format "*PowerShell%d*" (+ 1 (length blist)))) 191 | (format "%s" name))) 192 | 193 | (defun multi-term-create (name) 194 | "Create new term `NAME'" 195 | (let ((old default-directory)) 196 | (unless (file-exists-p name) 197 | (error "path %s does't exists, failed." name)) 198 | (setq default-directory name) 199 | ;; (message "old=%s dir=%s" old default-directory) 200 | (multi-term))) 201 | 202 | (defun multi-term-get-recover-alist () 203 | "Produce multi-term recover alist." 204 | (mapcar (lambda (elt) 205 | (save-current-buffer 206 | (set-buffer elt) 207 | (cons (buffer-name) default-directory))) 208 | multi-term-buffer-list)) 209 | 210 | (defun multi-term-save-term-alist () 211 | "Save multi-term-recover-alist to file." 212 | (setq multi-term-recover-alist (multi-term-get-recover-alist)) 213 | (with-temp-buffer 214 | (insert 215 | ";; -*- mode: emacs-lisp -*-\n" 216 | ";; Opened multi-term alist used for recovery.\n") 217 | (prin1 `(setq multi-term-recover-alist ',multi-term-recover-alist) 218 | (current-buffer)) 219 | (write-region (point-min) (point-max) multi-term-recover-alist-file nil 220 | 'quiet))) 221 | 222 | (defun multi-term-recover-terms () 223 | "Recover multi-term previous buffers." 224 | (when multi-term-recovery-p 225 | (message "recovery multi-term previous buffers.") 226 | (dolist (elt multi-term-recover-alist) 227 | (multi-term-create (cdr elt)) 228 | (run-hooks 'multi-term-recover-hook)))) 229 | 230 | (defun multi-term-plus-init () 231 | "Recover previous term sessions when emacs bootup." 232 | (when (version<= "26.1" emacs-version) ;; emacs 26.1, t default 233 | (setq term-char-mode-point-at-process-mark nil)) 234 | (add-hook 'kill-emacs-hook 235 | 'multi-term-save-term-alist) 236 | (when (file-readable-p multi-term-recover-alist-file) 237 | (load-file multi-term-recover-alist-file)) 238 | (multi-term-recover-terms) 239 | (message "multi-term-plus inited.")) 240 | 241 | (provide 'multi-term-plus) 242 | ;;; multi-term-plus.el ends here 243 | -------------------------------------------------------------------------------- /multi-term.el: -------------------------------------------------------------------------------- 1 | ;;; multi-term.el --- Managing multiple terminal buffers in Emacs. 2 | 3 | ;; Author: Andy Stewart 4 | ;; Maintainer: Andy Stewart 5 | ;; Copyright (C) 2008 ~ 2016 Andy Stewart, all rights reserved. 6 | ;; Copyright (C) 2010, ahei, all rights reserved. 7 | ;; Created: <2008-09-19 23:02:42> 8 | ;; Version: 1.3 9 | ;; Last-Updated: 2016-06-19 17:30:20 10 | ;; URL: http://www.emacswiki.org/emacs/download/multi-term.el 11 | ;; Keywords: term, terminal, multiple buffer 12 | ;; Compatibility: GNU Emacs 23.2.1, GNU Emacs 24.4 (and prereleases) 13 | 14 | ;; This program is free software; you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation; either version 3, or (at your option) 17 | ;; any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program; see the file COPYING. If not, write to 26 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 27 | ;; Floor, Boston, MA 02110-1301, USA. 28 | 29 | ;; Features that might be required by this library: 30 | ;; 31 | ;; `term' `cl' `advice' 32 | ;; 33 | 34 | ;;; Commentary: 35 | ;; 36 | ;; This package is for creating and managing multiple terminal buffers in Emacs. 37 | ;; 38 | ;; By default, term.el provides a great terminal emulator in Emacs. 39 | ;; But I have some troubles with term-mode: 40 | ;; 41 | ;; 1. term.el just provides commands `term' or `ansi-term' 42 | ;; for creating a terminal buffer. 43 | ;; And there is no special command to create or switch 44 | ;; between multiple terminal buffers quickly. 45 | ;; 46 | ;; 2. By default, the keystrokes of term.el conflict with global-mode keystrokes, 47 | ;; which makes it difficult for the user to integrate term.el with Emacs. 48 | ;; 49 | ;; 3. By default, executing *NIX command “exit” from term-mode, 50 | ;; it will leave an unused buffer. 51 | ;; 52 | ;; 4. term.el won’t quit running sub-process when you kill terminal buffer forcibly. 53 | ;; 54 | ;; 5. Haven’t a dedicated window for debug program. 55 | ;; 56 | ;; And multi-term.el is enhanced with those features. 57 | ;; 58 | 59 | ;;; Installation: 60 | ;; 61 | ;; Copy multi-term.el to your load-path and add to your ~/.emacs 62 | ;; 63 | ;; (require 'multi-term) 64 | ;; 65 | ;; And setup program that `multi-term' will need: 66 | ;; 67 | ;; (setq multi-term-program "/bin/bash") 68 | ;; 69 | ;; or setup like me "/bin/zsh" ;) 70 | ;; 71 | ;; Below are the commands you can use: 72 | ;; 73 | ;; `multi-term' Create a new term buffer. 74 | ;; `multi-term-next' Switch to next term buffer. 75 | ;; `multi-term-prev' Switch to previous term buffer. 76 | ;; `multi-term-dedicated-open' Open dedicated term window. 77 | ;; `multi-term-dedicated-close' Close dedicated term window. 78 | ;; `multi-term-dedicated-toggle' Toggle dedicated term window. 79 | ;; `multi-term-dedicated-select' Select dedicated term window. 80 | ;; 81 | ;; Tips: 82 | ;; 83 | ;; You can type `C-u' before command `multi-term' or `multi-term-dedicated-open' 84 | ;; then will prompt you shell name for creating terminal buffer. 85 | ;; 86 | 87 | ;;; Customize: 88 | ;; 89 | ;; `multi-term-program' default is nil, so when creating new term buffer, 90 | ;; send environment variable of `SHELL' (`ESHELL', `/bin/sh') to `make-term'. 91 | ;; 92 | ;; And you can set it to your liking, like me: ;-) 93 | ;; 94 | ;; (setq multi-term-program "/bin/zsh") 95 | ;; 96 | ;; `multi-term-default-dir' default is `~/', only use when current buffer 97 | ;; is not in a real directory. 98 | ;; 99 | ;; `multi-term-buffer-name' is the name of term buffer. 100 | ;; 101 | ;; `multi-term-scroll-show-maximum-output' controls how interpreter 102 | ;; output causes window to scroll. 103 | ;; 104 | ;; `multi-term-scroll-to-bottom-on-output' controls whether interpreter 105 | ;; output causes window to scroll. 106 | ;; 107 | ;; `multi-term-switch-after-close' try to switch other `multi-term' buffer 108 | ;; after close current one. 109 | ;; If you don't like this feature just set it with nil. 110 | ;; 111 | ;; `term-unbind-key-list' is a key list to unbind some keystroke. 112 | ;; 113 | ;; `term-bind-key-alist' is a key alist that binds some keystroke. 114 | ;; If you don't like default, modify it. 115 | ;; 116 | ;; `multi-term-dedicated-window-height' the height of a dedicated term window. 117 | ;; 118 | ;; `multi-term-dedicated-max-window-height' the max height limit that dedicated 119 | ;; window is allowed. 120 | ;; 121 | ;;; multi-term.el --- Managing multiple terminal buffers in Emacs. 122 | 123 | ;; Author: Andy Stewart 124 | ;; Maintainer: Andy Stewart 125 | ;; Copyright (C) 2008 ~ 2016 Andy Stewart, all rights reserved. 126 | ;; Copyright (C) 2010, ahei, all rights reserved. 127 | ;; Created: <2008-09-19 23:02:42> 128 | ;; Version: 1.5 129 | ;; Last-Updated: Mon May 11 10:46:18 2020 (-0400) 130 | ;; URL: http://www.emacswiki.org/emacs/download/multi-term.el 131 | ;; Keywords: term, terminal, multiple buffer 132 | ;; Compatibility: GNU Emacs 23.2.1, GNU Emacs 24.4 (and prereleases) 133 | 134 | ;; This program is free software; you can redistribute it and/or modify 135 | ;; it under the terms of the GNU General Public License as published by 136 | ;; the Free Software Foundation; either version 3, or (at your option) 137 | ;; any later version. 138 | 139 | ;; This program is distributed in the hope that it will be useful, 140 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 141 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 142 | ;; GNU General Public License for more details. 143 | 144 | ;; You should have received a copy of the GNU General Public License 145 | ;; along with this program; see the file COPYING. If not, write to 146 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 147 | ;; Floor, Boston, MA 02110-1301, USA. 148 | 149 | ;; Features that might be required by this library: 150 | ;; 151 | ;; `term' `cl-lib' `advice' 152 | ;; 153 | 154 | ;;; Commentary: 155 | ;; 156 | ;; This package is for creating and managing multiple terminal buffers in Emacs. 157 | ;; 158 | ;; By default, term.el provides a great terminal emulator in Emacs. 159 | ;; But I have some troubles with term-mode: 160 | ;; 161 | ;; 1. term.el just provides commands `term' or `ansi-term' 162 | ;; for creating a terminal buffer. 163 | ;; And there is no special command to create or switch 164 | ;; between multiple terminal buffers quickly. 165 | ;; 166 | ;; 2. By default, the keystrokes of term.el conflict with global-mode keystrokes, 167 | ;; which makes it difficult for the user to integrate term.el with Emacs. 168 | ;; 169 | ;; 3. By default, executing *NIX command “exit” from term-mode, 170 | ;; it will leave an unused buffer. 171 | ;; 172 | ;; 4. term.el won’t quit running sub-process when you kill terminal buffer forcibly. 173 | ;; 174 | ;; 5. Haven’t a dedicated window for debug program. 175 | ;; 176 | ;; And multi-term.el is enhanced with those features. 177 | ;; 178 | 179 | ;;; Installation: 180 | ;; 181 | ;; Copy multi-term.el to your load-path and add to your ~/.emacs 182 | ;; 183 | ;; (require 'multi-term) 184 | ;; 185 | ;; And setup program that `multi-term' will need: 186 | ;; 187 | ;; (setq multi-term-program "/bin/bash") 188 | ;; 189 | ;; or setup like me "/bin/zsh" ;) 190 | ;; 191 | ;; Below are the commands you can use: 192 | ;; 193 | ;; `multi-term' Create a new term buffer. 194 | ;; `multi-term-next' Switch to next term buffer. 195 | ;; `multi-term-prev' Switch to previous term buffer. 196 | ;; `multi-term-dedicated-open' Open dedicated term window. 197 | ;; `multi-term-dedicated-close' Close dedicated term window. 198 | ;; `multi-term-dedicated-toggle' Toggle dedicated term window. 199 | ;; `multi-term-dedicated-select' Select dedicated term window. 200 | ;; 201 | ;; Tips: 202 | ;; 203 | ;; You can type `C-u' before command `multi-term' or `multi-term-dedicated-open' 204 | ;; then will prompt you shell name for creating terminal buffer. 205 | ;; 206 | 207 | ;;; Customize: 208 | ;; 209 | ;; `multi-term-program' default is nil, so when creating new term buffer, 210 | ;; send environment variable of `SHELL' (`ESHELL', `/bin/sh') to `make-term'. 211 | ;; 212 | ;; And you can set it to your liking, like me: ;-) 213 | ;; 214 | ;; (setq multi-term-program "/bin/zsh") 215 | ;; 216 | ;; `multi-term-default-dir' default is `~/', only use when current buffer 217 | ;; is not in a real directory. 218 | ;; 219 | ;; `multi-term-buffer-name' is the name of term buffer. 220 | ;; 221 | ;; `multi-term-scroll-show-maximum-output' controls how interpreter 222 | ;; output causes window to scroll. 223 | ;; 224 | ;; `multi-term-scroll-to-bottom-on-output' controls whether interpreter 225 | ;; output causes window to scroll. 226 | ;; 227 | ;; `multi-term-switch-after-close' try to switch other `multi-term' buffer 228 | ;; after close current one. 229 | ;; If you don't like this feature just set it with nil. 230 | ;; 231 | ;; `term-unbind-key-list' is a key list to unbind some keystroke. 232 | ;; 233 | ;; `term-bind-key-alist' is a key alist that binds some keystroke. 234 | ;; If you don't like default, modify it. 235 | ;; 236 | ;; `multi-term-dedicated-window-height' the height of a dedicated term window. 237 | ;; 238 | ;; `multi-term-dedicated-max-window-height' the max height limit that dedicated 239 | ;; window is allowed. 240 | ;; 241 | ;; `multi-term-dedicated-skip-other-window-p' whether skip dedicated term 242 | ;; window when use command `other-window' to cycle windows order. 243 | ;; 244 | ;; All of the above can be customize by: 245 | ;; M-x customize-group RET multi-term RET 246 | ;; 247 | 248 | ;;; Change log: 249 | ;; 250 | ;; 2019/11/04 251 | ;; * Fix #2: use featurep test tramp library is load. 252 | ;; 253 | ;; 2019/10/19 254 | ;; * Support tramp 255 | ;; 256 | ;; 2017/03/03 257 | ;; * Switch to cl-lib 258 | ;; 259 | ;; 2016/06/19 260 | ;; * Add Hogren's patch: `term-send-delete-word' and binding to key 'M-d'. 261 | ;; 262 | ;; 2015/02/20 263 | ;; * Binding C-Backspace to `term-send-backward-kill-word' to follow emacs behaviour. 264 | ;; 265 | ;; 2014/12/04 266 | ;; * Ernesto Rodriguez Reina 267 | ;; Fixed the bug of cursor not return to the position it was before opened the dedicated terminal window when 268 | ;; `multi-term-dedicated-close-back-to-open-buffer-p' and `multi-term-dedicated-select-after-open-p' are t. 269 | ;; 270 | ;; 2014/08/27 271 | ;; * Kevin Peng 272 | ;; Keep multi-term buffer list make multi-term-next/prev can switch temrinal buffer even terminal buffer's name is changed. 273 | ;; 274 | ;; 2014/07/21 275 | ;; * Andy Stewart 276 | ;; Bind C-m with `term-send-return' instead `term-send-input' to fixed bug that 277 | ;; duplicate input when you C-a and C-m in terminal. 278 | ;; 279 | ;; 2014/06/21 280 | ;; * Fixed bug that can't found define of `multi-term-dedicated-handle-other-window-advice'. 281 | ;; 282 | ;; 2014/05/12 283 | ;; * Make Emacs 24.4 compatibility cleaner by avoiding version sniffing. 284 | ;; 285 | ;; 2014/03/23 286 | ;; * Add `term-send-esc' and binding with 'C-c C-e', send esc is useful for some program, such as vim. ;) 287 | ;; * Add new option `multi-term-dedicated-close-back-to-open-buffer-p' . 288 | ;; * Bind C-y with `term-paste' to avoid paste content can't insert in term mode. 289 | ;; 290 | ;; 2014/03/17 Andy Stewart 291 | ;; * Swap key binding of `term-send-raw' and `term-send-input', i think it's better send yank data when user hit ctrl+m. 292 | ;; 293 | ;; 2014/01/16 294 | ;; * Fix breakage introduced in Emacs 24.4. 295 | ;; 296 | ;; 2013/01/08 297 | ;; * Fix customize group of `multi-term-try-create'. 298 | ;; * Add autoloads. 299 | ;; * Add new commands `term-send-quote' and `term-send-M-x'. 300 | ;; 301 | ;; 2009/07/04 302 | ;; * Add new option `multi-term-dedicated-select-after-open-p'. 303 | ;; 304 | ;; 2009/06/29 305 | ;; * Fix regexp bug. 306 | ;; 307 | ;; 2009/04/21 308 | ;; * Fix a bug that bring at `2009/03/28': 309 | ;; It will kill sub-process in other multi-term buffer 310 | ;; when we kill current multi-term buffer. 311 | ;; 312 | ;; 2009/03/29 313 | ;; * Add new command `term-send-reverse-search-history'. 314 | ;; 315 | ;; 2009/03/28 316 | ;; * Add new option `multi-term-switch-after-close'. 317 | ;; 318 | ;; 2009/02/18 319 | ;; * Fix bug between ECB and `multi-term-dedicated-close'. 320 | ;; 321 | ;; 2009/02/05 322 | ;; * Prompt user shell name when type `C-u' before command 323 | ;; `multi-term' or `multi-term-dedicated-open'. 324 | ;; * Fix doc. 325 | ;; 326 | ;; 2009/01/29 327 | ;; * Use `term-quit-subjob' instead `term-interrupt-subjob'. 328 | ;; * Fix doc. 329 | ;; 330 | ;; 2009/01/13 331 | ;; * Rewrite advice for `pop-to-buffer' to avoid `pop-to-buffer' not effect 332 | ;; when have many dedicated window in current frame. 333 | ;; * Rewrite advice for `delete-other-windows' to avoid use common variable 334 | ;; `delete-protected-window-list' and use `window-dedicated-p' instead. 335 | ;; Remove variable `delete-protected-window-list' and function 336 | ;; `multi-term-dedicated-match-protected-window-p'. 337 | ;; 338 | ;; 2009/01/06 339 | ;; * Improve document. 340 | ;; 341 | ;; 2008/12/29 342 | ;; * Remove option `multi-term-current-window-height' and 343 | ;; function `multi-term-current-directory'. 344 | ;; * Add some functions to make get dedicated term buffer, 345 | ;; those functions is beginning with `multi-term-dedicated-'. 346 | ;; * Modified advice `delete-window', make command `delete-window' 347 | ;; and delete dedicated window, but will remember window height 348 | ;; before deleted. 349 | ;; * Don't remember dedicated window height if larger than max value. 350 | ;; * Fix some bug with `delete-other-windows' and window configuration. 351 | ;; And this bug exists with another extension `sr-speedbar'. 352 | ;; * Add new variable `delete-protected-window-list' for protected 353 | ;; special window that won't be deleted. 354 | ;; This variable is common for any extension that use dedicated 355 | ;; window. 356 | ;; * Fix doc. 357 | ;; 358 | ;; 2008/12/21 359 | ;; * Default bind `C-m' with `term-send-input'. 360 | ;; 361 | ;; 2008/12/10 362 | ;; * Improve customize interface. 363 | ;; * Setup customize automatically, don't need to user setup it up. 364 | ;; * Add option `multi-term-try-create'. 365 | ;; * Make function `multi-term-switch' accept offset argument. 366 | ;; * Fix doc. 367 | ;; 368 | ;; 2008/10/22 369 | ;; * Add variable `multi-term-current-window-height'. 370 | ;; * Add variable `multi-term-buffer-name'. 371 | ;; * Add variable `term-unbind-key-list'. 372 | ;; * Add variable `term-rebind-key-alist'. 373 | ;; * Move key setup and some extension from `term-extension.el'. 374 | ;; * Create new function `multi-term-keystroke-setup'. 375 | ;; * Fix doc. 376 | ;; 377 | ;; 2008/09/19 378 | ;; * First released. 379 | ;; 380 | 381 | ;;; Acknowledgments: 382 | ;; 383 | ;; Mark Triggs 384 | ;; For create multi-shell.el 385 | ;; Aaron S. Hawley 386 | ;; For improve document. 387 | ;; 388 | 389 | ;;; Bug 390 | ;; 391 | ;; 392 | 393 | ;;; TODO 394 | ;; 395 | ;; 396 | ;; 397 | 398 | ;;; Require: 399 | (require 'term) 400 | (require 'cl-lib) 401 | (require 'advice) 402 | 403 | ;;; Code: 404 | 405 | ;;; Customize 406 | 407 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Customize ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 408 | (defgroup multi-term nil 409 | "Multi term manager." 410 | :group 'term) 411 | 412 | (defcustom multi-term-program nil 413 | "The program of term. 414 | If this is nil, setup to environment variable of `SHELL'." 415 | :type 'string 416 | :group 'multi-term) 417 | 418 | (defcustom multi-term-program-switches nil 419 | "The command-line switches to pass to the term program." 420 | :type 'string 421 | :group 'multi-term) 422 | 423 | (defcustom multi-term-try-create t 424 | "Try to create a new term buffer when switch. 425 | 426 | When use `multi-term-next' or `multi-term-prev', switch term buffer, 427 | and try to create a new term buffer if no term buffers exist." 428 | :type 'boolean 429 | :group 'multi-term) 430 | 431 | (defcustom multi-term-default-dir "~/" 432 | "The default directory for terms if current directory doesn't exist." 433 | :type 'string 434 | :group 'multi-term) 435 | 436 | (defcustom multi-term-buffer-name "terminal" 437 | "The buffer name of term buffer." 438 | :type 'string 439 | :group 'multi-term) 440 | 441 | (defcustom multi-term-scroll-show-maximum-output nil 442 | "*Controls how interpreter output causes window to scroll. 443 | If non-nil, then show the maximum output when the window is scrolled. 444 | 445 | See variable `multi-term-scroll-to-bottom-on-output'." 446 | :type 'boolean 447 | :group 'multi-term) 448 | 449 | (defcustom multi-term-scroll-to-bottom-on-output nil 450 | "*Controls whether interpreter output causes window to scroll. 451 | If nil, then do not scroll. If t or `all', scroll all windows showing buffer. 452 | If `this', scroll only the selected window. 453 | If `others', scroll only those that are not the selected window. 454 | 455 | The default is nil. 456 | 457 | See variable `multi-term-scroll-show-maximum-output'." 458 | :type 'boolean 459 | :group 'multi-term) 460 | 461 | (defcustom multi-term-switch-after-close 'NEXT 462 | "Try to switch other `multi-term' buffer after close current one. 463 | If this option is 'NEXT, switch to next `multi-term' buffer; 464 | If this option is 'PREVIOUS, switch to previous `multi-term' buffer. 465 | If this option is nil, don't switch other `multi-term' buffer." 466 | :type 'symbol 467 | :group 'multi-term) 468 | 469 | (defcustom term-unbind-key-list 470 | '("C-z" "C-x" "C-c" "C-h" "C-y" "") 471 | "The key list that will need to be unbind." 472 | :type 'list 473 | :group 'multi-term) 474 | 475 | (defcustom term-bind-key-alist 476 | '( 477 | ("C-c C-c" . term-interrupt-subjob) 478 | ("C-c C-e" . term-send-esc) 479 | ("C-p" . previous-line) 480 | ("C-n" . next-line) 481 | ("C-s" . isearch-forward) 482 | ("C-r" . isearch-backward) 483 | ("C-m" . term-send-return) 484 | ("C-y" . term-paste) 485 | ("M-f" . term-send-forward-word) 486 | ("M-b" . term-send-backward-word) 487 | ("M-o" . term-send-backspace) 488 | ("M-p" . term-send-up) 489 | ("M-n" . term-send-down) 490 | ("M-M" . term-send-forward-kill-word) 491 | ("M-N" . term-send-backward-kill-word) 492 | ("" . term-send-backward-kill-word) 493 | ("M-r" . term-send-reverse-search-history) 494 | ("M-d" . term-send-delete-word) 495 | ("M-," . term-send-raw) 496 | ("M-." . comint-dynamic-complete)) 497 | "The key alist that will need to be bind. 498 | If you do not like default setup, modify it, with (KEY . COMMAND) format." 499 | :type 'alist 500 | :group 'multi-term) 501 | 502 | (defcustom multi-term-dedicated-window-height 14 503 | "The height of `multi-term' dedicated window." 504 | :type 'integer 505 | :group 'multi-term) 506 | 507 | (defcustom multi-term-dedicated-max-window-height 30 508 | "The max height limit of `multi-term' dedicated window. 509 | Default, when hide `multi-term' dedicated window, will remember 510 | window height before hide, except height is larger than this.`" 511 | :type 'integer 512 | :group 'multi-term) 513 | 514 | (defcustom multi-term-dedicated-skip-other-window-p nil 515 | "Default, can have `other-window' select window in cyclic ordering of windows. 516 | In cases you don't want to select `multi-term' dedicated window, use `other-window' 517 | and make `multi-term' dedicated window as a viewable sidebar. 518 | 519 | So please turn on this option if you want to skip `multi-term' dedicated window with `other-window'. 520 | 521 | Default is nil." 522 | :type 'boolean 523 | :set (lambda (symbol value) 524 | (set symbol value) 525 | ;; ad-advised-definition-p no longer exists on Emacs 24.4 as of 2014-01-03. 526 | (when (fboundp 'multi-term-dedicated-handle-other-window-advice) 527 | (if (fboundp 'ad-advised-definition-p) 528 | (when (ad-advised-definition-p 'other-window) 529 | (multi-term-dedicated-handle-other-window-advice value)) 530 | (when (ad-is-advised 'other-window) 531 | (multi-term-dedicated-handle-other-window-advice value))))) 532 | :group 'multi-term) 533 | 534 | (defcustom multi-term-dedicated-select-after-open-p nil 535 | "Default, multi-term won't focus terminal window after you open dedicated window. 536 | Please make this option with t if you want focus terminal window. 537 | 538 | Default is nil." 539 | :type 'boolean 540 | :group 'multi-term) 541 | 542 | (defcustom multi-term-dedicated-close-back-to-open-buffer-p nil 543 | "Some userlike the cursor return to the position it was before I opened the dedicated terminal window. 544 | Please make this option with t if you want it. ;) 545 | 546 | Default is nil." 547 | :type 'boolean 548 | :group 'multi-term 549 | ) 550 | 551 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Constant ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 552 | (defconst multi-term-dedicated-buffer-name "MULTI-TERM-DEDICATED" 553 | "The buffer name of dedicated `multi-term'.") 554 | 555 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Variable ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 556 | (defvar multi-term-dedicated-window nil 557 | "The dedicated `multi-term' window.") 558 | 559 | (defvar multi-term-dedicated-buffer nil 560 | "The dedicated `multi-term' buffer.") 561 | 562 | (defvar multi-term-dedicated-close-buffer nil 563 | "The buffer that first time open dedicated `multi-term' buffer. 564 | Details look option `multi-term-dedicated-close-back-to-open-buffer-p'.") 565 | 566 | (defvar multi-term-buffer-list nil 567 | "The list of non-dedicated terminal buffers managed by `multi-term'.") 568 | 569 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interactive Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 570 | ;;;###autoload 571 | (defun multi-term () 572 | "Create new term buffer. 573 | Will prompt you shell name when you type `C-u' before this command." 574 | (interactive) 575 | (let (term-buffer) 576 | ;; Set buffer. 577 | (setq term-buffer (multi-term-get-buffer current-prefix-arg)) 578 | (setq multi-term-buffer-list (nconc multi-term-buffer-list (list term-buffer))) 579 | (set-buffer term-buffer) 580 | ;; Internal handle for `multi-term' buffer. 581 | (multi-term-internal) 582 | (multi-term-switch-buffer term-buffer default-directory))) 583 | 584 | ;;;###autoload 585 | (defun multi-term-next (&optional offset) 586 | "Go to the next term buffer. 587 | If OFFSET is `non-nil', will goto next term buffer with OFFSET." 588 | (interactive "P") 589 | (multi-term-switch 'NEXT (or offset 1))) 590 | 591 | ;;;###autoload 592 | (defun multi-term-prev (&optional offset) 593 | "Go to the previous term buffer. 594 | If OFFSET is `non-nil', will goto previous term buffer with OFFSET." 595 | (interactive "P") 596 | (multi-term-switch 'PREVIOUS (or offset 1))) 597 | 598 | ;;;###autoload 599 | (defun multi-term-dedicated-open () 600 | "Open dedicated `multi-term' window. 601 | Will prompt you shell name when you type `C-u' before this command." 602 | (interactive) 603 | (if (not (multi-term-dedicated-exist-p)) 604 | (let ((current-window (selected-window))) 605 | (if (multi-term-buffer-exist-p multi-term-dedicated-buffer) 606 | (unless (multi-term-window-exist-p multi-term-dedicated-window) 607 | (multi-term-dedicated-get-window)) 608 | ;; Set buffer. 609 | (setq multi-term-dedicated-buffer (multi-term-get-buffer current-prefix-arg t)) 610 | (set-buffer (multi-term-dedicated-get-buffer-name)) 611 | ;; Get dedicate window. 612 | (multi-term-dedicated-get-window) 613 | ;; Whether skip `other-window'. 614 | (multi-term-dedicated-handle-other-window-advice multi-term-dedicated-skip-other-window-p) 615 | ;; Internal handle for `multi-term' buffer. 616 | (multi-term-internal)) 617 | (set-window-buffer multi-term-dedicated-window (get-buffer (multi-term-dedicated-get-buffer-name))) 618 | (set-window-dedicated-p multi-term-dedicated-window t) 619 | ;; Select window. 620 | (select-window 621 | (if multi-term-dedicated-select-after-open-p 622 | ;; Focus dedicated terminal window if option `multi-term-dedicated-select-after-open-p' is enable. 623 | multi-term-dedicated-window 624 | ;; Otherwise focus current window. 625 | current-window))) 626 | (message "`multi-term' dedicated window has exist."))) 627 | 628 | (defun multi-term-dedicated-close () 629 | "Close dedicated `multi-term' window." 630 | (interactive) 631 | (if (multi-term-dedicated-exist-p) 632 | (let ((current-window (selected-window))) 633 | ;; Remember height. 634 | (multi-term-dedicated-select) 635 | (multi-term-dedicated-remember-window-height) 636 | ;; Close window. 637 | (if (and (require 'ecb nil t) 638 | ecb-activated-window-configuration) 639 | ;; Toggle ECB window when ECB window activated. 640 | (progn 641 | (ecb-deactivate) 642 | (ecb-activate)) 643 | ;; Otherwise delete dedicated window. 644 | (delete-window multi-term-dedicated-window) 645 | (if (multi-term-window-exist-p current-window) 646 | (select-window current-window)))) 647 | (message "`multi-term' window is not exist."))) 648 | 649 | (defun multi-term-dedicated-remember-window-height () 650 | "Remember window height." 651 | (let ((win-height (multi-term-current-window-take-height))) 652 | (if (and (multi-term-dedicated-window-p) ;in `multi-term' window 653 | (> win-height 1) 654 | (<= win-height multi-term-dedicated-max-window-height)) 655 | (setq multi-term-dedicated-window-height win-height)))) 656 | 657 | ;;;###autoload 658 | (defun multi-term-dedicated-toggle () 659 | "Toggle dedicated `multi-term' window." 660 | (interactive) 661 | (if (multi-term-dedicated-exist-p) 662 | (progn 663 | (multi-term-dedicated-close) 664 | (if (and multi-term-dedicated-close-back-to-open-buffer-p 665 | multi-term-dedicated-close-buffer) 666 | (switch-to-buffer multi-term-dedicated-close-buffer) 667 | )) 668 | (if multi-term-dedicated-close-back-to-open-buffer-p 669 | (setq multi-term-dedicated-close-buffer (current-buffer))) 670 | (multi-term-dedicated-open) 671 | )) 672 | 673 | ;;;###autoload 674 | (defun multi-term-dedicated-select () 675 | "Select the `multi-term' dedicated window." 676 | (interactive) 677 | (if (multi-term-dedicated-exist-p) 678 | (select-window multi-term-dedicated-window) 679 | (message "`multi-term' window is not exist."))) 680 | 681 | (defun term-send-esc () 682 | "Send ESC in term mode." 683 | (interactive) 684 | (term-send-raw-string "\e")) 685 | 686 | (defun term-send-return () 687 | "Use term-send-raw-string \"\C-m\" instead term-send-input. 688 | Because term-send-input have bug that will duplicate input when you C-a and C-m in terminal." 689 | (interactive) 690 | (term-send-raw-string "\C-m") 691 | ) 692 | 693 | (defun term-send-backward-kill-word () 694 | "Backward kill word in term mode." 695 | (interactive) 696 | (term-send-raw-string "\C-w")) 697 | 698 | (defun term-send-forward-kill-word () 699 | "Kill word in term mode." 700 | (interactive) 701 | (term-send-raw-string "\ed")) 702 | 703 | (defun term-send-backward-word () 704 | "Move backward word in term mode." 705 | (interactive) 706 | (term-send-raw-string "\eb")) 707 | 708 | (defun term-send-forward-word () 709 | "Move forward word in term mode." 710 | (interactive) 711 | (term-send-raw-string "\ef")) 712 | 713 | (defun term-send-reverse-search-history () 714 | "Search history reverse." 715 | (interactive) 716 | (term-send-raw-string "\C-r")) 717 | 718 | (defun term-send-delete-word () 719 | "Delete word in term mode." 720 | (interactive) 721 | (term-send-raw-string "\ed")) 722 | 723 | (defun term-send-quote () 724 | "Quote the next character in term-mode. 725 | Similar to how `quoted-insert' works in a regular buffer." 726 | (interactive) 727 | (term-send-raw-string "\C-v")) 728 | 729 | (defun term-send-M-x () 730 | "Type M-x in term-mode." 731 | (interactive) 732 | (term-send-raw-string "\ex")) 733 | 734 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Utilise Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 735 | (defun multi-term-internal () 736 | "Internal handle for `multi-term' buffer." 737 | ;; Add customize keystroke with `term-mode-hook' 738 | (remove-hook 'term-mode-hook 'multi-term-keystroke-setup) 739 | (add-hook 'term-mode-hook 'multi-term-keystroke-setup) 740 | ;; Load term mode 741 | (term-mode) 742 | (term-char-mode) 743 | ;; Handle term buffer close 744 | (multi-term-handle-close) 745 | ;; Handle `output' variable. 746 | (setq term-scroll-show-maximum-output multi-term-scroll-show-maximum-output 747 | term-scroll-to-bottom-on-output multi-term-scroll-to-bottom-on-output) 748 | ;; Add hook to be sure `term' quit subjob before buffer killed. 749 | (add-hook 'kill-buffer-hook 'multi-term-kill-buffer-hook)) 750 | 751 | (defun multi-term-switch-buffer (term-buffer default-dir) 752 | "If we are in `tramp-mode', switch to TERM-BUFFER based on DEFAULT-DIR." 753 | (switch-to-buffer term-buffer) 754 | ;; Just test tramp file when library `tramp' is loaded. 755 | (when (and (featurep 'tramp) 756 | (tramp-tramp-file-p default-dir)) 757 | (with-parsed-tramp-file-name default-dir path 758 | (let ((method (cadr (assoc `tramp-login-program (assoc path-method tramp-methods))))) 759 | (term-send-raw-string (concat method " " (when path-user (concat path-user "@")) path-host "\C-m")) 760 | (term-send-raw-string (concat "cd '" path-localname "'\C-m")))))) 761 | 762 | (defun multi-term-get-buffer (&optional special-shell dedicated-window) 763 | "Get term buffer. 764 | If option SPECIAL-SHELL is `non-nil', will use shell from user input. 765 | If option DEDICATED-WINDOW is `non-nil' will create dedicated `multi-term' window ." 766 | (with-temp-buffer 767 | (let ((shell-name (or multi-term-program ;shell name 768 | (getenv "SHELL") 769 | (getenv "ESHELL") 770 | "/bin/sh")) 771 | (index 1) ;setup new term index 772 | term-name) ;term name 773 | (if dedicated-window 774 | (setq term-name multi-term-dedicated-buffer-name) 775 | ;; Compute index. 776 | (while (buffer-live-p (get-buffer (format "*%s<%s>*" multi-term-buffer-name index))) 777 | (setq index (1+ index))) 778 | ;; switch to current local directory, 779 | ;; if in-existence, switch to `multi-term-default-dir'. 780 | (cd (or default-directory (expand-file-name multi-term-default-dir))) 781 | ;; adjust value N when max index of term buffer is less than length of term list 782 | (setq term-name (format "%s<%s>" multi-term-buffer-name index))) 783 | ;; Try get other shell name if `special-shell' is non-nil. 784 | (if special-shell 785 | (setq shell-name (read-from-minibuffer "Run program: " shell-name))) 786 | ;; Make term, details to see function `make-term' in `term.el'. 787 | (if multi-term-program-switches 788 | (make-term term-name shell-name nil multi-term-program-switches) 789 | (make-term term-name shell-name))))) 790 | 791 | 792 | (defun multi-term-handle-close () 793 | "Close current term buffer when `exit' from term buffer." 794 | (when (ignore-errors (get-buffer-process (current-buffer))) 795 | (set-process-sentinel (get-buffer-process (current-buffer)) 796 | (lambda (proc change) 797 | (when (string-match "\\(finished\\|exited\\)" change) 798 | (kill-buffer (process-buffer proc))))))) 799 | 800 | (defun multi-term-kill-buffer-hook () 801 | "Function that hook `kill-buffer-hook'." 802 | (when (eq major-mode 'term-mode) 803 | ;; Quit the current subjob 804 | ;; when have alive process with current term buffer. 805 | ;; Must do this job BEFORE `multi-term-switch-after-close' action. 806 | (when (term-check-proc (current-buffer)) 807 | ;; Quit sub-process. 808 | (term-quit-subjob)) 809 | ;; Remember dedicated window height. 810 | (multi-term-dedicated-remember-window-height) 811 | (let ((killed-buffer (current-buffer))) 812 | ;; Try to switch other multi-term buffer 813 | ;; when option `multi-term-switch-after-close' is non-nil. 814 | (when multi-term-switch-after-close 815 | (multi-term-switch-internal multi-term-switch-after-close 1)) 816 | ;; Remove killed buffer from the buffer list if it's in there 817 | (setq multi-term-buffer-list 818 | (delq killed-buffer multi-term-buffer-list))))) 819 | 820 | (defun multi-term-switch (direction offset) 821 | "Switch `multi-term' buffers. 822 | If DIRECTION is `NEXT', switch to the next term. 823 | If DIRECTION `PREVIOUS', switch to the previous term. 824 | Option OFFSET for skip OFFSET number term buffer." 825 | (unless (multi-term-switch-internal direction offset) 826 | (if multi-term-try-create 827 | (progn 828 | (multi-term) 829 | (message "Created a new `multi-term' buffer.")) 830 | (message "No `multi-term' buffers exist.")))) 831 | 832 | (defun multi-term-switch-internal (direction offset) 833 | "Internal `multi-term' buffers switch function. 834 | If DIRECTION is `NEXT', switch to the next term. 835 | If DIRECTION `PREVIOUS', switch to the previous term. 836 | Option OFFSET for skip OFFSET number term buffer." 837 | (if multi-term-buffer-list 838 | (let ((buffer-list-len (length multi-term-buffer-list)) 839 | (my-index (cl-position (current-buffer) multi-term-buffer-list))) 840 | (if my-index 841 | (let ((target-index (if (eq direction 'NEXT) 842 | (mod (+ my-index offset) buffer-list-len) 843 | (mod (- my-index offset) buffer-list-len)))) 844 | (switch-to-buffer (nth target-index multi-term-buffer-list))) 845 | (switch-to-buffer (car multi-term-buffer-list)))) 846 | nil)) 847 | 848 | (defun multi-term-keystroke-setup () 849 | "Keystroke setup of `term-char-mode'. 850 | 851 | By default, the key bindings of `term-char-mode' conflict with user's keystroke. 852 | So this function unbinds some keys with `term-raw-map', 853 | and binds some keystroke with `term-raw-map'." 854 | (let (bind-key bind-command) 855 | ;; Unbind base key that conflict with user's keys-tokes. 856 | (cl-dolist (unbind-key term-unbind-key-list) 857 | (cond 858 | ((stringp unbind-key) (setq unbind-key (read-kbd-macro unbind-key))) 859 | ((vectorp unbind-key) nil) 860 | (t (signal 'wrong-type-argument (list 'array unbind-key)))) 861 | (define-key term-raw-map unbind-key nil)) 862 | ;; Add some i use keys. 863 | ;; If you don't like my keystroke, 864 | ;; just modified `term-bind-key-alist' 865 | (cl-dolist (element term-bind-key-alist) 866 | (setq bind-key (car element)) 867 | (setq bind-command (cdr element)) 868 | (cond 869 | ((stringp bind-key) (setq bind-key (read-kbd-macro bind-key))) 870 | ((vectorp bind-key) nil) 871 | (t (signal 'wrong-type-argument (list 'array bind-key)))) 872 | (define-key term-raw-map bind-key bind-command)))) 873 | 874 | (defun multi-term-dedicated-handle-other-window-advice (activate) 875 | "Handle advice for function `other-window'. 876 | If ACTIVATE is `non-nil', will enable advice 877 | `multi-term-dedicated-other-window-advice'. 878 | Otherwise, disable it." 879 | (if activate 880 | (ad-enable-advice 'other-window 'after 'multi-term-dedicated-other-window-advice) 881 | (ad-disable-advice 'other-window 'after 'multi-term-dedicated-other-window-advice)) 882 | (ad-activate 'other-window)) 883 | 884 | (defun multi-term-current-window-take-height (&optional window) 885 | "Return the height the `window' takes up. 886 | Not the value of `window-height', it returns usable rows available for WINDOW. 887 | If `window' is nil, get current window." 888 | (let ((edges (window-edges window))) 889 | (- (nth 3 edges) (nth 1 edges)))) 890 | 891 | (defun multi-term-dedicated-get-window () 892 | "Get `multi-term' dedicated window." 893 | (setq multi-term-dedicated-window 894 | (split-window 895 | (selected-window) 896 | (- (multi-term-current-window-take-height) multi-term-dedicated-window-height)))) 897 | 898 | (defun multi-term-dedicated-get-buffer-name () 899 | "Get the buffer name of `multi-term' dedicated window." 900 | (format "*%s*" multi-term-dedicated-buffer-name)) 901 | 902 | (defun multi-term-dedicated-exist-p () 903 | "Return `non-nil' if `multi-term' dedicated window exist." 904 | (and (multi-term-buffer-exist-p multi-term-dedicated-buffer) 905 | (multi-term-window-exist-p multi-term-dedicated-window))) 906 | 907 | (defun multi-term-window-exist-p (window) 908 | "Return `non-nil' if WINDOW exist. 909 | Otherwise return nil." 910 | (and window (window-live-p window))) 911 | 912 | (defun multi-term-buffer-exist-p (buffer) 913 | "Return `non-nil' if `BUFFER' exist. 914 | Otherwise return nil." 915 | (and buffer (buffer-live-p buffer))) 916 | 917 | (defun multi-term-dedicated-window-p () 918 | "Return `non-nil' if current window is `multi-term' dedicated window. 919 | Otherwise return nil." 920 | (equal (multi-term-dedicated-get-buffer-name) (buffer-name (window-buffer)))) 921 | 922 | (defun multi-term-window-dedicated-only-one-p () 923 | "Only have one non-dedicated window." 924 | (interactive) 925 | (let ((window-number 0) 926 | (dedicated-window-number 0)) 927 | (walk-windows 928 | (lambda (w) 929 | (with-selected-window w 930 | (cl-incf window-number) 931 | (if (window-dedicated-p w) 932 | (cl-incf dedicated-window-number))))) 933 | (if (and (> dedicated-window-number 0) 934 | (= (- window-number dedicated-window-number) 1)) 935 | t nil))) 936 | 937 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Advice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 938 | (defadvice delete-other-windows (around multi-term-delete-other-window-advice activate) 939 | "This is advice to make `multi-term' avoid dedicated window deleted. 940 | Dedicated window can't deleted by command `delete-other-windows'." 941 | (let ((multi-term-dedicated-active-p (multi-term-window-exist-p multi-term-dedicated-window))) 942 | (if multi-term-dedicated-active-p 943 | (let ((current-window (selected-window))) 944 | (cl-dolist (win (window-list)) 945 | (when (and (window-live-p win) 946 | (not (eq current-window win)) 947 | (not (window-dedicated-p win))) 948 | (delete-window win)))) 949 | ad-do-it))) 950 | 951 | (defadvice delete-window (before multi-term-delete-window-advice activate) 952 | "Use `delete-window' delete `multi-term' dedicated window. 953 | Have same effect as command `multi-term-dedicated-close'. 954 | This advice to remember `multi-term' dedicated window height before deleting." 955 | ;; Remember window height before deleted. 956 | (multi-term-dedicated-remember-window-height)) 957 | 958 | (defadvice pop-to-buffer (before multi-term-pop-to-buffer-advice activate) 959 | "This advice fix the problem between `pop-to-buffer' and dedicated window. 960 | By default, function `display-buffer' can't display buffer in selected window 961 | if current window is `dedicated'. 962 | 963 | So function `display-buffer' conflicts with `sr-speedbar' window, because 964 | `sr-speedbar' window is a `dedicated' window. 965 | 966 | That is to say, when current frame just have one `non-dedicated' window, 967 | any functions that uses `display-buffer' can't split windows 968 | to display buffer, even when the option `pop-up-windows' is enabled. 969 | 970 | And the example function that can induce the problem is `pop-to-buffer'. 971 | 972 | This advice will fix this problem when current frame just have one `non-dedicated' window." 973 | (when (and pop-up-windows ;`pop-up-windows' is enable 974 | (multi-term-window-dedicated-only-one-p) ;just have one `non-dedicated' window. 975 | (multi-term-window-exist-p multi-term-dedicated-window) 976 | (not (multi-term-dedicated-window-p))) ;not in `sr-speedbar' window 977 | (split-window-vertically) 978 | (windmove-down))) 979 | 980 | (defadvice other-window (after multi-term-dedicated-other-window-advice) 981 | "Default, can use `other-window' select window in cyclic ordering of windows. 982 | But sometimes we don't want to select `sr-speedbar' window, 983 | but use `other-window' and just make `multi-term' dedicated 984 | window as a viewable sidebar. 985 | 986 | This advice can make `other-window' skip `multi-term' dedicated window." 987 | (let ((count (or (ad-get-arg 0) 1))) 988 | (when (and (multi-term-window-exist-p multi-term-dedicated-window) 989 | (eq multi-term-dedicated-window (selected-window))) 990 | (other-window count)))) 991 | 992 | (provide 'multi-term) 993 | 994 | ;; Local Variables: 995 | ;; time-stamp-line-limit: 10 996 | ;; time-stamp-start: "Last-Updated: <" 997 | ;; time-stamp-end: ">" 998 | ;; End: 999 | 1000 | ;;; multi-term.el ends here 1001 | 1002 | ;;; LocalWords: multi el dir sr Hawley eb ef cd 1003 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | main() { 2 | echo "Current directory: `pwd`" 3 | DIRECTORY="${HOME}/multi-term-plus" 4 | if [ ! -d "$DIRECTORY" ]; then 5 | echo "git clone https://github.com/aborn/multi-term-plus.git ${DIRECTORY}" 6 | git clone https://github.com/aborn/multi-term-plus.git $DIRECTORY 7 | echo "Add following code to your emacs init file:" 8 | echo " (add-to-list 'load-path \"~/multi-term-plus\")" 9 | echo " (require 'multi-term-config)" 10 | else 11 | echo "direcotry ${DIRECTORY} already exists." 12 | fi 13 | } 14 | 15 | main 16 | --------------------------------------------------------------------------------