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