1;;; follow.el --- synchronize windows showing the same buffer 2 3;; Copyright (C) 1995, 1996, 1997, 1999, 2001, 2002, 2003, 2004, 4;; 2005, 2006, 2007 Free Software Foundation, Inc. 5 6;; Author: Anders Lindgren <andersl@andersl.com> 7;; Maintainer: FSF (Anders' email bounces, Sep 2005) 8;; Created: 1995-05-25 9;; Keywords: display, window, minor-mode, convenience 10 11;; This file is part of GNU Emacs. 12 13;; GNU Emacs is free software; you can redistribute it and/or modify 14;; it under the terms of the GNU General Public License as published by 15;; the Free Software Foundation; either version 2, or (at your option) 16;; any later version. 17 18;; GNU Emacs is distributed in the hope that it will be useful, 19;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21;; GNU General Public License for more details. 22 23;; You should have received a copy of the GNU General Public License 24;; along with GNU Emacs; see the file COPYING. If not, write to the 25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26;; Boston, MA 02110-1301, USA. 27 28;;; Commentary: 29 30;;{{{ Documentation 31 32;; `Follow mode' is a minor mode for Emacs and XEmacs that 33;; combines windows into one tall virtual window. 34;; 35;; The feeling of a "virtual window" has been accomplished by the use 36;; of two major techniques: 37;; 38;; * The windows always displays adjacent sections of the buffer. 39;; This means that whenever one window is moved, all the 40;; others will follow. (Hence the name Follow Mode.) 41;; 42;; * Should the point (cursor) end up outside a window, another 43;; window displaying that point is selected, if possible. This 44;; makes it possible to walk between windows using normal cursor 45;; movement commands. 46;; 47;; Follow mode comes to its prime when a large screen and two 48;; side-by-side window are used. The user can, with the help of Follow 49;; mode, use two full-height windows as though they would have been 50;; one. Imagine yourself editing a large function, or section of text, 51;; and being able to use 144 lines instead of the normal 72... (your 52;; mileage may vary). 53 54;; To test this package, make sure `follow' is loaded, or will be 55;; autoloaded when activated (see below). Then do the following: 56;; 57;; * Find your favorite file (preferably a long one). 58;; 59;; * Resize Emacs so that it will be wide enough for two full size 60;; columns. Delete the other windows and split the window with 61;; the commands `C-x 1 C-x 3'. 62;; 63;; * Give the command: 64;; M-x follow-mode <RETURN> 65;; 66;; * Now the display should look something like (assuming the text "71" 67;; is on line 71): 68;; 69;; +----------+----------+ 70;; |1 |73 | 71;; |2 |74 | 72;; |3 |75 | 73;; ... ... 74;; |71 |143 | 75;; |72 |144 | 76;; +----------+----------+ 77;; 78;; As you can see, the right-hand window starts at line 73, the line 79;; immediately below the end of the left-hand window. As long as 80;; `follow-mode' is active, the two windows will follow eachother! 81;; 82;; * Play around and enjoy! Scroll one window and watch the other. 83;; Jump to the beginning or end. Press `Cursor down' at the last 84;; line of the left-hand window. Enter new lines into the 85;; text. Enter long lines spanning several lines, or several 86;; windows. 87;; 88;; * Should you find `Follow' mode annoying, just type 89;; M-x follow-mode <RETURN> 90;; to turn it off. 91 92 93;; The command `follow-delete-other-windows-and-split' maximises the 94;; visible area of the current buffer. 95;; 96;; I recommend adding it, and `follow-mode', to hotkeys in the global 97;; key map. To do so, add the following lines (replacing `[f7]' and 98;; `[f8]' with your favorite keys) to the init file: 99;; 100;; (global-set-key [f8] 'follow-mode) 101;; (global-set-key [f7] 'follow-delete-other-windows-and-split) 102 103 104;; There exists two system variables that controls the appearence of 105;; lines that are wider than the window containing them. The default 106;; is to truncate long lines whenever a window isn't as wide as the 107;; frame. 108;; 109;; To make sure lines are never truncated, please place the following 110;; lines in your init file: 111;; 112;; (setq truncate-lines nil) 113;; (setq truncate-partial-width-windows nil) 114 115 116;; Since the display of XEmacs is pixel-oriented, a line could be 117;; clipped in half at the bottom of the window. 118;; 119;; To make XEmacs avoid clipping (normal) lines, please place the 120;; following line in your init-file: 121;; 122;; (setq pixel-vertical-clip-threshold 30) 123 124 125;; The correct way to cofigurate Follow mode, or any other mode for 126;; that matter, is to create one (or more) function that does 127;; whatever you would like to do. The function is then added to 128;; a hook. 129;; 130;; When `Follow' mode is activated, functions stored in the hook 131;; `follow-mode-hook' are called. When it is deactivated 132;; `follow-mode-off-hook' is run. 133;; 134;; The keymap `follow-key-map' contains key bindings activated by 135;; `follow-mode'. 136;; 137;; Example: 138;; (add-hook 'follow-mode-hook 'my-follow-mode-hook) 139;; 140;; (defun my-follow-mode-hook () 141;; (define-key follow-mode-map "\C-ca" 'your-favorite-function) 142;; (define-key follow-mode-map "\C-cb" 'another-function)) 143 144 145;; Usage: 146;; 147;; To activate issue the command "M-x follow-mode" 148;; and press return. To deactivate, do it again. 149;; 150;; The following is a list of commands useful when follow-mode is active. 151;; 152;; follow-scroll-up C-c . C-v 153;; Scroll text in a Follow Mode window chain up. 154;; 155;; follow-scroll-down C-c . v 156;; Like `follow-scroll-up', but in the other direction. 157;; 158;; follow-delete-other-windows-and-split C-c . 1 159;; Maximise the visible area of the current buffer, 160;; and enter Follow Mode. This is a very convenient 161;; way to start Follow Mode, hence it is recomended 162;; that this command is added to the global keymap. 163;; 164;; follow-recenter C-c . C-l 165;; Place the point in the center of the middle window, 166;; or a specified number of lines from either top or bottom. 167;; 168;; follow-switch-to-buffer C-c . b 169;; Switch buffer in all windows displaying the current buffer 170;; in this frame. 171;; 172;; follow-switch-to-buffer-all C-c . C-b 173;; Switch buffer in all windows in the active frame. 174;; 175;; follow-switch-to-current-buffer-all 176;; Show the current buffer in all windows on the current 177;; frame and turn on `follow-mode'. 178;; 179;; follow-first-window C-c . < 180;; Select the first window in the frame showing the same buffer. 181;; 182;; follow-last-window C-c . > 183;; Select the last window in the frame showing the same buffer. 184;; 185;; follow-next-window C-c . n 186;; Select the next window in the frame showing the same buffer. 187;; 188;; follow-previous-window C-c . p 189;; Select the previous window showing the same buffer. 190 191 192;; Well, it seems ok, but what if I really want to look at two different 193;; positions in the text? Here are two simple methods to use: 194;; 195;; 1) Use multiple frames; `follow' mode only affects windows displayed 196;; in the same frame. (My apoligies to you who can't use frames.) 197;; 198;; 2) Bind `follow-mode' to key so you can turn it off whenever 199;; you want to view two locations. Of course, `follow' mode can 200;; be reactivated by hitting the same key again. 201;; 202;; Example from my ~/.emacs: 203;; (global-set-key [f8] 'follow-mode) 204 205 206;; Implementation: 207;; 208;; In an ideal world, follow mode would have been implemented in the 209;; kernel of the display routines, making sure that the windows (using 210;; follow mode) ALWAYS are aligned. On planet earth, however, we must 211;; accept a solution where we ALMOST ALWAYS can make sure that the 212;; windows are aligned. 213;; 214;; Follow mode does this in three places: 215;; 1) After each user command. 216;; 2) After a process output has been perfomed. 217;; 3) When a scrollbar has been moved. 218;; 219;; This will cover most situations. (Let me know if there are other 220;; situations that should be covered.) 221;; 222;; Note that only the selected window is checked, for the reason of 223;; efficiency and code complexity. (I.e. it is possible to make a 224;; non-selected windows unaligned. It will, however, pop right back 225;; when it is selected.) 226 227;;}}} 228 229;;; Code: 230 231;;{{{ Preliminaries 232 233;; Make the compiler shut up! 234;; There are two strategies: 235;; 1) Shut warnings off completely. 236;; 2) Handle each warning separately. 237;; 238;; Since I would like to see real errors, I've selected the latter 239;; method. 240;; 241;; The problem with undefined variables and functions has been solved 242;; by using `set', `symbol-value' and `symbol-function' rather than 243;; `setq' and direct references to variables and functions. 244;; 245;; For example: 246;; (if (boundp 'foo) ... (symbol-value 'foo) ) 247;; (set 'foo ...) <-- XEmacs doesn't fall for this one. 248;; (funcall (symbol-function 'set) 'bar ...) 249;; 250;; Note: When this file is interpreted, `eval-when-compile' is 251;; evaluted. Since it doesn't hurt to evaluate it, but it is a bit 252;; annoying, we test if the byte-compiler has been loaded. This can, 253;; of course, lead to some occasional unintended evaluation... 254;; 255;; Should someone come up with a better solution, please let me 256;; know. 257 258(eval-when-compile 259 (if (or (featurep 'bytecomp) 260 (featurep 'byte-compile)) 261 (cond ((featurep 'xemacs) 262 ;; Make XEmacs shut up! I'm using standard Emacs 263 ;; functions, they are NOT obsolete! 264 (if (eq (get 'force-mode-line-update 'byte-compile) 265 'byte-compile-obsolete) 266 (put 'force-mode-line-update 'byte-compile 'nil)) 267 (if (eq (get 'frame-first-window 'byte-compile) 268 'byte-compile-obsolete) 269 (put 'frame-first-window 'byte-compile 'nil)))))) 270 271;;}}} 272;;{{{ Variables 273 274(defgroup follow nil 275 "Synchronize windows showing the same buffer." 276 :prefix "follow-" 277 :group 'windows 278 :group 'convenience) 279 280(defcustom follow-mode-hook nil 281 "Hooks to run when follow-mode is turned on." 282 :type 'hook 283 :group 'follow) 284 285(defcustom follow-mode-off-hook nil 286 "Hooks to run when follow-mode is turned off." 287 :type 'hook 288 :group 'follow) 289 290 291;;{{{ Keymap/Menu 292 293;; Define keys for the follow-mode minor mode map and replace some 294;; functions in the global map. All `follow' mode special functions 295;; can be found on (the somewhat cumbersome) "C-c . <key>" 296;; (Control-C dot <key>). (As of Emacs 19.29 the keys 297;; C-c <punctuation character> are reserved for minor modes.) 298;; 299;; To change the prefix, redefine `follow-mode-prefix' before 300;; `follow' is loaded, or see the section on `follow-mode-hook' 301;; above for an example of how to bind the keys the way you like. 302;; 303;; Please note that the keymap is defined the first time this file is 304;; loaded. Also note that the only legal way to manipulate the 305;; keymap is to use `define-key'. Don't change it using `setq' or 306;; similar! 307 308(defcustom follow-mode-prefix "\C-c." 309 "Prefix key to use for follow commands in Follow mode. 310The value of this variable is checked as part of loading Follow mode. 311After that, changing the prefix key requires manipulating keymaps." 312 :type 'string 313 :group 'follow) 314 315(defvar follow-mode-map 316 (let ((mainmap (make-sparse-keymap)) 317 (map (make-sparse-keymap))) 318 (define-key map "\C-v" 'follow-scroll-up) 319 (define-key map "\M-v" 'follow-scroll-down) 320 (define-key map "v" 'follow-scroll-down) 321 (define-key map "1" 'follow-delete-other-windows-and-split) 322 (define-key map "b" 'follow-switch-to-buffer) 323 (define-key map "\C-b" 'follow-switch-to-buffer-all) 324 (define-key map "\C-l" 'follow-recenter) 325 (define-key map "<" 'follow-first-window) 326 (define-key map ">" 'follow-last-window) 327 (define-key map "n" 'follow-next-window) 328 (define-key map "p" 'follow-previous-window) 329 330 (define-key mainmap follow-mode-prefix map) 331 332 ;; Replace the standard `end-of-buffer', when in Follow Mode. (I 333 ;; don't see the point in trying to replace every function that 334 ;; could be enhanced in Follow mode. End-of-buffer is a special 335 ;; case since it is very simple to define and it greatly enhances 336 ;; the look and feel of Follow mode.) 337 (define-key mainmap [remap end-of-buffer] 'follow-end-of-buffer) 338 339 ;; 340 ;; The menu. 341 ;; 342 343 (if (not (featurep 'xemacs)) 344 345 ;; 346 ;; Emacs 347 ;; 348 (let ((menumap (funcall (symbol-function 'make-sparse-keymap) 349 "Follow")) 350 (count 0) 351 id) 352 (mapcar 353 (function 354 (lambda (item) 355 (setq id 356 (or (cdr item) 357 (progn 358 (setq count (+ count 1)) 359 (intern (format "separator-%d" count))))) 360 (define-key menumap (vector id) item) 361 (or (eq id 'follow-mode) 362 (put id 'menu-enable 'follow-mode)))) 363 ;; In reverse order: 364 '(("Toggle Follow mode" . follow-mode) 365 ("--") 366 ("Recenter" . follow-recenter) 367 ("--") 368 ("Previous Window" . follow-previous-window) 369 ("Next Windows" . follow-next-window) 370 ("Last Window" . follow-last-window) 371 ("First Window" . follow-first-window) 372 ("--") 373 ("Switch To Buffer (all windows)" 374 . follow-switch-to-buffer-all) 375 ("Switch To Buffer" . follow-switch-to-buffer) 376 ("--") 377 ("Delete Other Windows and Split" 378 . follow-delete-other-windows-and-split) 379 ("--") 380 ("Scroll Down" . follow-scroll-down) 381 ("Scroll Up" . follow-scroll-up))) 382 383 ;; If there is a `tools' menu, we use it. However, we can't add a 384 ;; minor-mode specific item to it (it's broken), so we make the 385 ;; contents ghosted when not in use, and add ourselves to the 386 ;; global map. If no `tools' menu is present, just make a 387 ;; top-level menu visible when the mode is activated. 388 389 (let ((tools-map (lookup-key (current-global-map) [menu-bar tools])) 390 (last nil)) 391 (if (sequencep tools-map) 392 (progn 393 ;; Find the last entry in the menu and store it in `last'. 394 (mapcar (function 395 (lambda (x) 396 (setq last (or (cdr-safe 397 (cdr-safe 398 (cdr-safe x))) 399 last)))) 400 tools-map) 401 (if last 402 (progn 403 (funcall (symbol-function 'define-key-after) 404 tools-map [separator-follow] '("--") last) 405 (funcall (symbol-function 'define-key-after) 406 tools-map [follow] (cons "Follow" menumap) 407 'separator-follow)) 408 ;; Didn't find the last item, Adding to the top of 409 ;; tools. (This will probably never happend...) 410 (define-key (current-global-map) [menu-bar tools follow] 411 (cons "Follow" menumap)))) 412 ;; No tools menu, add "Follow" to the menubar. 413 (define-key mainmap [menu-bar follow] 414 (cons "Follow" menumap))))) 415 416 ;; 417 ;; XEmacs. 418 ;; 419 420 ;; place the menu in the `Tools' menu. 421 (let ((menu '("Follow" 422 :filter follow-menu-filter 423 ["Scroll Up" follow-scroll-up t] 424 ["Scroll Down" follow-scroll-down t] 425 ["Delete Other Windows and Split" 426 follow-delete-other-windows-and-split t] 427 ["Switch To Buffer" follow-switch-to-buffer t] 428 ["Switch To Buffer (all windows)" 429 follow-switch-to-buffer-all t] 430 ["First Window" follow-first-window t] 431 ["Last Window" follow-last-window t] 432 ["Next Windows" follow-next-window t] 433 ["Previous Window" follow-previous-window t] 434 ["Recenter" follow-recenter t] 435 ["Deactivate" follow-mode t]))) 436 437 ;; Why not just `(set-buffer-menubar current-menubar)'? The 438 ;; question is a very good question. The reason is that under 439 ;; Emacs, neither `set-buffer-menubar' nor 440 ;; `current-menubar' is defined, hence the byte-compiler will 441 ;; warn. 442 (funcall (symbol-function 'set-buffer-menubar) 443 (symbol-value 'current-menubar)) 444 (funcall (symbol-function 'add-submenu) '("Tools") menu)) 445 446 ;; When the mode is not activated, only one item is visible: 447 ;; "Activate". 448 (defun follow-menu-filter (menu) 449 (if follow-mode 450 menu 451 '(["Activate " follow-mode t])))) 452 453 mainmap) 454 "Minor mode keymap for Follow mode.") 455 456;;}}} 457 458(defcustom follow-mode-line-text " Follow" 459 "Text shown in the mode line when Follow mode is active. 460Defaults to \" Follow\". Examples of other values 461are \" Fw\", or simply \"\"." 462 :type 'string 463 :group 'follow) 464 465(defcustom follow-auto nil 466 "Non-nil activates Follow mode whenever a file is loaded." 467 :type 'boolean 468 :group 'follow) 469 470(defcustom follow-intercept-processes (fboundp 'start-process) 471 "When non-nil, Follow Mode will monitor process output." 472 :type 'boolean 473 :group 'follow) 474 475(defvar follow-avoid-tail-recenter-p (not (featurep 'xemacs)) 476 "*When non-nil, patch Emacs so that tail windows won't be recentered. 477 478A \"tail window\" is a window that displays only the end of 479the buffer. Normally it is practical for the user that empty 480windows are recentered automatically. However, when using 481Follow Mode it breaks the display when the end is displayed 482in a window \"above\" the last window. This is for 483example the case when displaying a short page in info. 484 485Must be set before Follow Mode is loaded. 486 487Please note that it is not possible to fully prevent Emacs from 488recentering empty windows. Please report if you find a repeatable 489situation in which Emacs recenters empty windows. 490 491XEmacs, as of 19.12, does not recenter windows, good!") 492 493(defvar follow-cache-command-list 494 '(next-line previous-line forward-char backward-char) 495 "List of commands that don't require recalculation. 496 497In order to be able to use the cache, a command should not change the 498contents of the buffer, nor should it change selected window or current 499buffer. 500 501The commands in this list are checked at load time. 502 503To mark other commands as suitable for caching, set the symbol 504property `follow-mode-use-cache' to non-nil.") 505 506(defvar follow-debug nil 507 "*Non-nil when debugging Follow mode.") 508 509 510;; Internal variables: 511 512(defvar follow-internal-force-redisplay nil 513 "True when Follow mode should redisplay the windows.") 514 515(defvar follow-process-filter-alist '() 516 "The original filters for processes intercepted by Follow mode.") 517 518(defvar follow-active-menu nil 519 "The menu visible when Follow mode is active.") 520 521(defvar follow-deactive-menu nil 522 "The menu visible when Follow mode is deactivated.") 523 524(defvar follow-inside-post-command-hook nil 525 "Non-nil when inside Follow modes `post-command-hook'. 526Used by `follow-window-size-change'.") 527 528(defvar follow-windows-start-end-cache nil 529 "Cache used by `follow-window-start-end'.") 530 531;;}}} 532;;{{{ Debug messages 533 534;; This inline function must be as small as possible! 535;; Maybe we should define a macro that expands to nil if 536;; the variable is not set. 537 538(defsubst follow-debug-message (&rest args) 539 "Like message, but only active when `follow-debug' is non-nil." 540 (if (and (boundp 'follow-debug) follow-debug) 541 (apply 'message args))) 542 543;;}}} 544;;{{{ Cache 545 546(dolist (cmd follow-cache-command-list) 547 (put cmd 'follow-mode-use-cache t)) 548 549;;}}} 550 551;;{{{ The mode 552 553;;;###autoload 554(defun turn-on-follow-mode () 555 "Turn on Follow mode. Please see the function `follow-mode'." 556 (interactive) 557 (follow-mode 1)) 558 559 560;;;###autoload 561(defun turn-off-follow-mode () 562 "Turn off Follow mode. Please see the function `follow-mode'." 563 (interactive) 564 (follow-mode -1)) 565 566(put 'follow-mode 'permanent-local t) 567;;;###autoload 568(define-minor-mode follow-mode 569 "Minor mode that combines windows into one tall virtual window. 570 571The feeling of a \"virtual window\" has been accomplished by the use 572of two major techniques: 573 574* The windows always displays adjacent sections of the buffer. 575 This means that whenever one window is moved, all the 576 others will follow. (Hence the name Follow Mode.) 577 578* Should the point (cursor) end up outside a window, another 579 window displaying that point is selected, if possible. This 580 makes it possible to walk between windows using normal cursor 581 movement commands. 582 583Follow mode comes to its prime when used on a large screen and two 584side-by-side window are used. The user can, with the help of Follow 585mode, use two full-height windows as though they would have been 586one. Imagine yourself editing a large function, or section of text, 587and being able to use 144 lines instead of the normal 72... (your 588mileage may vary). 589 590To split one large window into two side-by-side windows, the commands 591`\\[split-window-horizontally]' or \ 592`M-x follow-delete-other-windows-and-split' can be used. 593 594Only windows displayed in the same frame follow each-other. 595 596If the variable `follow-intercept-processes' is non-nil, Follow mode 597will listen to the output of processes and redisplay accordingly. 598\(This is the default.) 599 600When Follow mode is switched on, the hook `follow-mode-hook' 601is called. When turned off, `follow-mode-off-hook' is called. 602 603Keys specific to Follow mode: 604\\{follow-mode-map}" 605 :keymap follow-mode-map 606 (if (and follow-mode follow-intercept-processes) 607 (follow-intercept-process-output)) 608 (cond (follow-mode ; On 609 ;; XEmacs: If this is non-nil, the window will scroll before 610 ;; the point will have a chance to get into the next window. 611 (if (boundp 'scroll-on-clipped-lines) 612 (setq scroll-on-clipped-lines nil)) 613 (force-mode-line-update) 614 (add-hook 'post-command-hook 'follow-post-command-hook t) 615 (run-hooks 'follow-mode-hook)) 616 617 ((not follow-mode) ; Off 618 (force-mode-line-update) 619 (run-hooks 'follow-mode-off-hook)))) 620 621;;}}} 622;;{{{ Find file hook 623 624;; This will start follow-mode whenever a new file is loaded, if 625;; the variable `follow-auto' is non-nil. 626 627(add-hook 'find-file-hook 'follow-find-file-hook t) 628 629(defun follow-find-file-hook () 630 "Find-file hook for Follow Mode. See the variable `follow-auto'." 631 (if follow-auto (follow-mode t))) 632 633;;}}} 634 635;;{{{ User functions 636 637;;; 638;;; User functions usable when in Follow mode. 639;;; 640 641;;{{{ Scroll 642 643;; `scroll-up' and `-down', but for windows in Follow Mode. 644;; 645;; Almost like the real thing, excpet when the cursor ends up outside 646;; the top or bottom... In our case however, we end up outside the 647;; window and hence we are recenterd. Should we let `recenter' handle 648;; the point position we would never leave the selected window. To do 649;; it ourselves we would need to do our own redisplay, which is easier 650;; said than done. (Why didn't I do a real display abstraction from 651;; the beginning?) 652;; 653;; We must sometimes set `follow-internal-force-redisplay', otherwise 654;; our post-command-hook will move our windows back into the old 655;; position... (This would also be corrected if we would have had a 656;; good redisplay abstraction.) 657 658(defun follow-scroll-up (&optional arg) 659 "Scroll text in a Follow Mode window chain up. 660 661If called with no ARG, the `next-screen-context-lines' last lines of 662the bottom window in the chain will be visible in the top window. 663 664If called with an argument, scroll ARG lines up. 665Negative ARG means scroll downward. 666 667Works like `scroll-up' when not in Follow Mode." 668 (interactive "P") 669 (cond ((not (and (boundp 'follow-mode) follow-mode)) 670 (scroll-up arg)) 671 (arg 672 (save-excursion (scroll-up arg)) 673 (setq follow-internal-force-redisplay t)) 674 (t 675 (let* ((windows (follow-all-followers)) 676 (end (window-end (car (reverse windows))))) 677 (if (eq end (point-max)) 678 (signal 'end-of-buffer nil) 679 (select-window (car windows)) 680 ;; `window-end' might return nil. 681 (if end 682 (goto-char end)) 683 (vertical-motion (- next-screen-context-lines)) 684 (set-window-start (car windows) (point))))))) 685 686 687(defun follow-scroll-down (&optional arg) 688 "Scroll text in a Follow Mode window chain down. 689 690If called with no ARG, the `next-screen-context-lines' top lines of 691the top window in the chain will be visible in the bottom window. 692 693If called with an argument, scroll ARG lines down. 694Negative ARG means scroll upward. 695 696Works like `scroll-up' when not in Follow Mode." 697 (interactive "P") 698 (cond ((not (and (boundp 'follow-mode) follow-mode)) 699 (scroll-up arg)) 700 (arg 701 (save-excursion (scroll-down arg))) 702 (t 703 (let* ((windows (follow-all-followers)) 704 (win (car (reverse windows))) 705 (start (window-start (car windows)))) 706 (if (eq start (point-min)) 707 (signal 'beginning-of-buffer nil) 708 (select-window win) 709 (goto-char start) 710 (vertical-motion (- (- (window-height win) 711 1 712 next-screen-context-lines))) 713 (set-window-start win (point)) 714 (goto-char start) 715 (vertical-motion (- next-screen-context-lines 1)) 716 (setq follow-internal-force-redisplay t)))))) 717 718;;}}} 719;;{{{ Buffer 720 721;;;###autoload 722(defun follow-delete-other-windows-and-split (&optional arg) 723 "Create two side by side windows and enter Follow Mode. 724 725Execute this command to display as much as possible of the text 726in the selected window. All other windows, in the current 727frame, are deleted and the selected window is split in two 728side-by-side windows. Follow Mode is activated, hence the 729two windows always will display two successive pages. 730\(If one window is moved, the other one will follow.) 731 732If ARG is positive, the leftmost window is selected. If it negative, 733the rightmost is selected. If ARG is nil, the leftmost window is 734selected if the original window is the first one in the frame. 735 736To bind this command to a hotkey, place the following line 737in your `~/.emacs' file, replacing [f7] by your favourite key: 738 (global-set-key [f7] 'follow-delete-other-windows-and-split)" 739 (interactive "P") 740 (let ((other (or (and (null arg) 741 (not (eq (selected-window) 742 (frame-first-window (selected-frame))))) 743 (and arg 744 (< (prefix-numeric-value arg) 0)))) 745 (start (window-start))) 746 (delete-other-windows) 747 (split-window-horizontally) 748 (if other 749 (progn 750 (other-window 1) 751 (set-window-start (selected-window) start) 752 (setq follow-internal-force-redisplay t))) 753 (follow-mode 1))) 754 755(defun follow-switch-to-buffer (buffer) 756 "Show BUFFER in all windows in the current Follow Mode window chain." 757 (interactive "BSwitch to Buffer: ") 758 (let ((orig-window (selected-window)) 759 (windows (follow-all-followers))) 760 (while windows 761 (select-window (car windows)) 762 (switch-to-buffer buffer) 763 (setq windows (cdr windows))) 764 (select-window orig-window))) 765 766 767(defun follow-switch-to-buffer-all (&optional buffer) 768 "Show BUFFER in all windows on this frame. 769Defaults to current buffer." 770 (interactive (list (read-buffer "Switch to Buffer: " 771 (current-buffer)))) 772 (or buffer (setq buffer (current-buffer))) 773 (let ((orig-window (selected-window))) 774 (walk-windows 775 (function 776 (lambda (win) 777 (select-window win) 778 (switch-to-buffer buffer)))) 779 (select-window orig-window) 780 (follow-redisplay))) 781 782 783(defun follow-switch-to-current-buffer-all () 784 "Show current buffer in all windows on this frame, and enter Follow Mode. 785 786To bind this command to a hotkey place the following line 787in your `~/.emacs' file: 788 (global-set-key [f7] 'follow-switch-to-current-buffer-all)" 789 (interactive) 790 (or (and (boundp 'follow-mode) follow-mode) 791 (follow-mode 1)) 792 (follow-switch-to-buffer-all)) 793 794;;}}} 795;;{{{ Movement 796 797;; Note, these functions are not very useful, atleast not unless you 798;; rebind the rather cumbersome key sequence `C-c . p'. 799 800(defun follow-next-window () 801 "Select the next window showing the same buffer." 802 (interactive) 803 (let ((succ (cdr (follow-split-followers (follow-all-followers))))) 804 (if succ 805 (select-window (car succ)) 806 (error "%s" "No more windows")))) 807 808 809(defun follow-previous-window () 810 "Select the previous window showing the same buffer." 811 (interactive) 812 (let ((pred (car (follow-split-followers (follow-all-followers))))) 813 (if pred 814 (select-window (car pred)) 815 (error "%s" "No more windows")))) 816 817 818(defun follow-first-window () 819 "Select the first window in the frame showing the same buffer." 820 (interactive) 821 (select-window (car (follow-all-followers)))) 822 823 824(defun follow-last-window () 825 "Select the last window in the frame showing the same buffer." 826 (interactive) 827 (select-window (car (reverse (follow-all-followers))))) 828 829;;}}} 830;;{{{ Redraw 831 832(defun follow-recenter (&optional arg) 833 "Recenter the middle window around point. 834Rearrange all other windows around the middle window. 835 836With a positive argument, place the current line ARG lines 837from the top. With a negative, place it -ARG lines from the 838bottom." 839 (interactive "P") 840 (if arg 841 (let ((p (point)) 842 (arg (prefix-numeric-value arg))) 843 (if (>= arg 0) 844 ;; Recenter relative to the top. 845 (progn 846 (follow-first-window) 847 (goto-char p) 848 (recenter arg)) 849 ;; Recenter relative to the bottom. 850 (follow-last-window) 851 (goto-char p) 852 (recenter arg) 853 ;; Otherwise, our post-command-hook will move the window 854 ;; right back. 855 (setq follow-internal-force-redisplay t))) 856 ;; Recenter in the middle. 857 (let* ((dest (point)) 858 (windows (follow-all-followers)) 859 (win (nth (/ (- (length windows) 1) 2) windows))) 860 (select-window win) 861 (goto-char dest) 862 (recenter) 863 ;;(setq follow-internal-force-redisplay t) 864 ))) 865 866 867(defun follow-redraw () 868 "Arrange windows displaying the same buffer in successor order. 869This function can be called even if the buffer is not in Follow mode. 870 871Hopefully, there should be no reason to call this function when in 872Follow mode since the windows should always be aligned." 873 (interactive) 874 (sit-for 0) 875 (follow-redisplay)) 876 877;;}}} 878;;{{{ End of buffer 879 880(defun follow-end-of-buffer (&optional arg) 881 "Move point to the end of the buffer, Follow Mode style. 882 883If the end is not visible, it will be displayed in the last possible 884window in the Follow Mode window chain. 885 886The mark is left at the previous position. With arg N, put point N/10 887of the way from the true end." 888 (interactive "P") 889 (let ((followers (follow-all-followers)) 890 (pos (point))) 891 (cond (arg 892 (select-window (car (reverse followers)))) 893 ((follow-select-if-end-visible 894 (follow-windows-start-end followers))) 895 (t 896 (select-window (car (reverse followers))))) 897 (goto-char pos) 898 (with-no-warnings 899 (end-of-buffer arg)))) 900 901;;}}} 902 903;;}}} 904 905;;{{{ Display 906 907;;;; The display routines 908 909;;{{{ Information gathering functions 910 911(defun follow-all-followers (&optional testwin) 912 "Return all windows displaying the same buffer as the TESTWIN. 913The list contains only windows displayed in the same frame as TESTWIN. 914If TESTWIN is nil the selected window is used." 915 (or (and testwin (window-live-p testwin)) 916 (setq testwin (selected-window))) 917 (let* ((top (frame-first-window (window-frame testwin))) 918 (win top) 919 (done nil) 920 (windows '()) 921 (buffer (window-buffer testwin))) 922 (while (and (not done) win) 923 (if (eq (window-buffer win) buffer) 924 (setq windows (cons win windows))) 925 (setq win (next-window win 'not)) 926 (if (eq win top) 927 (setq done t))) 928 (nreverse windows))) 929 930 931(defun follow-split-followers (windows &optional win) 932 "Split the WINDOWS into the sets: predecessors and successors. 933Return `(PRED . SUCC)' where `PRED' and `SUCC' are ordered starting 934from the selected window." 935 (or win 936 (setq win (selected-window))) 937 (let ((pred '())) 938 (while (not (eq (car windows) win)) 939 (setq pred (cons (car windows) pred)) 940 (setq windows (cdr windows))) 941 (cons pred (cdr windows)))) 942 943 944;; This function is optimized function for speed! 945 946(defun follow-calc-win-end (&optional win) 947 "Calculate the presumed window end for WIN. 948 949Actually, the position returned is the start of the next 950window, normally is the end plus one. 951 952If WIN is nil, the selected window is used. 953 954Returns (end-pos end-of-buffer-p)" 955 (if (featurep 'xemacs) 956 ;; XEmacs can calculate the end of the window by using 957 ;; the 'guarantee options. GOOD! 958 (let ((end (window-end win t))) 959 (if (= end (funcall (symbol-function 'point-max) 960 (window-buffer win))) 961 (list end t) 962 (list (+ end 1) nil))) 963 ;; Emacs: We have to calculate the end by ourselves. 964 ;; This code works on both XEmacs and Emacs, but now 965 ;; that XEmacs has got custom-written code, this could 966 ;; be optimized for Emacs. 967 (let ((orig-win (and win (selected-window))) 968 height 969 buffer-end-p) 970 (if win (select-window win)) 971 (prog1 972 (save-excursion 973 (goto-char (window-start)) 974 (setq height (- (window-height) 1)) 975 (setq buffer-end-p 976 (if (bolp) 977 (not (= height (vertical-motion height))) 978 (save-restriction 979 ;; Fix a mis-feature in `vertical-motion': 980 ;; The start of the window is assumed to 981 ;; coinside with the start of a line. 982 (narrow-to-region (point) (point-max)) 983 (not (= height (vertical-motion height)))))) 984 (list (point) buffer-end-p)) 985 (if orig-win 986 (select-window orig-win)))))) 987 988 989;; Can't use `save-window-excursion' since it triggers a redraw. 990(defun follow-calc-win-start (windows pos win) 991 "Calculate where WIN will start if the first in WINDOWS start at POS. 992 993If WIN is nil the point below all windows is returned." 994 (let (start) 995 (while (and windows (not (eq (car windows) win))) 996 (setq start (window-start (car windows))) 997 (set-window-start (car windows) pos 'noforce) 998 (setq pos (car (inline (follow-calc-win-end (car windows))))) 999 (set-window-start (car windows) start 'noforce) 1000 (setq windows (cdr windows))) 1001 pos)) 1002 1003 1004;; The result from `follow-windows-start-end' is cached when using 1005;; a handful simple commands, like cursor movement commands. 1006 1007(defsubst follow-cache-valid-p (windows) 1008 "Test if the cached value of `follow-windows-start-end' can be used. 1009Note that this handles the case when the cache has been set to nil." 1010 (let ((res t) 1011 (cache follow-windows-start-end-cache)) 1012 (while (and res windows cache) 1013 (setq res (and (eq (car windows) 1014 (car (car cache))) 1015 (eq (window-start (car windows)) 1016 (car (cdr (car cache)))))) 1017 (setq windows (cdr windows)) 1018 (setq cache (cdr cache))) 1019 (and res (null windows) (null cache)))) 1020 1021 1022(defsubst follow-invalidate-cache () 1023 "Force `follow-windows-start-end' to recalculate the end of the window." 1024 (setq follow-windows-start-end-cache nil)) 1025 1026 1027;; Build a list of windows and their start and end positions. 1028;; Useful to avoid calculating start/end position whenever they are needed. 1029;; The list has the format: 1030;; ((Win Start End End-of-buffer-visible-p) ...) 1031 1032;; Used to have a `save-window-excursion', but it obviously triggered 1033;; redraws of the display. Check if I used it for anything. 1034 1035 1036(defun follow-windows-start-end (windows) 1037 "Builds a list of (WIN START END BUFFER-END-P) for every window in WINDOWS." 1038 (if (follow-cache-valid-p windows) 1039 follow-windows-start-end-cache 1040 (let ((win-start-end '()) 1041 (orig-win (selected-window))) 1042 (while windows 1043 (select-window (car windows)) 1044 (setq win-start-end 1045 (cons (cons (car windows) 1046 (cons (window-start) 1047 (follow-calc-win-end))) 1048 win-start-end)) 1049 (setq windows (cdr windows))) 1050 (select-window orig-win) 1051 (setq follow-windows-start-end-cache (nreverse win-start-end)) 1052 follow-windows-start-end-cache))) 1053 1054 1055(defsubst follow-pos-visible (pos win win-start-end) 1056 "Non-nil when POS is visible in WIN." 1057 (let ((wstart-wend-bend (cdr (assq win win-start-end)))) 1058 (and (>= pos (car wstart-wend-bend)) 1059 (or (< pos (car (cdr wstart-wend-bend))) 1060 (nth 2 wstart-wend-bend))))) 1061 1062 1063;; By `aligned' we mean that for all adjecent windows, the end of the 1064;; first is equal with the start of the successor. The first window 1065;; should start at a full screen line. 1066 1067(defsubst follow-windows-aligned-p (win-start-end) 1068 "Non-nil if the follower WINDOWS are aligned." 1069 (let ((res t)) 1070 (save-excursion 1071 (goto-char (window-start (car (car win-start-end)))) 1072 (if (bolp) 1073 nil 1074 (vertical-motion 0 (car (car win-start-end))) 1075 (setq res (eq (point) (window-start (car (car win-start-end))))))) 1076 (while (and res (cdr win-start-end)) 1077 ;; At least two followers left 1078 (setq res (eq (car (cdr (cdr (car win-start-end)))) 1079 (car (cdr (car (cdr win-start-end)))))) 1080 (setq win-start-end (cdr win-start-end))) 1081 res)) 1082 1083 1084;; Check if the point is visible in all windows. (So that 1085;; no one will be recentered.) 1086 1087(defun follow-point-visible-all-windows-p (win-start-end) 1088 "Non-nil when the window-point is visible in all windows." 1089 (let ((res t)) 1090 (while (and res win-start-end) 1091 (setq res (follow-pos-visible (window-point (car (car win-start-end))) 1092 (car (car win-start-end)) 1093 win-start-end)) 1094 (setq win-start-end (cdr win-start-end))) 1095 res)) 1096 1097 1098;; Make sure WIN always starts at the beginning of an whole screen 1099;; line. If WIN is not aligned the start is updated which probably 1100;; will lead to a redisplay of the screen later on. 1101;; 1102;; This is used with the first window in a follow chain. The reason 1103;; is that we want to detect that the point is outside the window. 1104;; (Without the update, the start of the window will move as the 1105;; user presses BackSpace, and the other window redisplay routines 1106;; will move the start of the window in the wrong direction.) 1107 1108(defun follow-update-window-start (win) 1109 "Make sure that the start of WIN starts at a full screen line." 1110 (save-excursion 1111 (goto-char (window-start win)) 1112 (if (bolp) 1113 nil 1114 (vertical-motion 0 win) 1115 (if (eq (point) (window-start win)) 1116 nil 1117 (vertical-motion 1 win) 1118 (set-window-start win (point) 'noforce))))) 1119 1120;;}}} 1121;;{{{ Selection functions 1122 1123;; Make a window in WINDOWS selected if it currently 1124;; is displaying the position DEST. 1125;; 1126;; We don't select a window if it just has been moved. 1127 1128(defun follow-select-if-visible (dest win-start-end) 1129 "Select and return a window, if DEST is visible in it. 1130Return the selected window." 1131 (let ((win nil)) 1132 (while (and (not win) win-start-end) 1133 ;; Don't select a window that was just moved. This makes it 1134 ;; possible to later select the last window after a `end-of-buffer' 1135 ;; command. 1136 (if (follow-pos-visible dest (car (car win-start-end)) win-start-end) 1137 (progn 1138 (setq win (car (car win-start-end))) 1139 (select-window win))) 1140 (setq win-start-end (cdr win-start-end))) 1141 win)) 1142 1143 1144;; Lets select a window showing the end. Make sure we only select it if it 1145;; it wasn't just moved here. (i.e. M-> shall not unconditionally place 1146;; the point in the selected window.) 1147;; 1148;; (Compability cludge: in Emacs `window-end' is equal to `point-max'; 1149;; in XEmacs, it is equal to `point-max + 1'. Should I really bother 1150;; checking `window-end' now when I check `end-of-buffer' explicitly?) 1151 1152(defun follow-select-if-end-visible (win-start-end) 1153 "Select and return a window, if end is visible in it." 1154 (let ((win nil)) 1155 (while (and (not win) win-start-end) 1156 ;; Don't select a window that was just moved. This makes it 1157 ;; possible to later select the last window after a `end-of-buffer' 1158 ;; command. 1159 (if (and (eq (point-max) (nth 2 (car win-start-end))) 1160 (nth 3 (car win-start-end)) 1161 ;; `window-end' might return nil. 1162 (let ((end (window-end (car (car win-start-end))))) 1163 (and end 1164 (eq (point-max) (min (point-max) end))))) 1165 (progn 1166 (setq win (car (car win-start-end))) 1167 (select-window win))) 1168 (setq win-start-end (cdr win-start-end))) 1169 win)) 1170 1171 1172;; Select a window that will display the point if the windows would 1173;; be redisplayed with the first window fixed. This is useful for 1174;; example when the user has pressed return at the bottom of a window 1175;; as the point is not visible in any window. 1176 1177(defun follow-select-if-visible-from-first (dest windows) 1178 "Select and return a window with DEST, if WINDOWS are redrawn from top." 1179 (let ((win nil) 1180 end-pos-end-p) 1181 (save-excursion 1182 (goto-char (window-start (car windows))) 1183 ;; Make sure the line start in the beginning of a real screen 1184 ;; line. 1185 (vertical-motion 0 (car windows)) 1186 (if (< dest (point)) 1187 ;; Above the start, not visible. 1188 nil 1189 ;; At or below the start. Check the windows. 1190 (save-window-excursion 1191 (while (and (not win) windows) 1192 (set-window-start (car windows) (point) 'noforce) 1193 (setq end-pos-end-p (follow-calc-win-end (car windows))) 1194 (goto-char (car end-pos-end-p)) 1195 ;; Visible, if dest above end, or if eob is visible inside 1196 ;; the window. 1197 (if (or (car (cdr end-pos-end-p)) 1198 (< dest (point))) 1199 (setq win (car windows)) 1200 (setq windows (cdr windows))))))) 1201 (if win 1202 (select-window win)) 1203 win)) 1204 1205 1206;;}}} 1207;;{{{ Redisplay 1208 1209;; Redraw all the windows on the screen, starting with the top window. 1210;; The window used as as marker is WIN, or the selcted window if WIN 1211;; is nil. 1212 1213(defun follow-redisplay (&optional windows win) 1214 "Reposition the WINDOWS around WIN. 1215Should the point be too close to the roof we redisplay everything 1216from the top. WINDOWS should contain a list of windows to 1217redisplay, it is assumed that WIN is a member of the list. 1218Should WINDOWS be nil, the windows displaying the 1219same buffer as WIN, in the current frame, are used. 1220Should WIN be nil, the selected window is used." 1221 (or win 1222 (setq win (selected-window))) 1223 (or windows 1224 (setq windows (follow-all-followers win))) 1225 (follow-downward windows (follow-calculate-first-window-start windows win))) 1226 1227 1228;; Redisplay a chain of windows. Start every window directly after the 1229;; end of the previous window, to make sure long lines are displayed 1230;; correctly. 1231 1232(defun follow-downward (windows pos) 1233 "Redisplay all WINDOWS starting at POS." 1234 (while windows 1235 (set-window-start (car windows) pos) 1236 (setq pos (car (follow-calc-win-end (car windows)))) 1237 (setq windows (cdr windows)))) 1238 1239 1240;;(defun follow-downward (windows pos) 1241;; "Redisplay all WINDOWS starting at POS." 1242;; (let (p) 1243;; (while windows 1244;; (setq p (window-point (car windows))) 1245;; (set-window-start (car windows) pos) 1246;; (set-window-point (car windows) (max p pos)) 1247;; (setq pos (car (follow-calc-win-end (car windows)))) 1248;; (setq windows (cdr windows))))) 1249 1250 1251;; Return the start of the first window. 1252;; 1253;; First, estimate the position. It the value is not perfect (i.e. we 1254;; have somewhere splited a line between windows) we try to enhance 1255;; the value. 1256;; 1257;; The guess is always perfect if no long lines is split between 1258;; windows. 1259;; 1260;; The worst case peformace of probably very bad, but it is very 1261;; unlikely that we ever will miss the correct start by more than one 1262;; or two lines. 1263 1264(defun follow-calculate-first-window-start (windows &optional win start) 1265 "Calculate the start of the first window. 1266 1267WINDOWS is a chain of windows to work with. WIN is the window 1268to recenter around. It is assumed that WIN starts at position 1269START." 1270 (or win 1271 (setq win (selected-window))) 1272 (or start 1273 (setq start (window-start win))) 1274 (let ((guess (follow-estimate-first-window-start windows win start))) 1275 (if (car guess) 1276 (cdr guess) 1277 ;; The guess wasn't exact, try to enhance it. 1278 (let ((win-start (follow-calc-win-start windows (cdr guess) win))) 1279 (cond ((= win-start start) 1280 (follow-debug-message "exact") 1281 (cdr guess)) 1282 ((< win-start start) 1283 (follow-debug-message "above") 1284 (follow-calculate-first-window-start-from-above 1285 windows (cdr guess) win start)) 1286 (t 1287 (follow-debug-message "below") 1288 (follow-calculate-first-window-start-from-below 1289 windows (cdr guess) win start))))))) 1290 1291 1292;; `exact' is disabled due to XEmacs and fonts of variable 1293;; height. 1294(defun follow-estimate-first-window-start (windows win start) 1295 "Estimate the position of the first window. 1296 1297Returns (EXACT . POS). If EXACT is non-nil, POS is the starting 1298position of the first window. Otherwise it is a good guess." 1299 (let ((pred (car (follow-split-followers windows win))) 1300 (exact nil)) 1301 (save-excursion 1302 (goto-char start) 1303 ;(setq exact (bolp)) 1304 (vertical-motion 0 win) 1305 (while pred 1306 (vertical-motion (- 1 (window-height (car pred))) (car pred)) 1307 (if (not (bolp)) 1308 (setq exact nil)) 1309 (setq pred (cdr pred))) 1310 (cons exact (point))))) 1311 1312 1313;; Find the starting point, start at GUESS and search downward. 1314;; The returned point is always a point below GUESS. 1315 1316(defun follow-calculate-first-window-start-from-above 1317 (windows guess win start) 1318 (save-excursion 1319 (let ((done nil) 1320 win-start 1321 res) 1322 (goto-char guess) 1323 (while (not done) 1324 (if (not (= (vertical-motion 1 (car windows)) 1)) 1325 ;; Hit bottom! (Can we really do this?) 1326 ;; We'll keep it, since it ensures termination. 1327 (progn 1328 (setq done t) 1329 (setq res (point-max))) 1330 (setq win-start (follow-calc-win-start windows (point) win)) 1331 (if (>= win-start start) 1332 (progn 1333 (setq done t) 1334 (setq res (point)))))) 1335 res))) 1336 1337 1338;; Find the starting point, start at GUESS and search upward. Return 1339;; a point on the same line as GUESS, or above. 1340;; 1341;; (Is this ever used? I must make sure it works just in case it is 1342;; ever called.) 1343 1344(defun follow-calculate-first-window-start-from-below 1345 (windows guess &optional win start) 1346 (setq win (or win (selected-window))) 1347 (setq start (or start (window-start win))) 1348 (save-excursion 1349 (let ((done nil) 1350 win-start 1351 res) 1352 ;; Always calculate what happend when no line is displayed in the first 1353 ;; window. (The `previous' res is needed below!) 1354 (goto-char guess) 1355 (vertical-motion 0 (car windows)) 1356 (setq res (point)) 1357 (while (not done) 1358 (if (not (= (vertical-motion -1 (car windows)) -1)) 1359 ;; Hit roof! 1360 (progn 1361 (setq done t) 1362 (setq res (point-min))) 1363 (setq win-start (follow-calc-win-start windows (point) win)) 1364 (cond ((= win-start start) ; Perfect match, use this value 1365 (setq done t) 1366 (setq res (point))) 1367 ((< win-start start) ; Walked to far, use preious result 1368 (setq done t)) 1369 (t ; Store result for next iteration 1370 (setq res (point)))))) 1371 res))) 1372 1373;;}}} 1374;;{{{ Avoid tail recenter 1375 1376;; This sets the window internal flag `force_start'. The effect is that 1377;; windows only displaying the tail isn't recentered. 1378;; Has to be called before every redisplay... (Great isn't it?) 1379;; 1380;; XEmacs doesn't recenter the tail, GOOD! 1381;; 1382;; A window displaying only the tail, is a windows whose 1383;; window-start position is equal to (point-max) of the buffer it 1384;; displays. 1385;; 1386;; This function is also added to `post-command-idle-hook', introduced 1387;; in Emacs 19.30. This is needed since the vaccine injected by the 1388;; call from `post-command-hook' only works until the next redisplay. 1389;; It is possible that the functions in the `post-command-idle-hook' 1390;; can cause a redisplay, and hence a new vaccine is needed. 1391;; 1392;; Sometimes, calling this function could actually cause a redisplay, 1393;; especially if it is placed in the debug filter section. I must 1394;; investigate this further... 1395 1396(defun follow-avoid-tail-recenter (&rest rest) 1397 "Make sure windows displaying the end of a buffer aren't recentered. 1398 1399This is done by reading and rewriting the start position of 1400non-first windows in Follow Mode." 1401 (if follow-avoid-tail-recenter-p 1402 (let* ((orig-buffer (current-buffer)) 1403 (top (frame-first-window (selected-frame))) 1404 (win top) 1405 (who '()) ; list of (buffer . frame) 1406 start 1407 pair) ; (buffer . frame) 1408 ;; If the only window in the frame is a minibuffer 1409 ;; window, `next-window' will never find it again... 1410 (if (window-minibuffer-p top) 1411 nil 1412 (while ;; look, no body! 1413 (progn 1414 (setq start (window-start win)) 1415 (set-buffer (window-buffer win)) 1416 (setq pair (cons (window-buffer win) (window-frame win))) 1417 (if (member pair who) 1418 (if (and (boundp 'follow-mode) follow-mode 1419 (eq (point-max) start)) 1420 ;; Write the same window start back, but don't 1421 ;; set the NOFORCE flag. 1422 (set-window-start win start)) 1423 (setq who (cons pair who))) 1424 (setq win (next-window win 'not t)) 1425 (not (eq win top)))) ;; Loop while this is true. 1426 (set-buffer orig-buffer))))) 1427 1428;;}}} 1429 1430;;}}} 1431;;{{{ Post Command Hook 1432 1433;; The magic little box. This function is called after every command. 1434 1435;; This is not as complicated as it seems. It is simply a list of common 1436;; display situations and the actions to take, plus commands for redrawing 1437;; the screen if it should be unaligned. 1438;; 1439;; We divide the check into two parts; whether we are at the end or not. 1440;; This is due to the fact that the end can actaually be visible 1441;; in several window even though they are aligned. 1442 1443(defun follow-post-command-hook () 1444 "Ensure that the windows in Follow mode are adjacent after each command." 1445 (setq follow-inside-post-command-hook t) 1446 (if (or (not (input-pending-p)) 1447 ;; Sometimes, in XEmacs, mouse events are not handled 1448 ;; properly by `input-pending-p'. A typical example is 1449 ;; when clicking on a node in `info'. 1450 (and (boundp 'current-mouse-event) 1451 (symbol-value 'current-mouse-event) 1452 (fboundp 'button-event-p) 1453 (funcall (symbol-function 'button-event-p) 1454 (symbol-value 'current-mouse-event)))) 1455 ;; Work in the selected window, not in the current buffer. 1456 (let ((orig-buffer (current-buffer)) 1457 (win (selected-window))) 1458 (set-buffer (window-buffer win)) 1459 (or (and (symbolp this-command) 1460 (get this-command 'follow-mode-use-cache)) 1461 (follow-invalidate-cache)) 1462 (if (and (boundp 'follow-mode) follow-mode 1463 (not (window-minibuffer-p win))) 1464 ;; The buffer shown in the selected window is in follow 1465 ;; mode, lets find the current state of the display and 1466 ;; cache the result for speed (i.e. `aligned' and `visible'.) 1467 (let* ((windows (inline (follow-all-followers win))) 1468 (dest (point)) 1469 (win-start-end (inline 1470 (follow-update-window-start (car windows)) 1471 (follow-windows-start-end windows))) 1472 (aligned (follow-windows-aligned-p win-start-end)) 1473 (visible (follow-pos-visible dest win win-start-end))) 1474 (if (not (and aligned visible)) 1475 (follow-invalidate-cache)) 1476 (inline (follow-avoid-tail-recenter)) 1477 ;; Select a window to display the point. 1478 (or follow-internal-force-redisplay 1479 (progn 1480 (if (eq dest (point-max)) 1481 ;; We're at the end, we have to be careful since 1482 ;; the display can be aligned while `dest' can 1483 ;; be visible in several windows. 1484 (cond 1485 ;; Select the current window, but only when 1486 ;; the display is correct. (When inserting 1487 ;; character in a tail window, the display is 1488 ;; not correct, as they are shown twice.) 1489 ;; 1490 ;; Never stick to the current window after a 1491 ;; deletion. The reason is cosmetic, when 1492 ;; typing `DEL' in a window showing only the 1493 ;; end of the file, character are removed 1494 ;; from the window above, which is very 1495 ;; unintuitive. 1496 ((and visible 1497 aligned 1498 (not (memq this-command 1499 '(backward-delete-char 1500 delete-backward-char 1501 backward-delete-char-untabify 1502 kill-region)))) 1503 (follow-debug-message "Max: same")) 1504 ;; If the end is visible, and the window 1505 ;; doesn't seems like it just has been moved, 1506 ;; select it. 1507 ((follow-select-if-end-visible win-start-end) 1508 (follow-debug-message "Max: end visible") 1509 (setq visible t) 1510 (setq aligned nil) 1511 (goto-char dest)) 1512 ;; Just show the end... 1513 (t 1514 (follow-debug-message "Max: default") 1515 (select-window (car (reverse windows))) 1516 (goto-char dest) 1517 (setq visible nil) 1518 (setq aligned nil))) 1519 1520 ;; We're not at the end, here life is much simpler. 1521 (cond 1522 ;; This is the normal case! 1523 ;; It should be optimized for speed. 1524 ((and visible aligned) 1525 (follow-debug-message "same")) 1526 ;; Pick a position in any window. If the 1527 ;; display is ok, this will pick the `correct' 1528 ;; window. If the display is wierd do this 1529 ;; anyway, this will be the case after a delete 1530 ;; at the beginning of the window. 1531 ((follow-select-if-visible dest win-start-end) 1532 (follow-debug-message "visible") 1533 (setq visible t) 1534 (goto-char dest)) 1535 ;; Not visible anywhere else, lets pick this one. 1536 ;; (Is this case used?) 1537 (visible 1538 (follow-debug-message "visible in selected.")) 1539 ;; Far out! 1540 ((eq dest (point-min)) 1541 (follow-debug-message "min") 1542 (select-window (car windows)) 1543 (goto-char dest) 1544 (set-window-start (selected-window) (point-min)) 1545 (setq win-start-end (follow-windows-start-end windows)) 1546 (follow-invalidate-cache) 1547 (setq visible t) 1548 (setq aligned nil)) 1549 ;; If we can position the cursor without moving the first 1550 ;; window, do it. This is the case that catches `RET' 1551 ;; at the bottom of a window. 1552 ((follow-select-if-visible-from-first dest windows) 1553 (follow-debug-message "Below first") 1554 (setq visible t) 1555 (setq aligned t) 1556 (follow-redisplay windows (car windows)) 1557 (goto-char dest)) 1558 ;; None of the above. For simplicity, we stick to the 1559 ;; selected window. 1560 (t 1561 (follow-debug-message "None") 1562 (setq visible nil) 1563 (setq aligned nil)))) 1564 ;; If a new window has been selected, make sure that the 1565 ;; old is not scrolled when the point is outside the 1566 ;; window. 1567 (or (eq win (selected-window)) 1568 (let ((p (window-point win))) 1569 (set-window-start win (window-start win) nil) 1570 (set-window-point win p))))) 1571 ;; Make sure the point is visible in the selected window. 1572 ;; (This could lead to a scroll.) 1573 (if (or visible 1574 (follow-pos-visible dest win win-start-end)) 1575 nil 1576 (sit-for 0) 1577 (follow-avoid-tail-recenter) 1578 (setq win-start-end (follow-windows-start-end windows)) 1579 (follow-invalidate-cache) 1580 (setq aligned nil)) 1581 ;; Redraw the windows whenever needed. 1582 (if (or follow-internal-force-redisplay 1583 (not (or aligned 1584 (follow-windows-aligned-p win-start-end))) 1585 (not (inline (follow-point-visible-all-windows-p 1586 win-start-end)))) 1587 (progn 1588 (setq follow-internal-force-redisplay nil) 1589 (follow-redisplay windows (selected-window)) 1590 (setq win-start-end (follow-windows-start-end windows)) 1591 (follow-invalidate-cache) 1592 ;; When the point ends up in another window. This 1593 ;; happends when dest is in the beginning of the 1594 ;; file and the selected window is not the first. 1595 ;; It can also, in rare situations happend when 1596 ;; long lines are used and there is a big 1597 ;; difference between the width of the windows. 1598 ;; (When scrolling one line in a wide window which 1599 ;; will cause a move larger that an entire small 1600 ;; window.) 1601 (if (follow-pos-visible dest win win-start-end) 1602 nil 1603 (follow-select-if-visible dest win-start-end) 1604 (goto-char dest)))) 1605 1606 ;; If the region is visible, make it look good when spanning 1607 ;; multiple windows. 1608 (if (or (and (boundp 'mark-active) (symbol-value 'mark-active)) 1609 (and (fboundp 'region-active-p) 1610 (funcall (symbol-function 'region-active-p)))) 1611 (follow-maximize-region 1612 (selected-window) windows win-start-end)) 1613 1614 (inline (follow-avoid-tail-recenter)) 1615 ;; DEBUG 1616 ;;(if (not (follow-windows-aligned-p 1617 ;; (follow-windows-start-end windows))) 1618 ;; (message "follow-mode: windows still unaligend!")) 1619 ;; END OF DEBUG 1620 ) ; Matches (let* 1621 ;; Buffer not in follow mode: 1622 ;; We still must update the windows displaying the tail so that 1623 ;; Emacs won't recenter them. 1624 (follow-avoid-tail-recenter)) 1625 (set-buffer orig-buffer))) 1626 (setq follow-inside-post-command-hook nil)) 1627 1628;;}}} 1629;;{{{ The region 1630 1631;; Tries to make the highlighted area representing the region look 1632;; good when spanning several windows. 1633;; 1634;; Not perfect, as the point can't be placed at window end, only at 1635;; end-1. This will highlight a little bit in windows above 1636;; the current. 1637 1638(defun follow-maximize-region (win windows win-start-end) 1639 "Make a highlighted region stretching multiple windows look good." 1640 (let* ((all (follow-split-followers windows win)) 1641 (pred (car all)) 1642 (succ (cdr all)) 1643 data) 1644 (while pred 1645 (setq data (assq (car pred) win-start-end)) 1646 (set-window-point (car pred) (max (nth 1 data) (- (nth 2 data) 1))) 1647 (setq pred (cdr pred))) 1648 (while succ 1649 (set-window-point (car succ) (nth 1 (assq (car succ) win-start-end))) 1650 (setq succ (cdr succ))))) 1651 1652;;}}} 1653;;{{{ Scroll bar 1654 1655;;;; Scroll-bar support code. 1656 1657;; Why is it needed? Well, if the selected window is in follow mode, 1658;; all its follower stick to it blindly. If one of them is scrolled, 1659;; it immediately returns to the original position when the mouse is 1660;; released. If the selected window is not a follower of the dragged 1661;; window the windows will be unaligned. 1662 1663;; The advices doesn't get compiled. Aestetically, this might be a 1664;; problem but in practical life it isn't. 1665 1666;; Discussion: Now when the other windows in the chain follow the 1667;; dragged, should we really select it? 1668 1669(cond ((fboundp 'scroll-bar-drag) 1670 ;;; 1671 ;;; Emacs style scrollbars. 1672 ;;; 1673 1674 ;; Select the dragged window if it is a follower of the 1675 ;; selected window. 1676 ;; 1677 ;; Generate advices of the form: 1678 ;; (defadvice scroll-bar-drag (after follow-scroll-bar-drag activate) 1679 ;; "Adviced by `follow-mode'." 1680 ;; (follow-redraw-after-event (ad-get-arg 0))) 1681 (let ((cmds '(scroll-bar-drag 1682 scroll-bar-drag-1 ; Executed at every move. 1683 scroll-bar-scroll-down 1684 scroll-bar-scroll-up 1685 scroll-bar-set-window-start))) 1686 (while cmds 1687 (eval 1688 `(defadvice ,(intern (symbol-name (car cmds))) 1689 (after 1690 ,(intern (concat "follow-" (symbol-name (car cmds)))) 1691 activate) 1692 "Adviced by Follow Mode." 1693 (follow-redraw-after-event (ad-get-arg 0)))) 1694 (setq cmds (cdr cmds)))) 1695 1696 1697 (defun follow-redraw-after-event (event) 1698 "Adviced by Follow mode." 1699 (condition-case nil 1700 (let* ((orig-win (selected-window)) 1701 (win (nth 0 (funcall 1702 (symbol-function 'event-start) event))) 1703 (fmode (assq 'follow-mode 1704 (buffer-local-variables 1705 (window-buffer win))))) 1706 (if (and fmode (cdr fmode)) 1707 ;; The selected window is in follow-mode 1708 (progn 1709 ;; Recenter around the dragged window. 1710 (select-window win) 1711 (follow-redisplay) 1712 (select-window orig-win)))) 1713 (error nil)))) 1714 1715 1716 ((fboundp 'scrollbar-vertical-drag) 1717 ;;; 1718 ;;; XEmacs style scrollbars. 1719 ;;; 1720 1721 ;; Advice all scrollbar functions on the form: 1722 ;; 1723 ;; (defadvice scrollbar-line-down 1724 ;; (after follow-scrollbar-line-down activate) 1725 ;; (follow-xemacs-scrollbar-support (ad-get-arg 0))) 1726 1727 (let ((cmds '(scrollbar-line-down ; Window 1728 scrollbar-line-up 1729 scrollbar-page-down ; Object 1730 scrollbar-page-up 1731 scrollbar-to-bottom ; Window 1732 scrollbar-to-top 1733 scrollbar-vertical-drag ; Object 1734 ))) 1735 1736 (while cmds 1737 (eval 1738 `(defadvice ,(intern (symbol-name (car cmds))) 1739 (after 1740 ,(intern (concat "follow-" (symbol-name (car cmds)))) 1741 activate) 1742 "Adviced by `follow-mode'." 1743 (follow-xemacs-scrollbar-support (ad-get-arg 0)))) 1744 (setq cmds (cdr cmds)))) 1745 1746 1747 (defun follow-xemacs-scrollbar-support (window) 1748 "Redraw windows showing the same buffer as shown in WINDOW. 1749WINDOW is either the dragged window, or a cons containing the 1750window as its first element. This is called while the user drags 1751the scrollbar. 1752 1753WINDOW can be an object or a window." 1754 (condition-case nil 1755 (progn 1756 (if (consp window) 1757 (setq window (car window))) 1758 (let ((fmode (assq 'follow-mode 1759 (buffer-local-variables 1760 (window-buffer window)))) 1761 (orig-win (selected-window))) 1762 (if (and fmode (cdr fmode)) 1763 (progn 1764 ;; Recenter around the dragged window. 1765 (select-window window) 1766 (follow-redisplay) 1767 (select-window orig-win))))) 1768 (error nil))))) 1769 1770;;}}} 1771;;{{{ Process output 1772 1773;; The following sections installs a spy that listens to process 1774;; output and tries to reposition the windows whose buffers are in 1775;; Follow mode. We play safe as much as possible... 1776;; 1777;; When follow-mode is activated all active processes are 1778;; intercepted. All new processes that change their filter function 1779;; using `set-process-filter' are also intercepted. The reason is 1780;; that a process can cause a redisplay recentering "tail" windows. 1781;; Note that it doesn't hurt to spy on more processes than needed. 1782;; 1783;; Technically, we set the process filter to `follow-generic-filter'. 1784;; The original filter is stored in `follow-process-filter-alist'. 1785;; Our generic filter calls the original filter, or inserts the 1786;; output into the buffer, if the buffer originally didn't have an 1787;; output filter. It also makes sure that the windows connected to 1788;; the buffer are aligned. 1789;; 1790;; Discussion: How do we find processes that don't call 1791;; `set-process-filter'? (How often are processes created in a 1792;; buffer after Follow mode are activated?) 1793;; 1794;; Discussion: Should we also advice `process-filter' to make our 1795;; filter invisible to others? 1796 1797;;{{{ Advice for `set-process-filter' 1798 1799;; Do not call this with 'follow-generic-filter as the name of the 1800;; filter... 1801 1802(defadvice set-process-filter (before follow-set-process-filter activate) 1803 "Ensure process output will be displayed correctly in Follow Mode buffers. 1804 1805Follow Mode inserts its own process filter to do its 1806magic stuff before the real process filter is called." 1807 (if follow-intercept-processes 1808 (progn 1809 (setq follow-process-filter-alist 1810 (delq (assq (ad-get-arg 0) follow-process-filter-alist) 1811 follow-process-filter-alist)) 1812 (follow-tidy-process-filter-alist) 1813 (cond ((eq (ad-get-arg 1) t)) 1814 ((eq (ad-get-arg 1) nil) 1815 (ad-set-arg 1 'follow-generic-filter)) 1816 (t 1817 (setq follow-process-filter-alist 1818 (cons (cons (ad-get-arg 0) (ad-get-arg 1)) 1819 follow-process-filter-alist)) 1820 (ad-set-arg 1 'follow-generic-filter)))))) 1821 1822 1823(defun follow-call-set-process-filter (proc filter) 1824 "Call original `set-process-filter' without the Follow mode advice." 1825 (ad-disable-advice 'set-process-filter 'before 1826 'follow-set-process-filter) 1827 (ad-activate 'set-process-filter) 1828 (prog1 1829 (set-process-filter proc filter) 1830 (ad-enable-advice 'set-process-filter 'before 1831 'follow-set-process-filter) 1832 (ad-activate 'set-process-filter))) 1833 1834 1835(defadvice process-filter (after follow-process-filter activate) 1836 "Return the original process filter, not `follow-generic-filter'." 1837 (cond ((eq ad-return-value 'follow-generic-filter) 1838 (setq ad-return-value 1839 (cdr-safe (assq (ad-get-arg 0) 1840 follow-process-filter-alist)))))) 1841 1842 1843(defun follow-call-process-filter (proc) 1844 "Call original `process-filter' without the Follow mode advice." 1845 (ad-disable-advice 'process-filter 'after 1846 'follow-process-filter) 1847 (ad-activate 'process-filter) 1848 (prog1 1849 (process-filter proc) 1850 (ad-enable-advice 'process-filter 'after 1851 'follow-process-filter) 1852 (ad-activate 'process-filter))) 1853 1854 1855(defun follow-tidy-process-filter-alist () 1856 "Remove old processes from `follow-process-filter-alist'." 1857 (let ((alist follow-process-filter-alist) 1858 (ps (process-list)) 1859 (new ())) 1860 (while alist 1861 (if (and (not (memq (process-status (car (car alist))) 1862 '(exit signal closed nil))) 1863 (memq (car (car alist)) ps)) 1864 (setq new (cons (car alist) new))) 1865 (setq alist (cdr alist))) 1866 (setq follow-process-filter-alist new))) 1867 1868;;}}} 1869;;{{{ Start/stop interception of processes. 1870 1871;; Normally, all new processed are intercepted by our `set-process-filter'. 1872;; This is needed to intercept old processed that were started before we were 1873;; loaded, and processes we have forgotten by calling 1874;; `follow-stop-intercept-process-output'. 1875 1876(defun follow-intercept-process-output () 1877 "Intercept all active processes. 1878 1879This is needed so that Follow Mode can track all display events in the 1880system. (See `follow-mode')" 1881 (interactive) 1882 (let ((list (process-list))) 1883 (while list 1884 (if (eq (process-filter (car list)) 'follow-generic-filter) 1885 nil 1886 ;; The custom `set-process-filter' defined above. 1887 (set-process-filter (car list) (process-filter (car list)))) 1888 (setq list (cdr list)))) 1889 (setq follow-intercept-processes t)) 1890 1891 1892(defun follow-stop-intercept-process-output () 1893 "Stop Follow Mode from spying on processes. 1894 1895All current spypoints are removed and no new will be added. 1896 1897The effect is that Follow mode won't be able to handle buffers 1898connected to processes. 1899 1900The only reason to call this function is if the Follow mode spy filter 1901would interfere with some other package. If this happens, please 1902report this using the `report-emacs-bug' function." 1903 (interactive) 1904 (follow-tidy-process-filter-alist) 1905 (let ((list (process-list))) 1906 (while list 1907 (if (eq (process-filter (car list)) 'follow-generic-filter) 1908 (progn 1909 (follow-call-set-process-filter 1910 (car list) 1911 (cdr-safe (assq (car list) follow-process-filter-alist))) 1912 (setq follow-process-filter-alist 1913 (delq (assq (car list) follow-process-filter-alist) 1914 follow-process-filter-alist)))) 1915 (setq list (cdr list)))) 1916 (setq follow-intercept-processes nil)) 1917 1918;;}}} 1919;;{{{ The filter 1920 1921;; The following section is a naive method to make buffers with 1922;; process output to work with Follow mode. Whenever the start of the 1923;; window displaying the buffer is moved, we moves it back to its 1924;; original position and try to select a new window. (If we fail, 1925;; the normal redisplay functions of Emacs will scroll it right 1926;; back!) 1927 1928(defun follow-generic-filter (proc output) 1929 "Process output filter for process connected to buffers in Follow mode." 1930 (let* ((old-buffer (current-buffer)) 1931 (orig-win (selected-window)) 1932 (buf (process-buffer proc)) 1933 (win (and buf (if (eq buf (window-buffer orig-win)) 1934 orig-win 1935 (get-buffer-window buf t)))) 1936 (return-to-orig-win (and win (not (eq win orig-win)))) 1937 (orig-window-start (and win (window-start win)))) 1938 1939 ;; If input is pending, the `sit-for' below won't redraw the 1940 ;; display. In that case, calling `follow-avoid-tail-recenter' may 1941 ;; provoke the process hadnling code to sceduling a redisplay. 1942 ;(or (input-pending-p) 1943 ; (follow-avoid-tail-recenter)) 1944 1945 ;; Output the `output'. 1946 (let ((filter (cdr-safe (assq proc follow-process-filter-alist)))) 1947 (cond 1948 ;; Call the original filter function 1949 (filter 1950 (funcall filter proc output)) 1951 1952 ;; No filter, but we've got a buffer. Just output into it. 1953 (buf 1954 (set-buffer buf) 1955 (if (not (marker-buffer (process-mark proc))) 1956 (set-marker (process-mark proc) (point-max))) 1957 (let ((moving (= (point) (process-mark proc))) 1958 deactivate-mark 1959 (inhibit-read-only t)) 1960 (save-excursion 1961 (goto-char (process-mark proc)) 1962 ;; `insert-before-markers' just in case the users next 1963 ;; command is M-y. 1964 (insert-before-markers output) 1965 (set-marker (process-mark proc) (point))) 1966 (if moving (goto-char (process-mark proc))))))) 1967 1968 ;; If we're in follow mode, do our stuff. Select a new window and 1969 ;; redisplay. (Actually, it is redundant to check `buf', but I 1970 ;; feel it's more correct.) 1971 (if (and buf win (window-live-p win)) 1972 (progn 1973 (set-buffer buf) 1974 (if (and (boundp 'follow-mode) follow-mode) 1975 (progn 1976 (select-window win) 1977 (let* ((windows (follow-all-followers win)) 1978 (win-start-end (follow-windows-start-end windows)) 1979 (new-window-start (window-start win)) 1980 (new-window-point (window-point win))) 1981 (cond 1982 ;; The start of the selected window was repositioned. 1983 ;; Try to use the original start position and continue 1984 ;; working with a window to the "right" in the window 1985 ;; chain. This will create the effect that the output 1986 ;; starts in one window and continues into the next. 1987 1988 ;; If the display has changed so much that it is not 1989 ;; possible to keep the original window fixed and still 1990 ;; display the point then we give up and use the new 1991 ;; window start. 1992 1993 ;; This case is typically used when the process filter 1994 ;; tries to reposition the start of the window in order 1995 ;; to view the tail of the output. 1996 ((not (eq orig-window-start new-window-start)) 1997 (follow-debug-message "filter: Moved") 1998 (set-window-start win orig-window-start) 1999 (follow-redisplay windows win) 2000 (setq win-start-end (follow-windows-start-end windows)) 2001 (follow-select-if-visible new-window-point 2002 win-start-end) 2003 (goto-char new-window-point) 2004 (if (eq win (selected-window)) 2005 (set-window-start win new-window-start)) 2006 (setq win-start-end (follow-windows-start-end windows))) 2007 ;; Stick to this window, if point is visible in it. 2008 ((pos-visible-in-window-p new-window-point) 2009 (follow-debug-message "filter: Visible in window")) 2010 ;; Avoid redisplaying the first window. If the 2011 ;; point is visible at a window below, 2012 ;; redisplay and select it. 2013 ((follow-select-if-visible-from-first 2014 new-window-point windows) 2015 (follow-debug-message "filter: Seen from first") 2016 (follow-redisplay windows (car windows)) 2017 (goto-char new-window-point) 2018 (setq win-start-end 2019 (follow-windows-start-end windows))) 2020 ;; None of the above. We stick to the current window. 2021 (t 2022 (follow-debug-message "filter: nothing"))) 2023 2024 ;; Here we have slected a window. Make sure the 2025 ;; windows are aligned and the point is visible 2026 ;; in the selected window. 2027 (if (and (not (follow-pos-visible 2028 (point) (selected-window) win-start-end)) 2029 (not return-to-orig-win)) 2030 (progn 2031 (sit-for 0) 2032 (setq win-start-end 2033 (follow-windows-start-end windows)))) 2034 2035 (if (or follow-internal-force-redisplay 2036 (not (follow-windows-aligned-p win-start-end))) 2037 (follow-redisplay windows))))))) 2038 2039 ;; return to the original window. 2040 (if return-to-orig-win 2041 (select-window orig-win)) 2042 ;; Restore the orignal buffer, unless the filter explicitly 2043 ;; changed buffer or killed the old buffer. 2044 (if (and (eq buf (current-buffer)) 2045 (buffer-name old-buffer)) 2046 (set-buffer old-buffer))) 2047 2048 (follow-invalidate-cache) 2049 2050 ;; Normally, if the display has been changed, it is redrawn. All 2051 ;; windows showing only the end of a buffer is unconditionally 2052 ;; recentered, we can't prevent it by calling 2053 ;; `follow-avoid-tail-recenter'. 2054 ;; 2055 ;; By performing a redisplay on our own, Emacs need not perform 2056 ;; the above described redisplay. (However, bu performing it when 2057 ;; there are input available just seems to make things worse.) 2058 (if (and follow-avoid-tail-recenter-p 2059 (not (input-pending-p))) 2060 (sit-for 0))) 2061 2062;;}}} 2063 2064;;}}} 2065;;{{{ Window size change 2066 2067;; In Emacs 19.29, the functions in `window-size-change-functions' are 2068;; called every time a window in a frame changes size. Most notably, it 2069;; is called after the frame has been resized. 2070;; 2071;; We basically call our post-command-hook for every buffer that is 2072;; visible in any window in the resized frame, which is in follow-mode. 2073;; 2074;; Since this function can be called indirectly from 2075;; `follow-post-command-hook' we have a potential infinite loop. We 2076;; handle this problem by simply not doing anything at all in this 2077;; situation. The variable `follow-inside-post-command-hook' contains 2078;; information about whether the execution actually is inside the 2079;; post-command-hook or not. 2080 2081(if (boundp 'window-size-change-functions) 2082 (add-hook 'window-size-change-functions 'follow-window-size-change)) 2083 2084 2085(defun follow-window-size-change (frame) 2086 "Redraw all windows in FRAME, when in Follow mode." 2087 ;; Below, we call `post-command-hook'. This makes sure that we 2088 ;; doesn't start a mutally recursive endless loop. 2089 (if follow-inside-post-command-hook 2090 nil 2091 (let ((buffers '()) 2092 (orig-window (selected-window)) 2093 (orig-buffer (current-buffer)) 2094 (orig-frame (selected-frame)) 2095 windows 2096 buf) 2097 (select-frame frame) 2098 (unwind-protect 2099 (walk-windows 2100 (function 2101 (lambda (win) 2102 (setq buf (window-buffer win)) 2103 (if (memq buf buffers) 2104 nil 2105 (set-buffer buf) 2106 (if (and (boundp 'follow-mode) 2107 follow-mode) 2108 (progn 2109 (setq windows (follow-all-followers win)) 2110 (if (memq orig-window windows) 2111 (progn 2112 ;; Make sure we're redrawing around the 2113 ;; selected window. 2114 ;; 2115 ;; We must be really careful not to do this 2116 ;; when we are (indirectly) called by 2117 ;; `post-command-hook'. 2118 (select-window orig-window) 2119 (follow-post-command-hook) 2120 (setq orig-window (selected-window))) 2121 (follow-redisplay windows win)) 2122 (setq buffers (cons buf buffers)))))))) 2123 (select-frame orig-frame) 2124 (set-buffer orig-buffer) 2125 (select-window orig-window))))) 2126 2127;;}}} 2128 2129;;{{{ XEmacs isearch 2130 2131;; In XEmacs, isearch often finds matches in other windows than the 2132;; currently selected. However, when exiting the old window 2133;; configuration is restored, with the exception of the beginning of 2134;; the start of the window for the selected window. This is not much 2135;; help for us. 2136;; 2137;; We overwrite the stored window configuration with the current, 2138;; unless we are in `slow-search-mode', i.e. only a few lines 2139;; of text is visible. 2140 2141(if (featurep 'xemacs) 2142 (defadvice isearch-done (before follow-isearch-done activate) 2143 (if (and (boundp 'follow-mode) 2144 follow-mode 2145 (boundp 'isearch-window-configuration) 2146 isearch-window-configuration 2147 (boundp 'isearch-slow-terminal-mode) 2148 (not isearch-slow-terminal-mode)) 2149 (let ((buf (current-buffer))) 2150 (setq isearch-window-configuration 2151 (current-window-configuration)) 2152 (set-buffer buf))))) 2153 2154;;}}} 2155;;{{{ Tail window handling 2156 2157;; In Emacs (not XEmacs) windows showing nothing are sometimes 2158;; recentered. When in Follow Mode, this is not desireable for 2159;; non-first windows in the window chain. This section tries to 2160;; make the windows stay where they should be. 2161;; 2162;; If the display is updated, all windows starting at (point-max) are 2163;; going to be recentered at the next redisplay, unless we do a 2164;; read-and-write cycle to update the `force' flag inside the windows. 2165;; 2166;; In 19.30, a new varible `window-scroll-functions' is called every 2167;; time a window is recentered. It is not perfect for our situation, 2168;; since when it is called for a tail window, it is to late. However, 2169;; if it is called for another window, we can try to update our 2170;; windows. 2171;; 2172;; By patching `sit-for' we can make sure that to catch all explicit 2173;; updates initiated by lisp programs. Internal calls, on the other 2174;; hand, are not handled. 2175;; 2176;; Please note that the function `follow-avoid-tail-recenter' is also 2177;; called from other places, e.g. `post-command-hook' and 2178;; `post-command-idle-hook'. 2179 2180;; If this function is called it is too late for this window, but 2181;; we might save other windows from being recentered. 2182 2183(if (and follow-avoid-tail-recenter-p (boundp 'window-scroll-functions)) 2184 (add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t)) 2185 2186 2187;; This prevents all packages that calls `sit-for' directly 2188;; to recenter tail windows. 2189 2190(if follow-avoid-tail-recenter-p 2191 (defadvice sit-for (before follow-sit-for activate) 2192 "Adviced by Follow Mode. 2193 2194Avoid to recenter windows displaying only the end of a file as when 2195displaying a short file in two windows, using Follow Mode." 2196 (follow-avoid-tail-recenter))) 2197 2198 2199;; Without this advice, `mouse-drag-region' would start to recenter 2200;; tail windows. 2201 2202(if (and follow-avoid-tail-recenter-p 2203 (fboundp 'move-overlay)) 2204 (defadvice move-overlay (before follow-move-overlay activate) 2205 "Adviced by Follow Mode. 2206Don't recenter windows showing only the end of a buffer. 2207This prevents `mouse-drag-region' from messing things up." 2208 (follow-avoid-tail-recenter))) 2209 2210;;}}} 2211;;{{{ profile support 2212 2213;; The following (non-evaluated) section can be used to 2214;; profile this package using `elp'. 2215;; 2216;; Invalid indentation on purpose! 2217 2218(cond (nil 2219(setq elp-function-list 2220 '(window-end 2221 vertical-motion 2222 ; sit-for ;; elp can't handle advices... 2223 follow-mode 2224 follow-all-followers 2225 follow-split-followers 2226 follow-redisplay 2227 follow-downward 2228 follow-calculate-first-window-start 2229 follow-estimate-first-window-start 2230 follow-calculate-first-window-start-from-above 2231 follow-calculate-first-window-start-from-below 2232 follow-calc-win-end 2233 follow-calc-win-start 2234 follow-pos-visible 2235 follow-windows-start-end 2236 follow-cache-valid-p 2237 follow-select-if-visible 2238 follow-select-if-visible-from-first 2239 follow-windows-aligned-p 2240 follow-point-visible-all-windows-p 2241 follow-avoid-tail-recenter 2242 follow-update-window-start 2243 follow-post-command-hook 2244 )))) 2245 2246;;}}} 2247 2248;;{{{ The end 2249 2250;; 2251;; We're done! 2252;; 2253 2254(provide 'follow) 2255 2256;;}}} 2257 2258;; /------------------------------------------------------------------------\ 2259;; | "I [..] am rarely happier then when spending an entire day programming | 2260;; | my computer to perform automatically a task that it would otherwise | 2261;; | take me a good ten seconds to do by hand. Ten seconds, I tell myself, | 2262;; | is ten seconds. Time is valuable and ten seconds' worth of it is well | 2263;; | worth the investment of a day's happy activity working out a way to | 2264;; | save it". -- Douglas Adams, "Last Chance to See" | 2265;; \------------------------------------------------------------------------/ 2266 2267;; arch-tag: 7b16bb1a-808c-4991-a8cc-66d3822936d0 2268;;; follow.el ends here 2269