Girintiyle gezin


15

Girintiye dayalı bir dosyanın satırları arasında gezinmek istiyorum. Dosya girintiyle yapılandırılmıştır: önceki satırdan daha girintili bir satır önceki satırın alt öğesidir, önceki satırla aynı girintiye sahip olan satır kardeşidir. Çoğunlukla üç komut arıyorum:

  • Bir sonraki kardeşe, yani aynı girintili bir sonraki satıra geçin, daha girintili olan çizgileri atlayın, ancak daha az girintili bir çizgiyi geçmeyin.
  • Önceki kardeşe, yani diğer yöne doğru olana geçin.
  • Üst öğeye, yani daha az girintili bir önceki satıra gitme.

Noktanın sütun konumu değişmemelidir.

Bu girinti-yapılandırılmış veri analoglandır forward-sexp, backward-sexpve backward-up-listsexp-yapılandırılmış veri. Girinti, Haskell ve Python gibi dillerde program yapısına karşılık gelir; bu işlevler bu bağlamda özellikle yararlı olabilir, ancak moda özgü bir şey aramıyorum (birincil kullanım durumum, başka bir dosya biçiminde zayıflama yapılı verilerdir).

Renk girintisi seviyeleriUp / ile manuel olarak gezinmeye yardımcı olabilir, Downancak otomatik bir şey istiyorum.

Bu süper kullanıcı sorusu benzer ancak daha zayıf gereksinimleri olan ve şu anda gereksinimlerimi karşılayan cevapları yok.


Does set-selective-displayneye ihtiyacınız yakın götürelim?
Kaushal Modi

1
@KaushalModi Yararlı ve bunu bilmiyordum, bu yüzden teşekkür ederim, ama her zaman ihtiyacım olan şey değil. Tam şimdi, hareket etmek ve ilerlediğim çizgilerin çocuklarını görmek istedim.
Gilles 'SO- kötü olmayı bırak'

Bu soruyu sorduğun için teşekkürler; Temelde aynı soruyu daha az iyi sormak üzereydim. İstediğim tek ek şey "son kardeşe geç" yani aynı girintiye sahip son satır, daha az girintili satırları atlamamak. (Hiç bulunmayana kadar "bir sonraki kardeşe geç" i tekrar
etmekle eşdeğerdir

Sadece bu amaçla çalışan indent-toolsmelpa ( indent-tools ) paketini fark ettim . İlk taahhüt, bu sorunun sorulmasından yaklaşık 3 ay sonra 2016-Mayıs-16'da gerçekleşti.
ShreevatsaR

Yanıtlar:


4

Şu anda mevcut olan dört yanıtı inceleyerek ( ikisi Süper Kullanıcı'da ve ikisi bu soruda) aşağıdaki sorunları görüyorum:

  • Stefan ve Peng Bai tarafından süper üzerindeki olanlar , mevcut sütun konumunu koruyarak ve ebeveyne yukarı hareket uygulamak gerekmez (şimdiki girinti bakarak çizgi-by-line hareketli)
  • Dan cevap hayır sonraki kardeş varken bilmez ve bu nedenle bir kardeş olmayan bir öğe taşıyabilirsiniz: Daha az girinti ile hatları üzerinden atlar (aynı girinti ile bir sonraki satırı bulmak için ileri yeniden arama özelliğini kullanarak) ama başka bir ebeveynin çocuğu… belki bir sonraki “kuzen”.
  • Gilles tarafından cevap (anahat-modunu kullanarak) sütun konumunu tutmaz ve sıfır girinti ( "üst düzey" hat) ile çizgilerle işi yapmaz. Ayrıca, koduna baktığımızda outline.el, temelde her durumda satır satır gidiyor (kullanıyoruz outline-next-visible-heading), çünkü (neredeyse) tüm satırlar anahat regexp'si ile eşleşecek ve bir "başlık" olarak sayılacaktır.

Yani, her birinin fikirlerini bir araya getirerek, aşağıdakilere sahibim: boş ve daha girintili çizgilerin üzerinden atlayarak, satır satır ilerleyin. Eşit girintiliyseniz, o zaman bir sonraki kardeştir. Temel fikir şuna benzer:

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

Uygun şekilde genelleştirilmiş (ileri / geri / yukarı / aşağı), kullandığım şu anda aşağıdaki gibi görünüyor:

(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"))

Hala daha fazla işlevsellik isteniyor ve bunlardan bazılarına bakmak outline.elve yeniden uygulamak yardımcı olabilir, ancak şimdilik, amaçlarım için bundan memnunum.


@Gilles: Düzenlemeler için teşekkürler! Görünüşe göre (current-line)bir misc-fns.elşekilde Aquamacs kurulumumda bazı oneonone.elkütüphanelerin bir parçası olarak varım .
ShreevatsaR

6

Bu özellik Emacs'ta bulunmaktadır. Anahat modu , bir belgeyi düzeye sahip başlık çizgileri içerdiğini açıklar ve düzeyler arasında hareket etme olanaklarına sahiptir. Her satırı girintisini yansıtan bir seviyeye sahip bir başlık satırı olarak tanımlayabiliriz: girintiye ayarlanır outline-regexp. Daha doğrusu, girinti artı ilk boşluk olmayan karakter (ve dosyanın başlangıcı en üst seviyesidir): \`\|\s-+\S-.

M-x load-libray outline RET
M-: (make-local-variable 'outline-regexp) RET
M-: (setq outline-regexp "\\`\\|\\s-+\\S-") RET
M-x outline-minor-mode RET

Emacs 22.1–24.3'te bunu basitleştirmek için:

M-x load-libray outline RET
M-1 M-x set-variable RET outline-regexp RET "\\`\\|\\s-+\\S-" RET
M-x outline-minor-mode RET

Ardından anahat hareket komutlarını kullanabilirsiniz :

  • C-C @ C-f( outline-forward-same-level) bir sonraki kardeşe geçmek;
  • C-C @ C-b( outline-backward-same-level) bir önceki kardeşe geçmek;
  • C-C @ C-u( outline-up-heading) üst öğeye gitmek için.

Aynı girintinin miktarı için bir sekme ve bir boşluk sayılır. Sekmeler ve boşluklardan oluşan bir karışımınız varsa, uygun şekilde ayarlayın tab-widthve arayınuntabify .

Geçerli ana modda anahat ayarları varsa, çakışabilirler. Bu durumda, birden çok ana mod çözümünden birini kullanabilirsiniz , en basiti dolaylı bir tampon oluşturmak ve Ana Hat Ana Hattına ayarlamaktır. Anahat Ana Modunda, varsayılan klavye kısayollarının yazılması daha kolaydır: C-c C-fvb.


Bu işe yarayacak gibi görünüyor, ama aslında bir nedenden dolayı benim için çalışmıyor. M-x make-local-variable RET outline-regexp RETbu değişkeni kabul etmez ve yalnızca `` Eşleşme yok '' der. Henüz daha dikkatli bakmaya çalışmadım.
ShreevatsaR

@ShreevatsaR Emacs 24.4'teki uyumsuz bir değişiklik: outline-regexpartık bir alışkanlık değil ve etkileşimli olarak kolayca ayarlanamıyor.
Gilles 'SO- kötü olmayı bırak'

Çok güzel teşekkür ederim. İki küçük sorunlar vardır: (Ben aracı anahat-regexp için maç sanırım hiçbir girinti ile bir çizgi,) en üstteki düzeyde ise (1) daha sonra ne ileri ne de geri işler, ve bazı nedenlerden dolayı gittiği kadar iki satırlar (2) sonraki veya önceki kardeşe gittiğinde, satırın başına gider (sütun 0), ancak sütunu korumak güzel olurdu. (Soruda belirttiğiniz gibi.) Sanırım her ikisi de anahat modunun kendisinin sınırlamaları olabilir.
ShreevatsaR

5

Aşağıdaki üç komut, en az düzeyde test edilmiş, girintili çizgilerle temel gezinmeye izin vermelidir. Kod tekrarı için özür dileriz.

(defun ind-forward-sibling ()
  "Move forward to the next sibling line with the same indentation."
  (interactive)
  (save-match-data
    (let ((col (current-column))
          (pad (progn
                 (back-to-indentation)
                 (current-column))))
      (end-of-line 1)
      (re-search-forward (concat "^\\s-\\{"
                                 (number-to-string pad)
                                 "\\}[^ ]") nil t)
      (move-to-column col))))

(defun ind-backward-sibling ()
  "Move backward to the next sibling line with the same indentation."
  (interactive)
  (save-match-data
    (let ((col (current-column))
          (pad (progn
                 (back-to-indentation)
                 (current-column))))
      (beginning-of-line 1)
      (re-search-backward (concat "^\\s-\\{"
                                 (number-to-string pad)
                                 "\\}[^ ]") nil t)
      (move-to-column col))))

(defun ind-up-parent ()
  "Move up to parent line with less indentation."
  (interactive)
  (save-match-data
    (let ((col (current-column))
          (pad (progn
                 (back-to-indentation)
                 (current-column))))
      (when (> pad 0)
        (beginning-of-line 1)
        (re-search-backward (concat "^\\s-\\{0,"
                                    (number-to-string (1- pad))
                                    "\\}[^ ]") nil t))
      (move-to-column col))))

Bu iyi (düzeltmeden sonra - 1'in çıkarılmasıyla ne yapmaya çalıştığınızı anlamıyorum (current-column)ama imlecin hareket etmemesine neden oluyor), ancak tam olarak spesifikasyonumu karşılamıyor: girinti düzeyinde hareket daha az ilerliyor - girintili çizgiler.
Gilles 'SO- kötü olmayı bırak'

Bu işe yaramıyor. Örneğin ind-forward-sibling, aynı girintiye sahip bir sonraki satırı arar, böylece daha az girintili çizgilerin üzerinden atlar (ileri kardeş olmasa bile ileri gider).
ShreevatsaR
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.