GNU Info

Info Node: (emacs-lisp-intro.info)count-words-in-defun

(emacs-lisp-intro.info)count-words-in-defun


Next: Several defuns Prev: Syntax Up: Words in a defun
Enter node , (file) or (file)node

The `count-words-in-defun' Function
===================================

   We have seen that there are several ways to write a
`count-word-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 (Note: Syntax), 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-region':

     ;;; 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.


automatically generated by info2www version 1.2.2.9