orgmode-to-gemini-blog/org-counting-words.el

95 lines
4 KiB
EmacsLisp
Raw Permalink Normal View History

2025-10-26 14:58:49 +01:00
;;; org-counting-words.el --- Compter les mots en excluant les tags Org -*- lexical-binding: t; -*-
(defun org--text-without-common-tags (text)
"Retourne TEXT sans éléments Org communs considérés comme 'tags'.
Supprime notamment:
- les drawers PROPERTIES
- les lignes directives #+...
- les tags en fin d'entêtes Org (* titre :tag1:tag2:)."
(when text
(let ((clean text))
;; Supprimer les drawers PROPERTIES
(setq clean (replace-regexp-in-string
"^:PROPERTIES:[\s\S]*?:END:\n?" ""
clean nil 'literal))
;; Supprimer les lignes #+keyword: ... (Filetags, tags, title, etc.)
(setq clean (replace-regexp-in-string
"^#\+[^\n]*\n" ""
clean))
;; Supprimer les tags en fin d'entête Org (ex: * Titre :tag1:tag2:)
(setq clean (replace-regexp-in-string
"^\*+ .*? \(:[[:alnum:]_@#%:]+:\)[[:space:]]*$" ""
clean))
clean)))
(defun org-count-words-without-tags (&optional beg end)
"Compter les mots dans le buffer ou région, en excluant les tags Org.
Utilise `count-words-region' après nettoyage du texte. Si une région
active est présente (ou si BEG/END sont fournis), compte dans la région,
sinon dans tout le buffer. Affiche le résultat et retourne l'entier."
(interactive)
(let* ((region-active (or beg end (use-region-p)))
(start (or beg (if region-active (region-beginning) (point-min))))
(finish (or end (if region-active (region-end) (point-max))))
(raw (buffer-substring-no-properties start finish))
(clean (org--text-without-common-tags raw))
(count (with-temp-buffer
(insert clean)
(count-words-region (point-min) (point-max)))))
(when (called-interactively-p 'any)
(message (if region-active
"Mots (sans tags) dans la région: %d"
"Mots (sans tags) dans le buffer: %d")
count))
count))
(provide 'org-counting-words)
;;; org-counting-words.el ends here
;;; --- Mode d'affichage du nombre de mots dans la modeline ---
(defvar org-count-words--idle-seconds 1
"Délai d'inactivité (en secondes) avant mise à jour du compteur.")
(defvar-local org-count-words--modeline ""
"Texte affiché dans la modeline pour le compteur de mots.")
(defvar org-count-words--idle-timer nil
"Timer d'inactivité pour mettre à jour le compteur de mots.")
(defun org-count-words--maybe-update ()
"Mettre à jour la modeline si on est dans un buffer Org visible."
(when (and (eq major-mode 'org-mode)
(get-buffer-window (current-buffer) 'visible))
(let ((n (org-count-words-without-tags)))
(setq org-count-words--modeline (format " W:%d" n))
(force-mode-line-update t))))
(define-minor-mode org-count-words-modeline-mode
"Affiche le nombre de mots (sans tags Org) dans la modeline.
Le décompte est recalculé ~1s après la dernière action (idle)."
:init-value nil
:global nil
:lighter nil
(if org-count-words-modeline-mode
(progn
;; S'assurer que la variable est dans `global-mode-string`
(make-local-variable 'global-mode-string)
(add-to-list 'global-mode-string 'org-count-words--modeline t)
;; Démarrer le timer d'inactivité (buffer-local via wrapper)
(setq org-count-words--idle-timer
(run-with-idle-timer org-count-words--idle-seconds t
#'org-count-words--maybe-update))
(org-count-words--maybe-update))
;; Désactivation
(setq global-mode-string (delq 'org-count-words--modeline global-mode-string))
(when (and org-count-words--idle-timer (timerp org-count-words--idle-timer))
(cancel-timer org-count-words--idle-timer))
(setq org-count-words--idle-timer nil)
(setq org-count-words--modeline "")
(force-mode-line-update t)))
;; Activer automatiquement dans org-mode
(add-hook 'org-mode-hook #'org-count-words-modeline-mode)
;; elisp : compter les mots dans le buffer en excluant les tags orgmode