pfeiffer@deva.irit.fr (Daniel Pfeiffer) (04/25/91)
Anta^u iaj tagoj mi sendis Some days back, I sent a program for programeron por dulingva tajpado bilingual editing in Emacs. I have kiel en ^ci artikolo sub Emacs. Mi since received some ideas and ricevis iajn ideojn kaj demandojn de questions from you, netters. Along vi, retanoj. Kune kun miaj spertoj with my experiences, this has uzante ^gin, tio permesis al mi ^gin allowed me to improve it quite a plibonigi multe. Pluraj bufroj nun bit. Multiple buffers are no longer ne plu estas probleme, kaj ekzistas a problem, and there are now various pliaj helpoj por uzi ^gin. Du uloj aids available. Two guys asked me demandis al mi kial mi ^gin celis al why this was bilingual oriented dulingva tajpado, ^cu estus problemo since it seemed to work for any two ^generale tajpi dukolumne? Fakte column job. In fact, it does, so I ne, ^gi ta^ugas por ^cia dukolumna renamed it appropriately: laboro, do mi renomis ^gin tiel: two-column.el Amuzu vin kun ^gi kaj signalu iaj Play with it, and let me know about ideoj a^u plendoj al mi! any ideas or complaints! -- -- Daniel Pfeiffer <pfeiffer@cix.cict.fr> -- Tolosa (Toulouse), Midi-Pyrenees, Europe <pfeiffer@irit.fr> -- "Beware - polyglot esperantist" <pfeiffer@frcict81.bitnet> -- N _---_ / \ NEWS, it goes around the world. W (-------) E (sorry, my bitmap doesn't have a world-class resolution) \_ _/ --- S --8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<--- ; Esperanto: English: ; Minora modalo por samtempa dukolumna Minor mode for simultaneous ; tajpado two-column editing ; Daniel Pfeiffer <pfeiffer@cix.cict.fr, @irit.fr>, 1991-04-25 ; Copyright (C) 1991 Free Software Foundation, Inc. ; ^Ci dataro estas ero de GNU Emacs. This file is part of GNU Emacs. ; GNU Emacs estas disdonata en la GNU Emacs is distributed in the hope ; espero ke ^gi estos utila, sed SEN that it will be useful, but WITHOUT ; IA GARANTIO. Neniu a^utoro a^u ANY WARRANTY. No author or ; disdonanto akceptas respondecon al distributor accepts responsibility ; iu ajn por la sekvoj de ^gia uzado, to anyone for the consequences of ; a^u ^cu ^gi serveblas al iu celo, using it or for whether it serves ; a^u e^c entute funkcias, se li ni any particular purpose or works at ; estas skribinta tion. Vidu la GNU all, unless he says so in writing. ; Emacs ^Generala Publika Licenco por Refer to the GNU Emacs General ; plenaj detaloj. Public License for full details. ; ^Ciu rajtas kopii, modifi kaj ree Everyone is granted permission to ; disdoni GNU Emacs, sed nur sub la copy, modify and redistribute GNU ; condi^coj priskribitaj en la GNU Emacs, but only under the conditions ; Emacs ^Generala Publika Licenco. described in the GNU Emacs General ; Kopio de tiu licenso estas supozata Public License. A copy of this ; donita al vi kune kun GNU Emacs, por license is supposed to have been ; ke vi sciu viajn rajtojn kaj given to you along with GNU Emacs so ; respondecojn. ^Gi devus esti en you can know your rights and ; dataro nomata COPYING. Inter responsibilities. It should be in a ; alia^joj, la notico pri kopirajto file named COPYING. Among other ; kaj ^ci notico devas esti gardata things, the copyright notice and ; sur ^ciuj kopioj. this notice must be preserved on all ; copies. ; Tiu minora modalo ebligas al vi This minor mode allows you to ; tajpi sendepende en du apudaj independently edit two adjacent ; bufroj. Vi havas tri eblecojn por buffers. You have three ways to ; eki ^gin. ^Ciu donas al vi start it up. Each gives you a ; horizontale disigatan fenestron, horizontally split window similar to ; simila al fina apareco de via the final outcome of your text: ; teksto: ; C-x 6 2 associates a new buffer called ; C-x 6 2 asocias novan bufron nomatan the same, but with 2C/ prepen- ; same, sed kun 2C/ anta^u. ded. ; C-x 6 b asocias alian bufron. Vi C-x 6 b associates another buffer. ; povas anka^u asocii dataron, This can be used to associate ; se vi ^jus anta^ue faris a file if you just did ; C-x C-f. C-x C-f. ; C-x 6 u disigas jam dukolumnan C-x 6 u unmerges a two-column text ; tekston en du bufroj ekde la into two buffers from the ; nuna linio, kaj je la nuna current line and at the ; kolumno. La anta^ua signo current column. The preceding ; (ofte tabeligilon a^u |) estas character (often tab or |) is ; la kolumna disiganto. Linioj the column separator. Lines ; kiuj ne enhavas ^gin ne estas that don't have it won't be ; disigitaj. Kiel la kvara kaj separated. Like the fourth ; la kvina linio se vi disigas and fifth line if you unmerge ; ^ci dataron ekde la unua angla this file from the first ; vorto. english word. ; Prefiksa argumento al iu de la tri A prefix argument to any of these ; komandoj signifas ke Emacs metu la three commands means that Emacs put ; nunan bufron en la duan kolumnon, the current buffer into the second ; an^stata^u en la unuan. column, instead of into the first. ; Je ^cia flanko estas bufro, kiu On each side is a buffer that knows ; konas la alian. Kun la ordonoj C-x about the other. With the commands ; 6 SPC, C-x 6 DEL kaj C-x 6 RET oni C-x 6 SPC, C-x 6 DEL and C-x 6 RET ; povas suben- a^u supreniri unu you can simultaneously scroll up or ; ekranon, kaj subeniri linion, down by a screenfull and by a line ; samtempe en la du bufroj. Per C-x 6 in both buffers. With C-x 6 C-l you ; C-l vi povas recentrigi la linion. can recenter the line. When you ; Kiam vi nur plu havas unu el la du only have one of the two buffers ; bufroj surekrane vi revidos la alian onscreen you can get the other back ; per denove C-x 6 2. with C-x 6 2 once more. ; Kiu bufro estas dekstre a^u Which buffer is right or left on the ; maldekstre ne gravas por la fina screen has no importance for the ; rezulto. Sed se vi volas meti final outcome. However, if you ; longajn liniojn (ekz. programerojn) include long lines, i.e which will ; en la kunigotan tekston, ili devas span both columns (eg. source code), ; esti en la estonte unua kolumno. La they should be in what will be the ; alia devas havi malplenajn linion first column, with the associated ; apud ili. buffer having empty lines next to ; them. ; Averto: en Emacs kiam vi ^san^gas la Attention: in Emacs when you change ; ma^joran modalon, la minoraj modaloj the major mode, the minor modes are ; estas anka^u elmemorigitaj. Tiu- also purged from memory. In that ; okaze vi devas religi la du bufrojn case you must reassociate the two ; per C-x 6 b. buffers with C-x 6 b. ; Kiam vi estos kontenta de la When you have edited both buffers to ; rezulto, vi kunmetos la du kolumnoj your content, you merge them with ; per C-x 6 1. Tiel vi metas la unuan C-x 6 1. This puts the first line ; linion de la alia bufro dekstre de of the other buffer to the right of ; la nuna linio. Se vi poste vidas the current line. If you then see a ; problemon, vi neniigu la kunmeton problem, you undo the merge with C-x ; per C-x u kaj plue modifu la du u and continue to edit the two ; bufrojn. Kiam vi ne plu volas tajpi buffers. When you no longer want to ; dukolumne, vi eliru el la minora edit in two columns, you turn off ; modalo per C-x 6 k. the minor mode with C-x 6 k. (provide 'two-column) (defvar two-column:prefix "\C-x6" "Prefix two-column:mode-map gets bound to.") (defvar two-column:mode-map nil "Keymap useful with two-column minor mode.") (if two-column:mode-map () (setq two-column:mode-map (make-sparse-keymap)) (define-key two-column:mode-map "1" 'two-column:merge) (define-key two-column:mode-map "2" 'two-column:split) (define-key two-column:mode-map "b" 'two-column:associate-buffer) (define-key two-column:mode-map "k" 'two-column:kill-association) (define-key two-column:mode-map "\C-l" 'two-column:recenter) (define-key two-column:mode-map "o" 'two-column:other) (define-key two-column:mode-map "u" 'two-column:unmerge) (define-key two-column:mode-map " " 'two-column:scroll-up) (define-key two-column:mode-map "\^?" 'two-column:scroll-down) (define-key two-column:mode-map "\C-m" 'two-column:scroll-line)) (global-set-key two-column:prefix two-column:mode-map) ; markers seem to be the only buffer-id not affected by renaming ; a buffer (defvar two-column:other nil "Marker to the other buffer which will be merged to this one, if non-nil.") (make-variable-buffer-local 'two-column:other) (setq minor-mode-alist (cons '(two-column:other " 2C") minor-mode-alist)) ; rearranged, so that the pertinent info will show in 40 columns (defvar two-column:mode-line-format '("-%*- %15b --" (-3 . "%p") "--%[(" mode-name minor-mode-alist "%n" mode-line-process ")%]%-") "*Value of mode-line-format for a buffer in two-column minor mode.") (defvar two-column:window-width 40 "*The width of the first column.") (defvar two-column:fill-column 36 "*Value of fill-column for a buffer in two-column minor mode.") (defvar two-column:mode-hook nil "Function called, if non-nil, whenever two-column:split is called, on both buffers if they are not already in two-column minor mode.") ;;;;; base functions ;;;;; (defun two-column:split (arg &optional buffer) "Split current window vertically for two-column editing. When called the first time, associates the buffer *Two-Column* with the current buffer. Both buffers are put in two-column minor mode and two-column:mode-hook gets called on both. These buffers remember about one another, even when renamed. When called again, restores the screen layout with the current buffer first and the associated buffer to it's right. Prefix means to put current buffer to the right. Which buffer is right or left on the screen has no importance for the final outcome. However, if you include long lines, i.e which will span both columns (eg. source code), they should be in what will be the first column, with the associated buffer having empty lines next to them. You have the following commands at your disposal: \\[two-column:split] Rearrange screen \\[two-column:associate-buffer] Reassociate buffer after changing major mode \\[two-column:scroll-up] Scroll both buffers up by a screenfull \\[two-column:scroll-down] Scroll both buffers down by a screenful \\[two-column:scroll-line] Scroll both buffers up by one or more lines \\[two-column:recenter] Recenter and scroll other buffer by same amount \\[two-column:other] Switch to associated buffer \\[two-column:merge] Merge the other buffer, starting next to this line These keybindings can be customized in your ~/.emacs by two-column:prefix and two-column:mode-map. The appearance of the screen can be customized by the variables two-column:window-width, two-column:fill-column, two-column:mode-line-format and truncate-partial-width-windows." (interactive "P") ; first go to full width, so that we can certainly split into ; two windows (if (< (window-width) (screen-width)) (enlarge-window-horizontally 99999)) (split-window nil two-column:window-width t) (if two-column:other (progn (or arg (other-window 1)) (switch-to-buffer (marker-buffer two-column:other)) (other-window (if arg 1 -1))) ; set up minor mode linking the two buffers (setq fill-column two-column:fill-column mode-line-format two-column:mode-line-format) (run-hooks two-column:mode-hook) (let ((other (point-marker))) (or arg (other-window 1)) (switch-to-buffer (or buffer (generate-new-buffer (concat "2C/" (buffer-name (current-buffer)))))) (or buffer (text-mode)) (setq fill-column two-column:fill-column mode-line-format two-column:mode-line-format two-column:other other other (point-marker)) (run-hooks two-column:mode-hook) (other-window (if arg 1 -1)) (setq two-column:other other)))) (fset 'two-column:mode 'two-column:split) (defun two-column:associate-buffer (arg) "Prompts for a buffer to associate with this one, and puts both in two-column minor mode. Can also be used to associate a just previously visited file, by accepting the proposed default buffer." (interactive "P") (and two-column:other (marker-buffer two-column:other) (error "Buffer already in two-column minor mode.")) (let ((buffer (read-buffer "Associate buffer: " (other-buffer) t))) (save-excursion (set-buffer buffer) (and two-column:other (bufferp (marker-buffer two-column:other)) (error "Buffer already in two-column minor mode."))) (two-column:split arg buffer))) (defun two-column:unmerge (arg) "Unmerge a two-column text into two buffers, both in two-column minor mode." (interactive "P") (if two-column:other (error "Buffer already in two-column minor mode.")) (if (= two-column:window-width (current-column)) () (make-variable-buffer-local 'two-column:window-width) (setq two-column:window-width (current-column))) (two-column:split arg) (save-excursion (let ((other (marker-buffer two-column:other)) (char (preceding-char)) (n 0) (goal-column (current-column)) point) (while (not (eobp)) (if (not (and (eq char (preceding-char)) (eq (current-column) goal-column))) (setq n (1+ n)) (setq point (point)) (backward-char) (skip-chars-backward " \t") (delete-region point (point)) (setq point (point)) (insert-char ?\n n) (append-to-buffer other point (progn (end-of-line) (1+ (point)))) (delete-region point (point)) (setq n 0)) (next-line 1))))) (defun two-column:kill-association () "Turn off two-column minor mode in current and associated buffer. If the associated buffer is unmodified and empty, it is killed." (interactive) (or two-column:other (error "Buffer not in two-column minor mode.")) (let ((b1 (current-buffer)) (b2 (marker-buffer two-column:other))) (kill-local-variable 'two-column:other) (kill-local-variable 'two-column:window-width) (kill-local-variable 'mode-line-format) (kill-local-variable 'fill-column) (set-buffer b2) (and two-column:other (or (not (marker-buffer two-column:other)) (eq b1 (marker-buffer two-column:other))) (if (and (not (buffer-modified-p)) (eobp) (= (point) 1)) (kill-buffer b2) (kill-local-variable 'two-column:other) (kill-local-variable 'two-column:window-width) (kill-local-variable 'mode-line-format) (kill-local-variable 'fill-column))))) ; this doesn't use yank-rectangle, so that the first column can ; contain long lines (defun two-column:merge () "Merges the associated buffer to the right of the current buffer, at the column, which is the value of two-column:window-width. You should have split the window with \\[two-column:split], in which case the effect is as though you erase the vertical window separator. That is, the two columns are pasted side by side, with the first line of column 2 appearing next to the current line of column 1, in a single text." (interactive) (or two-column:other (error "You must first set two-column minor mode.")) (save-excursion (let ((b1 (current-buffer)) (b2 (marker-buffer two-column:other)) string) (set-buffer b2) (goto-char (point-min)) (while (not (eobp)) (setq string (buffer-substring (point) (progn (end-of-line) (point)))) (or (eobp) (forward-char)) ; next line (set-buffer b1) (if (string= string "") () (end-of-line) (indent-to-column two-column:window-width) (insert string)) (next-line 1) ; add one if necessary (set-buffer b2)) (set-buffer b2))) (if (< (window-width) (screen-width)) (enlarge-window-horizontally 99999))) ;;;;; utility functions ;;;;; (defun two-column:other () "Switch to associated buffer." (interactive) (or two-column:other (error "You must set two-column minor mode.")) (if (get-buffer-window (marker-buffer two-column:other)) (select-window (get-buffer-window (marker-buffer two-column:other))) (switch-to-buffer (marker-buffer two-column:other)))) (defun two-column:scroll-line (arg) "Scroll current window and associated window upward by ARG lines." (interactive "p") (or two-column:other (error "You must set two-column minor mode.")) (scroll-up arg) ; too bad that pre 18.57 Emacs makes save-window-excursion restore ; the point. When it becomes extinct, we can simplify this. (if (get-buffer-window (marker-buffer two-column:other)) (let ((window (selected-window))) (select-window (get-buffer-window (marker-buffer two-column:other))) (unwind-protect (scroll-up arg) (select-window window))))) (defun two-column:scroll-up (arg) "Scroll current window and associated window upward by ARG screens." (interactive "p") (two-column:scroll-line (* arg (- (window-height) next-screen-context-lines 1)))) (defun two-column:scroll-down (arg) "Scroll current window and associated window downward by ARG screens." (interactive "p") (two-column:scroll-line (* arg (- next-screen-context-lines (window-height) -1)))) (defun two-column:recenter (arg) "Center point in window. With ARG, put point on line ARG. The associated window is scrolled by the same amount." (interactive "P") ; is recenter as complicated as this? I must be missing a base fn (setq arg (and arg (prefix-numeric-value arg))) (let* (point (distance (count-lines (point) (save-excursion (move-to-window-line (cond ((null arg) nil) ((< arg 0) -1) ( 0))) (setq point (point)))))) (two-column:scroll (cond ((null arg) (if (> point (point)) (- distance) distance)) ((< arg 0) (- -1 arg distance)) ( (- distance arg))))))