]> gitweb.michael.orlitzky.com - emacs.d.git/blob - multi-term.el
Autoload octave-mode for *.m files.
[emacs.d.git] / multi-term.el
1 ;;; multi-term.el --- Managing multiple terminal buffers in Emacs.
2
3 ;; Author: Andy Stewart <lazycat.manatee@gmail.com>
4 ;; Maintainer: ahei <ahei0802@gmail.com>
5 ;; Copyright (C) 2008, 2009, Andy Stewart, all rights reserved.
6 ;; Copyright (C) 2010, ahei, all rights reserved.
7 ;; Created: <2008-09-19 23:02:42>
8 ;; Version: 0.8.8
9 ;; Last-Updated: <2010-05-13 00:40:24 Thursday by ahei>
10 ;; URL: http://www.emacswiki.org/emacs/download/multi-term.el
11 ;; Keywords: term, terminal, multiple buffer
12 ;; Compatibility: GNU Emacs 23.2.1
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-dedicated-skip-other-window-p' whether skip dedicated term
122 ;; window when use command `other-window' to cycle windows order.
123 ;;
124 ;; All of the above can be customize by:
125 ;; M-x customize-group RET multi-term RET
126 ;;
127
128 ;;; Change log:
129 ;;
130 ;; 2009/07/04
131 ;; * Add new option `multi-term-dedicated-select-after-open-p'.
132 ;;
133 ;; 2009/06/29
134 ;; * Fix regexp bug.
135 ;;
136 ;; 2009/04/21
137 ;; * Fix a bug that bring at `2009/03/28':
138 ;; It will kill sub-process in other multi-term buffer
139 ;; when we kill current multi-term buffer.
140 ;;
141 ;; 2009/03/29
142 ;; * Add new command `term-send-reverse-search-history'.
143 ;;
144 ;; 2009/03/28
145 ;; * Add new option `multi-term-switch-after-close'.
146 ;;
147 ;; 2009/02/18
148 ;; * Fix bug between ECB and `multi-term-dedicated-close'.
149 ;;
150 ;; 2009/02/05
151 ;; * Prompt user shell name when type `C-u' before command
152 ;; `multi-term' or `multi-term-dedicated-open'.
153 ;; * Fix doc.
154 ;;
155 ;; 2009/01/29
156 ;; * Use `term-quit-subjob' instead `term-interrupt-subjob'.
157 ;; * Fix doc.
158 ;;
159 ;; 2009/01/13
160 ;; * Rewrite advice for `pop-to-buffer' to avoid `pop-to-buffer' not effect
161 ;; when have many dedicated window in current frame.
162 ;; * Rewrite advice for `delete-other-windows' to avoid use common variable
163 ;; `delete-protected-window-list' and use `window-dedicated-p' instead.
164 ;; Remove variable `delete-protected-window-list' and function
165 ;; `multi-term-dedicated-match-protected-window-p'.
166 ;;
167 ;; 2009/01/06
168 ;; * Improve document.
169 ;;
170 ;; 2008/12/29
171 ;; * Remove option `multi-term-current-window-height' and
172 ;; function `multi-term-current-directory'.
173 ;; * Add some functions to make get dedicated term buffer,
174 ;; those functions is beginning with `multi-term-dedicated-'.
175 ;; * Modified advice `delete-window', make command `delete-window'
176 ;; and delete dedicated window, but will remember window height
177 ;; before deleted.
178 ;; * Don't remember dedicated window height if larger than max value.
179 ;; * Fix some bug with `delete-other-windows' and window configuration.
180 ;; And this bug exists with another extension `sr-speedbar'.
181 ;; * Add new variable `delete-protected-window-list' for protected
182 ;; special window that won't be deleted.
183 ;; This variable is common for any extension that use dedicated
184 ;; window.
185 ;; * Fix doc.
186 ;;
187 ;; 2008/12/21
188 ;; * Default bind `C-m' with `term-send-input'.
189 ;;
190 ;; 2008/12/10
191 ;; * Improve customize interface.
192 ;; * Setup customize automatically, don't need to user setup it up.
193 ;; * Add option `multi-term-try-create'.
194 ;; * Make function `multi-term-switch' accept offset argument.
195 ;; * Fix doc.
196 ;;
197 ;; 2008/10/22
198 ;; * Add variable `multi-term-current-window-height'.
199 ;; * Add variable `multi-term-buffer-name'.
200 ;; * Add variable `term-unbind-key-list'.
201 ;; * Add variable `term-rebind-key-alist'.
202 ;; * Move key setup and some extension from `term-extension.el'.
203 ;; * Create new function `multi-term-keystroke-setup'.
204 ;; * Fix doc.
205 ;;
206 ;; 2008/09/19
207 ;; * First released.
208 ;;
209
210 ;;; Acknowledgments:
211 ;;
212 ;; Mark Triggs <mst@dishevelled.net>
213 ;; For create multi-shell.el
214 ;; Aaron S. Hawley <aaron.s.hawley@gmail.com>
215 ;; For improve document.
216 ;;
217
218 ;;; Bug
219 ;;
220 ;;
221
222 ;;; TODO
223 ;;
224 ;;
225 ;;
226
227 ;;; Require:
228 (require 'term)
229 (require 'cl)
230 (require 'advice)
231
232 ;;; Code:
233
234 ;;; Customize
235
236 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Customize ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
237 (defgroup multi-term nil
238 "Multi term manager."
239 :group 'term)
240
241 (defcustom multi-term-program nil
242 "The program of term.
243 If this is nil, setup to environment variable of `SHELL'."
244 :type 'string
245 :group 'multi-term)
246
247 (defcustom multi-term-program-switches nil
248 "The command-line switches to pass to the term program."
249 :type 'string
250 :group 'multi-term)
251
252 (defcustom multi-term-try-create t
253 "Try to create a new term buffer when switch.
254
255 When use `multi-term-next' or `multi-term-prev', switch term buffer,
256 and try to create a new term buffer if no term buffers exist."
257 :type 'boolean
258 :group 'multi-shell)
259
260 (defcustom multi-term-default-dir "~/"
261 "The default directory for terms if current directory doesn't exist."
262 :type 'string
263 :group 'multi-term)
264
265 (defcustom multi-term-buffer-name "terminal"
266 "The buffer name of term buffer."
267 :type 'string
268 :group 'multi-term)
269
270 (defcustom multi-term-scroll-show-maximum-output nil
271 "*Controls how interpreter output causes window to scroll.
272 If non-nil, then show the maximum output when the window is scrolled.
273
274 See variable `multi-term-scroll-to-bottom-on-output'."
275 :type 'boolean
276 :group 'multi-term)
277
278 (defcustom multi-term-scroll-to-bottom-on-output nil
279 "*Controls whether interpreter output causes window to scroll.
280 If nil, then do not scroll. If t or `all', scroll all windows showing buffer.
281 If `this', scroll only the selected window.
282 If `others', scroll only those that are not the selected window.
283
284 The default is nil.
285
286 See variable `multi-term-scroll-show-maximum-output'."
287 :type 'boolean
288 :group 'multi-term)
289
290 (defcustom multi-term-switch-after-close 'NEXT
291 "Try to switch other `multi-term' buffer after close current one.
292 If this option is 'NEXT, switch to next `multi-term' buffer;
293 If this option is 'PREVIOUS, switch to previous `multi-term' buffer.
294 If this option is nil, don't switch other `multi-term' buffer."
295 :type 'symbol
296 :group 'multi-term)
297
298 (defcustom term-unbind-key-list
299 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")
300 "The key list that will need to be unbind."
301 :type 'list
302 :group 'multi-term)
303
304 (defcustom term-bind-key-alist
305 '(
306 ("C-c C-c" . term-interrupt-subjob)
307 ("C-p" . previous-line)
308 ("C-n" . next-line)
309 ("C-s" . isearch-forward)
310 ("C-r" . isearch-backward)
311 ("C-m" . term-send-raw)
312 ("M-f" . term-send-forward-word)
313 ("M-b" . term-send-backward-word)
314 ("M-o" . term-send-backspace)
315 ("M-p" . term-send-up)
316 ("M-n" . term-send-down)
317 ("M-M" . term-send-forward-kill-word)
318 ("M-N" . term-send-backward-kill-word)
319 ("M-r" . term-send-reverse-search-history)
320 ("M-," . term-send-input)
321 ("M-." . comint-dynamic-complete))
322 "The key alist that will need to be bind.
323 If you do not like default setup, modify it, with (KEY . COMMAND) format."
324 :type 'alist
325 :group 'multi-term)
326
327 (defcustom multi-term-dedicated-window-height 14
328 "The height of `multi-term' dedicated window."
329 :type 'integer
330 :group 'multi-term)
331
332 (defcustom multi-term-dedicated-max-window-height 30
333 "The max height limit of `multi-term' dedicated window.
334 Default, when hide `multi-term' dedicated window, will remember
335 window height before hide, except height is larger than this.`"
336 :type 'integer
337 :group 'multi-term)
338
339 (defcustom multi-term-dedicated-skip-other-window-p nil
340 "Default, can have `other-window' select window in cyclic ordering of windows.
341 In cases you don't want to select `multi-term' dedicated window, use `other-window'
342 and make `multi-term' dedicated window as a viewable sidebar.
343
344 So please turn on this option if you want to skip `multi-term' dedicated window with `other-window'.
345
346 Default is nil."
347 :type 'boolean
348 :set (lambda (symbol value)
349 (set symbol value)
350 (when (ad-advised-definition-p 'other-window)
351 (multi-term-dedicated-handle-other-window-advice value)))
352 :group 'multi-term)
353
354 (defcustom multi-term-dedicated-select-after-open-p nil
355 "Default, multi-term won't focus terminal window after you open dedicated window.
356 Please make this option with t if you want focus terminal window.
357
358 Default is nil."
359 :type 'boolean
360 :group 'multi-term)
361
362 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Constant ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
363 (defconst multi-term-dedicated-buffer-name "MULTI-TERM-DEDICATED"
364 "The buffer name of dedicated `multi-term'.")
365
366 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Variable ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
367 (defvar multi-term-dedicated-window nil
368 "The dedicated `multi-term' window.")
369
370 (defvar multi-term-dedicated-buffer nil
371 "The dedicated `multi-term' buffer.")
372
373 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interactive Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
374 ;;;###autoload
375 (defun multi-term ()
376 "Create new term buffer.
377 Will prompt you shell name when you type `C-u' before this command."
378 (interactive)
379 (let (term-buffer)
380 ;; Set buffer.
381 (setq term-buffer (multi-term-get-buffer current-prefix-arg))
382 (set-buffer term-buffer)
383 ;; Internal handle for `multi-term' buffer.
384 (multi-term-internal)
385 ;; Switch buffer
386 (switch-to-buffer term-buffer)))
387
388 (defun multi-term-next (&optional offset)
389 "Go to the next term buffer.
390 If OFFSET is `non-nil', will goto next term buffer with OFFSET."
391 (interactive "P")
392 (multi-term-switch 'NEXT (or offset 1)))
393
394 (defun multi-term-prev (&optional offset)
395 "Go to the previous term buffer.
396 If OFFSET is `non-nil', will goto previous term buffer with OFFSET."
397 (interactive "P")
398 (multi-term-switch 'PREVIOUS (or offset 1)))
399
400 (defun multi-term-dedicated-open ()
401 "Open dedicated `multi-term' window.
402 Will prompt you shell name when you type `C-u' before this command."
403 (interactive)
404 (if (not (multi-term-dedicated-exist-p))
405 (let ((current-window (selected-window)))
406 (if (multi-term-buffer-exist-p multi-term-dedicated-buffer)
407 (unless (multi-term-window-exist-p multi-term-dedicated-window)
408 (multi-term-dedicated-get-window))
409 ;; Set buffer.
410 (setq multi-term-dedicated-buffer (multi-term-get-buffer current-prefix-arg t))
411 (set-buffer (multi-term-dedicated-get-buffer-name))
412 ;; Get dedicate window.
413 (multi-term-dedicated-get-window)
414 ;; Whether skip `other-window'.
415 (multi-term-dedicated-handle-other-window-advice multi-term-dedicated-skip-other-window-p)
416 ;; Internal handle for `multi-term' buffer.
417 (multi-term-internal))
418 (set-window-buffer multi-term-dedicated-window (get-buffer (multi-term-dedicated-get-buffer-name)))
419 (set-window-dedicated-p multi-term-dedicated-window t)
420 ;; Select window.
421 (select-window
422 (if multi-term-dedicated-select-after-open-p
423 ;; Focus dedicated terminal window if option `multi-term-dedicated-select-after-open-p' is enable.
424 multi-term-dedicated-window
425 ;; Otherwise focus current window.
426 current-window)))
427 (message "`multi-term' dedicated window has exist.")))
428
429 (defun multi-term-dedicated-close ()
430 "Close dedicated `multi-term' window."
431 (interactive)
432 (if (multi-term-dedicated-exist-p)
433 (let ((current-window (selected-window)))
434 ;; Remember height.
435 (multi-term-dedicated-select)
436 (multi-term-dedicated-remember-window-height)
437 ;; Close window.
438 (if (and (require 'ecb nil t)
439 ecb-activated-window-configuration)
440 ;; Toggle ECB window when ECB window activated.
441 (progn
442 (ecb-deactivate)
443 (ecb-activate))
444 ;; Otherwise delete dedicated window.
445 (delete-window multi-term-dedicated-window)
446 (if (multi-term-window-exist-p current-window)
447 (select-window current-window))))
448 (message "`multi-term' window is not exist.")))
449
450 (defun multi-term-dedicated-remember-window-height ()
451 "Remember window height."
452 (let ((win-height (multi-term-current-window-take-height)))
453 (if (and (multi-term-dedicated-window-p) ;in `multi-term' window
454 (> win-height 1)
455 (<= win-height multi-term-dedicated-max-window-height))
456 (setq multi-term-dedicated-window-height win-height))))
457
458 (defun multi-term-dedicated-toggle ()
459 "Toggle dedicated `multi-term' window."
460 (interactive)
461 (if (multi-term-dedicated-exist-p)
462 (multi-term-dedicated-close)
463 (multi-term-dedicated-open)))
464
465 (defun multi-term-dedicated-select ()
466 "Select the `multi-term' dedicated window."
467 (interactive)
468 (if (multi-term-dedicated-exist-p)
469 (select-window multi-term-dedicated-window)
470 (message "`multi-term' window is not exist.")))
471
472 (defun term-send-backward-kill-word ()
473 "Backward kill word in term mode."
474 (interactive)
475 (term-send-raw-string "\C-w"))
476
477 (defun term-send-forward-kill-word ()
478 "Kill word in term mode."
479 (interactive)
480 (term-send-raw-string "\ed"))
481
482 (defun term-send-backward-word ()
483 "Move backward word in term mode."
484 (interactive)
485 (term-send-raw-string "\eb"))
486
487 (defun term-send-forward-word ()
488 "Move forward word in term mode."
489 (interactive)
490 (term-send-raw-string "\ef"))
491
492 (defun term-send-reverse-search-history ()
493 "Search history reverse."
494 (interactive)
495 (term-send-raw-string "\C-r"))
496
497 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Utilise Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
498 (defun multi-term-internal ()
499 "Internal handle for `multi-term' buffer."
500 ;; Add customize keystroke with `term-mode-hook'
501 (remove-hook 'term-mode-hook 'multi-term-keystroke-setup)
502 (add-hook 'term-mode-hook 'multi-term-keystroke-setup)
503 ;; Load term mode
504 (term-mode)
505 (term-char-mode)
506 ;; Handle term buffer close
507 (multi-term-handle-close)
508 ;; Handle `output' variable.
509 (setq term-scroll-show-maximum-output multi-term-scroll-show-maximum-output
510 term-scroll-to-bottom-on-output multi-term-scroll-to-bottom-on-output)
511 ;; Add hook to be sure `term' quit subjob before buffer killed.
512 (add-hook 'kill-buffer-hook 'multi-term-kill-buffer-hook))
513
514 (defun multi-term-get-buffer (&optional special-shell dedicated-window)
515 "Get term buffer.
516 If option SPECIAL-SHELL is `non-nil', will use shell from user input.
517 If option DEDICATED-WINDOW is `non-nil' will create dedicated `multi-term' window ."
518 (with-temp-buffer
519 (let ((shell-name (or multi-term-program ;shell name
520 (getenv "SHELL")
521 (getenv "ESHELL")
522 "/bin/sh"))
523 term-list-length ;get length of term list
524 index ;setup new term index
525 term-name) ;term name
526 (if dedicated-window
527 (setq term-name multi-term-dedicated-buffer-name)
528 ;; Compute index.
529 (setq term-list-length (length (multi-term-list)))
530 (setq index (if term-list-length (1+ term-list-length) 1))
531 ;; switch to current local directory,
532 ;; if in-existence, switch to `multi-term-default-dir'.
533 (cd (or default-directory (expand-file-name multi-term-default-dir)))
534 ;; adjust value N when max index of term buffer is less than length of term list
535 (while (buffer-live-p (get-buffer (format "*%s<%s>*" multi-term-buffer-name index)))
536 (setq index (1+ index)))
537 (setq term-name (format "%s<%s>" multi-term-buffer-name index)))
538 ;; Try get other shell name if `special-shell' is non-nil.
539 (if special-shell
540 (setq shell-name (read-from-minibuffer "Run program: " shell-name)))
541 ;; Make term, details to see function `make-term' in `term.el'.
542 (if multi-term-program-switches
543 (make-term term-name shell-name nil multi-term-program-switches)
544 (make-term term-name shell-name)))))
545
546
547 (defun multi-term-handle-close ()
548 "Close current term buffer when `exit' from term buffer."
549 (when (ignore-errors (get-buffer-process (current-buffer)))
550 (set-process-sentinel (get-buffer-process (current-buffer))
551 (lambda (proc change)
552 (when (string-match "\\(finished\\|exited\\)" change)
553 (kill-buffer (process-buffer proc)))))))
554
555 (defun multi-term-kill-buffer-hook ()
556 "Function that hook `kill-buffer-hook'."
557 (when (eq major-mode 'term-mode)
558 ;; Quit the current subjob
559 ;; when have alive process with current term buffer.
560 ;; Must do this job BEFORE `multi-term-switch-after-close' action.
561 (when (term-check-proc (current-buffer))
562 ;; Quit sub-process.
563 (term-quit-subjob))
564 ;; Remember dedicated window height.
565 (multi-term-dedicated-remember-window-height)
566 ;; Try to switch other multi-term buffer
567 ;; when option `multi-term-switch-after-close' is non-nil.
568 (when multi-term-switch-after-close
569 (multi-term-switch-internal multi-term-switch-after-close 1))))
570
571 (defun multi-term-list ()
572 "List term buffers presently active."
573 ;; Autload command `remove-if-not'.
574 (autoload 'remove-if-not "cl-seq")
575 (sort
576 (remove-if-not (lambda (b)
577 (setq case-fold-search t)
578 (string-match
579 (format "^\\\*%s<[0-9]+>\\\*$" multi-term-buffer-name)
580 (buffer-name b)))
581 (buffer-list))
582 (lambda (a b)
583 (< (string-to-number
584 (cadr (split-string (buffer-name a) "[<>]")))
585 (string-to-number
586 (cadr (split-string (buffer-name b) "[<>]")))))))
587
588 (defun multi-term-switch (direction offset)
589 "Switch `multi-term' buffers.
590 If DIRECTION is `NEXT', switch to the next term.
591 If DIRECTION `PREVIOUS', switch to the previous term.
592 Option OFFSET for skip OFFSET number term buffer."
593 (unless (multi-term-switch-internal direction offset)
594 (if multi-term-try-create
595 (progn
596 (multi-term)
597 (message "Create a new `multi-term' buffer."))
598 (message "Haven't any `multi-term' buffer exist."))))
599
600 (defun multi-term-switch-internal (direction offset)
601 "Internal `multi-term' buffers switch function.
602 If DIRECTION is `NEXT', switch to the next term.
603 If DIRECTION `PREVIOUS', switch to the previous term.
604 Option OFFSET for skip OFFSET number term buffer."
605 (let (terms this-buffer)
606 (setq terms (multi-term-list))
607 (if (consp terms)
608 (progn
609 (setf (cdr (last terms)) terms)
610 (setq this-buffer (position (current-buffer) (multi-term-list)))
611 (if this-buffer
612 (if (eql direction 'NEXT)
613 (switch-to-buffer (nth (+ this-buffer offset) terms))
614 (switch-to-buffer (nth (+ (- (length (multi-term-list)) offset)
615 this-buffer) terms)))
616 (switch-to-buffer (car terms)))
617 t)
618 nil)))
619
620 (defun multi-term-keystroke-setup ()
621 "Keystroke setup of `term-char-mode'.
622
623 By default, the key bindings of `term-char-mode' conflict with user's keystroke.
624 So this function unbinds some keys with `term-raw-map',
625 and binds some keystroke with `term-raw-map'."
626 (let (bind-key bind-command)
627 ;; Unbind base key that conflict with user's keys-tokes.
628 (dolist (unbind-key term-unbind-key-list)
629 (cond
630 ((stringp unbind-key) (setq unbind-key (read-kbd-macro unbind-key)))
631 ((vectorp unbind-key) nil)
632 (t (signal 'wrong-type-argument (list 'array unbind-key))))
633 (define-key term-raw-map unbind-key nil))
634 ;; Add some i use keys.
635 ;; If you don't like my keystroke,
636 ;; just modified `term-bind-key-alist'
637 (dolist (element term-bind-key-alist)
638 (setq bind-key (car element))
639 (setq bind-command (cdr element))
640 (cond
641 ((stringp bind-key) (setq bind-key (read-kbd-macro bind-key)))
642 ((vectorp bind-key) nil)
643 (t (signal 'wrong-type-argument (list 'array bind-key))))
644 (define-key term-raw-map bind-key bind-command))))
645
646 (defun multi-term-dedicated-handle-other-window-advice (activate)
647 "Handle advice for function `other-window'.
648 If ACTIVATE is `non-nil', will enable advice
649 `multi-term-dedicated-other-window-advice'.
650 Otherwise, disable it."
651 (if activate
652 (ad-enable-advice 'other-window 'after 'multi-term-dedicated-other-window-advice)
653 (ad-disable-advice 'other-window 'after 'multi-term-dedicated-other-window-advice))
654 (ad-activate 'other-window))
655
656 (defun multi-term-current-window-take-height (&optional window)
657 "Return the height the `window' takes up.
658 Not the value of `window-height', it returns usable rows available for WINDOW.
659 If `window' is nil, get current window."
660 (let ((edges (window-edges window)))
661 (- (nth 3 edges) (nth 1 edges))))
662
663 (defun multi-term-dedicated-get-window ()
664 "Get `multi-term' dedicated window."
665 (setq multi-term-dedicated-window
666 (split-window
667 (selected-window)
668 (- (multi-term-current-window-take-height) multi-term-dedicated-window-height))))
669
670 (defun multi-term-dedicated-get-buffer-name ()
671 "Get the buffer name of `multi-term' dedicated window."
672 (format "*%s*" multi-term-dedicated-buffer-name))
673
674 (defun multi-term-dedicated-exist-p ()
675 "Return `non-nil' if `multi-term' dedicated window exist."
676 (and (multi-term-buffer-exist-p multi-term-dedicated-buffer)
677 (multi-term-window-exist-p multi-term-dedicated-window)))
678
679 (defun multi-term-window-exist-p (window)
680 "Return `non-nil' if WINDOW exist.
681 Otherwise return nil."
682 (and window (window-live-p window)))
683
684 (defun multi-term-buffer-exist-p (buffer)
685 "Return `non-nil' if `BUFFER' exist.
686 Otherwise return nil."
687 (and buffer (buffer-live-p buffer)))
688
689 (defun multi-term-dedicated-window-p ()
690 "Return `non-nil' if current window is `multi-term' dedicated window.
691 Otherwise return nil."
692 (equal (multi-term-dedicated-get-buffer-name) (buffer-name (window-buffer))))
693
694 (defun multi-term-window-dedicated-only-one-p ()
695 "Only have one non-dedicated window."
696 (interactive)
697 (let ((window-number 0)
698 (dedicated-window-number 0))
699 (walk-windows
700 (lambda (w)
701 (with-selected-window w
702 (incf window-number)
703 (if (window-dedicated-p w)
704 (incf dedicated-window-number)))))
705 (if (and (> dedicated-window-number 0)
706 (= (- window-number dedicated-window-number) 1))
707 t nil)))
708
709 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Advice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
710 (defadvice delete-other-windows (around multi-term-delete-other-window-advice activate)
711 "This is advice to make `multi-term' avoid dedicated window deleted.
712 Dedicated window can't deleted by command `delete-other-windows'."
713 (let ((multi-term-dedicated-active-p (multi-term-window-exist-p multi-term-dedicated-window)))
714 (if multi-term-dedicated-active-p
715 (let ((current-window (selected-window)))
716 (dolist (win (window-list))
717 (when (and (window-live-p win)
718 (not (eq current-window win))
719 (not (window-dedicated-p win)))
720 (delete-window win))))
721 ad-do-it)))
722
723 (defadvice delete-window (before multi-term-delete-window-advice activate)
724 "Use `delete-window' delete `multi-term' dedicated window.
725 Have same effect as command `multi-term-dedicated-close'.
726 This advice to remember `multi-term' dedicated window height before deleting."
727 ;; Remember window height before deleted.
728 (multi-term-dedicated-remember-window-height))
729
730 (defadvice pop-to-buffer (before multi-term-pop-to-buffer-advice activate)
731 "This advice fix the problem between `pop-to-buffer' and dedicated window.
732 By default, function `display-buffer' can't display buffer in selected window
733 if current window is `dedicated'.
734
735 So function `display-buffer' conflicts with `sr-speedbar' window, because
736 `sr-speedbar' window is a `dedicated' window.
737
738 That is to say, when current frame just have one `non-dedicated' window,
739 any functions that uses `display-buffer' can't split windows
740 to display buffer, even when the option `pop-up-windows' is enabled.
741
742 And the example function that can induce the problem is `pop-to-buffer'.
743
744 This advice will fix this problem when current frame just have one `non-dedicated' window."
745 (when (and pop-up-windows ;`pop-up-windows' is enable
746 (multi-term-window-dedicated-only-one-p) ;just have one `non-dedicated' window.
747 (multi-term-window-exist-p multi-term-dedicated-window)
748 (not (multi-term-dedicated-window-p))) ;not in `sr-speedbar' window
749 (split-window-vertically)
750 (windmove-down)))
751
752 (defadvice other-window (after multi-term-dedicated-other-window-advice)
753 "Default, can use `other-window' select window in cyclic ordering of windows.
754 But sometimes we don't want to select `sr-speedbar' window,
755 but use `other-window' and just make `multi-term' dedicated
756 window as a viewable sidebar.
757
758 This advice can make `other-window' skip `multi-term' dedicated window."
759 (let ((count (or (ad-get-arg 0) 1)))
760 (when (and (multi-term-window-exist-p multi-term-dedicated-window)
761 (eq multi-term-dedicated-window (selected-window)))
762 (other-window count))))
763
764 (provide 'multi-term)
765
766 ;; Local Variables:
767 ;; time-stamp-line-limit: 10
768 ;; time-stamp-start: "Last-Updated: <"
769 ;; time-stamp-end: ">"
770 ;; End:
771
772 ;;; multi-term.el ends here
773
774 ;;; LocalWords: multi el dir sr Hawley eb ef cd