├── README.org └── elscreen-separate-buffer-list.el /README.org: -------------------------------------------------------------------------------- 1 | * About 2 | This makes elscreen can manage buffer list for each screen. 3 | * Requires 4 | GNU Emacs 24.4 or later 5 | * Usage 6 | To use this, add the following line somewhere in your init file: 7 | 8 | #+BEGIN_SRC emacs-lisp 9 | (require 'elscreen-separate-buffer-list) 10 | (elscreen-separate-buffer-list-mode) 11 | #+END_SRC 12 | 13 | This apply to ido-mode or something that uses ido-make-buffer-list such as helm. 14 | -------------------------------------------------------------------------------- /elscreen-separate-buffer-list.el: -------------------------------------------------------------------------------- 1 | ;;; elscreen-separate-buffer-list.el --- Separate buffer list manager for elscreen 2 | 3 | ;; Author: wamei 4 | ;; Keywords: elscreen 5 | ;; Version: 0.1.3 6 | ;; Package-Requires: ((emacs "24.4") (elscreen "1.4.6")) 7 | 8 | ;; License: 9 | 10 | ;; This program is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; This makes elscreen can manage buffer list for each screen. 26 | ;; 27 | ;; To use this, add the following line somewhere in your init file: 28 | ;; 29 | ;; (require 'elscreen-separate-buffer-list) 30 | ;; (elscreen-separate-buffer-list-mode) 31 | ;; 32 | ;; This apply to ido-mode or something that uses ido-make-buffer-list such as helm. 33 | 34 | ;;; Code: 35 | 36 | (eval-when-compile (require 'cl)) 37 | (require 'elscreen) 38 | 39 | (defvar esbl-separate-buffer-list-default '("*scratch*" "*Messages*")) 40 | (defvar esbl-separate-buffer-list '()) 41 | (defvar esbl-separate-buffer-count-list '()) 42 | 43 | (defvar ido-temp-list) 44 | 45 | (defun esbl-save-separate-window-history (&optional screen) 46 | "SCREENに現在のWINDOW-HISTORYを保存する." 47 | (let ((screen (or screen (elscreen-get-current-screen))) 48 | (screen-property (elscreen-get-screen-property screen))) 49 | (elscreen--set-alist 'screen-property 'separate-window-history (esbl-get-all-window-history-alist)) 50 | (elscreen-set-screen-property screen screen-property))) 51 | 52 | (defun esbl-restore-separate-window-history (&optional screen) 53 | "SCREENに保存されているWINDOW-HISTORYを復元する." 54 | (let* ((screen (or screen (elscreen-get-current-screen))) 55 | (screen-property (elscreen-get-screen-property screen))) 56 | (esbl-restore-all-window-history-alist (assoc-default 'separate-window-history screen-property)))) 57 | 58 | (defun esbl-save-separate-buffer-list (&optional screen) 59 | "SCREENに現在のSEPARATE-BUFFER-LISTを保存する." 60 | (let* ((screen (or screen (elscreen-get-current-screen))) 61 | (screen-property (elscreen-get-screen-property screen))) 62 | (elscreen--set-alist 'screen-property 'separate-buffer-list (esbl-get-separate-buffer-list)) 63 | (elscreen-set-screen-property screen screen-property))) 64 | 65 | (defun esbl-restore-separate-buffer-list (&optional screen) 66 | "SCREENに保存されているSEPARATE-BUFFER-LISTを復元する." 67 | (let* ((screen (or screen (elscreen-get-current-screen))) 68 | (screen-property (elscreen-get-screen-property screen)) 69 | (buffList (assoc-default 'separate-buffer-list screen-property))) 70 | (if buffList 71 | (setq esbl-separate-buffer-list buffList) 72 | (esbl-set-default-separate-buffer-list)))) 73 | 74 | (defun esbl-add-separate-buffer-list (buffer) 75 | "SEPARATE-BUFFER-LISTにBUFFERを加える." 76 | (unless (member buffer (esbl-get-separate-buffer-list)) 77 | (setq esbl-separate-buffer-list (append (list buffer) (esbl-get-separate-buffer-list))) 78 | (esbl-separate-buffer-list-count-inc buffer))) 79 | 80 | (defun esbl-remove-separate-buffer-list (buffer) 81 | "SEPARATE-BUFFER-LISTからBUFFERを取り除く." 82 | (esbl-separate-buffer-list-count-dec buffer) 83 | (setq esbl-separate-buffer-list (loop for i in (esbl-get-separate-buffer-list) 84 | unless (equal i buffer) 85 | collect i))) 86 | 87 | (defun esbl-update-separate-buffer-list () 88 | "SEPARATE-BUFFER-LISTを更新する." 89 | (esbl-separate-buffer-list-count-clean) 90 | (setq esbl-separate-buffer-list (loop for i in (esbl-get-separate-buffer-list) 91 | if (buffer-live-p i) 92 | collect i))) 93 | 94 | (defun esbl-get-separate-buffer-list () 95 | "SEPARATE-BUFFER-LISTを取得する." 96 | (when (equal 0 (length esbl-separate-buffer-list)) 97 | (esbl-set-default-separate-buffer-list)) 98 | esbl-separate-buffer-list) 99 | 100 | (defun esbl-set-default-separate-buffer-list () 101 | "デフォルトのバッファリストを設定する." 102 | (setq esbl-separate-buffer-list (loop for i in esbl-separate-buffer-list-default 103 | collect (get-buffer i)))) 104 | 105 | (defun esbl-separate-buffer-list-count-inc (buffer) 106 | "BUFFERのカウントを上げる." 107 | (loop for i in esbl-separate-buffer-count-list 108 | if (equal (car i) buffer) 109 | do (setcdr i (+ 1 (cdr i))) 110 | and return nil 111 | finally (push (cons buffer 1) esbl-separate-buffer-count-list))) 112 | 113 | (defun esbl-separate-buffer-list-count-dec (buffer) 114 | "BUFFERのカウントを下げる." 115 | (setq esbl-separate-buffer-count-list (loop for i in esbl-separate-buffer-count-list 116 | if (equal (car i) buffer) 117 | do (setcdr i (- (cdr i) 1)) 118 | if (< 0 (cdr i)) 119 | collect i))) 120 | 121 | (defun esbl-separate-buffer-list-count (buffer) 122 | "BUFFERのカウントを返す." 123 | (loop for i in esbl-separate-buffer-count-list 124 | if (equal (car i) buffer) 125 | return (cdr i) 126 | finally return 0)) 127 | 128 | (defun esbl-separate-buffer-list-count-clean () 129 | "BUFFER-COUNTの掃除をする." 130 | (setq esbl-separate-buffer-count-list (loop for i in esbl-separate-buffer-count-list 131 | if (buffer-live-p (car i)) 132 | collect i))) 133 | 134 | (defun esbl-goto:around (origin &rest args) 135 | "SCREENの切替時にSEPARATE-BUFFER-LIST,WINDOW-HISTORYを復元する." 136 | (let ((number (elscreen-get-current-screen))) 137 | (esbl-save-separate-window-history (elscreen-get-current-screen)) 138 | (apply origin args) 139 | (esbl-restore-separate-window-history (elscreen-get-current-screen)) 140 | (unless (eq number (elscreen-get-current-screen)) 141 | (when (elscreen-screen-live-p (elscreen-get-previous-screen)) 142 | (esbl-save-separate-buffer-list (elscreen-get-previous-screen))) 143 | (esbl-restore-separate-buffer-list (elscreen-get-current-screen))))) 144 | 145 | (defun esbl-swap:around (origin &rest args) 146 | "SCREENのswap時にSEPARATE-BUFFER-LIST,WINDOW-HISTORYを復元する." 147 | (esbl-save-separate-window-history (elscreen-get-current-screen)) 148 | (apply origin args) 149 | (esbl-restore-separate-window-history (elscreen-get-current-screen)) 150 | (when (elscreen-screen-live-p (elscreen-get-previous-screen)) 151 | (esbl-save-separate-buffer-list (elscreen-get-previous-screen))) 152 | (esbl-restore-separate-buffer-list (elscreen-get-current-screen))) 153 | 154 | (defun esbl-clone:after (&rest _) 155 | "SCREENの複製時にSEPARATE-BUFFER-LISTも複製する." 156 | (esbl-restore-separate-buffer-list (elscreen-get-previous-screen)) 157 | (loop for i in (esbl-get-separate-buffer-list) 158 | do (esbl-separate-buffer-list-count-inc i))) 159 | 160 | (defvar esbl-kill-buffer-another-screen-p nil) 161 | 162 | (defun esbl-kill:around (origin &rest args) 163 | "SCREENの削除時にBUFFERの削除、SEPARATE-BUFFER-LISTの復元をする." 164 | (let* ((screen (or (and (integerp (car args)) (car args)) 165 | (elscreen-get-current-screen))) 166 | (current-screen-p (eq screen (elscreen-get-current-screen))) 167 | (separate-buffer-list 168 | (if current-screen-p 169 | (esbl-get-separate-buffer-list) 170 | (assoc-default 'separate-buffer-list 171 | (elscreen-get-screen-property screen)))) 172 | (one-screen-p (and current-screen-p (elscreen-one-screen-p))) 173 | (separate-buffer-list-default 174 | (mapcar 'get-buffer esbl-separate-buffer-list-default)) 175 | (origin-return (apply origin args))) 176 | (when (or origin-return one-screen-p) 177 | (mapc (lambda (buffer) 178 | (unless (memq buffer separate-buffer-list-default) 179 | (let ((esbl-kill-buffer-another-screen-p t) 180 | (esbl-separate-buffer-list separate-buffer-list-default)) 181 | (esbl-separate-buffer-list-count-dec buffer) 182 | (when elscreen-separate-buffer-list-mode 183 | (kill-buffer buffer))))) 184 | separate-buffer-list) 185 | (when one-screen-p 186 | (esbl-set-default-separate-buffer-list) 187 | (esbl-save-separate-buffer-list (elscreen-get-current-screen)) 188 | (elscreen-apply-window-configuration (elscreen-default-window-configuration))) 189 | (esbl-restore-separate-buffer-list (elscreen-get-current-screen))) 190 | origin-return)) 191 | 192 | (defun esbl-kill-buffer-hook () 193 | "BUFFER削除時にSEPARATE-BUFFER-LISTからも削除する." 194 | (let ((buffer (current-buffer))) 195 | (when (member buffer (esbl-get-separate-buffer-list)) 196 | (esbl-remove-separate-buffer-list buffer)) 197 | (if elscreen-separate-buffer-list-mode 198 | (if (> 1 (esbl-separate-buffer-list-count buffer)) 199 | t 200 | (unless esbl-kill-buffer-another-screen-p 201 | (walk-windows 202 | `(lambda (win) 203 | (when (eq (window-buffer win) ,buffer) 204 | (switch-to-prev-buffer win t))) 205 | nil (window-frame)) 206 | (bury-buffer buffer)) 207 | nil) 208 | t))) 209 | 210 | (defun esbl-buffer-list-update-hook () 211 | "BUFFER-LIST更新時にSEPARATE-BUFFER-LISTも更新する." 212 | (esbl-update-separate-buffer-list)) 213 | 214 | (defun esbl-add-separate-buffer-list:advice (buffer &rest _) 215 | "BUFFERをSEPARATE-BUFFER-LISTに追加するADVICE用関数." 216 | (esbl-add-separate-buffer-list (get-buffer buffer))) 217 | 218 | (defun esbl-return-separate-buffer-list:buffer-list (origin &rest _) 219 | "BUFFER-LISTが呼ばれた際にSEPARATE-BUFFER-LISTでフィルタリングを行う." 220 | (loop for i in (apply origin _) 221 | if (member (get-buffer i) (esbl-get-separate-buffer-list)) 222 | collect i)) 223 | 224 | (defun esbl-set-ido-separate-buffer-list () 225 | "IDO-MAKE-BUFFER-LISTが呼ばれた際にSEPARATE-BUFFER-LISTでフィルタリングを行う." 226 | (let ((list (loop for i in ido-temp-list 227 | if (member (get-buffer i) (esbl-get-separate-buffer-list)) 228 | collect i))) 229 | (setq ido-temp-list list))) 230 | 231 | (defun esbl-buffer-name-filter (buffer-names) 232 | "SEPARATE-BUFFER-LISTでフィルタリングを行う." 233 | (let ((buffer-names-to-keep (mapcar #'buffer-name 234 | (esbl-get-separate-buffer-list)))) 235 | (seq-filter (lambda (elt) 236 | (member elt buffer-names-to-keep)) 237 | buffer-names))) 238 | 239 | (defun esbl-around-internal-complete-buffer (orig &rest rest) 240 | (if (memq this-command '(ivy-switch-buffer 241 | ivy-switch-buffer-other-window 242 | counsel-switch-buffer 243 | counsel-switch-buffer-other-window)) 244 | (esbl-buffer-name-filter (mapcar #'buffer-name (buffer-list))) 245 | (apply orig rest))) 246 | 247 | (defun esbl-switch-frame:around (origin &rest args) 248 | "FRAMEの切替時にSEPARATE-BUFFER-LIST,WINDOW-HISTORYを保存・復元する." 249 | (esbl-save-separate-window-history (elscreen-get-current-screen)) 250 | (esbl-save-separate-buffer-list (elscreen-get-current-screen)) 251 | (apply origin args) 252 | (esbl-restore-separate-window-history (elscreen-get-current-screen)) 253 | (esbl-restore-separate-buffer-list (elscreen-get-current-screen))) 254 | 255 | (defun esbl-after-make-frame (frame) 256 | "FRAMEの作成時にSEPARATE-BUFFER-LIST,WINDOW-HISTORYを保存・復元する." 257 | (let ((selected-frame (selected-frame))) 258 | (esbl-save-separate-window-history (elscreen-get-current-screen)) 259 | (esbl-save-separate-buffer-list (elscreen-get-current-screen)) 260 | (save-current-buffer 261 | (select-frame frame) 262 | (esbl-restore-separate-window-history (elscreen-get-current-screen)) 263 | (esbl-restore-separate-buffer-list (elscreen-get-current-screen)) 264 | (select-frame selected-frame)))) 265 | 266 | (defun esbl-delete-frame-confs:before (frame) 267 | "FRAMEの削除時にBUFFERを削除する." 268 | (when (eq frame (selected-frame)) 269 | (esbl-save-separate-buffer-list (elscreen-get-current-screen))) 270 | (let* ((esbl-kill-buffer-another-screen-p t) 271 | (separate-buffer-list-default 272 | (mapcar 'get-buffer esbl-separate-buffer-list-default)) 273 | (esbl-separate-buffer-list separate-buffer-list-default)) 274 | (loop for screen-property in (assoc-default 'screen-property 275 | (elscreen-get-frame-confs frame)) 276 | do (dolist (buffer (assoc-default 'separate-buffer-list screen-property)) 277 | (unless (memq buffer separate-buffer-list-default) 278 | (esbl-separate-buffer-list-count-dec buffer) 279 | (when elscreen-separate-buffer-list-mode 280 | (kill-buffer buffer))))))) 281 | 282 | (defvar esbl-selected-frame (selected-frame) 283 | "WINDOW-CONFIGURATION-CHANGE-HOOKが呼ばれる前に選択していたFRAME.") 284 | 285 | (defun esbl-window-configuration-change-hook () 286 | "FRAMEの削除後にSEPARATE-BUFFER-LIST,WINDOW-HISTORYを復元する." 287 | ;; HANDLE-SWITCH-FRAMEが呼ばれない場合のために復元する. 288 | (unless (frame-live-p esbl-selected-frame) 289 | (esbl-restore-separate-window-history (elscreen-get-current-screen)) 290 | (esbl-restore-separate-buffer-list (elscreen-get-current-screen))) 291 | (setq esbl-selected-frame (selected-frame))) 292 | 293 | ;; elscreenのパッチからパクってきた 294 | (defun esbl-window-history-supported-p () 295 | "WINDOW-HISTORYに対応しているかどうか." 296 | (and (fboundp 'window-prev-buffers) 297 | (fboundp 'window-next-buffers) 298 | (fboundp 'set-window-prev-buffers) 299 | (fboundp 'set-window-next-buffers))) 300 | 301 | (defun esbl-get-all-window-history-alist () 302 | "全てのウィンドウのWINDOW-HISTORYをALISTにして取得する." 303 | (when (esbl-window-history-supported-p) 304 | (mapcar (lambda (window) 305 | (let ((prevs (window-prev-buffers window)) 306 | (nexts (window-next-buffers window))) 307 | (cons window (cons prevs nexts)))) 308 | (window-list)))) 309 | 310 | (defun esbl-restore-all-window-history-alist (history-alist) 311 | "HISTORY-ALISTからWINDOW-HISTORYを復元する." 312 | (when (esbl-window-history-supported-p) 313 | (mapc (lambda (entry) 314 | (let* ((window (car entry)) 315 | (histories (cdr entry)) 316 | (prevs (car histories)) 317 | (nexts (cdr histories))) 318 | (when (window-valid-p window) 319 | (set-window-prev-buffers window prevs) 320 | (set-window-next-buffers window nexts)))) 321 | history-alist))) 322 | 323 | (advice-add 'elscreen-goto :around 'esbl-goto:around) 324 | (advice-add 'elscreen-swap :around 'esbl-swap:around) 325 | (advice-add 'elscreen-clone :after 'esbl-clone:after) 326 | (advice-add 'elscreen-kill :around 'esbl-kill:around) 327 | (advice-add 'switch-to-buffer :after 'esbl-add-separate-buffer-list:advice) 328 | (advice-add 'display-buffer :after 'esbl-add-separate-buffer-list:advice) 329 | (advice-add 'handle-switch-frame :around 'esbl-switch-frame:around) 330 | (advice-add 'select-frame-set-input-focus :around 'esbl-switch-frame:around) 331 | (advice-add 'elscreen-delete-frame-confs :before 'esbl-delete-frame-confs:before) 332 | (add-hook 'kill-buffer-query-functions 'esbl-kill-buffer-hook) 333 | (add-hook 'buffer-list-update-hook 'esbl-buffer-list-update-hook) 334 | (add-hook 'window-configuration-change-hook 'esbl-window-configuration-change-hook) 335 | (add-hook 'after-make-frame-functions 'esbl-after-make-frame) 336 | 337 | ;;;###autoload 338 | (define-minor-mode elscreen-separate-buffer-list-mode 339 | "Toggle elscreen separate buffer list mode." 340 | :group 'elscreen 341 | :global t 342 | (if elscreen-separate-buffer-list-mode 343 | (progn 344 | (add-hook 'ido-make-buffer-list-hook 'esbl-set-ido-separate-buffer-list) 345 | (advice-add 'helm-buffer-list :filter-return 'esbl-buffer-name-filter) 346 | (advice-add #'internal-complete-buffer :around 'esbl-around-internal-complete-buffer)) 347 | (progn 348 | (remove-hook 'ido-make-buffer-list-hook 'esbl-set-ido-separate-buffer-list) 349 | (advice-remove 'helm-buffer-list 'esbl-buffer-name-filter) 350 | (advice-remove #'internal-complete-buffer 'esbl-around-internal-complete-buffer)))) 351 | 352 | (provide 'elscreen-separate-buffer-list) 353 | 354 | ;;; elscreen-separate-buffer-list.el ends here 355 | --------------------------------------------------------------------------------