2

Problem

Stopped using wc-mode as it is not snappy anymore (as text writing usually is) with text document of 10k words. It appears that it counts the whole buffer after every change which seems to make it slow as logged here. A recent change was made to speed it up, but still produces considerable lag.

Partial Solution

There is a "solution" here, but it is so hard for me to implement it as I am not familiar with Lisp and inner workings of Emacs. There are two functions wc-buffer and wc-region, which are empty in the answer (below), for which I have no clue what should be in the function.

;; Function that counts words in buffer
(defun wc-buffer ()
  ...)

;; Function that counts words in a region
(defun wc-region (rbeg rend)
  ...)
    
(defvar-local a1 nil)
(defvar-local a2 nil)
(defvar-local curr-wc nil)
    
(defun init-function ()
  (interactive)
  (save-excursion
    (setq curr-wc (wc-buffer))))

(defun wc-update-before (change-beg change-end)
  (setq pos1 (max 1 (1- change-beg)))
  (setq pos2 (min (point-max) (1+ change-end)))
    
  (setq a1 (wc-region pos1 pos2)))
    
(defun wc-update-after (change-beg change-end prev-len)
  (if (bound-and-true-p a1) 
      (progn
        (setq pos1 (max 1 (1- change-beg)))
        (setq pos2 (min (point-max) (1+ change-end)))
            
        (setq a2 (wc-region pos1 pos2))
            
        (setq curr-wc (+ curr-wc (- a2 a1))))
      nil))

(init-function)
(add-hook 'before-change-functions #'wc-update-before nil t) 
(add-hook 'after-change-functions #'wc-update-after nil t)
(setq inhibit-modification-hooks nil)

Help

I ask for someone either to tell me explicitly what goes in these functions or suggest alternatives for counting words and not having lag while displaying them in the mode-line.

Reproducing

You can reproduce this by: installing wc-mode and enabling it and then when you have more than 10k words, you can expect (depending on your PC) quite some lag while typing. It's not snappy.

Stefan
  • 26,154
  • 3
  • 46
  • 84
Pandian Le
  • 260
  • 3
  • 13
  • This sounds like a "please code something for me" pseudo-question - it lacks focus. What exactly is the question/problem? What does "lagged the hell out of my..." really mean? Please show your code - minimal recipe to reproduce the problem, starting from `emacs -Q` (no init file). Thx. – Drew Jan 04 '20 at 16:14
  • For time consuming functions that I use to obtain certain data for the mode-line (e.g., alphabetizing minor-mode lighter indicators), I use a two-step approach to deal with that: (1) record the most recent value and reuse that recorded value when certain criteria is met, instead of calculating anew; (2), if `this-command` is `eq` to `last-command` and the command is a `memq` of a predefined list of certain commands, then use the pre-recorded value from step 1 above. Flyspell uses step 2 if you are interested in seeing how that works. – lawlist Jan 04 '20 at 17:36
  • @drew I have edited the question to make it more clear. Please let me know if this is good enough? – Pandian Le Jan 05 '20 at 09:41
  • Much better, thanks. It still looks like a request to code up some functions, and it requires turning to another question for understanding. Can you show the code you tried, and pose a specific question about that, and can you make this question more standalone? This site is really better for specific Q&A, not discussion. You might want to post this more open-ended question on a site such as Reddit, where discussion etc. is invited. – Drew Jan 05 '20 at 18:13
  • @drew modified the question added code from the linked answer. No, I don't want to post it on reddit, I want direction in which I need to go. I want a solution. I will try using the suggestions provided by user12593215 and figure out how to implient it in the modeline I guess. – Pandian Le Jan 09 '20 at 20:18
  • @lawlist I will check out how flyspell is written in case the answer provided by user12593215 doesn't work. Thanks. – Pandian Le Jan 09 '20 at 20:18

3 Answers3

1

Solution 1, add (setq wc-idle-wait 2) into ~/.emacs if you need get updated with the data in real time,

enter image description here

Please note wc-idle-wait should be set before loading wc-mode.el because of below code in wc-mode.el,

(setq wc-timer-tracker
      (run-with-idle-timer
       wc-idle-wait t
       '(lambda ()
          (when wc-mode
            (setq wc-buffer-stats (wc-mode-update))))))

Solution 2, do the calculation only before saving current buffer. But you need set wc-idle-wait to a bigger value first.

Here is sample setup (see my comment, you can do some further optimization inside before-save-hook),

(setq wc-idle-wait most-positive-fixnum)
(add-hook 'before-save-hook
          (lambda ()
            ;; you can check `buffer-file-name' or `major-mode' first
            (setq wc-buffer-stats (wc-mode-update))))
chen bin
  • 4,781
  • 18
  • 36
0

I think you can use count-words to implement the wc-buffer function and count-words-region to implement the wc-region function.

Like this:

(defun wc-buffer ()
    (count-words (point-min) (point-max)))

(defun wc-region (rbeg rend)
    (count-words-region rbeg rend))
Muihlinn
  • 2,576
  • 1
  • 14
  • 22
  • I wonder if we even need these two functions. the `count-words` scope is already the whole buffer and the `count-words-region` returns the word and character count of the marked region. so imho these functions are redundant. That said, +1 for mentioning these two builtin functions. – Mehrad Mahmoudian Sep 08 '22 at 16:00
0

See mode-line-idle, the readme shows an example of showing a word count in a way that's not blocking.

This allows you to add anything you like to your mode line in a way that doesn't block your workflow.

ideasman42
  • 8,375
  • 1
  • 28
  • 105