Emacs
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)))))