123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- ;; word-counter.el: --- Count words in the buffer and in region
- ;; Time-stamp: "2002-04-18 13:52:05 drs"
- ;; Version: 0.2
- ;; $Revision:
- ;; $Id:
- ;; $Source:
- ;; Author: Dr. Rafael Sepúlveda. <drs@gnulinux.org.mx>
- ;; Mantainer: Dr. Rafael Sepúlveda. <drs@gnulinux.org.mx>
- ;; Copyright (C) 2001-2002, Dr. Rafael Sepúlveda <drs@gnulinux.org.mx>
- ;; This program is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation.
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program; if not, write to the Free Software Foundation,
- ;; Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- ;;; Commentary:
- ;; This file has all the functions that create a word counter. It has an
- ;; interactive word-counter and can display in the modeline the number of
- ;; words in the buffer and in the current region, plus more details.
- ;;; Usage:
- ;; To use the word counter, several steps have to be done. The
- ;; `word-counter.el' has to be in the load-path; let's say you'll put it in
- ;; `~/etc/emacs/elisp/word-counter.el', so your load path should be (suposing
- ;; that this is your only change to load-path):
- ;; (setq load-path (cons "~/etc/emacs/elisp" load-path))
- ;;
- ;; and also you can add this snippet to your `.emacs' to load and configure the
- ;; word-counter:
- ;; -----
- ;; (require 'word-counter) ;load the word counter
- ;;
- ;; ;; run word counter after each command (including key presses), the
- ;; ;; word counter code, will know when exactly to run and don't overload
- ;; ;; (too much) the computer.
- ;; (add-hook 'post-command-hook 'word-count)
- ;;
- ;; ;; set limit count to 10,000 words in every major mode; set limit to
- ;; ;; 50,000 words in text-mode.
- ;; (setq wc-max-words-alist
- ;; '((text-mode . 50000)
- ;; (t . 10000)))
- ;;
- ;; ;; toggle on the word-counter on every mode, except for `eshell'.
- ;; (setq wc-toggle-mode-alist
- ;; '((eshell-mode . nil)
- ;; (t . t)))
- ;; -----
- ;;
- ;; Finally you have to add a variable named `word-counter' to the mode-line
- ;; so it can be displayed there. If you use the default mode-line, use this
- ;; in your `.emacs':
- ;; -----
- ;; (setq default-mode-line-format
- ;; (list "-"
- ;; 'mode-line-mule-info
- ;; 'mode-line-modified
- ;; 'mode-line-frame-identification
- ;; 'mode-line-buffer-identification
- ;; " "
- ;; 'global-mode-string
- ;; " %[("
- ;; 'mode-name
- ;; 'mode-line-process
- ;; 'minor-mode-alist
- ;; "%n"
- ;; ")%]--"
- ;; '(which-func-mode ("" which-func-format "--"))
- ;; '(line-number-mode "L%l--")
- ;; '(column-number-mode "C%c--")
- ;; 'word-counter ;this is the only change.
- ;; '(-3 . "%p")
- ;; "-%-"))
- ;; (setq mode-line-format default-mode-line-format)
- ;; -----
- ;;; TODO:
- ;; - Valorate if a fix-point-goal-counter will be needed. (not point-min,
- ;; and not in a region; mainly to exclude a header or footer of
- ;; the buffer).
- ;; - Check why the hooks in html-mode (psgml) stop updating of the
- ;; mode-line counter.
- ;; - Check why the buffer doesn't make an update if called from a command
- ;; in Eshell until a key is pressed (Maybe Eshell bug).
- ;; - See if using a function eval instead of mayor modes in
- ;; `wc-max-words-alist', `wc-regexp-alist', `wc-toggle-mode-alist'
- ;; will be useful.
- ;;; Code:
- ;; User-definable Variables
- (defvar wc-max-words-alist
- '((t . 10000))
- "Define a limit for Word counter depending on the major mode.
- This limit is used so that computation time takes not too long.
- The bigger the buffer is, the longer the word count will take.
- The alist is formed like this:
- (MAJOR-MODE . MAX-WORDS)
- MAJOR-MODE: The name of the selected major mode.
- MAX-WORDS: Integer that defines the maximum number of words before
- word count stop the count.
- If word count is bigger than MAX-WORDS, the count will stop. This
- limit is only for a non-interactive call. Interactive calls count all
- words without any overflow limit.")
- (defvar wc-max-words-local nil
- "Define a limit for word counter in the local buffer.
- If word count is bigger than INTEGER, stop the count. Prevent overflow.
- This limit is only for a non-interactive call. Interactive calls count
- all words without any overflow limit.
- If this variable is set, `wc-max-words-alist' will be ignored on that
- buffer.")
- (make-variable-buffer-local 'wc-max-words-local)
- (defvar wc-reached-goal-function
- (function
- (lambda ()
- (beep)))
- "Variable that runs this SEXP when goal-words limit is reached.
- If nil, only the mark '!' in the minibuffer will be shown when the
- limit is reached or passed.
- To set the goal words (buffer-local) use `wc-set-goal-words' interactive
- function.")
- (defvar wc-regexp-alist
- '((t . "\\w+\\W*"))
- "Regexp that match words on a given major mode.
- This is an alist so it can define a different regexp for any given
- major mode, if a different criteria for 'word' is needed.")
- (defvar wc-regexp-local nil
- "*Regexp that match words in the local buffer.
- This buffer-local variable can hold a regexp definition different for
- that buffer without considering the major mode used.
- If this variable is set, `wc-regexp-alist' will be ignored on that
- buffer.")
- (make-variable-buffer-local 'wc-regexp-local)
- (defvar wc-toggle-mode-alist
- '((t . t))
- "List major modes that will activate or deactivate the word counter.
- The alist is formed like this:
- (MAJOR-MODE . t/nil)
- MAJOR-MODE: the name of the selected mayor mode.
- t/nil: 't' will activate the counter on that mode; 'nil' will deactivate
- the counter on that mode.
- If the last line is '(t . t)' it means that the counter will activate
- on every not-listed mode. If '(t . nil)' the counter will deactivate
- the counter on every not-listed mode.")
- ;;; No user costumizations beyond this point.
- ;; Control Variables
- (defvar last-buffer-modified-tick -1
- "Check if buffer needs a 'refresh' of the word count.
- Used to prevent the word count on every keystroke and to force an update
- in the modeline (usually after a region word count).
- The variable's value '-1' is only for convention; uses an integer as the
- default value type; uses a negative number to avoid any conflict with a
- possible value and to avoid any conflict using 'nil'.")
- (make-variable-buffer-local 'last-buffer-modified-tick)
- (defvar word-counter nil
- "Show in each buffer it's own word count.")
- (make-variable-buffer-local 'word-counter)
- (defvar wc-goal-words nil
- "Maximum number of words in the buffer before an alert is made.
- This variable is set by the `wc-set-goal-words' function.")
- (make-variable-buffer-local 'wc-goal-words)
- (defvar wc-words-in-buffer-flag nil
- "Non-nil means display current buffer word count.")
- (make-variable-buffer-local 'wc-words-in-buffer-flag)
- (defvar wc-words-in-region-flag nil
- "Non-nil means display current region word count.")
- (make-variable-buffer-local 'wc-words-in-region-flag)
- (defvar wc-time-control 0
- "Don't run the word counter less than a second from the last count.
- This way Emacs doesn't have to evaluate the word counter too often;
- like when you keep pressed a key.")
- (make-variable-buffer-local 'word-time-control)
- ;; Functions
- ;;; Main function.
- (defun word-count (&optional force)
- "Count the number of words in a buffer and region.
- Count words in buffer if region is not set; if region is set, then
- count the words in both the region and the buffer.
- If FORCE is non-nil then force the word count overriding
- `wc-time-control' variable (only used in non-interactive calls)."
- (interactive)
- (let ((overflow ;set the overflow limit
- (or
- wc-max-words-local
- (cond ((assq major-mode wc-max-words-alist)
- (cdr (assq major-mode wc-max-words-alist)))
- ((assq t wc-max-words-alist)
- (cdr (assq t wc-max-words-alist))))));)
- (regexp ;set the regexp
- (or wc-regexp-local
- (cond ((assq major-mode wc-regexp-alist)
- (cdr (assq major-mode wc-regexp-alist)))
- ((assq t wc-regexp-alist)
- (cdr (assq t wc-regexp-alist)))))))
- (if (interactive-p)
- (word-count-interactive regexp)
- (word-count-non-interactive regexp overflow force))))
- (defun word-count-interactive (regexp)
- "Report the word count on the echo area.
- This function is executed only when 'word-count' is called
- interactively.
- REGEXP: Regular expression used to match words."
- (let ((region-message
- (if mark-active
- "Region: %d words. "
- ""))
-
- (buffer-message
- "Buffer: %d words.")
- (goal-message
- (if wc-goal-words
- (if (> wc-words-in-buffer-flag wc-goal-words)
- " Goal count: %d Exceded!"
- " Goal count: %d.")
- "")))
- (message "Counting words, please wait...")
- (message (concat region-message buffer-message goal-message)
- (if (not (zerop (length region-message)))
- (setq wc-words-in-region-flag
- (wc-region (region-beginning) (region-end) regexp nil))
- (setq wc-words-in-buffer-flag
- (wc-region (point-min) (point-max) regexp nil)))
- (if (not (zerop (length region-message)))
- (setq wc-words-in-buffer-flag
- (wc-region (point-min) (point-max) regexp nil))
- wc-goal-words)
- wc-goal-words)))
- (defun word-count-non-interactive (regexp overflow force)
- "Report the word count on the modeline.
- This function is executed only when 'word-count' is called
- non-interactively.
- REGEXP: Regexp used to match words in the buffer.
- OVERFLOW: Integer that defines the maximum number of words before
- word count stop the count.
- FORCE: Force the counter to run."
- (let ((activate-counter ;see if word-counter will run on this major-mode.
- (cond ((assq major-mode wc-toggle-mode-alist)
- (cdr (assq major-mode wc-toggle-mode-alist)))
- ((assq t wc-toggle-mode-alist)
- (cdr (assq t wc-toggle-mode-alist)))))
- (wc-format-string ;set the minibuffer string
- (if mark-active
- "%sW%s/%s--"
- "%sW%s--")))
-
- (when
- (or force ;if non-nil, force update of counter
- (and activate-counter ;counter will run, depending on `wc-toggle-mode-alist'
- (not (active-minibuffer-window)) ;don't count words on minibuffer
- (or mark-active
- (and
- (not (= last-buffer-modified-tick ;if buffer has been modified
- (buffer-modified-tick)))
- (< wc-time-control (cadr (current-time)))) ;don't count too often
- (= last-buffer-modified-tick -1)))) ; eval 't' when quit from region
- (if mark-active
- (progn
- (setq wc-words-in-region-flag
- (wc-region (region-beginning) (region-end) regexp overflow))
- (wc-show wc-words-in-buffer-flag
- wc-words-in-region-flag
- wc-format-string
- wc-goal-words)
- (setq last-buffer-modified-tick -1))
- (progn
- (setq wc-words-in-buffer-flag
- (wc-region (point-min) (point-max) regexp overflow))
- (wc-show wc-words-in-buffer-flag
- wc-words-in-region-flag
- wc-format-string
- wc-goal-words)
- (setq last-buffer-modified-tick (buffer-modified-tick))))
-
- (setq wc-time-control (cadr (current-time))))))
- ;;; Update interactively the `wc-goal-words' variable.
- (defun wc-set-goal-words ()
- "Set the maximum number of words in the buffer before an alert is made.
- The number asked in the minibuffer has to be an integer.
- If the value entered is not a number, zero is asumed.
- If the value entered is a number but not an integer, only the integer
- part is used.
- If the value entered is a negative integer, the goal counter is
- deactivated."
- (interactive)
- (let ((goal ;set the goal word number.
- (string-to-number
- (read-string
- (concat
- (and wc-goal-words (format "Actual goal: %d. " wc-goal-words))
- "New Goal: (a negative integer deactivates goal count) ")))))
- (cond ((< goal 0)
- (message "Word goal counter deactivated!"
- (setq wc-goal-words nil)))
- ((= goal 0) ;if given a non-numeric value, '0' is assumed.
- (if (y-or-n-p (format "0 is not recomended. Are you sure? " goal))
- (message "New goal set to %d."
- (setq wc-goal-words goal))
- (wc-set-goal-words)))
- (t
- (message "New goal set to %d."
- (setq wc-goal-words goal))))
- (word-count t)))
- ;;; Update the `word-counter' variable info; will be updated in the modeline.
- (defun wc-show (buffer-count region-count format-string goal-words)
- "Set the correct values in the `word-counter' variable.
- All parameters (BUFFER-COUNT, REGION-COUNT, FORMAT-STRING and GOAL-WORDS)
- are passed from other functions.
- This variable will be displayed in the mode-line."
- (let ((first (if mark-active
- (or region-count "??")
- (or buffer-count "??")))
- (second (if mark-active
- (or buffer-count "??")))
- (goal-mark (if (and goal-words (> buffer-count goal-words))
- (progn
- (funcall wc-reached-goal-function)
- "!")
- "")))
- (setq word-counter
- (format
- format-string
- goal-mark
- first
- second))
- (force-mode-line-update)))
- ;;; Actual word counter.
- (defun wc-region (beg end regexp overflow)
- "Count the number of words between BEG and END.
- The argument REGEXP is the actual regular expresion used in this buffer.
- The argument OVERFLOW marks the maximum number of words found before
- stopping, to prevent an overflow."
- (let ((count 0))
- (save-excursion
- (goto-char beg)
- (while (and (< (point) end)
- (re-search-forward regexp end t)
- (if overflow
- (or (< count overflow) ;return 'nil' if overflow.
- (progn (setq count nil) nil))
- t))
- (setq count (1+ count))))
- count))
- (provide 'word-counter)
- ;;; word-counter.el ends here
|