Update for ewm with experimental support for GUD and ediff.
See also: http://lists.gnu.org/archive/html/gnu-emacs-sources/2010-05/msg00026.html
;; ------------------------------------------------------------- ;; ewm.el - emacs window manager ;; ;; An experimental proof-of-concept for an emacs window manager ;; with strict content->window association ;; ;; Copyright (C) 2010 grischka ;; ;; ;; This file is free software: you can redistribute it and/or modify it under ;; the terms of the GNU General Public License as published by the Free ;; Software Foundation, either version 2 of the License, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ;; more details. ;; v 0.1 published Thu, 20 May 2010 01:02:57 +0200 at: ;; http://lists.gnu.org/archive/html/gnu-emacs-sources/2010-05/msg00026.html ;; v 0.2 published Mo, 06 Dec 2010 ;; Quick Start: ;; ------------ ;; Visit ewm.el and type 'M-x eval-buffer RET' ;; ;; ;; Key Summary: ;; ------------ ;; Ctrl-1 - toggle sidebar-1 ;; Ctrl-2 - toggle sidebar-2 ;; Ctrl-3 - toggle sidebar-3 ;; Ctrl-4 - toggle compile window ;; Ctrl-5 - toggle top left window ;; Ctrl-6 - toggle top right window ;; Ctrl-7 - toggle primary edit window ;; Ctrl-8 - toggle secondary edit window ;; Ctrl-9 - toggle info window ;; ;; Ctrl-0 - refresh layout ;; Ctrl-F4 - kill active buffer ;; ;; F1 - info ;; F2 - switch window ;; F3 - switch frame ;; F7 - save and reload ewm.el ;; F9 - compilation ;; ;; ;; Example layout: ;; --------------- ;; The example layout (below) and bindings are ment to give the ;; following behaviour: ;; ;; A speedbar is created in the sidebar ;; ;; "*compilation*" is shown in a full-width bottom pane ;; "*Help* and "Buffer List" are shown in top panes ;; ;; "*info*" and "*grep*" are shown on a second frame ;; "*About GNU Emacs*" is shown on a third frame ;; ;; Other buffers are shown in the 'edit' window on the main frame ;; ;; ;; How it works: ;; ------------- ;; ewm provide two main interfaces: ;; - ewm-display-buffer ;; - ewm-hide-buffer ;; ;; emacs own functions such as 'display-buffer', 'switch-to-window' ;; 'kill/bury-buffer' are advised to use the two interfaces above. ;; ;; When asked to display some buffer, ewm looks for a suitable window ;; in 'ewm-window-bindings. This window is then marked as active ;; and the buffer is added on top of the window's buffer stack. ;; ;; To hide some buffer, ewm removes ot from the window's buffer ;; stack. If the stack is empty, the window is marked not active. ;; ;; After any change, an emacs window-tree with the active windows ;; is built accordingly to the layout in 'ewm-window-layout'. ;; Frames are created if necessary. Frames without active windows ;; are deleted. ;; ;; ewm is based on the thought that on a shared, tiled screen only ;; a central instance is able to coordinate the display for packages ;; and features and to decide where to display what. ;; ;; ewm is experimental because any calls to the emacs window ;; interface ('split-window', 'set-window-buffer', etc). from ;; code other than ewm itself is contraproductive to the concept. ;; Not implemented: ;; ---------------- ;; Manual resize or layout changes by the user are not recorded. ;; ------------------------------------------------------------- ;; Layout definition - Welcome to play with this. (defun ewm-set-layout () ;; the layout tree (frames and windows) -- sizes in percent (setq ewm-window-layout '( (frame-1 (div-rows (25 div-cols (40 top-1) (30 top-2) (30 top-3) ) (55 div-cols (25 div-rows (40 side-1) (30 side-2) (30 side-3) ) (75 div-cols (50 div-rows (50 edit-1) (50 edit-2) ) (50 edit-3) ) ) (20 div-cols (50 tools) (50 tools-2) ) )) (frame-2 (info) ) (frame-3 (about) ) )) ;; frame layout (setq ewm-frame-layout '( (frame-1 :params ((left . 10) (top . 26) (width . 90) (height . 64)) :default t ) (frame-2 :params ((left . 710) (top . 26) (width . 66) (height . 40)) ) (frame-3 :params ((left . 250) (top . 150) (width . 72) (height . 42)) ) )) ;; buffer to window association (setq ewm-window-bindings (copy-tree '( (side-1 :classes ("*side-1*" "*SPEEDBAR*" "^\\*registers of .*") ) (side-2 :classes ("*side-2*" " *undo-tree*" "^\\*locals of .*") ;; -> http://www.emacswiki.org/emacs/UndoTree ) (side-3 :classes ("*side-3*" "^\\*threads of .*") ) (edit-1 ;; primary general purpose window :classes ("*scratch*" * ediff-A) ) (edit-2 ;; secondary general purpose window :classes ("*scratch*" * "!Dired" "^\\*magit" "^\\*disassembly of .*" ediff-B) ;; strings starting witn ! are matched against buffer's mode-name ) (edit-3 ;; vertical secondary general purpose window :classes ("*scratch*") ) (top-1 :classes ("*eproject*" "!Debugger") ;; -> http://www.emacswiki.org/emacs/eproject ) (top-2 :classes ("*Help*" "*Buffer List*" "^\\*breakpoints of .*") ) (top-3 :classes ("*top-3" "!Inferior I/O") ) (tools :classes ("*compilation*" "*Completions*" "*Messages*" "^\\*stack frames of .*") ) (tools-2 :classes ("*tools-2*" "^\\*memory of .*" ediff-H) ) (info :classes ("*info*" "*grep*") ) (about :classes ("*GNU Emacs*" "*About GNU Emacs*" "^.*THE-GNU-PROJECT") ;; strings starting witn ^ are matched as regular expression ) ))) ) ;; "Debugger" "Locals: " "Inferior I/O" "Frames" "Breakpoints" ;; "Registers" "Disassembly: " "Memory" "Threads" (defgroup ewm-group '() "ewm custom stuff") (defcustom ewm-stuff '((a . 1) (b . 2)(c . 3)) "stuff in ewm" :group 'ewm-group ) (defun ewm-csa () (let ((new-value (cons '(x . 0) ewm-stuff))) (customize-save-variable 'ewm-stuff new-value) )) ;; ------------------------------------------------------------- ;; bind some keys for testing (dolist (k '( ;; toggle some windows ([?\C-1] (ewm-toggle 'side-1)) ([?\C-2] (ewm-toggle 'side-2)) ([?\C-3] (ewm-toggle 'side-3)) ([?\C-4] (ewm-toggle 'tools)) ([?\C-5] (ewm-toggle 'top-1)) ([?\C-6] (ewm-toggle 'top-2)) ([?\C-7] (ewm-toggle 'edit-1)) ([?\C-8] (ewm-toggle 'edit-2)) ([?\C-9] (ewm-toggle 'info)) ;; for convenience and demonstration ([?\C-0] (ewm-update)) ;; refresh layout ([C-f4] (kill-buffer nil)) ;; kill active buffer ([f7] (ewm-reload)) ;; save and reload this file ([f1] (info "(elisp)Top")) ;; show info ([f9] (compile "echo \"hello world\"")) ;; show compilation ;; switch focus ([f2] (other-window 1)) ;; focus next window ([S-f2] (other-window -1)) ;; focus previous window ([f3] (other-frame 1)) ;; focus next frame ([S-f3] (other-frame -1)) ;; focus previous frame )) (global-set-key (car k) `(lambda () (interactive) ,@(cdr k))) ) ;; ------------------------------------------------------------- ;; ------------------------------------------------------------- ;; implementation ;; make div or window (defun ewm-make-elem (w e c) (let (a b) (cond ((eq (car e) 'div-cols) (ewm-make-div w t (cdr e) c) ) ((eq (car e) 'div-rows) (ewm-make-div w nil (cdr e) c) ) ((setq a (assoc (car e) c)) (push (cons (car a) w) ewm-window-handles) (setq b (ewm-get-buffer (cdr a) t)) (set-window-buffer w b) )))) ;; check if element is activated (defun ewm-elem-active (e c) (let (a) (cond ((memq (car e) '(div-rows div-cols)) (while (and (setq e (cdr e)) (null (ewm-elem-active (cdar e) c)) )) (consp e) ) ((setq a (assoc (car e) c)) (plist-get (cdr a) :active) )))) ;; make all elements in a div (defun ewm-make-div (w h l c) (let (size w2 l2 e (ref 100) (n 0) (x 0) (s 0)) (dolist (e l) (if (eq (car e) '*) (setq x (1+ x)) (setq n (+ n (car e))) )) (when (> x 0) (setq s (/ (- ref n) x)) ) (setq ref (+ n (* x s))) (dolist (e l) (setq x (if (eq (car e) '*) s (car e))) (if (ewm-elem-active (cdr e) c) (push (cons x (cdr e)) l2) (setq ref (- ref x)) )) (setq l2 (nreverse l2)) (setq size (if h (window-width w) (window-height w))) (while (and l2 w) (setq e (car l2)) (when (setq l2 (cdr l2)) (setq x (/ (+ (* (car e) size) (/ ref 2)) ref)) (setq w2 (split-window w x h)) ) (ewm-make-elem w (cdr e) c) (setq w w2) ))) ;; generate entire layout (defun ewm-make-layout (layout bindings) (let (f h p a d bl (ewm-making-layout t)) (setq bl (buffer-list)) (setq ewm-window-handles nil) (dolist (e layout) (when (setq f (cdr (assoc (car e) ewm-frame-layout))) (unless (setq a (assoc (car e) ewm-frame-handles)) (push (setq a (list (car e))) ewm-frame-handles) ) (setq d (plist-get f :default)) (setq p (cons '(user-position . t) (plist-get f :params))) (setq h (cdr a)) (setq e (cadr e)) (and d (null (frame-live-p h)) (setq h (car (last (frame-list)))) ) (cond ((or d (ewm-elem-active e bindings)) (cond ((frame-live-p h) (when ewm-init-frames (modify-frame-parameters h p) (sit-for 0.1) )) (t (setq h (make-frame p)) (sit-for 0.1) )) (let ((w (frame-first-window h))) ;; delete all windows (set-window-dedicated-p w nil) (delete-other-windows w) (setq w (split-window w nil t)) (delete-other-windows w) ;; recreate window structure (ewm-make-elem w e bindings) )) ((frame-live-p h) (delete-frame h) (setq h nil) )) (setcdr a h) )) ;; restore original buffer order (while bl (bury-buffer (pop bl))) (setq ewm-init-frames nil) )) ;; ------------------------------------------------------------- ;; functions for buffer-to-window association ;; get window id from elisp 'window' (defun ewm-window-id (w) (car (rassoc w ewm-window-handles)) ) ;; get elisp window from window-id (defun ewm-get-window (i) (cdr (assoc i ewm-window-handles)) ) (defun ewm-get-binding (i) (cdr (assoc i ewm-window-bindings)) ) (defun ewm-put (i p v) (let ((e (ewm-get-binding i))) (and e (plist-put e p v)) )) (defun ewm-get (i p) (let ((e (ewm-get-binding i))) (and e (plist-get e p)) )) ;; remove buffer from the window's buffer-stack (defun ewm-del-buffer (e b) (let ((bl (plist-get e :buffers))) (plist-put e :buffers (delete b bl)) )) ;; add buffer on top of the window's buffer-stack (defun ewm-add-buffer (e b) (let ((bl (plist-get e :buffers))) (plist-put e :buffers (cons b (delete b bl))) bl )) ;; cleanup and return buffer-stack (defun ewm-buffer-list (e prop) (let (l) (dolist (b (plist-get e prop)) (when (buffer-live-p b) (push b l) )) (setq l (nreverse l)) (plist-put e prop l) l )) ;; get buffer to display in a window (defun ewm-get-buffer (e force) (let ((bl (ewm-buffer-list e :buffers))) (when (and (null bl) force) (setq bl (list (or (car (ewm-buffer-list e :buffers-old)) (get-buffer-create (or (car (plist-get e :classes)) "*scratch*") )))) (plist-put e :buffers bl) ) (car bl) )) ;; select window and the frame where it's on (defun ewm-select-window (w) (when (window-live-p w) (let (focus-follows-mouse) (select-window w) (select-frame-set-input-focus (window-frame w)) w ))) (defun ewm-get-window-start () (mapcar (lambda (e) (cons (car e) (window-start (cdr e)))) ewm-window-handles)) (defun ewm-set-window-start (l) (dolist (x l) ((lambda (a) (and a (set-window-start (cdr a) (cdr x)))) (assoc (car x) ewm-window-handles) ))) ;; update layout and focus window 'i' or the previously focussed (defun ewm-update (&optional i) (let ((w0 (selected-window)) i0 p) (setq i0 (or i (ewm-window-id w0))) ;;(setq p (ewm-get-window-start)) (ewm-make-layout ewm-window-layout ewm-window-bindings) ;;(ewm-set-window-start p) (ewm-select-window (or (ewm-get-window i0) w0)) )) ;; ------------------------------------------------------------- ;; show/hide/toggle window (defun ewm-show-window (i sel) (ewm-put i :active t) (ewm-update (and sel i)) ) ;; mark window as inactive and clear its buffer-stack (but keep a copy) (defun ewm-hide-window (i) (let ((e (ewm-get-binding i)) b1 b2 sel) (when e (setq b1 (ewm-buffer-list e :buffers)) (setq b2 (ewm-buffer-list e :buffers-old)) (dolist (b (reverse b1)) (setq b2 (cons b (delete b b2)))) (plist-put e :buffers-old b2) ) (ewm-put i :buffers nil) (ewm-put i :active nil) (setq ewm-focus-stack (delete i ewm-focus-stack)) (if (eq (selected-window) (ewm-get-window i)) (setq sel (or (car ewm-focus-stack) 'edit-1)) ) (ewm-update sel) )) (defun ewm-toggle (i &optional a) (if (if a (> a 0) (not (ewm-get i :active))) (ewm-show-window i nil) (ewm-hide-window i) ) ) ;; ------------------------------------------------------------- ;; figure out the preferred window to display buffer (defun ewm-get-buffer-class (buffer class) (let ((wb ewm-window-bindings) (name (buffer-name buffer)) (mode (with-current-buffer buffer mode-name)) a c e r d ) (when (consp mode) (setq mode (symbol-name (car mode))) ) (while (and wb (null r)) (setq a (pop wb)) (setq c (plist-get (cdr a) :classes)) (while (and c (null r)) (setq e (pop c)) (when (cond ((eq e '*) (progn (push a d) nil) ) ((symbolp e) (eq e class) ) ((equal (substring e 0 1) "^") (string-match e name) ) ((equal (substring e 0 1) "!") (string-match (substring e 1) mode) ) (t (equal e name) )) (setq r a) ))) (or r (ewm-get-buffer-class-default buffer (nreverse d))) )) ;; emulate (very) basic "other-window" logic (defun ewm-get-buffer-class-default (buffer d) (cond ((cdr d) (let ((d1 (car d)) (d2 (car (cdr d))) (w (selected-window)) w1 w2 ) (setq w1 (ewm-get-window (car d1))) (setq w2 (ewm-get-window (car d2))) ;;(b1 (plist-get (cdr d1) :buffers)) ;;(b2 (plist-get (cdr d2) :buffers)) (cond (ewm-other-window (if (eq w w1) d2 d1) ) (t (if (eq w w2) d2 d1) ))) ) (t (car d) ))) ;; return binding if any where buffer 'b' is currently displayed (defun ewm-lookup-buffer (b) (let ((l ewm-window-bindings) bl c e) (while (and l (null c)) (setq e (car l) l (cdr l)) (when (plist-get (cdr e) :active) (setq bl (plist-get (cdr e) :buffers)) (when (eq (car bl) b) (setq c e) ))) c)) ;; ------------------------------------------------------------- ;; ewm interface : display & hide buffer (defun ewm-display-buffer (b &optional sel lbl) (let (c e i w (ewm-making-layout t)) (setq b (get-buffer-create (or b (current-buffer)))) ;;(message "ewm-display-buffer %s" b) (unless lbl (setq c (ewm-lookup-buffer b)) ) (unless c (setq c (ewm-get-buffer-class b lbl)) ) (when c (setq i (car c)) (setq e (cdr c)) (ewm-add-buffer e b) (setq w (ewm-get-window i)) (cond ((and (plist-get e :active) (window-live-p w)) (set-window-buffer w b) (if sel (ewm-select-window w)) w ) (t (ewm-show-window i sel) (ewm-get-window i) ))))) (defun ewm-hide-buffer (w b) (let (i e (ewm-making-layout t)) (setq i (ewm-window-id w)) (setq e (ewm-get-binding i)) (when e (ewm-del-buffer e b) ;; get previously displayed buffer (setq b (ewm-get-buffer e nil)) (cond (b (set-window-buffer w b) ) (t (ewm-hide-window i) ))))) (defun ewm-display-multi-buffers (bsl) (mapcar 'ewm-get-window (mapcar (lambda (b) (and (car b) (ewm-window-id (ewm-display-buffer (car b) nil (cdr b)) ))) bsl))) ;; ------------------------------------------------------------- ;; focus-change-hook replacement (defun ewm-post-command-hook () (unless ewm-making-layout (let ((w1 (selected-window)) i1 i2) (setq i1 (ewm-window-id w1)) (setq i2 ewm-focus) (when (and i1 (null (eq i1 i2))) (unless (window-live-p (ewm-get-window i2)) (setq ewm-focus-stack (delete i2 ewm-focus-stack)) ) (setq ewm-focus-stack (cons i1 (delete i1 ewm-focus-stack))) (setq ewm-focus i1) ;;(message "focus: %s -> %s" i2 i1) ) ))) ;; ------------------------------------------------------------- ;; redirect emacs core functions to the ewm interface (defun ewm-advise (f) (when (fboundp 'ad-unadvise) (ad-unadvise 'display-buffer) (ad-unadvise 'switch-to-buffer) (ad-unadvise 'switch-to-buffer-other-window) (ad-unadvise 'pop-to-buffer) (ad-unadvise 'kill-buffer) (ad-unadvise 'bury-buffer) (ad-unadvise 'delete-window) (ad-unadvise 'delete-frame) (ad-unadvise 'set-window-buffer) (ad-unadvise 'split-window) (ad-unadvise 'set-window-dedicated-p) (when (boundp 'display-buffer-regexps) (ad-unadvise 'switch-to-prev-buffer) (ad-unadvise 'replace-buffer-in-windows) ) (remove-hook 'post-command-hook 'ewm-post-command-hook) (setq ewm-focus-stack nil) (setq display-buffer-function nil) ) (when f (add-hook 'post-command-hook 'ewm-post-command-hook) (defadvice set-window-dedicated-p (around ewm activate) (if ewm-making-layout ad-do-it (let ((w (or (ad-get-arg 0) (selected-window))) (f (ad-get-arg 1)) ) (unless f ad-do-it) (setq ad-return-value f) ))) (defadvice set-window-buffer (around ewm activate) (if ewm-making-layout ad-do-it (let ((w (or (ad-get-arg 0) (selected-window))) (b (ad-get-arg 1)) ) ;;(message "set") (ewm-display-buffer b) (setq ad-return-value nil) ))) (defadvice split-window (around ewm activate) (if ewm-making-layout ad-do-it (let (w) ;;(message "split") (setq w (frame-first-window (car (last (frame-list))))) (setq ad-return-value w) ))) ;; (setq display-buffer-function ;; (lambda (b &optional ntw f) ;; (let ((ewm-other-window ntw)) ;; ;;(message "display") ;; (ewm-display-buffer b) ;; ))) (defadvice display-buffer (around ewm activate) (let ((b (ad-get-arg 0)) (ewm-other-window (ad-get-arg 1)) w) ;;(message "display") (setq w (ewm-display-buffer b)) (setq ad-return-value w) )) (when (boundp 'display-buffer-regexps) (defadvice switch-to-prev-buffer (around ewm activate) (let ((w (or (ad-get-arg 0) (selected-window))) b) (setq b (window-buffer w)) (ewm-hide-buffer w b) )) (defadvice replace-buffer-in-windows (around ewm activate)) ) (defadvice switch-to-buffer (around ewm activate) (let ((b (or (ad-get-arg 0) (other-buffer))) w) ;;(message "switch") (unless ewm-ignore (setq w (ewm-display-buffer b t)) ;;ad-do-it (setq b (set-buffer (window-buffer w))) (setq ad-return-value b) ))) (defadvice switch-to-buffer-other-window (around ewm activate) (let ((ewm-other-window t) b) (setq b (switch-to-buffer (ad-get-arg 0) (ad-get-arg 1))) (setq ad-return-value b) )) (defadvice pop-to-buffer (around ewm activate) (let ((b (or (ad-get-arg 0) (other-buffer)))) ;;(message "pop") (ewm-display-buffer b t) (ad-set-arg 1 nil) ;;ad-do-it (setq b (set-buffer b)) (setq ad-return-value b) )) (defadvice kill-buffer (around ewm activate) (let ((b (or (ad-get-arg 0) (current-buffer))) (ewm-ignore t) w) ;;(message "kill") (setq w (get-buffer-window b)) ad-do-it (ewm-hide-buffer w b) )) (defadvice bury-buffer (around ewm activate) (if ewm-making-layout ad-do-it (let ((a (ad-get-arg 0)) (b (current-buffer)) (ewm-ignore t) w) ;;(message "bury") (setq w (get-buffer-window b)) ad-do-it (unless a (ewm-hide-buffer w b)) ))) (defadvice delete-window (around ewm activate) (if ewm-making-layout ad-do-it (let ((w (or (ad-get-arg 0) (selected-window)))) ad-do-it (ewm-hide-window (ewm-window-id w)) ))) (defadvice delete-frame (around ewm activate) (if ewm-making-layout ad-do-it (let ((f (or (ad-get-arg 0) (selected-frame))) wl) (when (frame-live-p f) (setq wl (window-list f)) ad-do-it (dolist (w wl) (ewm-hide-window (ewm-window-id w))) )))) )) ;; ------------------------------------------------------------- ;; speedbar integration (defun ewm-speedbar () (require 'speedbar) ;; speedbar would not update if it thinks that it's ;; on the same frame (defadvice speedbar-timer-fn (around ewm activate) (defadvice selected-frame (around ewm activate) nil) (defadvice select-frame (around ewm activate) nil) ad-do-it (ad-unadvise 'select-frame) (ad-unadvise 'selected-frame) ) (setq speedbar-frame (selected-frame)) (with-current-buffer (setq speedbar-buffer (get-buffer-create "*SPEEDBAR*")) (speedbar-mode) ) (speedbar-frame-mode 1) (buffer-disable-undo speedbar-buffer) (display-buffer speedbar-buffer) ) ;; ------------------------------------------------------------- ;; ediff hook (setq ediff-window-setup-function (lambda (A B C H) ;; buffers (apply (lambda (WA WB WC WH) ;; windows (ediff-with-current-buffer control-buffer (setq ediff-window-A WA ediff-window-B WB ediff-window-C WC ) (when ediff-windows-job (set-window-start WA (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'A ediff-narrow-bounds))) (set-window-start WB (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'B ediff-narrow-bounds))) )) (ewm-select-window WH) (ediff-setup-control-buffer control-buffer) ) (ewm-display-multi-buffers `((,A . ediff-A) (,B . ediff-B) (,C . ediff-C) (,H . ediff-H) )) ))) ;; ------------------------------------------------------------- ;; initialization ;; global variables (defvar ewm-frame-layout nil) (defvar ewm-window-layout nil) (defvar ewm-window-bindings nil) (defvar ewm-window-handles nil) (defvar ewm-frame-handles nil) (defvar ewm-init-frames nil) (defvar ewm-making-layout nil) (defvar ewm-focus nil) (defvar ewm-ignore nil) (defvar ewm-focus-stack nil) (defvar ewm-other-window nil) (defun ewm-init () (let ((b (current-buffer))) (ewm-set-layout) (ewm-advise t) (ewm-speedbar) (switch-to-buffer b) )) (defun ewm-exit () (setq speedbar-frame nil) (kill-buffer speedbar-buffer) (ewm-advise nil) ) (defun ewm-reload () (let ((f (symbol-file 'ewm-reload)) b) (if (setq b (get-file-buffer f)) (with-current-buffer b (save-buffer 0)) ) (load-file f) )) ;; ------------------------------------------------------------- ;; start the thing (provide 'ewm) (ewm-init)
_______________________________________________ gnu-emacs-sources mailing list [email protected] http://lists.gnu.org/mailman/listinfo/gnu-emacs-sources
