I am a beginner in ELisp
, but have programmed in C++
and a number of other programming languages before. My rule of thumb (and I think it is a common one) that a function should fit on the screen. However, it should preferably be even smaller (20 lines is on the long side). However, I notice that Emacs has functions that are very long, with lots of opportunities for decomposition. An example of what I mean follows.
The question is: Is there something I am missing with regards to the difference between programming in ELisp and programming in procedural languages? Are those who wrote Emacs bad programmers? (hard to believe) Other reasons?
(defun forward-paragraph (&optional arg)
"Move forward to end of paragraph.
With argument ARG, do it ARG times;
a negative argument ARG = -N means move backward N paragraphs.
A line which `paragraph-start' matches either separates paragraphs
\(if `paragraph-separate' matches it also) or is the first line of a paragraph.
A paragraph end is the beginning of a line which is not part of the paragraph
to which the end of the previous line belongs, or the end of the buffer.
Returns the count of paragraphs left to move."
(interactive "^p")
(or arg (setq arg 1))
(let* ((opoint (point))
(fill-prefix-regexp
(and fill-prefix (not (equal fill-prefix ""))
(not paragraph-ignore-fill-prefix)
(regexp-quote fill-prefix)))
;; Remove ^ from paragraph-start and paragraph-sep if they are there.
;; These regexps shouldn't be anchored, because we look for them
;; starting at the left-margin. This allows paragraph commands to
;; work normally with indented text.
;; This hack will not find problem cases like "whatever\\|^something".
(parstart (if (and (not (equal "" paragraph-start))
(equal ?^ (aref paragraph-start 0)))
(substring paragraph-start 1)
paragraph-start))
(parsep (if (and (not (equal "" paragraph-separate))
(equal ?^ (aref paragraph-separate 0)))
(substring paragraph-separate 1)
paragraph-separate))
(parsep
(if fill-prefix-regexp
(concat parsep "\\|"
fill-prefix-regexp "[ \t]*$")
parsep))
;; This is used for searching.
(sp-parstart (concat "^[ \t]*\\(?:" parstart "\\|" parsep "\\)"))
start found-start)
(while (and (< arg 0) (not (bobp)))
(if (and (not (looking-at parsep))
(re-search-backward "^\n" (max (1- (point)) (point-min)) t)
(looking-at parsep))
(setq arg (1+ arg))
(setq start (point))
;; Move back over paragraph-separating lines.
(forward-char -1) (beginning-of-line)
(while (and (not (bobp))
(progn (move-to-left-margin)
(looking-at parsep)))
(forward-line -1))
(if (bobp)
nil
(setq arg (1+ arg))
;; Go to end of the previous (non-separating) line.
(end-of-line)
;; Search back for line that starts or separates paragraphs.
(if (if fill-prefix-regexp
;; There is a fill prefix; it overrides parstart.
(let (multiple-lines)
(while (and (progn (beginning-of-line) (not (bobp)))
(progn (move-to-left-margin)
(not (looking-at parsep)))
(looking-at fill-prefix-regexp))
(unless (= (point) start)
(setq multiple-lines t))
(forward-line -1))
(move-to-left-margin)
;; This deleted code caused a long hanging-indent line
;; not to be filled together with the following lines.
;; ;; Don't move back over a line before the paragraph
;; ;; which doesn't start with fill-prefix
;; ;; unless that is the only line we've moved over.
;; (and (not (looking-at fill-prefix-regexp))
;; multiple-lines
;; (forward-line 1))
(not (bobp)))
(while (and (re-search-backward sp-parstart nil 1)
(setq found-start t)
;; Found a candidate, but need to check if it is a
;; REAL parstart.
(progn (setq start (point))
(move-to-left-margin)
(not (looking-at parsep)))
(not (and (looking-at parstart)
(or (not use-hard-newlines)
(bobp)
(get-text-property
(1- start) 'hard)))))
(setq found-start nil)
(goto-char start))
found-start)
;; Found one.
(progn
;; Move forward over paragraph separators.
;; We know this cannot reach the place we started
;; because we know we moved back over a non-separator.
(while (and (not (eobp))
(progn (move-to-left-margin)
(looking-at parsep)))
(forward-line 1))
;; If line before paragraph is just margin, back up to there.
(end-of-line 0)
(if (> (current-column) (current-left-margin))
(forward-char 1)
(skip-chars-backward " \t")
(if (not (bolp))
(forward-line 1))))
;; No starter or separator line => use buffer beg.
(goto-char (point-min))))))
(while (and (> arg 0) (not (eobp)))
;; Move forward over separator lines...
(while (and (not (eobp))
(progn (move-to-left-margin) (not (eobp)))
(looking-at parsep))
(forward-line 1))
(unless (eobp) (setq arg (1- arg)))
;; ... and one more line.
(forward-line 1)
(if fill-prefix-regexp
;; There is a fill prefix; it overrides parstart.
(while (and (not (eobp))
(progn (move-to-left-margin) (not (eobp)))
(not (looking-at parsep))
(looking-at fill-prefix-regexp))
(forward-line 1))
(while (and (re-search-forward sp-parstart nil 1)
(progn (setq start (match-beginning 0))
(goto-char start)
(not (eobp)))
(progn (move-to-left-margin)
(not (looking-at parsep)))
(or (not (looking-at parstart))
(and use-hard-newlines
(not (get-text-property (1- start) 'hard)))))
(forward-char 1))
(if (< (point) (point-max))
(goto-char start))))
(constrain-to-field nil opoint t)
;; Return the number of steps that could not be done.
arg))