Emacs

From Apertium
Revision as of 08:16, 22 June 2009 by Unhammer (talk | contribs) (sort&goto-pardef)
Jump to navigation Jump to search

Emacs has a nice xml editing mode called nXML.

I often define keyboard macros as I edit, some of these I record as functions which you can put in your .emacs, since they come in handy time and again. I use Apertium-dixtools-formatted dix, some of the functions below won't work with the regular format (at least sort-pardef). The code below adds keyboard shortcuts C-c L and C-c R which make LR or RL restricted copies of <e>'s, C-c G which finds the pardef of a dictionary entry (and lets you go back with C-u C-SPC) and C-c S which sorts a pardef by its right-hand-side <r>.

(defun nxml-dix-up-to (eltname)
  "Move point before the element `eltname' (a string, eg. \"e\")
which we're looking at."
  (nxml-token-after)
  (goto-char xmltok-start)
  (let ((tok (xmltok-start-tag-qname)))
    (while (not (or (equal tok eltname) (equal tok (concat "<" eltname))))
      (nxml-backward-up-element)
      (nxml-token-after)
      (setq tok (xmltok-start-tag-qname)))))

(defun nxml-dix-restriction-copy (&optional RL)
  "Make a copy of the Apertium element we're looking at, and add
an LR restriction to the copy. A prefix argument makes it an RL
restriction."
  (interactive "P")
  (nxml-dix-up-to "e")
  (kill-sexp) (yank) (newline-and-indent) (yank)
  (goto-char (mark t))
  (let ((dir (if RL "RL" "LR")))
    (forward-word) (insert (concat " r=\"" dir "\"")))
  (forward-char) (just-one-space) (delete-backward-char 1)
  (forward-word 3)
  (when RL
    (nxml-backward-up-element) (nxml-forward-element) (forward-word 2)))

(defun nxml-dix-goto-pardef ()
  "Call from an entry to go to its pardef. Mark is pushed so you
can go back with C-u \\[set-mark-command]."
  (interactive)
  (if (save-excursion
	(nxml-dix-up-to "e")
	(re-search-forward "n=\"\\([^\"]*\\)\"")
	(let ((pdname (match-string 1)))
	  (goto-char (point-min))
	  (if (re-search-forward
	       (concat "<pardef *n=\"" pdname "\"") nil t)
	      (setq pos (match-beginning 0)))))
      (progn (push-mark)
	     (goto-char pos))))

(defun nxml-dix-sort-e-by-r (reverse beg end)
  "Sort region alphabetically by contents of <r> element;
argument means descending order. Assumes <e> elements never
occupy more than one line.

Called from a program, there
are three arguments: REVERSE (non-nil means reverse order), BEG
and END (region to sort).  The variable `sort-fold-case'
determines whether alphabetic case affects the sort order."
  (interactive "P\nr")
  (save-excursion
    (save-restriction
      (narrow-to-region beg end)
      (goto-char (point-min))
      (let ;; To make `end-of-line' and etc. to ignore fields.
	  ((inhibit-field-text-motion t))
	(sort-subr reverse
		   ;; todo: use nxml-dix-up-to and
		   ;; nxml-forward-element to make it XML general
		   'forward-line
		   'end-of-line
		   (lambda ()
		     (nxml-forward-element)
		     (nxml-backward-down-element 2)
		     (nxml-backward-element)))))))

(defun nxml-dix-sort-pardef (reverse)
  "Sort a pardef using `nxml-dix-sort-e-by-r'. Requires pardef to
be formatted as per apertium-dixtools."
  (interactive "P")
  (save-excursion
    (nxml-dix-up-to "pardef")
    (mark-sexp)
    (forward-line)
    (exchange-point-and-mark)
    (beginning-of-line)
    (nxml-dix-sort-e-by-r reverse (mark) (point))))

;; add keyboard shortcuts on loading nxml-mode:
(add-hook 'nxml-mode-hook	
	  (lambda ()
	    (define-key nxml-mode-map (kbd "C-c L") 'nxml-dix-restriction-copy)
	    (define-key nxml-mode-map (kbd "C-c R")
	      (lambda nil (interactive)
		"Make a copy of the Apertium element we're looking at, and
add an RL restriction to the copy."
		(nxml-dix-restriction-copy 'RL)))
	    (define-key nxml-mode-map (kbd "C-c S") 'nxml-dix-sort-pardef)
	    (define-key nxml-mode-map (kbd "C-c G") 'nxml-dix-goto-pardef)))

Also, if you like having all <i> elements aligned at eg. column 25, the following in your .emacs lets you do M-x align on a region to achieve that, and also aligns <p> to 10 and <r> to 44 (for bidix):

 (add-hook 'align-load-hook
	  (lambda ()
	    (add-to-list 'align-rules-list
			 '(nxml-dix-i-align
			   (regexp . "\\(\\s-*\\)\\(<i.*\\)$")
			   (modes . '(nxml-mode))
			   (column . 25)))
	    (add-to-list 'align-rules-list
			 '(nxml-dix-r-align
			   (regexp . "\\(\\s-*\\)\\(<r>.*\\)$")
			   (tab-stop . nil)
			   (modes . '(nxml-mode))
			   (column . 44)))
	    (add-to-list 'align-rules-list
			 '(nxml-dix-p-align
			   (regexp . "\\(\\s-*\\)\\(<p>.*\\)$")
			   (modes . '(nxml-mode))
			   (column . 10)))))

See also

Emacs C style for Apertium hacking