6197 comments
2357 subscribers
Follow me on Twitter (@sachac)
Subscribe! Feed reader E-mail

On this page:

Emacs: Quick way to collect references

(defvar sacha/planner-collector-buffer nil "Buffer for collecting links.")
(defun sacha/planner-collect-reference ()
  (interactive)
  (let ((text (sacha/yank-blog-reference-for-summary)))
    (with-current-buffer sacha/planner-collector-buffer
      (insert text))))

(defun sacha/planner-collect-reference ()
  (interactive)
  (let ((text (buffer-substring (line-beginning-position)
                                (1+ (line-end-position)))))
    (with-current-buffer sacha/planner-collector-buffer
      (insert text))))

On Technorati: ,

Random Emacs symbol: sgml-tag – Command: Prompt for a tag and insert it, optionally with attributes.

Short URL: http://sachachua.com/blog/p/3937

Emacs: Hideshow

One of the things I love about vising irc.freenode.net #emacs is that helping people with their questions lets me discover all sorts of cool things about Emacs. Today’s nugget? hideshow.el, which allows you to automatically hide / show code. (Equivalent of vim folding).

I’ve added the following code to my config:

(load-library "hideshow")
(add-hook 'emacs-lisp-mode-hook
          (lambda () (hs-minor-mode 1)
            (hs-hide-all)
            (set (make-variable-buffer-local 'my-hs-hide) t)))
(defvar my-hs-hide t "Current state of hideshow for toggling all.")
;;;###autoload
(defun my-toggle-hideshow-all ()
  "Toggle hideshow all."
  (interactive)
  (set (make-variable-buffer-local 'my-hs-hide) (not my-hs-hide))
  (if my-hs-hide
      (hs-hide-all)
    (hs-show-all)))
(global-set-key (kbd "C-c @ @") 'my-toggle-hideshow-all)
(global-set-key (kbd "C-c @ h") 'hs-hide-block)
(global-set-key (kbd "C-c @ s") 'hs-show-block)
(global-set-key (kbd "C-c @ SPC") 'hs-show-block)

Sweet.

… and then I find that I already have a
hideshow config, of course, and that
I added it on 2003.11.21… <bonk>!

On Technorati: ,

Random Emacs symbol: timezone-parse-date – Function: Parse DATE and return a vector [YEAR MONTH DAY TIME TIMEZONE].

Short URL: http://sachachua.com/blog/p/3931

Emacs Gnus hack: Prioritize based on the number of recipients

Ever found yourself confronted with an inbox overflowing with general
messages that you can ignore and messages that you and only you can
act on? Here’s something to help you sort the wheat from the chaff.

This indicates how personal messages are so you can immediately see which messages are just for you and which are part of a long Cc. Stephen Perelgut showed me the feature in Lotus Notes and I wanted to steal it sometime, so I did it while waiting for the Instant Rails archive.

To use it, add %ur to your gnus-summary-line-format.

(add-to-list 'nnmail-extra-headers 'To)
(add-to-list 'nnmail-extra-headers 'Cc)
(defvar sacha/gnus-count-recipients-threshold 5
  "*Number of recipients to consider as large.")

(defun sacha/gnus-count-recipients (header)
  "Given a Gnus message header, returns priority mark.
If I am the only recipient, return \"!\".
If I am one of a few recipients, but I'm listed in To:, return \"*\".
If I am one of a few recipients, return \"/\".
If I am one of many recipients, return \".\".
Else, return \" \"."
  (let* ((to (or (cdr (assoc 'To (mail-header-extra header))) ""))
         (cc (or (cdr (assoc 'Cc (mail-header-extra header))) "")))
    (cond
     ((string-match gnus-ignored-from-addresses to)
      (let ((len (length (bbdb-split to ","))))
        (cond
         ((= len 1) "!")
         ((< len sacha/gnus-count-recipients-threshold) "*")
         (t "/"))))
     ((string-match gnus-ignored-from-addresses
                    (concat to ", " cc))
      (if (< (length (bbdb-split (concat to ", " cc) ","))
             sacha/gnus-count-recipients-threshold)
          "/"
        "."))
     (t " "))))

(defalias 'gnus-user-format-function-r 'sacha/gnus-count-recipients)

Random Emacs symbol: dired-listing-switches - Variable: *Switches passed to `ls' for Dired. MUST contain the `l' option.

On Technorati: , , ,

Short URL: http://sachachua.com/blog/p/3920

Developing a better sense of time

One of the things I want to do is develop a good sense of how long it
takes me to do something. Better time estimates lead to better
scheduling, better sense of what I can commit to, and ultimately less
stress and more happiness. =)

Fortunately, Planner makes it easy for me to do really detailed
time-tracking. When I mark a task as in progress, the clock starts
ticking. When I postpone or close a task, the system clocks out
automatically.

I find that my attention occasionally strays. Sometime ago, I wrote a
function to help me keep track of what I’m supposed to be doing.
Today, I’ve decided to try estimating task completion times for more
of the tasks on my list. I modified my old function to tell me how
much time has elapsed since I started the task. This doesn’t take into
account previous clock-in/clock-outs, but it will do for now.

We’ll see how well it works. =)

;; I've bound sacha/planner-what-am-i-supposed-to-be-doing to F9 F11. I
;; start out by clocking into the task (use planner-timeclock.el and
;; C-c TAB to mark a task as in progress). Then, when I find myself
;; getting distracted, I hit F9 F9 to see my current task in the
;; minibuffer. C-u F9 F9 jumps back to the task so that I can either
;; mark it as postponed. M-x planner-task-pending (bound to C-c C-p in
;; my local config) and M-x planner-task-done (C-c C-x) both clock out
;; of the task. If I want to jump back to the previous window
;; configuration from that planner page, I can just hit F9 F9 again.

(defvar sacha/window-register "w"
  "Register for jumping back and forth between planner and wherever I am.")
(defvar sacha/planner-current-task nil
  "Current task info.")
(defadvice planner-task-in-progress (after sacha activate)
  "Keep track of the task info."
  (setq sacha/planner-current-task (planner-current-task-info)))

(defun sacha/planner-what-am-i-supposed-to-be-doing (&optional prefix)
  "Make it easy to keep track of what I'm supposed to be working on.
If PREFIX is non-nil, jump to the current task, else display it
in a message. If called from the plan page, jump back to whatever
I was looking at."
  (interactive "P")
  (if planner-timeclock-current-task
      (if (string= (planner-task-page sacha/planner-current-task)
                   (planner-page-name))
          (jump-to-register sacha/window-register)
        (if (null prefix)
            (message "%s %s"
                     ;; Minutes so far
                     (timeclock-seconds-to-string (timeclock-last-period))
                     planner-timeclock-current-task)
          (frame-configuration-to-register sacha/window-register)
          (planner-find-file (planner-task-page sacha/planner-current-task))
          (planner-find-task sacha/planner-current-task)))
    (if prefix
        (planner-goto-today)
      (message "No current task. HEY!"))))

On Technorati: , , ,

Random Emacs symbol: cal-tex-mouse-filofax-week – Command: Two page Filofax calendar for week indicated by cursor. (Hey, I didn’t know we could print Filofax calendars from Emacs cal…)

Short URL: http://sachachua.com/blog/p/3921

Crazy idea for Emacs: Random Emacs taglines

Would anyone happen to know of a way to select a random symbol with a
description?

Actually. Hmm.

(progn
  (apropos ".")
  (write-file "~/.taglines.random-emacs-symbols")
  (delete-matching-lines "Plist")
  (delete-matching-lines "not documented")
  (replace-regexp "\n  " " - " nil)
  (delete-non-matching-lines " - "))

Et voila! Random Emacs taglines together with the code:

(defun sacha/random-tagline (&optional file)
  "Return a random tagline and put it in the kill ring."
  (interactive)
  (with-current-buffer (find-file-noselect (or file "~/.taglines"))
    (goto-char (random (point-max)))
    (let ((string
           (buffer-substring (line-beginning-position)
                             (line-end-position))))
      (kill-new string)
      string)))

(defadvice remember (after sacha-tagline activate)
  "Add random tagline."
  (save-excursion
  (goto-char (point-max))
  (insert "\n\nRandom Emacs symbol: "
          (sacha/random-tagline "~/.taglines.random-emacs-symbols")
          "\n\n")))))

On Technorati: , ,

Random Emacs symbol: eshell-remove-entries – Function: From PATH, remove all of the given FILES, perhaps interactively.

Short URL: http://sachachua.com/blog/p/3905

Emacs BBDB: Prioritize exact matches

I often include people’s names in my notes on other people, such as
when I’m tracking who introduced me to whom. The following code
modifies BBDB’s behavior to put exact matches for name, company, or
network address above matches for notes.

(defun sacha/bbdb (string elidep)
  "Display all entries in the BBDB matching the regexp STRING
in either the name(s), company, network address, or notes.
Prioritize non-note matches."
  (interactive
   (list (bbdb-search-prompt "Search records %m regexp: ")
         current-prefix-arg))
  (let* ((bbdb-display-layout (bbdb-grovel-elide-arg elidep))
         (notes (cons '* string))
         (records-top
          (bbdb-search (bbdb-records) string string string nil
                       nil))
         (records
          (bbdb-search (bbdb-records) string string string notes
                       nil))
         temp)
    (setq temp records-top)
    (while temp
      (setq records (delete (car temp) records))
      (setq temp (cdr temp)))
    (if (or records-top records)
        (bbdb-display-records (append
                               records-top
                               records))
      ;; we could use error here, but it's not really an error.
      (message "No records matching '%s'" string))))

(defalias 'bbdb 'sacha/bbdb)

On Technorati: , , , ,

Short URL: http://sachachua.com/blog/p/3890

Emacs BBDB: Filtering tags with the power of lambda expressions

What do you do when you’re into both Emacs geeks and social
networking? Well, you build a really really weird contact management
tool, that’s what!

One of the things I often need to do is filter my contacts for a
particular set of interests. I would have no idea how to do this in
Microsoft Outlook and other proprietary contact management systems.
Because Emacs is infinitely programmable, though, I can just hack it
in.

You’d expect the intersection of the set “emacs geek” and the set
“social networker” to be a null set or a singleton (me!). As it turns
out, there’s at least one other geek in this space – hooray!

Paul Lussier’s been bouncing all sorts of
crazy ideas off me, which explains all the weird
porridge-and-toe-nails posts of Emacs Lisp code on my blog lately.
He’s responsible for my puttting together yesterday’s LinkedIn
importer. Today, he wrote:

Then found your sacha/bbdb-search-tags stuff which
totally, completely rocks. I just wish I had the first inkling as to
how it worked :) Now, the question I have is: How can I use
sacha/bbdb-search-tags to search for entries which are tagged with one
label, but NOT with another? For example, I want to search on: (and
(taq eq “planner”) (not (tag eq “muse”)))

I’d completely forgotten about sacha/bbdb-search-tags! Anyway, I’d
been meaning to write a fancy alias management thing for a while now,
and this code does a reasonable job for me. I can now filter my
displayed records by arbitrary Lisp expressions, bringing me closer to
insane contact relationship management. I mean, c’mon…

;; M-x sacha/bbdb-filter-by-alias-function RET
;;     (lambda (aliases) (and (member "planner" aliases)
;;                       (not (member "muse" aliases))))) RET

If I do this often enough, I might make up an easier syntax, but
lisp expressions work fine for me.

MWAHAHAHAHA!

Here’s the code:

;;;_+ Mail aliases

;; Code for working with aliases

;; You can use "a" (bbdb-add-or-remove-mail-alias) in BBDB buffers to add
;; a mail alias to the current entry, or "* a" to add a mail alias to
;; all displayed entries.

;; Goal: Be able to specify ALIAS and ALIAS
;; M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias
;; Goal: Be able to specify ALIAS or ALIAS
;; C-u M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias
;; Goal: Be able to specify not ...
;; M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias
;; C-u M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias

(defun sacha/bbdb-filter-by-alias-match-all (query-aliases record-aliases)
  "Return non-nil if all QUERY-ALIASES are in RECORD-ALIASES."
  (let ((result t))
    (while query-aliases
      (unless (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result nil))
      (setq query-aliases (cdr query-aliases)))
    result))

(defun sacha/bbdb-filter-by-alias-match-any (query-aliases record-aliases)
  "Return non-nil if any in QUERY-ALIASES can be found in RECORD-ALIASES."
  (let (result)
    (while query-aliases
      (when (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result t))
      (setq query-aliases (cdr query-aliases)))
    result))

;; Moved this to a convenience function so that we don't
;; have to deal with invert and property splitting.
(defun sacha/bbdb-filter-by-alias (bbdb-records
                                   alias-filter-function
                                   query
                                   &optional invert)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept two arguments:
 - QUERY, a list of keywords to search for
 - aliases, a list of keywords from the record
If INVERT is non-nil, return only the records that do
not match."
  (delq nil
        (mapcar
         (lambda (rec)
           (if (funcall alias-filter-function
                        query
                        (split-string
                         (or (bbdb-record-getprop
                              (if (vectorp rec)
                                  rec
                                (car rec))
                              propsym) "")
                         "[ \n\t,]+"))
               (when (null invert) rec)
             (when invert rec)))
         bbdb-records)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-filter-displayed-records-by-alias (query &optional any)
  "Display only records whose mail-aliases match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.
See also `sacha/bbdb-omit-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases)))
                current-prefix-arg))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias-by-function
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-omit-displayed-records-by-alias (query &optional any)
  "Display only records whose mail-aliases do not match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.

See also `sacha/bbdb-filter-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases))
                  current-prefix-arg)))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias-by-function
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query
    t)))

;;;_+ Advanced mail alias queries

;; Goal: Use complicated lambda expressions to filter displayed records
;; M-x sacha/bbdb-filter-by-alias-function RET
;;     (lambda (aliases) (and (member "planner" aliases)
;;                       (not (member "muse" aliases))))) RET
;; Thanks to Paul Lussier for the suggestion!

(defun sacha/bbdb-filter-by-alias-function (bbdb-records
                                            alias-filter-function)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept one argument:
 - aliases, a list of keywords from the record."
  (interactive (list (or bbdb-records (bbdb-records))
                     (read t)))
  (let (records)
    (setq records
          (delq nil
                (mapcar
                 (lambda (rec)
                   (when (funcall alias-filter-function
                                  (split-string
                                   (or (bbdb-record-getprop
                                        (if (vectorp rec)
                                            rec
                                          (car rec))
                                        propsym) "")
                                   "[ \n\t,]+"))
                     rec))
                 bbdb-records)))
    (if (interactive-p) (bbdb-display-records records))
    records))

On Technorati: , , , ,

Short URL: http://sachachua.com/blog/p/3896