summary refs log tree commit diff stats
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/acdw-org.el87
1 files changed, 87 insertions, 0 deletions
diff --git a/lisp/acdw-org.el b/lisp/acdw-org.el index bb72eb8..3f0c4ea 100644 --- a/lisp/acdw-org.el +++ b/lisp/acdw-org.el
@@ -243,4 +243,91 @@ the deletion might narrow the column."
243 (org-table-copy-down n) 243 (org-table-copy-down n)
244 (acdw-org/return-dwim n))) 244 (acdw-org/return-dwim n)))
245 245
246(defun acdw-org/word-count (beg end
247 &optional count-latex-macro-args?
248 count-footnotes?)
249 "Report the number of words in the Org mode buffer or selected region.
250Ignores:
251- comments
252- tables
253- source code blocks (#+BEGIN_SRC ... #+END_SRC, and inline blocks)
254- hyperlinks (but does count words in hyperlink descriptions)
255- tags, priorities, and TODO keywords in headers
256- sections tagged as 'not for export'.
257
258The text of footnote definitions is ignored, unless the optional argument
259COUNT-FOOTNOTES? is non-nil.
260
261If the optional argument COUNT-LATEX-MACRO-ARGS? is non-nil, the word count
262includes LaTeX macro arguments (the material between {curly braces}).
263Otherwise, and by default, every LaTeX macro counts as 1 word regardless
264of its arguments."
265 (interactive "r")
266 (unless mark-active
267 (setf beg (point-min)
268 end (point-max)))
269 (let ((wc 0)
270 (latex-macro-regexp "\\\\[A-Za-z]+\\(\\[[^]]*\\]\\|\\){\\([^}]*\\)}"))
271 (save-excursion
272 (goto-char beg)
273 (while (< (point) end)
274 (cond
275 ;; Ignore comments.
276 ((or (org-in-commented-line) (org-at-table-p))
277 nil)
278 ;; Ignore hyperlinks. But if link has a description, count
279 ;; the words within the description.
280 ((looking-at org-bracket-link-analytic-regexp)
281 (when (match-string-no-properties 5)
282 (let ((desc (match-string-no-properties 5)))
283 (save-match-data
284 (incf wc (length (remove "" (org-split-string
285 desc "\\W")))))))
286 (goto-char (match-end 0)))
287 ((looking-at org-any-link-re)
288 (goto-char (match-end 0)))
289 ;; Ignore source code blocks.
290 ((org-in-regexps-block-p "^#\\+BEGIN_SRC\\W" "^#\\+END_SRC\\W")
291 nil)
292 ;; Ignore inline source blocks, counting them as 1 word.
293 ((save-excursion
294 (backward-char)
295 (looking-at org-babel-inline-src-block-regexp))
296 (goto-char (match-end 0))
297 (setf wc (+ 2 wc)))
298 ;; Count latex macros as 1 word, ignoring their arguments.
299 ((save-excursion
300 (backward-char)
301 (looking-at latex-macro-regexp))
302 (goto-char (if count-latex-macro-args?
303 (match-beginning 2)
304 (match-end 0)))
305 (setf wc (+ 2 wc)))
306 ;; Ignore footnotes.
307 ((and (not count-footnotes?)
308 (or (org-footnote-at-definition-p)
309 (org-footnote-at-reference-p)))
310 nil)
311 (t
312 (let ((contexts (org-context)))
313 (cond
314 ;; Ignore tags and TODO keywords, etc.
315 ((or (assoc :todo-keyword contexts)
316 (assoc :priority contexts)
317 (assoc :keyword contexts)
318 (assoc :checkbox contexts))
319 nil)
320 ;; Ignore sections marked with tags that are
321 ;; excluded from export.
322 ((assoc :tags contexts)
323 (if (intersection (org-get-tags-at) org-export-exclude-tags
324 :test 'equal)
325 (org-forward-same-level 1)
326 nil))
327 (t
328 (incf wc))))))
329 (re-search-forward "\\w+\\W*")))
330 (message (format "%d words in %s." wc
331 (if mark-active "region" "buffer")))))
332
246(provide 'acdw-org) 333(provide 'acdw-org)