From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Abrahamsen Subject: v4, now with properties and inclusion tags Date: Wed, 08 Jun 2011 18:33:59 -0700 Message-ID: <87d3inhjp4.fsf_-_@ericabrahamsen.net> References: <867hal4a6t.wl%simon.guest@tesujimath.org> <87iptzoa5x.fsf@ucl.ac.uk> <86zknb9v1y.wl%simon.guest@tesujimath.org> <87oc3qg4f8.fsf@ucl.ac.uk> <86y62uxc90.wl%simon.guest@tesujimath.org> <86y62tyg2u.wl%simon.guest@tesujimath.org> <871v0leq3h.fsf@ericabrahamsen.net> <17141.1304048141@alphaville.dokosmarshall.org> <87k4edd6fw.fsf@ericabrahamsen.net> <3543.1304089011@alphaville.americas.hpqcorp.net> <8639l020r3.wl%simon.guest@tesujimath.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([140.186.70.92]:37689) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QUU8K-0001Go-V2 for emacs-orgmode@gnu.org; Wed, 08 Jun 2011 21:34:06 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QUU8J-0000oM-7f for emacs-orgmode@gnu.org; Wed, 08 Jun 2011 21:34:04 -0400 Received: from mail-pv0-f169.google.com ([74.125.83.169]:55250) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QUU8I-0000o4-Su for emacs-orgmode@gnu.org; Wed, 08 Jun 2011 21:34:03 -0400 Received: by pvc12 with SMTP id 12so614261pvc.0 for ; Wed, 08 Jun 2011 18:34:01 -0700 (PDT) In-Reply-To: <8639l020r3.wl%simon.guest@tesujimath.org> (Simon Guest's message of "Sat, 30 Apr 2011 09:42:08 +1200") List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: Simon Guest Cc: emacs-orgmode@gnu.org --=-=-= Content-Type: text/plain Simon Guest writes: > At Fri, 29 Apr 2011 10:56:51 -0400, > Nick Dokos wrote: >> Indeed: it would require a bit of refactoring of Simon's code to provide the >> function(s) to apply to each entry, and changes to the top level functions to >> use the mapping API instead of looping explicitly. > > That sounds like a good idea. I may at some stage want to exclude > counting certain trees, and then I may have another look at this if > someone else hasn't already done it. > > But for now, time pressure dictates I stop hacking on my word count > function. > > cheers, > Simon I'm afraid this is a bit of a two-steps-forward, one-step-back situation, but I've rejiggered Simon's code so that it now: 1. Uses the mapping and property APIs 2. Allows selection of subtrees for count via a tag 3. Sets wordcount totals for each subtree as a property, instead of an overlay First of all, this requires the fix to =org-end-of-meta-data-and-drawers= that I sent (and then re-sent) to this list earlier today. Otherwise it will "work funny". I changed it to use properties instead of overlays because I wanted something that was persistent, and available for programmatic manipulation. Plus, you can get an overlay effect with column view. So right now M-x org-word-count will do the following: 1. Add the subtree word count as a property (=org-wc-prop-name=) to each headline in the buffer 2. Respect the region, if it's active 3. Operate only on trees tagged with =org-wc-include-tag=, if that tag is present 4. Report a buffer/region word count total in the minibuffer 5. With a prefix arg, *only* give a minibuffer report, don't set properties The two variables =org-wc-include-tag= and =org-wc-prop-name= are buffer local, unless I've misunderstood how buffer local works and screwed it up. There's a helper function, =org-wc-remove-all-props= that can be used to remove the =org-wc-prop-name= property from all headlines. Does anyone else think that =org-entry-delete= should remove the whole drawer if there are no other properties left? This is very much a proposal, and I've got a bit of time to work on it, so I'm willing to field requests, though my elisp is bad. Two immediate possibilities would be: automatically excluding subtrees tagged "noexport", and using =org-context= to be cleverer about what to avoid. Further suggestions (and code fixes) welcome! Eric --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=org-wc.el Content-Transfer-Encoding: quoted-printable ;; org-wc.el ;; ;; Count words in org mode trees. ;; Shows word count per heading line, summed over sub-headings. ;; Aims to be fast, so doesn't check carefully what it's counting. ;-) ;; ;; Simon Guest, 23/4/11 ;; ;; Eric Abrahamsen, 8/6/11 ;; ;; Implementation based on: ;; - Paul Sexton's word count posted on org-mode mailing list 21/2/11. ;; - clock overlays ;; ;; v2=20 ;; 29/4/11 ;; Don't modify buffer, and fixed handling of empty sections. ;; ;; v3 ;; 29/4/11 ;; Handle narrowing correctly, so partial word count works on narrowed regi= ons. ;; ;; v4 ;; 8/6/11 ;; Use mapping and property APIs, no more overlays (setq org-wc-include-tag "count" org-wc-prop-name "WORDCOUNT") (make-variable-buffer-local 'org-wc-include-tag) (make-variable-buffer-local 'org-wc-prop-name) (defun org-word-count (beg end) (interactive "r") (let ((bmp (buffer-modified-p)) (tag (if (save-excursion (goto-char (point-min)) (re-search-forward (concat "^\\*+[ \t].*" ":\\(" org-wc-include-tag "\\):" "[^ \t\n]*[ \t]*$") nil t)) org-wc-include-tag t))) (save-restriction (if (org-region-active-p) (narrow-to-region beg end)) (let* ((funky (if current-prefix-arg 'org-wc-count-report 'org-wc-count-add-props)) (words (apply '+ (org-map-entries funky tag nil)))) (set-buffer-modified-p bmp) (message (format "Counted %d words in %s." words (if mark-active "region" "buffer"))))))) (defun org-wc-count-add-props () (org-wc-aux t)) (defun org-wc-count-report () (org-wc-aux nil)) (defun org-wc-aux (props) "Report the number of words in the selected region. Ignores: heading lines, blocks, comments, drawers. LaTeX macros are counted as 1 word." (let ((wc 0) (block-begin-re "^#\\\+BEGIN") (block-end-re "^#\\+END") (latex-macro-regexp "\\\\[A-Za-z]+\\(\\[[^]]*\\]\\|\\){\\([^}]*\\)}")) (let ((end (save-excursion (or (outline-next-heading) (point-max))))) (save-excursion (org-end-of-meta-data-and-drawers) (while (< (point) end) (cond ((looking-at block-begin-re) (re-search-forward block-end-re)) ;; Ignore comments. ((org-in-commented-line) (forward-line)) ;; skip tables ((org-at-table-p) (forward-line)) ;; Count latex macros as 1 word, ignoring their arguments. ((save-excursion (backward-char) (looking-at latex-macro-regexp)) (goto-char (match-end 0)) (setf wc (+ 2 wc))) (t (progn (and (re-search-forward "\\w+\\W*" end 'skip) (incf wc)))))))) (when props (org-entry-put (point) org-wc-prop-name (number-to-string wc))) wc)) (defun org-wc-remove-prop () "Delete an existing wordcount property" (org-entry-delete (point) org-wc-prop-name)) (defun org-wc-remove-all-props () "Delete wordcount property from all headlines in buffer" (interactive) (org-map-entries 'org-wc-remove-prop t 'file)) (provide 'org-wc) --=-=-=--