4

A formatted input file (*.alm) often contains a large section of columned data. To fold this and improve navigation, I've tried to implement a hs-special-mode-alist entry from hideshow.el.

(setq hs-special-modes-alist 
   '((alamo-mode "begin_data" "end_data" "#" alamo-forward-sexp nil)))

After executing the the bare bones mode in the footer and opening `test.alm',

# Name: test.alm
# Long comments
# Like this will fold

BEGIN_DATA # This will not fold 
1 2 3
1 2 3
1 2 3
END_DATA

running M-x hs-hide-all results in:

# Name: test.alm...

begin_data # This will not fold
1 2 3 
1 2 3
1 2 3
end_data

Despite my list declaration including a begin_data / end_data pairing, the block is ignored. I expected the buffer to look like:

# Name: test.alm...

begin_data ... end_data

Is there a reason why hideshow.el doesn't recognize and hide the begin_data / end_data block or am I missing something in my usage or declarations?

To fold test.alm load the following file:

;;; Demo code       
(defun alamo-forward-sexp (&optional arg)
  "Search forward to the next alamo option or begin/end of a data block.  
ARG defines the forward or backwards count for the search."
  (interactive "p")
  (or arg (setq arg 1))
  (re-search-forward "\\<\\(\\(begin\\|end\\)_data\\)\\>" nil nil arg))

(setq hs-special-modes-alist
      '((alamo-mode "begin_data" "end_data" "#" alamo-forward-sexp nil)))

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.alm\\'" . alamo-mode))

;;;###autoload
(define-derived-mode alamo-mode prog-mode "ALAMO"
  "A major mode for editing ALAMO input files. (*.alm)"
(setq-local comment-start "#") ; required for hs-minor-mode
(hs-minor-mode 1))

(provide 'alamo-mode)
z-nut
  • 43
  • 5

1 Answers1

2

You did not implement alamo-forward-sexp correctly. If point is at begin_data almo-forward-sexp does not go to the block end, i.e., to the end of end_data but to the end of alamo-forward-sexp.

A fairly complete alamo-forward-sexp must treat nested sexps correctly. Something like that is most often done via a stack. Below I give a simple version of a stack based alamo-forward-sexp. Dislaimer: It does not treat comments.

(defun alamo-forward-one-sexp (&optional backward)
  "Search forward one alamo option or begin/end of a data block.
Search backward instead if BACKWARD is non-nil."
  (let (stack)
    (if backward
    (skip-chars-backward "[:space:]\n")
      (skip-chars-forward "[:space:]\n"))
    (if (if backward
        (and (looking-back "\\(^begin_data\\|\\s(\\)" nil)
         (goto-char (match-beginning 0)))
      (and (looking-at "\\(^begin_data\\|\\s(\\)")
           (goto-char (match-end 0))))
    (progn
      (push (list (match-string 0) (point)) stack)
      (while (and (> (length stack) 0)
              (if backward
              (re-search-backward "\\(^end_data\\|\\s)\\)\\|\\(^begin_data\\|\\s(\\)" nil t)
            (re-search-forward "\\(^begin_data\\|\\s(\\)\\|\\(^end_data\\|\\s)\\)" nil t))
              )
        (if (match-string 1) ;; opening a new sexp
        (push (list (match-string 0) (point)) stack)
          (pop stack) ;; TODO: error handling
          ))
      (when (> (length stack) 0)
        (signal 'scan-error (list "Unbalanced sexp boundaries." (cadar stack) (point)))))
      (if backward
      (skip-syntax-backward "w_")
    (skip-syntax-forward "w_")))))

(defun alamo-forward-sexp (&optional arg)
  "Search forward to the next alamo option or begin/end of a data block.
ARG defines the forward or backwards count for the search."
  (interactive "p")
  (if (> arg 0)
      (while (> arg 0)
    (alamo-forward-one-sexp)
    ;; (alamo--forward-sexp
    ;;  skip-chars-forward
    ;;  (looking-at "\\(^begin_data\\|\\s(\\)")
    ;;  (re-search-forward "\\(^begin_data\\|\\s(\\)\\|\\(^end_data\\|\\s)\\)" nil t)
    ;;  skip-syntax-forward)
    (decf arg))
    (while (> arg 0)
      (alamo-forward-one-sexp t)
      (decf arg))))

(setq hs-special-modes-alist
      '((alamo-mode "begin_data" "end_data" "#" alamo-forward-sexp nil)))

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.alm\\'" . alamo-mode))

;;;###autoload
(define-derived-mode alamo-mode prog-mode "ALAMO"
  "A major mode for editing ALAMO input files. (*.alm)"
(setq-local comment-start "#") ; required for hs-minor-mode
(hs-minor-mode 1))

(provide 'alamo-mode)
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • Ah, I noticed that odd movement behavior but wasn't really sure if that was right or not. There are a lot of lot of good elisp things to dig into here. **Thank you very much!** – z-nut Jan 04 '18 at 20:43