unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* How to detect words being added and removed to a buffer in a minor mode?
@ 2022-11-19 20:44 Dmitrii Pisarenko
  2022-11-21  9:39 ` Eric S Fraga
  0 siblings, 1 reply; 12+ messages in thread
From: Dmitrii Pisarenko @ 2022-11-19 20:44 UTC (permalink / raw)
  To: help-gnu-emacs

Hello!

I want to create a minor mode which allows you to see the number of words in a buffer.

I want it to work in the following way:

1. User enters "M-x pwc-set-counter" and sets the word counter to a particular number.
2. Whenever a word is added to a buffer, the word counter is incremented.
3. Whenever a word is removed from a buffer, the word counter is decremented.
4. As long as the minor mode is active, the value of the word counter is visible in the mode line (same place where current row and column are shown in some modes).

Restrictions:

1. This minor mode should work for text (Org mode) buffers with up to 3000 words (i. e. we are NOT talking gigabytes of text).
2. This minor mode must be compatible with the evil and Org mode.

How is the minor mode I want to develop different from existing solutions?

I want to be able to count words that I wrote on a particular day.

Imagine I have a goal of writing at least 1000 words per day. When I begin to write on a new day, I reset the word counter to zero. Then I write in one buffer and see the progress in the modeline. Then I may switch to a different buffer (a different chapter), manually change the word counter and continue to work.

What I want to implement is the word counter from Scrivener which shows you the number of words you wrote today across different chapters. It also shows you a visual representatio of the percentage of the daily quota.

As far as I know, none of the existing Emacs word counters offers this functionality (correct me if I'm wrong).

Questions:

1. How can I create an "event listener" (sorry for the non-Emacsian terminology) which would be called whenever the user types something in the buffer?
2. Are there existing functions that count the words in the current buffer which I can reuse in my minor mode?

Thanks in advance

Pravles

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-19 20:44 How to detect words being added and removed to a buffer in a minor mode? Dmitrii Pisarenko
@ 2022-11-21  9:39 ` Eric S Fraga
  2022-11-21 10:08   ` Jean Louis
  0 siblings, 1 reply; 12+ messages in thread
From: Eric S Fraga @ 2022-11-21  9:39 UTC (permalink / raw)
  To: help-gnu-emacs

On Saturday, 19 Nov 2022 at 20:44, Dmitrii Pisarenko wrote:
> 1. How can I create an "event listener" (sorry for the non-Emacsian
> terminology) which would be called whenever the user types something
> in the buffer?

Naïvely (i.e. I may be totally ignorant of a proper solution), there is
post-command-hook which can be used to process every single key event.

-- 
Eric S Fraga via gnus (Emacs 29.0.50 2022-11-13) on Debian 11.4




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21  9:39 ` Eric S Fraga
@ 2022-11-21 10:08   ` Jean Louis
  2022-11-21 10:34     ` Emanuel Berg
                       ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Jean Louis @ 2022-11-21 10:08 UTC (permalink / raw)
  To: Eric S Fraga; +Cc: help-gnu-emacs

* Eric S Fraga <e.fraga@ucl.ac.uk> [2022-11-21 12:40]:
> On Saturday, 19 Nov 2022 at 20:44, Dmitrii Pisarenko wrote:
> > 1. How can I create an "event listener" (sorry for the non-Emacsian
> > terminology) which would be called whenever the user types something
> > in the buffer?
> 
> Naïvely (i.e. I may be totally ignorant of a proper solution), there is
> post-command-hook which can be used to process every single key event.

There are 2 conditions here, one is that user wish to see the number
of words visualy, other is that one wants to compare that once editing
is finished.

One need not see every single word counted. What if user enters 500
words at once, then it would be futil to slow down the process until
every single word has been counted.

It implies, one could invoke function with number of words each in a
while. 

In that case, one could use this function:

run-with-timer is an interactive byte-compiled Lisp function in
‘timer.el’.

(run-with-timer SECS REPEAT FUNCTION &rest ARGS)


-- 
Jean

Take action in Free Software Foundation campaigns:
https://www.fsf.org/campaigns

In support of Richard M. Stallman
https://stallmansupport.org/



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 10:08   ` Jean Louis
@ 2022-11-21 10:34     ` Emanuel Berg
  2022-11-21 11:27     ` Eric S Fraga
  2022-11-21 17:17     ` Marcin Borkowski
  2 siblings, 0 replies; 12+ messages in thread
From: Emanuel Berg @ 2022-11-21 10:34 UTC (permalink / raw)
  To: help-gnu-emacs

I remember some counter that increased as the buffers increased
in size, but probably this was/is based on chars and not
words.

There are places I'll remember ...

-- 
underground experts united
https://dataswamp.org/~incal




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 10:08   ` Jean Louis
  2022-11-21 10:34     ` Emanuel Berg
@ 2022-11-21 11:27     ` Eric S Fraga
  2022-11-21 12:40       ` Emanuel Berg
  2022-11-21 17:17     ` Marcin Borkowski
  2 siblings, 1 reply; 12+ messages in thread
From: Eric S Fraga @ 2022-11-21 11:27 UTC (permalink / raw)
  To: help-gnu-emacs

On Monday, 21 Nov 2022 at 13:08, Jean Louis wrote:
> One need not see every single word counted. What if user enters 500
> words at once, then it would be futil to slow down the process until
> every single word has been counted.

I agree completely.  Hence my use of naïve... I was answering the
specific request by the OP who wanted to capture every keystroke which
is indeed overkill and likely to introduce significant lag.

-- 
Eric S Fraga via gnus (Emacs 29.0.50 2022-11-13) on Debian 11.4




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 11:27     ` Eric S Fraga
@ 2022-11-21 12:40       ` Emanuel Berg
  0 siblings, 0 replies; 12+ messages in thread
From: Emanuel Berg @ 2022-11-21 12:40 UTC (permalink / raw)
  To: help-gnu-emacs

Eric S Fraga wrote:

> I was answering the specific request by the OP who wanted to
> capture every keystroke which is indeed overkill [...]

?

Because Emacs already does that.

-- 
underground experts united
https://dataswamp.org/~incal




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 10:08   ` Jean Louis
  2022-11-21 10:34     ` Emanuel Berg
  2022-11-21 11:27     ` Eric S Fraga
@ 2022-11-21 17:17     ` Marcin Borkowski
  2022-11-21 17:21       ` Marcin Borkowski
                         ` (2 more replies)
  2 siblings, 3 replies; 12+ messages in thread
From: Marcin Borkowski @ 2022-11-21 17:17 UTC (permalink / raw)
  To: Jean Louis; +Cc: Eric S Fraga, help-gnu-emacs


On 2022-11-21, at 11:08, Jean Louis <bugs@gnu.support> wrote:

> * Eric S Fraga <e.fraga@ucl.ac.uk> [2022-11-21 12:40]:
>> On Saturday, 19 Nov 2022 at 20:44, Dmitrii Pisarenko wrote:
>> > 1. How can I create an "event listener" (sorry for the non-Emacsian
>> > terminology) which would be called whenever the user types something
>> > in the buffer?
>> 
>> Naïvely (i.e. I may be totally ignorant of a proper solution), there is
>> post-command-hook which can be used to process every single key event.
>
> There are 2 conditions here, one is that user wish to see the number
> of words visualy, other is that one wants to compare that once editing
> is finished.
>
> One need not see every single word counted. What if user enters 500
> words at once, then it would be futil to slow down the process until
> every single word has been counted.
>
> It implies, one could invoke function with number of words each in a
> while. 
>
> In that case, one could use this function:
>
> run-with-timer is an interactive byte-compiled Lisp function in
> ‘timer.el’.
>
> (run-with-timer SECS REPEAT FUNCTION &rest ARGS)

I would rather look at `run-with-idle-timer'.

Also, there are `count-words-region' and `count-words'.  I'm not sure
what the differences are, you might want to look at their docstrings
and/or code.

Would a mode which displays the number of words in the current buffer
whenever you stop typing for a few seconds be enough?  (I am not sure
how exactly you want to obtain the "number of words typed in today".)

Hth,

-- 
Marcin Borkowski
http://mbork.pl



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 17:17     ` Marcin Borkowski
@ 2022-11-21 17:21       ` Marcin Borkowski
  2022-11-21 18:56       ` [External] : " Drew Adams
  2022-11-22 15:33       ` Emanuel Berg
  2 siblings, 0 replies; 12+ messages in thread
From: Marcin Borkowski @ 2022-11-21 17:21 UTC (permalink / raw)
  To: Jean Louis; +Cc: Eric S Fraga, help-gnu-emacs


On 2022-11-21, at 18:17, Marcin Borkowski <mbork@mbork.pl> wrote:

> Would a mode which displays the number of words in the current buffer
> whenever you stop typing for a few seconds be enough?  (I am not sure
> how exactly you want to obtain the "number of words typed in today".)

Ah, ok.  I reread your initial message.  I don't think yours is the
easiest way to do that.  Here's how I would do it: I would /store/ the
number of words in a buffer somewhere (a buffer-local variable is one
possible idea) every time the counting function is called /the first
time on any given day/.  This way you wouldn't have to set the counter
to zero etc. - Emacs could even plot a graph showing your progress
during several days?

And now I'm /very/ much tempted to actually write this piece of code.
It could be quite fun!

It's even better.  It /might/ be a good idea ot turn it into an
educational blog post about Elisp or something similar.

WDYT?  I think I could come up with a prototype within a few days...

Best,

-- 
Marcin Borkowski
http://mbork.pl



^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [External] : Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 17:17     ` Marcin Borkowski
  2022-11-21 17:21       ` Marcin Borkowski
@ 2022-11-21 18:56       ` Drew Adams
  2022-11-22 15:33       ` Emanuel Berg
  2 siblings, 0 replies; 12+ messages in thread
From: Drew Adams @ 2022-11-21 18:56 UTC (permalink / raw)
  To: Marcin Borkowski, Jean Louis; +Cc: Eric S Fraga, help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 1375 bytes --]

> Would a mode which displays the number of words in the current buffer
> whenever you stop typing for a few seconds be enough?  (I am not sure
> how exactly you want to obtain the "number of words typed in today".)

I already mentioned that you can get this for
free in the mode-line, from redisplay, using
library modeline-region.el.

Well, you get it automatically for the active
region, not the whole buffer.  To see it for
the full buffer at any time, just use `C-x C-x'.

If someone is interested in instead showing it
always for the full buffer (not only when it's
the active region), that's easily done by
tweaking the source code.
__

Attached is such a quick tweaking (could be
simplified).  I just added an option,
`mlr-use-whole-buffer-flag' for this.  Set
it to non-nil to continually get full-buffer
(or narrowed-buffer) counts instead of
active-region counts.

Essentially all I did was change uses of
(region-beginning) and (region-end) to
(point-min) and (point-max), and test the
added option for which pair to use.

Just replace the same definitions in
modeline-region.el with the versions in the
attached.

(I won't change the library this way, as I
don't think it's generally useful.)

If all you care about is the number of words
in the buffer, you can considerably pare
down the code - the result will be small.

[-- Attachment #2: throw-modeline-region-BUFFER.el --]
[-- Type: application/octet-stream, Size: 7778 bytes --]

(defcustom mlr-use-whole-buffer-flag nil
  "Non-nil means use the whole buffer, not the active region."
  :group 'modeline-region :type 'boolean)


(defconst mlr--region-style-default
  '(;; Format string
    (if mlr-rect-p
        (if (and (boundp 'mlr-rectangle-style)
                 (eq 'rows+cols+words+chars mlr-rectangle-style))
            mlr-rows+cols+words+chars-format ; " %d rows, %d cols, %d words, %d chars"
          mlr-rows+cols-format)              ;" %d rows, %d cols"
      (if (eq 'lines+words+chars mlr-non-rectangle-style)
          mlr-lines+words+chars-format ; " %d lines, %d words, %d chars"
        mlr-lines+chars-format))       ; " %d lines, %d chars"

    ;; Rows (rectangle) / Lines (region)
    (if mlr-rect-p
        (1+ (abs (- (line-number-at-pos (region-end)) ; Rows (rectangle)
                    (line-number-at-pos (region-beginning)))))
      (if mlr-use-whole-buffer-flag
          (count-lines (point-min) (point-max))
        (count-lines (region-beginning) (region-end)))) ; Lines (region)

    ;; Columns (rectangle) / Chars (short region) or Words (full region)
    (if mlr-rect-p
        (let ((rpc  (save-excursion (rectangle--pos-cols (region-beginning) (region-end)))))
          (abs (- (car rpc) (cdr rpc)))) ; Columns (rectangle)
      (if (eq 'lines+words+chars mlr-non-rectangle-style)
          (if mlr-use-whole-buffer-flag
              (count-words (point-min) (point-max))
            (count-words (region-beginning) (region-end))) ; Words (full region)
        (if mlr-use-whole-buffer-flag
            (- (point-max) (point-min))
          (- (region-end) (region-beginning))))) ; Chars (short region)

    ;; Words (rectangle) / Chars (full region)
    (if (and mlr-rect-p
             (boundp 'mlr-rectangle-style)
             (eq 'rows+cols+words+chars mlr-rectangle-style))
        (let ((words  0)
              beg end this-ws)
          (dolist (beg+end  (extract-rectangle-bounds (region-beginning) (region-end)))
            (setq beg      (car beg+end)
	          end      (cdr beg+end)
	          this-ws  (count-words beg end)
	          words    (+ words this-ws))
            (unless mlr-count-partial-words-flag
              (when (and (not (zerop this-ws))
		         (char-after (1- beg))  (equal '(2) (syntax-after (1- beg)))
		         (char-after beg)       (equal '(2) (syntax-after beg)))
	        (setq words    (1- words)
                      this-ws  (1- this-ws)))
              (when (and (not (zerop this-ws))
		         (char-after (1- end))  (equal '(2) (syntax-after (1- end)))
		         (char-after end)       (equal '(2) (syntax-after     end)))
	        (setq words  (1- words)))))
          words)                        ; Words (rectangle)
      (and (eq 'lines+words+chars mlr-non-rectangle-style)
           (if mlr-use-whole-buffer-flag
               (- (point-max) (point-min))
             (- (region-end) (region-beginning))))) ; Chars (full region)

    ;; Chars (rect)
    (and mlr-rect-p
         (boundp 'mlr-rectangle-style)
         (eq 'rows+cols+words+chars mlr-rectangle-style)
         (let ((chars  0)
               beg end)
           (dolist (beg+end  (extract-rectangle-bounds (region-beginning) (region-end)))
             (setq beg    (car beg+end)
                   end    (cdr beg+end)
                   chars  (+ chars (- end beg))))
	   chars)))                     ; Chars (rectangle)
  "Default value for option `mlr-region-style'.
It corresponds to the Customize `Value Menu' choice
`Lines (& words) & chars / rows & columns (& words & chars)'.")

(defcustom mlr-region-style mlr--region-style-default
  "Mode-line info about the active region.
Choose a style from the `Value Menu':

 * `Characters'.  Number of characters in region or rectangle.

 * `Bytes'.  Number of bytes in region or rectangle.

 * `Lines (& words) & chars / rows & columns (& words & chars)'.
   For a regular region, you see lines and characters.
   For a rectangular region you see rows and columns.

   This can optionally include words for region and words & chars for
   rectangle: see options `mlr-non-rectangle-style' and
   `mlr-rectangle-style' (for Emacs 26+).

   To change the concrete formatting used, change variables
   `mlr-lines+chars-format', `mlr-lines+words+chars-format',
   `mlr-rows+cols-format', and `mlr-rows+cols+words+chars-format'.

 * `Customized format'.  An arbitrary format you specify."
  :group 'modeline-region
  :type
  `(choice

    (const :tag "Characters"
           (mlr-chars-format
            (if (and mlr-rect-p  (fboundp 'extract-rectangle-bounds)) ; Emacs 26+
                (let ((chars  0)
                      beg end)
                  (dolist (beg+end  (extract-rectangle-bounds (region-beginning) (region-end)))
                    (setq beg    (car beg+end)
                          end    (cdr beg+end)
                          chars  (+ chars (- end beg))))
                  chars)
              (if mlr-use-whole-buffer-flag
                  (- (region-end) (region-beginning))
                (- (point-max) (point-min))))))

    ;; Should we use this instead, for calculating bytes?  It can sometimes be costly.
    ;; See https://emacs.stackexchange.com/a/29912/105.
    ;; (const :tag "Bytes: \"_ bytes\""
    ;;  (" %d bytes"
    ;;   (if (fboundp 'bufferpos-to-filepos) ; Emacs 25+
    ;;       (- (bufferpos-to-filepos (region-end) 'exact)
    ;;          (bufferpos-to-filepos (region-beginning) 'exact))
    ;;     (string-bytes (buffer-substring-no-propertiesw (region-beginning) (region-end))))))
    ;;
    (const :tag "Bytes"
           (mlr-bytes-format
            (if (and mlr-rect-p  (fboundp 'extract-rectangle-bounds)) ; Emacs 26+
                (let ((bytes  0)
                      beg end)
                  (dolist (beg+end  (extract-rectangle-bounds (region-beginning) (region-end)))
                    (setq beg    (car beg+end)
                          end    (cdr beg+end)
                          bytes  (+ bytes (string-bytes (buffer-substring-no-properties beg end)))))
                  bytes)
              (string-bytes (if mlr-use-whole-buffer-flag
                                (buffer-substring-no-properties (region-beginning) (region-end))
                              (buffer-substring-no-properties (point-min) (point-max)))))))

    ;; This is just the single value that's the value of `mlr--region-style-default'.
    (restricted-sexp :tag "Lines (& words) & chars / rows & columns (& words & chars)"
                     :match-alternatives ((lambda (val) (equal val mlr--region-style-default))))

    (list :tag "Customized format"
          (string :tag "Format string")
          (repeat :inline t (sexp :tag "Sexp argument for format string")))))

(defun mlr-show-region-p ()
  "Whether to show mode-line region info and highlighting.
Return non-nil if the region is active and nonempty, or emptiness is
OK.  Option `mlr-empty-region-flag' non-nil means emptiness is OK.

More precisely, return non-nil for the active region if either of
these conditions is true:
 * The region is not empty.
 * Option `mlr-empty-region-flag' is non-nil, and the last input did
   not use `mouse-1'.

The `mouse-1' test prevents highlighting the mode line whenever you
press `mouse-1' without dragging at least one character."
  (ignore-errors 
    (if mlr-use-whole-buffer-flag
        (and (not (eq 'down-mouse-1 (car-safe last-input-event)))
             (not (mouse-movement-p last-input-event)))
      (and (region-active-p)
           (or (> (region-end) (region-beginning))
               (and mlr-empty-region-flag
                    (not (eq 'down-mouse-1 (car-safe last-input-event)))
                    (not (mouse-movement-p last-input-event))))))))


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-21 17:17     ` Marcin Borkowski
  2022-11-21 17:21       ` Marcin Borkowski
  2022-11-21 18:56       ` [External] : " Drew Adams
@ 2022-11-22 15:33       ` Emanuel Berg
  2022-11-26  6:14         ` Marcin Borkowski
  2 siblings, 1 reply; 12+ messages in thread
From: Emanuel Berg @ 2022-11-22 15:33 UTC (permalink / raw)
  To: help-gnu-emacs

Marcin Borkowski wrote:

> Also, there are `count-words-region'

Remove.

> and `count-words'.

DWIM region beg end defaults to buffer.

-- 
underground experts united
https://dataswamp.org/~incal




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-22 15:33       ` Emanuel Berg
@ 2022-11-26  6:14         ` Marcin Borkowski
  2022-11-26 14:51           ` Emanuel Berg
  0 siblings, 1 reply; 12+ messages in thread
From: Marcin Borkowski @ 2022-11-26  6:14 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: help-gnu-emacs


On 2022-11-22, at 16:33, Emanuel Berg <incal@dataswamp.org> wrote:

> Marcin Borkowski wrote:
>
>> Also, there are `count-words-region'
>
> Remove.

Not sure if I understood?

>> and `count-words'.
>
> DWIM region beg end defaults to buffer.

Yes, I know that.

-- 
Marcin Borkowski
http://mbork.pl



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to detect words being added and removed to a buffer in a minor mode?
  2022-11-26  6:14         ` Marcin Borkowski
@ 2022-11-26 14:51           ` Emanuel Berg
  0 siblings, 0 replies; 12+ messages in thread
From: Emanuel Berg @ 2022-11-26 14:51 UTC (permalink / raw)
  To: help-gnu-emacs

Marcin Borkowski wrote:

>>> Also, there are `count-words-region'
>>
>> Remove.
>
> Not sure if I understood?

Those -region functions should be removed IMO.

>>> and `count-words'.
>>
>> DWIM region beg end defaults to buffer.

Because if we do them that way they are superfluous ...

-- 
underground experts united
https://dataswamp.org/~incal




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2022-11-26 14:51 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-19 20:44 How to detect words being added and removed to a buffer in a minor mode? Dmitrii Pisarenko
2022-11-21  9:39 ` Eric S Fraga
2022-11-21 10:08   ` Jean Louis
2022-11-21 10:34     ` Emanuel Berg
2022-11-21 11:27     ` Eric S Fraga
2022-11-21 12:40       ` Emanuel Berg
2022-11-21 17:17     ` Marcin Borkowski
2022-11-21 17:21       ` Marcin Borkowski
2022-11-21 18:56       ` [External] : " Drew Adams
2022-11-22 15:33       ` Emanuel Berg
2022-11-26  6:14         ` Marcin Borkowski
2022-11-26 14:51           ` Emanuel Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).