home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / interfaces / two-column.el < prev    next >
Encoding:
Text File  |  1991-06-03  |  26.6 KB  |  664 lines

  1. ; Path: dg-rtp!dgcad!amdcad!apple!mips!zaphod.mps.ohio-state.edu!wuarchive!uunet!mcsun!corton!cix!irit!pfeiffer
  2. ; From: pfeiffer@irit.fr (Daniel Pfeiffer)
  3. ; Newsgroups: comp.emacs,gnu.emacs.sources,soc.culture.esperanto
  4. ; Subject: latest enhanced two-column editing support
  5. ; Date: 31 May 1991 11:55:55 GMT
  6. ; Organization: Instituto de Reser^cado pri Informadiko de Tuluzo (IRIT)
  7. ;           Universitato Paul Sabatier
  8. ; Here is the latest version, which is more robust against major mode
  9. ; changes, and which can handle any column widths.  The two columns now
  10. ; remain aligned if you use the minor mode commands for scrolling.  I
  11. ; was repeatedly asked to do this.  At first reluctant, since I thought
  12. ; that I didn't want it to work that way, I've since became a convert.
  13. ; In addition to two-column editing of text, for example for writing a
  14. ; bilingual text side-by-side as shown below in the file's prolog, other
  15. ; interesting uses have been found for this minor mode:
  16. ; You can separate the columns with    {+} C-x 6 u  or  <f2> u  if you prefer
  17. ; any string that pleases you, by      {+} handles these with a prefix argument
  18. ; setting two-column:separator.  For   {+} that enables you to declare the
  19. ; example "{+}  " if you like to       {+}  desired length of such a string.
  20. ; amuse yourself.
  21. ; keyword You can write any text corresponding to a
  22. ;       given keyword in a filled paragraph next to
  23. ;       it.  Note that the width of the first column
  24. ;       may be less than window-min-width in the
  25. ;       result, but will be displayed at that width.
  26. ; another This is not a three- or multi-column mode.
  27. ;       The example in the file's prolog required
  28. ;       working on two columns and then treating the
  29. ;       result as one column in order to add the
  30. ;       third.
  31. ; Programmers might like the ability to split off the comment column of
  32. ; a file that looks like the following.  The advantage is that with
  33. ; (setq fill-prefix "-- ") you can run M-q (fill-paragraph) on the
  34. ; comment.  The problem is, code quickly gets rather wide, so you need
  35. ; to use a narrower comment column, which is less interesting, unless
  36. ; you have a 132-column screen.  Code lines that reach beyond
  37. ; comment-column are no problem, except that you won't always see their
  38. ; end during editing.
  39. ; BEGIN                    -- This is just some meaningless
  40. ;     FOR i IN 1..10 LOOP        -- code in Ada, that runs foobar
  41. ;     foobar( i );            -- once for each argument from one
  42. ;     END LOOP;                -- to ten, and then we're already
  43. ; END;                    -- through with it.
  44. ; Better yet, you can put the point before "This", type M-3 C-x 6 u
  45. ; which makes "-- " the separator between a no-comments Ada buffer, and
  46. ; a plain text comment buffer.  When you put them back together, every
  47. ; non-empty line of the 2nd column will again be preceded by "-- ".
  48. ; The <f2> function key hack (which is one of the rare times when
  49. ; function keys are mnemonic) at the end of the file's prolog requires
  50. ; that the lisp/term/*.el for your terminal use the standard
  51. ; conventions.  Too bad that some don't (at least not in version 18.55).
  52. ; The Sun one is hopelessly non-standard, and vt2[024]0 somehow forgot
  53. ; to define <f1> thru <f5>.  (It defines <pf1> thru <pf4> instead, but
  54. ; that is not what we need on an X terminal.)  If you want to use those,
  55. ; you'll need another hack something like:
  56. ;       (if (string= (system-name) "cix")
  57. ;       (progn
  58. ;         (load-library "term/vt200.el")
  59. ;         (define-key CSI-map "12~" (cons function-keymap ?\^b)))
  60. ;     (global-unset-key "\e[")
  61. ;     (define-key esc-map "[225z" (cons function-keymap ?\^b)))
  62. ; where "cix" is the non-sun machine I use.  Actually I use the same X
  63. ; terminal to connect to both machines, and I want to keep my ~/.emacs
  64. ; identical on both.  Bother, the two Emacses don't recognize the same
  65. ; keys and assign different sequences to those they do!  I sure hope all
  66. ; this nonsense will stop with version 19 (or preferably soon) where I'd
  67. ; like to be able to say (define-key some-map '<f2> some-cmd), and see
  68. ; <f2> rather than some unintelligible ESC-sequence in command key
  69. ; sequences.
  70. ; Enjoy and keep me informed!
  71. ; --
  72. ; -- Daniel Pfeiffer                <pfeiffer@cix.cict.fr>
  73. ; -- Tolosa (Toulouse), Midi-Pyrenees, Europe    <pfeiffer@irit.fr>
  74. ; -- "Beware - polyglot esperantist"        <pfeiffer@frcict81.bitnet>
  75. ; --
  76. ; --8<---- two-column.el ----8<--------8<--------8<--------8<--------8<--------
  77. ; Esperanto:                English:
  78.  
  79. ; Minora modalo por samtempa dukolumna    Minor mode for simultaneous
  80. ; tajpado                two-column editing
  81.  
  82. ; Daniel Pfeiffer <pfeiffer@cix.cict.fr, @irit.fr>, 1991-05-14
  83. ; Copyright (C) 1991 Free Software Foundation, Inc.
  84.  
  85. ; ^Ci dataro estas ero de GNU Emacs.    This file is part of GNU Emacs.
  86.  
  87. ; GNU  Emacs  estas  disdonata   en la    GNU Emacs is distributed in the hope
  88. ; espero  ke ^gi estos utila,  sed SEN    that it will  be useful, but WITHOUT
  89. ; IA  GARANTIO.   Neniu   a^utoro  a^u    ANY    WARRANTY.     No  author   or
  90. ; disdonanto  akceptas respondecon  al    distributor   accepts responsibility
  91. ; iu ajn  por la sekvoj de ^gia uzado,    to  anyone for the   consequences of
  92. ; a^u  ^cu  ^gi serveblas al  iu celo,    using it or  for  whether it  serves
  93. ; a^u e^c  entute funkcias,  se  li ni    any particular purpose   or works at
  94. ; estas skribinta  tion.  Vidu la  GNU    all,  unless he says so  in writing.
  95. ; Emacs ^Generala Publika  Licenco por    Refer  to  the   GNU  Emacs  General
  96. ; plenaj detaloj.            Public License for full details.
  97.  
  98. ; ^Ciu rajtas  kopii,  modifi kaj  ree    Everyone  is  granted permission  to
  99. ; disdoni  GNU Emacs,  sed nur  sub la    copy,  modify and  redistribute  GNU
  100. ; condi^coj  priskribitaj  en  la  GNU    Emacs, but only under the conditions
  101. ; Emacs  ^Generala  Publika   Licenco.    described in  the  GNU Emacs General
  102. ; Kopio de  tiu licenso estas supozata    Public  License.   A copy   of  this
  103. ; donita al vi kune kun GNU Emacs, por    license  is supposed to have    been
  104. ; ke   vi  sciu   viajn  rajtojn   kaj    given to you along with GNU Emacs so
  105. ; respondecojn.   ^Gi  devus  esti  en    you   can   know   your   rights and
  106. ; dataro    nomata    COPYING.   Inter    responsibilities.  It should be in a
  107. ; alia^joj,  la notico  pri  kopirajto    file named    COPYING.   Among other
  108. ; kaj  ^ci  notico devas esti  gardata    things, the  copyright   notice  and
  109. ; sur ^ciuj kopioj.            this notice must be preserved on all
  110. ;                    copies.
  111.  
  112.  
  113. ; Tiu minora  modalo  ebligas  al   vi    This     minor mode  allows  you  to
  114. ; tajpi   sendepende  en   du   apudaj    independently    edit two   adjacent
  115. ; bufroj.  Vi  havas tri eblecojn  por    buffers.    You have three  ways  to
  116. ; eki    ^gin.   ^Ciu  donas  al    vi    start it  up.   Each  gives   you  a
  117. ; horizontale   disigatan   fenestron,    horizontally split window similar to
  118. ; simila  al  fina   apareco  de   via    the final outcome of your text:
  119. ; teksto:
  120.  
  121. ; C-x 6 2  asocias  novan  bufron  nomatan  associates a new  buffer called
  122. ;       same, sed kun 2C/ anta^u.        the   same,    but   with   2C/
  123. ;                        prepended.
  124.  
  125. ; C-x 6 b  asocias alian bufron.  Vi povas  associates    another   buffer.
  126. ;       anka^u asocii  dataron,   se vi  This can be used to associate a
  127. ;       ^jus anta^ue faris C-x C-f.        file if you just did C-x C-f.
  128.  
  129. ; C-x 6 u  disigas  jam dukolumnan tekston  unmerges a two-column text into
  130. ;       en  du   bufroj  ekde  la  nuna  two  buffers from  the  current
  131. ;       linio,  kaj je la nuna kolumno.  line and at the current column.
  132. ;       La    anta^uaj   signoj   (ofte  The preceding characters (often
  133. ;       tabeligilo  a^u  |)  estas   la  tab   or  |)  are   the  column
  134. ;       kolumna disiganto.  Linioj kiuj  separator.   Lines  that  don't
  135. ;       ne   enhavas   ilin   ne  estas  have them  won't  be separated.
  136. ;       disigitaj.   Kiel  la kvara kaj  Like the  fourth and fifth line
  137. ;       la   kvina  linio se vi disigas  if  you unmerge this  file from
  138. ;       ^ci dataron ekde la unua  angla  the first english word.
  139. ;       vorto.
  140.  
  141. ; Je ^cia  flanko  estas  bufro,   kiu    On each side is a buffer  that knows
  142. ; konas la  alian.  Kun la ordonoj C-x    about the other.  With the  commands
  143. ; 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
  144. ; povas   suben-   a^u  supreniri  unu    you can  simultaneously scroll up or
  145. ; ekranon,    kaj   subeniri   linion,    down by  a screenfull  and by a line
  146. ; samtempe en la du bufroj. Al la alia    in both buffers.   Empty lines   are
  147. ; bufro  estas   aldonataj  linioj  se    added to  the  other    buffer    if
  148. ; necesas,  por  ke  vi vidu la  saman    necessary, so that  you see the same
  149. ; parton.  Per  C-x  6  C-l vi   povas    part.   With   C-x 6  C-l    you can
  150. ; recentrigi la linion.    Kiam vi nur    recenter the line.   When  you  only
  151. ; plu  havas    unu el   la du  bufroj    have one of the two buffers onscreen
  152. ; surekrane vi  revidos la alian   per    you can get the other back  with C-x
  153. ; denove C-x 6 2.            6 2 once more.
  154.  
  155. ; Se  vi  volas  meti  longajn liniojn    If you include long lines, i.e which
  156. ; (ekz. programerojn) en la  kunigotan    will span both columns  (eg.  source
  157. ; tekston,   ili  devas  esti  en   la    code), they should  be  in what will
  158. ; estonte unua kolumno.  La alia devas    be the    first column,    with  the
  159. ; havi malplenajn linion apud ili.    associated buffer having empty lines
  160. ;                    next to them.
  161.  
  162. ; Averto: en Emacs kiam vi ^san^gas la    Attention:  in Emacs when you change
  163. ; ma^joran modalon, la minoraj modaloj    the major mode,  the minor modes are
  164. ; estas  anka^u  elmemorigitaj.   Tiu-    also  purged  from  memory.  In that
  165. ; okaze  vi devas religi la du bufrojn    case you   must  reassociate the two
  166. ; per iu  C-x 6-ordono,  ekz. C-x 6 b.    buffers with any C-x 6-command, e.g.
  167. ;                    C-x 6 b.
  168.  
  169. ; Kiam   vi   estos  kontenta   de  la    When you have edited both buffers to
  170. ; rezulto, vi kunmetos la du kolumnojn    your  content,  you merge them  with
  171. ; per  C-x 6 1.   Se  vi  poste  vidas    C-x 6 1.  If you then see a problem,
  172. ; problemon, vi  neniigu   la kunmeton    you undo the  merge with  C-x u  and
  173. ; per C-x u  kaj  plue  modifu  la  du    continue   to  edit the two buffers.
  174. ; bufrojn.  Kiam vi ne plu volas tajpi    When you  no longer  want to edit in
  175. ; dukolumne,  vi  eliru el  la  minora    two  columns, you turn off the minor
  176. ; modalo per C-x 6 k.            mode with C-x 6 k.
  177.  
  178.  
  179. ; An^stata^u tri `autoload' kaj tri  |  Instead  of  three  `autoload' and
  180. ; `global-set-key'  vi povas uzi la  |  three `global-set-key' you can use
  181. ; jenon en via dataro ~/.emacs, por  |  the    following   in  your   file
  182. ; memstare ^car^gi la modalon:         |  ~/.emacs,  to  automatically  load
  183. ;                     |  the mode:
  184.  
  185. ;    (global-set-key "\C-x6"
  186. ;            '(lambda () (interactive)
  187. ;               (load-library "two-column")
  188. ;               (call-interactively
  189. ;                (cdr (assq (read-char) two-column:mode-map)))))
  190.  
  191. ; Se vi ^satus  havi la dukolumnajn  |  If     you'd like   to  have   the
  192. ; ordonojn je funkciklavo <f2>,  vi  |  two-column  commands   on function
  193. ; povas  uzi la jenon en via dataro  |  key   <f2>,  you  can     use  the
  194. ; ~/.emacs:                 |  following in your file ~/.emacs:
  195.  
  196. ;    (define-key function-keymap "\^b"
  197. ;      '(lambda () (interactive)
  198. ;         (load-library "two-column")
  199. ;         (define-key function-keymap "\^b" two-column:mode-map)
  200. ;         (call-interactively
  201. ;          (cdr (assq (read-char) two-column:mode-map)))))
  202.  
  203. ;;;;; variable declarations ;;;;;
  204.  
  205.  
  206. (provide 'two-column)
  207.  
  208.  
  209. (defvar two-column:prefix "\C-x6"
  210.   "Prefix two-column:mode-map gets bound to.
  211. If you'd like to bind it to function key <f2>, see the prolog of the
  212. source file, lisp/two-column.el")
  213.  
  214. (defvar two-column:mode-map nil
  215.   "Keymap that contains all commands useful with two-column minor mode.
  216. This gets bound globally to `two-column:prefix' since minor modes have
  217. no local keymap.")
  218.  
  219. (if two-column:mode-map
  220.     ()
  221.   (setq two-column:mode-map (make-sparse-keymap))
  222.   (define-key two-column:mode-map "1" 'two-column:merge)
  223.   (define-key two-column:mode-map "2" 'two-column:split)
  224.   (define-key two-column:mode-map "b" 'two-column:associate-buffer)
  225.   (define-key two-column:mode-map "k" 'two-column:kill-association)
  226.   (define-key two-column:mode-map "\C-l" 'two-column:recenter)
  227.   (define-key two-column:mode-map "o" 'two-column:associated-buffer)
  228.   (define-key two-column:mode-map "u" 'two-column:unmerge)
  229.   (define-key two-column:mode-map "{" 'shrink-window-horizontally)
  230.   (define-key two-column:mode-map "}" 'enlarge-window-horizontally)
  231.   (define-key two-column:mode-map " " 'two-column:scroll-up)
  232.   (define-key two-column:mode-map "\^?" 'two-column:scroll-down)
  233.   (define-key two-column:mode-map "\C-m" 'two-column:scroll-line))
  234.  
  235. (global-set-key two-column:prefix two-column:mode-map)
  236.  
  237.  
  238. ; markers seem to be the only buffer-id not affected by renaming
  239. ; a buffer.  This nevertheless loses when a buffer is killed.
  240. (defvar two-column:other nil
  241.   "Marker to the associated buffer, if non-nil.")
  242. (make-variable-buffer-local 'two-column:other)
  243.  
  244.  
  245. (defvar two-column:buffer-list ()
  246.   "An alist of markers to associated buffers.  (Backs up `two-column:other')")
  247.  
  248.  
  249. (setq minor-mode-alist (cons '(two-column:other " 2C") minor-mode-alist))
  250.  
  251.  
  252. ; rearranged, so that the pertinent info will show in 40 columns
  253. (defvar two-column:mode-line-format
  254.     '("-%*- %15b --"  (-3 . "%p")  "--%[("  mode-name
  255.       minor-mode-alist  "%n"  mode-line-process  ")%]%-")
  256.   "*Value of mode-line-format for a buffer in two-column minor mode.")
  257.  
  258.  
  259. (defvar two-column:separator ""
  260.   "*A string inserted between the two columns when merging.
  261. This gets set locally by \\[two-column:unmerge].")
  262.  
  263.  
  264. (defvar two-column:window-width 40
  265.   "*The width of the first column.  (Must be at least `window-min-width')
  266. This value is local for every buffer that sets it.")
  267. (make-variable-buffer-local 'two-column:window-width)
  268.  
  269.  
  270. (defvar two-column:beyond-fill-column 4
  271.   "*Base for calculating `fill-column' for a buffer in two-column minor mode.
  272. The value of `fill-column' becomes `two-column:window-width' for this buffer
  273. minus this value.")
  274.  
  275.  
  276. (defvar two-column:mode-hook nil
  277.   "Function called, if non-nil, whenever turning on two-column minor mode.
  278. It can get called by \\[two-column:split] (two-column:split), \\[two-column:unmerge] (two-column:unmerge)
  279. and \\[two-column:associate-buffer] (two-column:associate-buffer), on both buffers.")
  280.  
  281. ;;;;; base functions ;;;;;
  282.  
  283.  
  284. ; the access method for the other buffer.  this tries to remedy against
  285. ; lost local variables and lost buffers.
  286. (defun two-column:other ()
  287.   (if (or two-column:other
  288.       (setq two-column:other
  289.         ; assoc with a different predicate, since we don't know
  290.         ; which marker points to this buffer
  291.         (let ((bl two-column:buffer-list))
  292.           (while (and bl (not (eq (current-buffer)
  293.                       (marker-buffer (car (car bl))))))
  294.             (setq bl (cdr bl)))
  295.           (cdr (car bl)))))
  296.       (or (prog1
  297.           (marker-buffer two-column:other)
  298.         (setq mode-line-format two-column:mode-line-format ))
  299.       ; The associated buffer somehow got killed.
  300.       (progn
  301.         ; The other variables may later be useful if the user
  302.         ; reestablishes the association.
  303.         (kill-local-variable 'two-column:other)
  304.         (kill-local-variable 'mode-line-format)
  305.         nil))))
  306.  
  307.  
  308. (defun two-column:split (&optional buffer)
  309.   "Split current window vertically for two-column editing.
  310.  
  311. When called the first time, associates a buffer with the current
  312. buffer.  Both buffers are put in two-column minor mode and
  313. two-column:mode-hook gets called on both.  These buffers remember
  314. about one another, even when renamed.
  315.  
  316. When called again, restores the screen layout with the current buffer
  317. first and the associated buffer to it's right.
  318.  
  319. If you include long lines, i.e which will span both columns (eg.
  320. source code), they should be in what will be the first column, with
  321. the associated buffer having empty lines next to them.
  322.  
  323. You have the following commands at your disposal:
  324.  
  325. \\[two-column:split]   Rearrange screen
  326. \\[two-column:associate-buffer]   Reassociate buffer after changing major mode
  327. \\[two-column:scroll-up]   Scroll both buffers up by a screenfull
  328. \\[two-column:scroll-down]   Scroll both buffers down by a screenful
  329. \\[two-column:scroll-line]   Scroll both buffers up by one or more lines
  330. \\[two-column:recenter]   Recenter and realign other buffer
  331. \\[shrink-window-horizontally], \\[enlarge-window-horizontally]   Shrink, enlarge current column
  332. \\[two-column:associated-buffer]   Switch to associated buffer
  333. \\[two-column:merge]   Merge both buffers
  334.  
  335. These keybindings can be customized in your ~/.emacs by `two-column:prefix'
  336. and `two-column:mode-map'.
  337.  
  338. The appearance of the screen can be customized by the variables
  339. `two-column:window-width', `two-column:beyond-fill-column',
  340. `two-column:mode-line-format' and `truncate-partial-width-windows'."
  341.  
  342.   (interactive "P")
  343.   ; first go to full width, so that we can certainly split into
  344.   ; two windows
  345.   (if (< (window-width) (screen-width))
  346.       (enlarge-window 99999 t))
  347.   (split-window-horizontally
  348.    (max window-min-width (min two-column:window-width
  349.                   (- (screen-width) window-min-width))))
  350.  
  351.   (if (two-column:other)
  352.       (progn
  353.     (other-window 1)
  354.     (switch-to-buffer (two-column:other))
  355.     (other-window -1)
  356.     ; align buffers if necessary
  357.     (two-column:scroll-line 0))
  358.  
  359.     ; set up minor mode linking two buffers
  360.     (setq fill-column (- two-column:window-width
  361.              two-column:beyond-fill-column)
  362.       mode-line-format two-column:mode-line-format)
  363.     (run-hooks two-column:mode-hook)
  364.     (let ((other (point-marker)))
  365.       (other-window 1)
  366.       (switch-to-buffer
  367.        (or buffer
  368.        (generate-new-buffer
  369.         (concat "2C/" (buffer-name)))))
  370.       (or buffer
  371.       (text-mode))
  372.       (setq fill-column (- two-column:window-width
  373.                two-column:beyond-fill-column)
  374.         mode-line-format two-column:mode-line-format
  375.         two-column:other other
  376.         other (point-marker))
  377.       (setq two-column:buffer-list (cons (cons two-column:other other)
  378.                      two-column:buffer-list))
  379.       (run-hooks two-column:mode-hook)
  380.       (other-window -1)
  381.       (setq two-column:buffer-list
  382.         (cons (cons other
  383.             (save-excursion
  384.               (set-buffer (two-column:other))
  385.               two-column:other))
  386.           two-column:buffer-list))
  387.       (setq two-column:other other))))
  388.  
  389. (fset 'two-column:mode 'two-column:split)
  390.  
  391.  
  392. (defun two-column:associate-buffer ()
  393.   "Associate another buffer with this one in two-column minor mode.
  394. Can also be used to associate a just previously visited file, by
  395. accepting the proposed default buffer.
  396.  
  397. See  \\[two-column:split]  and  `lisp/two-column.el'  for further details."
  398.   (interactive)
  399.   (let ((b1 (current-buffer))
  400.     (b2 (or (two-column:other)
  401.         (read-buffer "Associate buffer: " (other-buffer)))))
  402.     (save-excursion
  403.       (setq two-column:other nil)
  404.       (set-buffer b2)
  405.       (and (two-column:other)
  406.        (not (eq b1 (two-column:other)))
  407.        (error "Buffer already associated with buffer `%s'."
  408.           (buffer-name (two-column:other))))
  409.       (setq b1 (and (assq 'two-column:window-width (buffer-local-variables))
  410.             two-column:window-width)))
  411.     ; if other buffer has a local width, adjust here too
  412.     (if b1 (setq two-column:window-width (- (screen-width) b1)))
  413.     (two-column:split b2)))
  414.  
  415.  
  416. (defun two-column:unmerge (arg)
  417.   "Unmerge a two-column text into two buffers in two-column minor mode.
  418. The text is unmerged at the cursor's column which becomes the local
  419. value of two-column:window-width.  Only lines that have the ARG same
  420. preceding characters at that column get split.  The ARG preceding
  421. characters without any leading whitespace become the local value for
  422. `two-column:separator'.  This way lines that continue across both
  423. columns remain untouched in the first buffer.
  424.  
  425. This function can be used with a prototype line, to set up things as
  426. you like them.  You write the first line of each column with the
  427. separator you like and then unmerge that line.  E.g.:
  428.  
  429. First column's text    sSs  Second columns text
  430.                \\___/\\
  431.             /    \\
  432.    5 character Separator      You type  M-5 \\[two-column:unmerge]  with the point here
  433.  
  434. See  \\[two-column:split]  and  `lisp/two-column.el'  for further details."
  435.   (interactive "p")
  436.   (and (two-column:other)
  437.        (if (y-or-n-p (concat "Overwrite associated buffer `"
  438.                  (buffer-name (two-column:other))
  439.                  "'? "))
  440.        (save-excursion
  441.          (set-buffer (two-column:other))
  442.          (erase-buffer))
  443.      (signal 'quit nil)))
  444.   (let ((point (point))
  445.     ; make next-line always come back to same column
  446.     (goal-column (current-column))
  447.     ; a counter for empty lines in other buffer
  448.     (n (1- (count-lines (point-min) (point))))
  449.     chars other)
  450.     (save-excursion
  451.       (backward-char arg)
  452.       (setq chars (buffer-substring (point) point))
  453.       (skip-chars-forward " \t" point)
  454.       (make-variable-buffer-local 'two-column:separator)
  455.       (setq two-column:separator (buffer-substring (point) point)
  456.         two-column:window-width (current-column)))
  457.     (two-column:split)
  458.     (setq other (two-column:other))
  459.     ; now we're ready to actually unmerge
  460.     (save-excursion
  461.       (while (not (eobp))
  462.     (if (not (and (= (current-column) goal-column)
  463.               (string= chars
  464.                    (buffer-substring (point)
  465.                          (save-excursion
  466.                            (backward-char arg)
  467.                            (point))))))
  468.         (setq n (1+ n))
  469.       (setq point (point))
  470.       (backward-char arg)
  471.       (skip-chars-backward " \t")
  472.       (delete-region point (point))
  473.       (setq point (point))
  474.       (insert-char ?\n n)
  475.       (append-to-buffer other point (progn (end-of-line)
  476.                            (if (eobp)
  477.                            (point)
  478.                          (1+ (point)))))
  479.       (delete-region point (point))
  480.       (setq n 0))
  481.     (next-line 1)))))
  482.  
  483.  
  484. (defun two-column:kill-association ()
  485.   "Turn off two-column minor mode in current and associated buffer.
  486. If the associated buffer is unmodified and empty, it is killed."
  487.   (interactive)
  488.   (let ((buffer (current-buffer)))
  489.     (save-excursion
  490.       (and (two-column:other)
  491.        (prog2
  492.         (setq two-column:buffer-list
  493.           (delq (assq two-column:other two-column:buffer-list)
  494.             two-column:buffer-list))
  495.         (set-buffer (two-column:other))
  496.         (setq two-column:buffer-list
  497.           (delq (assq two-column:other two-column:buffer-list)
  498.             two-column:buffer-list)))
  499.        (or (not (two-column:other))
  500.            (eq buffer (two-column:other)))
  501.        (if (and (not (buffer-modified-p))
  502.             (eobp) (bobp))
  503.            (kill-buffer nil)
  504.          (kill-local-variable 'two-column:other)
  505.          (kill-local-variable 'two-column:window-width)
  506.          (kill-local-variable 'two-column:separator)
  507.          (kill-local-variable 'mode-line-format)
  508.          (kill-local-variable 'fill-column))))
  509.     (kill-local-variable 'two-column:other)
  510.     (kill-local-variable 'two-column:window-width)
  511.     (kill-local-variable 'two-column:separator)
  512.     (kill-local-variable 'mode-line-format)
  513.     (kill-local-variable 'fill-column)))
  514.  
  515.  
  516. ; this doesn't use yank-rectangle, so that the first column can
  517. ; contain long lines
  518. (defun two-column:merge ()
  519.   "Merges the associated buffer with the current buffer.
  520. They get merged at the column, which is the value of
  521. `two-column:window-width', i.e. usually at the vertical window
  522. separator.  This separator gets replaced with white space.  Beyond
  523. that the value of gets inserted on merged lines.  The two columns are
  524. thus pasted side by side, in a single text.  If the other buffer is
  525. not displayed to the left of this one, then this one becomes the left
  526. column.
  527.  
  528. If you want `two-column:separator' on empty lines in the second column,
  529. you should put just one space in them.  In the final result, you can strip
  530. off trailing spaces with \\[beginning-of-buffer] \\[replace-regexp] [ SPC TAB ] + $ RET RET"
  531.  
  532.   (interactive)
  533.   (or (two-column:other)
  534.       (error "You must first set two-column minor mode."))
  535.   (and (> (car (window-edges)) 0)    ; not touching left edge of screen
  536.        (eq (window-buffer (previous-window))
  537.        (two-column:other))
  538.        (other-window -1))
  539.   (save-excursion
  540.     (let ((b1 (current-buffer))
  541.       (b2 (two-column:other))
  542.       string)
  543.       (goto-char (point-min))
  544.       (set-buffer b2)
  545.       (goto-char (point-min))
  546.       (while (not (eobp))
  547.     (setq string (buffer-substring (point)
  548.                        (progn (end-of-line) (point))))
  549.     (or (eobp)
  550.         (forward-char))        ; next line
  551.     (set-buffer b1)
  552.     (if (string= string "")
  553.         ()
  554.       (end-of-line)
  555.       (indent-to-column two-column:window-width)
  556.       (insert two-column:separator string))
  557.     (next-line 1)            ; add one if necessary
  558.     (set-buffer b2))))
  559.   (if (< (window-width) (screen-width))
  560.       (enlarge-window 99999 t)))
  561.  
  562. ;;;;; utility functions ;;;;;
  563.  
  564.  
  565. (defun two-column:associated-buffer ()
  566.   "Switch to associated buffer."
  567.   (interactive)
  568.   (or (two-column:other)
  569.       (error "You must set two-column minor mode."))
  570.   (if (get-buffer-window (two-column:other))
  571.       (select-window (get-buffer-window (two-column:other)))
  572.     (switch-to-buffer (two-column:other))))
  573.  
  574.  
  575. ; It would be desirable to intercept anything that causes the current
  576. ; window to scroll.  Maybe a `scroll-hook'?
  577. (defun two-column:scroll-line (arg)
  578.   "Scroll current window upward by ARG lines.
  579. The associated window gets scrolled to the same line."
  580.   (interactive "p")
  581.   (or (two-column:other)
  582.       (error "You must set two-column minor mode."))
  583.   ; scroll-up has a bug on arg 0 at end of buffer
  584.   (or (zerop arg)
  585.       (scroll-up arg))
  586.   (setq arg (count-lines (point-min) (window-start)))
  587.   ; too bad that pre 18.57 Emacs makes save-window-excursion restore
  588.   ; the point.  When it becomes extinct, we can simplify this.
  589.   (if (get-buffer-window (two-column:other))
  590.       (let ((window (selected-window)))
  591.     (select-window (get-buffer-window (two-column:other)))
  592.     (setq arg (- arg (count-lines (point-min) (window-start))))
  593.     ; make sure that other buffer has enough lines
  594.     (save-excursion
  595.       (goto-char (point-max))
  596.       (insert-char ?\n
  597.                (- arg (count-lines (window-start) (point-max)) -1)))
  598.     (or (zerop arg)
  599.         (scroll-up arg))
  600.     (select-window window))))
  601.  
  602.  
  603. (defun two-column:scroll-up (arg)
  604.   "Scroll current window upward by ARG screens.
  605. The associated window gets scrolled to the same line."
  606.   (interactive "p")
  607.   (two-column:scroll-line (* arg (- (window-height)
  608.                     next-screen-context-lines 1))))
  609.  
  610.  
  611. (defun two-column:scroll-down (arg)
  612.   "Scroll current window downward by ARG screens.
  613. The associated window gets scrolled to the same line."
  614.   (interactive "p")
  615.   (two-column:scroll-line (* arg (- next-screen-context-lines
  616.                     (window-height) -1))))
  617.  
  618.  
  619. (defun two-column:recenter (arg)
  620.   "Center point in window.  With ARG, put point on line ARG.
  621. This counts from bottom if ARG is negative.  The associated window
  622. gets scrolled to the same line."
  623.   (interactive "P")
  624.   (setq arg (and arg (prefix-numeric-value arg)))
  625.   (two-column:scroll-line (- (count-lines (window-start) (point))
  626.                  (cond ((null arg)  (/ (window-height) 2))
  627.                    ((< arg 0)  (+ (window-height) arg))
  628.                    (  arg)))))
  629.  
  630.  
  631. (defun enlarge-window-horizontally (arg)
  632.   "Make current window ARG columns wider."
  633.   (interactive "p")
  634.   (enlarge-window arg t)
  635.   (and (two-column:other)
  636.        (setq two-column:window-width (+ two-column:window-width arg))
  637.        (set-buffer (two-column:other))
  638.        (setq two-column:window-width (- two-column:window-width arg))))
  639.  
  640.  
  641. (defun shrink-window-horizontally (arg)
  642.   "Make current window ARG columns narrower."
  643.   (interactive "p")
  644.   (enlarge-window-horizontally (- arg)))
  645.  
  646.