eintr: count-words-in-defun

 
 14.3 The ‘count-words-in-defun’ Function
 ========================================
 
 We have seen that there are several ways to write a ‘count-words-region’
 function.  To write a ‘count-words-in-defun’, we need merely adapt one
 of these versions.
 
    The version that uses a ‘while’ loop is easy to understand, so I am
 going to adapt that.  Because ‘count-words-in-defun’ will be part of a
 more complex program, it need not be interactive and it need not display
 a message but just return the count.  These considerations simplify the
 definition a little.
 
    On the other hand, ‘count-words-in-defun’ will be used within a
 buffer that contains function definitions.  Consequently, it is
 reasonable to ask that the function determine whether it is called when
 point is within a function definition, and if it is, to return the count
 for that definition.  This adds complexity to the definition, but saves
 us from needing to pass arguments to the function.
 
    These considerations lead us to prepare the following template:
 
      (defun count-words-in-defun ()
        "DOCUMENTATION..."
        (SET UP...
           (WHILE LOOP...)
         RETURN COUNT)
 
 As usual, our job is to fill in the slots.
 
    First, the set up.
 
    We are presuming that this function will be called within a buffer
 containing function definitions.  Point will either be within a function
 definition or not.  For ‘count-words-in-defun’ to work, point must move
 to the beginning of the definition, a counter must start at zero, and
 the counting loop must stop when point reaches the end of the
 definition.
 
    The ‘beginning-of-defun’ function searches backwards for an opening
 delimiter such as a ‘(’ at the beginning of a line, and moves point to
 that position, or else to the limit of the search.  In practice, this
 means that ‘beginning-of-defun’ moves point to the beginning of an
 enclosing or preceding function definition, or else to the beginning of
 the buffer.  We can use ‘beginning-of-defun’ to place point where we
 wish to start.
 
    The ‘while’ loop requires a counter to keep track of the words or
 symbols being counted.  A ‘let’ expression can be used to create a local
 variable for this purpose, and bind it to an initial value of zero.
 
    The ‘end-of-defun’ function works like ‘beginning-of-defun’ except
 that it moves point to the end of the definition.  ‘end-of-defun’ can be
 used as part of an expression that determines the position of the end of
 the definition.
 
    The set up for ‘count-words-in-defun’ takes shape rapidly: first we
 move point to the beginning of the definition, then we create a local
 variable to hold the count, and finally, we record the position of the
 end of the definition so the ‘while’ loop will know when to stop
 looping.
 
    The code looks like this:
 
      (beginning-of-defun)
      (let ((count 0)
            (end (save-excursion (end-of-defun) (point))))
 
 The code is simple.  The only slight complication is likely to concern
 ‘end’: it is bound to the position of the end of the definition by a
 ‘save-excursion’ expression that returns the value of point after
 ‘end-of-defun’ temporarily moves it to the end of the definition.
 
    The second part of the ‘count-words-in-defun’, after the set up, is
 the ‘while’ loop.
 
    The loop must contain an expression that jumps point forward word by
 word and symbol by symbol, and another expression that counts the jumps.
 The true-or-false-test for the ‘while’ loop should test true so long as
 point should jump forward, and false when point is at the end of the
 definition.  We have already redefined the regular expression for this,
 so the loop is straightforward:
 
      (while (and (< (point) end)
                  (re-search-forward
                   "\\(\\w\\|\\s_\\)+[^ \t\n]*[ \t\n]*" end t))
        (setq count (1+ count)))
 
    The third part of the function definition returns the count of words
 and symbols.  This part is the last expression within the body of the
 ‘let’ expression, and can be, very simply, the local variable ‘count’,
 which when evaluated returns the count.
 
    Put together, the ‘count-words-in-defun’ definition looks like this:
 
      (defun count-words-in-defun ()
        "Return the number of words and symbols in a defun."
        (beginning-of-defun)
        (let ((count 0)
              (end (save-excursion (end-of-defun) (point))))
          (while
              (and (< (point) end)
                   (re-search-forward
                    "\\(\\w\\|\\s_\\)+[^ \t\n]*[ \t\n]*"
                    end t))
            (setq count (1+ count)))
          count))
 
    How to test this?  The function is not interactive, but it is easy to
 put a wrapper around the function to make it interactive; we can use
 almost the same code as for the recursive version of
 ‘count-words-example’:
 
      ;;; Interactive version.
      (defun count-words-defun ()
        "Number of words and symbols in a function definition."
        (interactive)
        (message
         "Counting words and symbols in function definition ... ")
        (let ((count (count-words-in-defun)))
          (cond
           ((zerop count)
            (message
             "The definition does NOT have any words or symbols."))
           ((= 1 count)
            (message
             "The definition has 1 word or symbol."))
           (t
            (message
             "The definition has %d words or symbols." count)))))
 
 Let’s re-use ‘C-c =’ as a convenient keybinding:
 
      (global-set-key "\C-c=" 'count-words-defun)
 
    Now we can try out ‘count-words-defun’: install both
 ‘count-words-in-defun’ and ‘count-words-defun’, and set the keybinding,
 and then place the cursor within the following definition:
 
      (defun multiply-by-seven (number)
        "Multiply NUMBER by seven."
        (* 7 number))
           ⇒ 10
 
 Success!  The definition has 10 words and symbols.
 
    The next problem is to count the numbers of words and symbols in
 several definitions within a single file.