[comp.emacs] Solvo por dulingva teksttajpado / Solution for bilingual editing

pfeiffer@deva.irit.fr (Daniel Pfeiffer) (04/19/91)

Anta^u ia tempo, mi demandis al reto	Some time  back,  I asked  the  net,
^cu   estis    Emacs paketa^jo   por	whether there was an Emacs   package
samtempa, dulingva tajpado.  Kion mi	for  simultaneous bilingual editing.
demandis, eble estis troa laboro por	What I  asked for  was maybe to much
programisto.  Mi  ricevis shell pro-	work  to   program.  I received  one
gramon por kunmeti  du datarojn, kun	shell  script,  permitting to  merge
la bona ideo  modifi ^giaj lar^gecoj	two  files, with   the  neat idea of
^gis       ili    estas   samlongaj.	reformatting the files  until   they
Malfeli^ce li utiligas  nestandardaj	have  the same  length.  Too bad  he
argumentoj.				uses non-standard arguments.

RMS proponis ke mi disigu la ekranon	RMS proposed that I split the screen
horizontale,     tajpu,  kaj   poste	horizontally, edit,  and  then merge
kunmetu la  du  partoj.  Por  facile	the two parts.  To easily do this, I
fari  tion, mis skribis  la   apudan	have written the  following program.
programon.  Mi   ^satus  havi  viajn	I'd like to hear your remarks on it.
rimakrojn pri ^gi.

--
-- Daniel Pfeiffer				<pfeiffer@cix.cict.fr>
-- Tolosa (Toulouse), Midi-Pyrenees, Europe	<pfeiffer@irit.fr>
-- "Beware - polyglot esperantist"		<pfeiffer@frcict81.bitnet>
--

--8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<---
; Minora modalo por samtempa, dulingva	Minor mode for simultaneous
; tajpado				bilingual editing

; 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 Licenso  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   Licenso.	described in  the  GNU Emacs General
; Kopio de tiu licenso estas supozinta	Public  License.   A copy   of  this
; donita al vi kune kun GNU Emacs, por	license  is supposed to have    been
; ke     vi sciu  viaj   rajtoj    kaj	given to you along with GNU Emacs so
; respondecoj.    ^Gi  devus  esti  en	you   can   know   your   rights and
; dataro     nomata    COPYING.    Kun	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 ekas ^gin  per C-x  6  2	buffers.  You start it up with C-x 6
; (bilingual:split), kiu donas  al  vi	2 (bilingual:split), which gives you
; horizontale   disigatan   fenestron,	a  horizontally split window similar
; simila   al  fina   apareco de   via	to  the final outcome of  your text.
; teksto.  Je ^cia flanko estas bufro,	On each side is  a buffer that knows
; kiu konas la alian.  Kun  la ordonoj	about the other.   With the commands
; C-x 6  SPC, C-x 6 DEL kaj  C-x 6 RET	C-x 6 SPC, C-x  6 DEL  and C-x 6 RET
; oni 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.  Kiam vi	in both buffers.  When you only have
; nur plu  havas  unu el  la du bufroj	one of the  two buffers onscreen you
; surekrane  vi revidos la  alian  per	can get the other back with C-x  6 2
; denove 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.

; 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 ( (bilingual:merge).  Se	C-x 6   1 (bilingual:merge).  If you
; vi   poste  vidas  problemojn,    vi	then see  a  problem, you  undo  the
; neniigi  la kunmeton  per C-x u  kaj	merge with  C-x   u and continue  to
; plue modifu la du bufrojn.		edit.

(provide 'bilingual)

(defvar bilingual:mode-map nil
  "Keymap useful with bilingual minor mode.")

(if bilingual:mode-map
    ()
  (setq bilingual:mode-map (make-sparse-keymap))
  (define-key bilingual:mode-map "1" 'bilingual:merge)
  (define-key bilingual:mode-map "2" 'bilingual:split)
  (define-key bilingual:mode-map "o" 'bilingual:other)
  (define-key bilingual:mode-map " " 'bilingual:scroll-up)
  (define-key bilingual:mode-map "\^?" 'bilingual:scroll-down)
  (define-key bilingual:mode-map "\C-m" 'bilingual:scroll-line))

(global-set-key "\C-x6" bilingual:mode-map)


; markers seem to be the only buffer-id not affected by renaming
; a buffer
(defvar bilingual:other nil
  "Marker to the other buffer which will be merged to this one, if non-nil.")
(make-variable-buffer-local 'bilingual:other)

(setq minor-mode-alist (cons '(bilingual:other " 2L") minor-mode-alist))

; rearranged, so that the pertinent info will show in 40 columns
(defvar bilingual:mode-line-format
	'("-%*- %15b ("  mode-name  ")--"  (-3 . "%p")  "--%["
	  minor-mode-alist  "%n"  mode-line-process  " %]%-")
  "*Value of mode-line-format for a buffer in bilingual minor mode.")

(defvar bilingual:window-width 40
  "*The width of the first column.")

(defvar bilingual:fill-column 36
  "*Value of fill-column for a buffer in bilingual minor mode.")

(defvar bilingual:mode-hook nil
  "Function called, if non-nil, whenever bilingual:split is called,
on both buffers if they are not already in bilingual minor mode.")

;;;;; base functions

(defun bilingual:split ()
  "Split current window vertically for bilingual editing.  When called the
first time, associates the buffer *Bilingual* with the current buffer.
Both buffers are put in bilingual minor mode and bilingual: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.
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:

C-x 6 2		Enter this minor mode, or, afterwards, rearrange screen
C-x 6 SPC	Scroll both buffers up by a screenfull
C-x 6 DEL	Scroll both buffers down by a screenful
C-x 6 RET	Scroll both buffers up by one or more lines
C-x 6 o		Switch to associated buffer
C-x 6 1		Merge the other buffer, starting next to this line"
  (interactive)
  (if (< (window-width) (screen-width))
      (enlarge-window-horizontally 99999))
  (split-window nil bilingual:window-width t)

  (if bilingual:other
      (progn
	(other-window 1)
	(switch-to-buffer (marker-buffer bilingual:other))
	(other-window -1))
    (setq fill-column bilingual:fill-column
	  mode-line-format bilingual:mode-line-format)
    (run-hooks bilingual:mode-hook)
    (let ((other (point-marker)))
	(other-window 1)
	(switch-to-buffer "*Bilingual*")
	(setq fill-column bilingual:fill-column
	      mode-line-format bilingual:mode-line-format
	      bilingual:other other
	      other (point-marker))
	(run-hooks bilingual:mode-hook)
	(other-window -1)
	(setq bilingual:other other))))

(fset 'bilingual:mode 'bilingual:split)


; this doesn't use yank-rectangle, so that the first column can
; contain long lines
(defun bilingual:merge ()
  "Merges the associated buffer to the right of the current buffer, at the
column, which is the value of bilingual:window-width.
You should have split the window with \\[bilingual: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 bilingual:other
      (error "You must set bilingual minor mode with bilingual:split."))
  (save-excursion
    (let ((b1 (current-buffer))
	  (b2 (marker-buffer bilingual: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 bilingual: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 bilingual:other ()
  "Switch to associated buffer."
  (interactive)
  (or bilingual:other
      (error "You must set bilingual minor mode with bilingual:split."))
  (if (get-buffer-window (marker-buffer bilingual:other))
      (select-window (get-buffer-window (marker-buffer bilingual:other)))
    (switch-to-buffer (marker-buffer bilingual:other))))


(defun bilingual:scroll (arg function)
  (or bilingual:other
      (error "You must set bilingual minor mode with bilingual:split."))
  (funcall function arg)
  (if (get-buffer-window (marker-buffer bilingual:other))
      (let ((window (selected-window)))
	(select-window (get-buffer-window (marker-buffer bilingual:other)))
	(funcall function arg)
	(select-window window))))


(defun bilingual:scroll-up (arg)
  "Scroll current window and window showing associated buffer upward a page,
or upward by ARG lines."
  (interactive "P")
  (bilingual:scroll arg 'scroll-up))


(defun bilingual:scroll-down (arg)
  "Scroll current window and window showing associated buffer downward a page,
or downward by ARG lines."
  (interactive "P")
  (bilingual:scroll arg 'scroll-down))


(defun bilingual:scroll-line (arg)
  "Scroll current window and window showing associated buffer upward by ARG lines."
  (interactive "p")
  (bilingual:scroll arg 'scroll-up))