Examinando as quatro respostas disponíveis atualmente ( duas no Superusuário e duas nesta questão), vejo os seguintes problemas:
- Os que estão no SuperUser de Stefan e Peng Bai (movendo linha por linha, observando o recuo atual) não implementam a retenção da posição atual da coluna e a mudança para o pai,
- A resposta de Dan (usando re-search-forward para encontrar a próxima linha com o mesmo recuo) pula as linhas com menos recuo: não sabe quando não há próximo irmão e, portanto, pode passar para algo que não é um irmão mas um filho de outro pai ... um próximo "primo" talvez.
- A resposta de Gilles (usando o modo de contorno) não mantém a posição da coluna e não funciona com linhas com recuo zero (linhas de "nível superior"). Além disso, olhando seu código
outline.el, ele também está basicamente indo de linha em linha de qualquer maneira (usando outline-next-visible-heading) em nosso caso, pois (quase) todas as linhas corresponderiam ao regexp de contorno e contariam como um "cabeçalho".
Então, reunindo algumas idéias de cada uma, tenho o seguinte: avançar linha por linha, passando por linhas vazias e mais recuadas. Se você estiver em igual recuo, então é o próximo irmão. A ideia básica é assim:
(defun indentation-get-next-sibling-line ()
"The line number of the next sibling, or nil if there isn't any."
(let ((wanted-indentation (current-indentation)))
(save-excursion
(while (and (zerop (forward-line)) ; forward-line returns 0 on success
(or (eolp) ; Skip past blank lines and more-indented lines
(> (current-indentation) wanted-indentation))))
;; Now we can't go further. Which case is it?
(if (and (not (eobp)) (= (current-indentation) wanted-indentation))
(line-number-at-pos)
nil))))
(defun indentation-forward-to-next-sibling ()
(interactive)
(let ((saved-column (current-column)))
(forward-line (- (indentation-get-next-sibling-line) (line-number-at-pos)))
(move-to-column saved-column)))
Generalizado adequadamente (para frente / para trás / para cima / para baixo), o que estou usando se parece com o seguinte atualmente:
(defun indentation-get-next-good-line (direction skip good)
"Moving in direction `direction', and skipping over blank lines and lines that
satisfy relation `skip' between their indentation and the original indentation,
finds the first line whose indentation satisfies predicate `good'."
(let ((starting-indentation (current-indentation))
(lines-moved direction))
(save-excursion
(while (and (zerop (forward-line direction))
(or (eolp) ; Skip past blank lines and other skip lines
(funcall skip (current-indentation) starting-indentation)))
(setq lines-moved (+ lines-moved direction)))
;; Now we can't go further. Which case is it?
(if (and
(not (eobp))
(not (bobp))
(funcall good (current-indentation) starting-indentation))
lines-moved
nil))))
(defun indentation-get-next-sibling-line ()
"The line number of the next sibling, if any."
(indentation-get-next-good-line 1 '> '=))
(defun indentation-get-previous-sibling-line ()
"The line number of the previous sibling, if any"
(indentation-get-next-good-line -1 '> '=))
(defun indentation-get-parent-line ()
"The line number of the parent, if any."
(indentation-get-next-good-line -1 '>= '<))
(defun indentation-get-child-line ()
"The line number of the first child, if any."
(indentation-get-next-good-line +1 'ignore '>))
(defun indentation-move-to-line (func preserve-column name)
"Move the number of lines given by func. If not possible, use `name' to say so."
(let ((saved-column (current-column))
(lines-to-move-by (funcall func)))
(if lines-to-move-by
(progn
(forward-line lines-to-move-by)
(move-to-column (if preserve-column
saved-column
(current-indentation))))
(message "No %s to move to." name))))
(defun indentation-forward-to-next-sibling ()
"Move to the next sibling if any, retaining column position."
(interactive "@")
(indentation-move-to-line 'indentation-get-next-sibling-line t "next sibling"))
(defun indentation-backward-to-previous-sibling ()
"Move to the previous sibling if any, retaining column position."
(interactive "@")
(indentation-move-to-line 'indentation-get-previous-sibling-line t "previous sibling"))
(defun indentation-up-to-parent ()
"Move to the parent line if any."
(interactive "@")
(indentation-move-to-line 'indentation-get-parent-line nil "parent"))
(defun indentation-down-to-child ()
"Move to the first child line if any."
(interactive "@")
(indentation-move-to-line 'indentation-get-child-line nil "child"))
Ainda há algumas funcionalidades desejáveis, e observar outline.ele reimplementar algumas delas pode ajudar, mas estou feliz com isso por enquanto, para meus propósitos.
set-selective-displayfica perto do que precisa?