<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/atom.xsl" type="text/xsl"?><feed
	xmlns="http://www.w3.org/2005/Atom"
	xmlns:thr="http://purl.org/syndication/thread/1.0"
	xml:lang="en-US"
	><title>Sacha Chua - tag - elisp</title>
	<subtitle>Emacs, sketches, and life</subtitle>
	<link rel="self" type="application/atom+xml" href="https://sachachua.com/blog/tag/elisp/feed/atom/index.xml" />
  <link rel="alternate" type="text/html" href="https://sachachua.com/blog/tag/elisp" />
  <id>https://sachachua.com/blog/tag/elisp/feed/atom/index.xml</id>
  <generator uri="https://11ty.dev">11ty</generator>
	<updated>2008-12-19T20:00:33Z</updated>
<entry>
		<title type="html">Emacs: Working with multiple source trees</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2008/12/emacs-working-with-multiple-source-trees/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2008-12-20T01:00:33Z</updated>
    <published>2008-12-19T20:00:33Z</published>
    <category term="emacs" />
		<id>https://sachachua.com/blog/?p=5463</id>
		<content type="html"><![CDATA[<p>As a developer, I often find myself working with multiple trees of source code. Sometimes, I&#8217;m comparing the trunk and the branches. Sometimes, I&#8217;m copying ideas from one place to another. Sometimes, I&#8217;m working on one project and an urgent defect comes in for a different project. With good development environments such as Eclipse, I&#8217;d still need to click on the project or directory in order to change my context, and then use a keyboard shortcut to find the resource in that tree. </p>
<p>With an awesome development environment like my customized Emacs, however, I can easily juggle multiple source trees. Here&#8217;s the macro I wrote to make setting this up much easier:</p>
<pre>
(defmacro sacha/file-cache-setup-tree (prefix shortcut directories)
  "Set up the file-cache tree for PREFIX using the keyboard SHORTCUT.
DIRECTORIES should be a list of directory names."
  `(let ((file-cache-alist nil)
	 (directories ,directories))
     (file-cache-clear-cache)
     (while directories
       (file-cache-add-directory-using-find (car directories))
       (setq directories (cdr directories)))
     (setq ,(intern (concat "sacha/file-cache-" prefix "-alist")) file-cache-alist)
     (defun ,(intern (concat "sacha/file-cache-ido-find-" prefix)) ()
       (interactive)
       (let ((file-cache-alist ,(intern (concat "sacha/file-cache-" prefix "-alist"))))
	 (call-interactively 'file-cache-ido-find-file)))
     (global-set-key (kbd ,shortcut)
		     (quote ,(intern (concat "sacha/file-cache-ido-find-" prefix))))))
</pre>
<p>With that, I can use the following to map C-c p to finding files in my personal directories:</p>
<pre>
(sacha/file-cache-setup-tree
 "personal"
 "C-c p"
 '("/var/www/html/drupal"
   "~/elisp"
   "~/personal"))
</pre>
<p>I&#8217;ve also set up keyboard shortcuts for the other trees I&#8217;m working on.</p>
<p>You&#8217;ll need file-cache, ido, and probably something like this:</p>
<pre>
(require 'filecache)
(require 'ido)
(defun file-cache-ido-find-file (file)
  "Using ido, interactively open file from file cache'.
First select a file, matched using ido-switch-buffer against the contents
in `file-cache-alist'. If the file exist in more than one
directory, select directory. Lastly the file is opened."
  (interactive (list (file-cache-ido-read "File: "
                                          (mapcar
                                           (lambda (x)
                                             (car x))
                                           file-cache-alist))))
  (let* ((record (assoc file file-cache-alist)))
    (find-file
     (expand-file-name
      file
      (if (= (length record) 2)
          (car (cdr record))
        (file-cache-ido-read
         (format "Find %s in dir: " file) (cdr record)))))))

(defun file-cache-ido-read (prompt choices)
  (let ((ido-make-buffer-list-hook
	 (lambda ()
	   (setq ido-temp-list choices))))
    (ido-read-buffer prompt)))
(add-to-list 'file-cache-filter-regexps "docs/html")
(add-to-list 'file-cache-filter-regexps "\\.svn-base$")
(add-to-list 'file-cache-filter-regexps "\\.dump$")
</pre>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2008%2F12%2Femacs-working-with-multiple-source-trees%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Wicked Cool Emacs: BBDB: Use nicknames and custom salutations</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2008/03/wicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2014-05-14T03:06:41Z</updated>
    <published>2008-03-24T08:26:48Z</published>
    <category term="bbdb" />
<category term="emacs" />
<category term="wickedcoolemacs" />
		<id>https://sachachua.com/blog/?p=4808</id>
		<content type="html"><![CDATA[<div class="update">
<p>Update 2014-05-13: The original code is for BBDB version 2. Thomas Morgan sent this update which makes it work with BBDB version 3 &#8211; see below.</p>
</div>
<p>I like starting my e-mail with a short salutation such as “Hello, Mike!”, “Hello, Michael”, or “Hello, Mikong!”, but it can be hard to remember which nicknames people prefer to use, and calling someone by the wrong name is a bit of a faux pas. Sometimes people sign e-mail with their preferred name, but what if you haven&#8217;t sent e-mail to or received e-mail from someone in a while? In this project, you&#8217;ll learn how to set up my BBDB to remember people&#8217;s nicknames for you using a custom “nick” field, and to use those nicknames when replying to messages in Gnus or composing messages from my BBDB.</p>
<p>The nickname code worked so well that I started thinking of what else I could customize. It was easy to go from nicknames to personalized salutations. This hack started because one of my friends is from Romania, so I thought I&#8217;d greet her in Romanian with “Salut, Letitia!” instead of just “Hello, Letitia!”. The code in this project uses a “hello” field to store these salutations in your BBDB.</p>
<p>To set up personalized nicknames and salutations, add the following code to your <code>~/.emacs</code>:</p>
<p>For BBDB v2</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/gnus-nick-threshold</span> 5 <span class="org-doc">"*Number of people to stop greeting individually. Nil means always greet individually."</span>)  <span class="org-comment-delimiter">;; </span><span class="org-comment">(1)</span>
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-hello-string</span> <span class="org-string">"Hello, %s!"</span> <span class="org-doc">"Format string for hello. Example: \"Hello, %s!\""</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-hello-all-string</span> <span class="org-string">"Hello, all!"</span> <span class="org-doc">"String for hello when there are many people. Example: \"Hello, all!\""</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-nick-field</span> 'nick <span class="org-doc">"Symbol name for nickname field in BBDB."</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-salutation-field</span> 'hello <span class="org-doc">"Symbol name for salutation field in BBDB."</span>)

(<span class="org-keyword">defun</span> <span class="org-function-name">wicked/gnus-add-nick-to-message</span> ()
  <span class="org-doc">"Inserts \"Hello, NICK!\" in messages based on the recipient's nick field."</span>
  (interactive)
  (<span class="org-keyword">save-excursion</span>
    (<span class="org-keyword">let*</span> ((bbdb-get-addresses-headers <span class="org-comment-delimiter">;; </span><span class="org-comment">(2)</span>
            (list (assoc 'recipients bbdb-get-addresses-headers)))
           (recipients (bbdb-get-addresses
                        nil
                        gnus-ignored-from-addresses
                        'gnus-fetch-field))
           recipient nicks rec net salutations)
      (goto-char (point-min))
      (<span class="org-keyword">when</span> (re-search-forward <span class="org-string">"&#45;&#45;text follows this line&#45;&#45;"</span> nil t)
        (forward-line 1)
        (<span class="org-keyword">if</span> (and wicked/gnus-nick-threshold 
                 (&gt;= (length recipients) wicked/gnus-nick-threshold))
            (insert wicked/bbdb-hello-all-string <span class="org-string">"\n\n"</span>) <span class="org-comment-delimiter">;; </span><span class="org-comment">(3)</span>
          (<span class="org-keyword">while</span> recipients
            (setq recipient (car (cddr (car recipients))))
            (setq net (nth 1 recipient))
            (setq rec (car (bbdb-search (bbdb-records) nil nil net)))
            (<span class="org-keyword">cond</span>
             ((null rec) <span class="org-comment-delimiter">;; </span><span class="org-comment">(4)</span>
              (add-to-list 'nicks (car recipient))) 
             ((bbdb-record-getprop rec wicked/bbdb-salutation-field) <span class="org-comment-delimiter">;; </span><span class="org-comment">(5)</span>
              (add-to-list 'salutations 
                           (bbdb-record-getprop rec wicked/bbdb-salutation-field))) 
             ((bbdb-record-getprop rec wicked/bbdb-nick-field) <span class="org-comment-delimiter">;; </span><span class="org-comment">(6)</span>
              (add-to-list 'nicks 
                           (bbdb-record-getprop rec wicked/bbdb-nick-field)))
             (t (bbdb-record-name rec))) <span class="org-comment-delimiter">;; </span><span class="org-comment">(7) </span>
            (setq recipients (cdr recipients))))
        (<span class="org-keyword">when</span> nicks <span class="org-comment-delimiter">;; </span><span class="org-comment">(8)</span>
          (insert (format wicked/bbdb-hello-string 
                          (mapconcat 'identity (nreverse nicks) <span class="org-string">", "</span>))
                  <span class="org-string">" "</span>))
        (<span class="org-keyword">when</span> salutations <span class="org-comment-delimiter">;; </span><span class="org-comment">(9)</span>
          (insert (mapconcat 'identity salutations <span class="org-string">" "</span>)))
        (<span class="org-keyword">when</span> (or nicks salutations)
          (insert <span class="org-string">"\n\n"</span>)))))
  (goto-char (point-min)))

(<span class="org-keyword">defadvice</span> <span class="org-function-name">gnus-post-news</span> (after wicked/bbdb activate)
  <span class="org-doc">"Insert nicknames or custom salutations."</span>
  (wicked/gnus-add-nick-to-message))

(<span class="org-keyword">defadvice</span> <span class="org-function-name">gnus-msg-mail</span> (after wicked/bbdb activate)
  <span class="org-doc">"Insert nicknames or custom salutations."</span>
  (wicked/gnus-add-nick-to-message))

(<span class="org-keyword">defadvice</span> <span class="org-function-name">gnus-summary-reply</span> (after wicked/bbdb activate)
  <span class="org-doc">"Insert nicknames or custom salutations."</span>
  (wicked/gnus-add-nick-to-message))
</pre>
</div>
<p>For BBDB v3</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">This version is for BBDBv3 - thanks, Thomas!</span>

(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/gnus-nick-threshold</span> 5 <span class="org-doc">"*Number of people to stop greeting individually. Nil means always greet individually."</span>)  <span class="org-comment-delimiter">;; </span><span class="org-comment">(1)</span>
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-hello-string</span> <span class="org-string">"Hello, %s!"</span> <span class="org-doc">"Format string for hello. Example: \"Hello, %s!\""</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-hello-all-string</span> <span class="org-string">"Hello, all!"</span> <span class="org-doc">"String for hello when there are many people. Example: \"Hello, all!\""</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-nick-field</span> 'nick <span class="org-doc">"Symbol name for nickname field in BBDB."</span>)
(<span class="org-keyword">defvar</span> <span class="org-variable-name">wicked/bbdb-salutation-field</span> 'hello <span class="org-doc">"Symbol name for salutation field in BBDB."</span>)

(<span class="org-keyword">defun</span> <span class="org-function-name">wicked/gnus-add-nick-to-message</span> ()
  <span class="org-doc">"Inserts \"Hello, NICK!\" in messages based on the recipient's nick field."</span>
  (interactive)
  (<span class="org-keyword">let</span> ((recipients (bbdb-get-address-components))
        recipient nicks rec net salutations)
    (goto-char (point-min))
    (<span class="org-keyword">when</span> (re-search-forward <span class="org-string">"&#45;&#45;text follows this line&#45;&#45;"</span> nil t)
      (forward-line 1)
      (<span class="org-keyword">if</span> (and wicked/gnus-nick-threshold 
               (&gt;= (length recipients) wicked/gnus-nick-threshold))
          (insert wicked/bbdb-hello-all-string <span class="org-string">"\n\n"</span>) <span class="org-comment-delimiter">;; </span><span class="org-comment">(3)</span>
        (<span class="org-keyword">while</span> recipients
          (setq recipient (car recipients))
          (setq net (nth 1 recipient))
          (setq rec (car (bbdb-search (bbdb-records) nil nil net)))
          (<span class="org-keyword">cond</span>
           ((null rec) <span class="org-comment-delimiter">;; </span><span class="org-comment">(4)</span>
            (add-to-list 'nicks (car recipient))) 
           ((bbdb-record-xfield rec wicked/bbdb-salutation-field) <span class="org-comment-delimiter">;; </span><span class="org-comment">(5)</span>
            (add-to-list 'salutations 
                         (bbdb-record-xfield rec wicked/bbdb-salutation-field))) 
           ((bbdb-record-xfield rec wicked/bbdb-nick-field) <span class="org-comment-delimiter">;; </span><span class="org-comment">(6)</span>
            (add-to-list 'nicks 
                         (bbdb-record-xfield rec wicked/bbdb-nick-field)))
           (t
            (add-to-list 'nicks
                         (car (split-string (bbdb-record-name rec)))))) <span class="org-comment-delimiter">;; </span><span class="org-comment">(7) </span>
          (setq recipients (cdr recipients))))
      (<span class="org-keyword">when</span> nicks <span class="org-comment-delimiter">;; </span><span class="org-comment">(8)</span>
        (insert (format wicked/bbdb-hello-string 
                        (mapconcat 'identity (nreverse nicks) <span class="org-string">", "</span>))
                <span class="org-string">" "</span>))
      (<span class="org-keyword">when</span> salutations <span class="org-comment-delimiter">;; </span><span class="org-comment">(9)</span>
        (insert (mapconcat 'identity salutations <span class="org-string">" "</span>)))
      (<span class="org-keyword">when</span> (or nicks salutations)
        (insert <span class="org-string">"\n\n"</span>)))))

(<span class="org-keyword">defadvice</span> <span class="org-function-name">gnus-post-news</span> (after wicked/bbdb activate)
  <span class="org-doc">"Insert nicknames or custom salutations."</span>
  (wicked/gnus-add-nick-to-message))

(<span class="org-keyword">defadvice</span> <span class="org-function-name">gnus-msg-mail</span> (after wicked/bbdb activate)
  <span class="org-doc">"Insert nicknames or custom salutations."</span>
  (wicked/gnus-add-nick-to-message))

(<span class="org-keyword">defadvice</span> <span class="org-function-name">gnus-summary-reply</span> (after wicked/bbdb activate)
  <span class="org-doc">"Insert nicknames or custom salutations."</span>
  (wicked/gnus-add-nick-to-message))
</pre>
</div>
<p>After you add this code, you can store personalized nicknames and salutations in your BBDB. Nicknames and salutations will be looked up using people&#8217;s e-mail addresses. While in the <code>*BBDB*</code> buffer, you can type <code>C-o (bbdb-insert-new-field)</code> to add a field to the current record. Add a <code>nick</code> field with the person&#8217;s nickname, or a <code>hello</code> field with a custom salutation. When you compose a message to or reply to a message from that person, the salutation or nickname will be included. If no nickname can be found, the recipient&#8217;s name will be used instead.</p>
<p>A number of variables can be used to modify the behavior of this code(1). For example, you may or may not want to greet 20 people individually. The default value of <code>wicked/gnus-nick-threshold</code> is to greet up to four people individually, and greet more people collectively. If you always want to greet people individually, add <code>(setq wicked/gnus-nick-threshold nil)</code> to your <code>~/.emacs</code>. If you want to change the strings used to greet people individually or collectively, change <code>wicked/bbdb-hello-string</code> and <code>wicked/bbdb-hello-all-string</code>. If you want to store the data into different fields, change <code>wicked/bbdb-nick-field</code> and <code>wicked/bbdb-salutation-field</code>, but note that old data will not be automatically copied to the new fields.</p>
<p>Here&#8217;s how the code works. First, it retrieves the list of addresses from the header(2). If there are more addresses than <code>wicked/gnus-nick-threshold</code>, then <code>wicked/bbdb-hello-all-string</code> is used to greet everyone. If not, each recipient address is looked up. If the recipient cannot be found in your BBDB, then the recipient&#8217;s name or e-mail address is used(4). If there is a personalized salutation, it is used(5). If there is a nickname, it is used(6). If the person has a record but neither salutation or nickname, then the name of the record is used(7). After all recipients have been processed, the names are added to the message(8), followed by the salutations(9). This function is added to the different Gnus message-posting functions, so it should be called whenever you compose or reply to messages.</p>
<p>You can <a href="https://sachachua.com/blog/2008/03/wicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations/#comment">view 4 comments</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2008%2F03%2Fwicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Wicked Cool Emacs: BBDB: Keeping track of contact dates</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2008/03/wicked-cool-emacs-bbdb-keeping-track-of-contact-dates/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2009-02-11T19:19:51Z</updated>
    <published>2008-03-01T08:49:00Z</published>
    <category term="emacs" />
<category term="wickedcoolemacs" />
		<id>https://sachachua.com/blog/?p=4792</id>
		<content type="html"><![CDATA[<div>
  <p>I hadn’t realized just how much I missed my Big Brother Database until today. Three networking events packed into one week meant that I hadn’t set aside enough time for follow up, and I felt my memories of the conversations getting a little hazy. Fortunately I’d taken some notes on my Palm, but I knew I had to get it into some kind of contact management system quickly, and Gmail Contacts just wasn’t compelling enough for me. So it’s back to Emacs, plain text files, and a surprisingly sophisticated contact manager.</p>

  <p>I also promised to do some work on the book today, so everything dovetailed nicely.</p>

  <p>The following bit of code helps me filter displayed contacts to show only the people I haven’t contacted since a certain date. This is handy for remembering to keep in touch with old friends, for example. Or at least it would be handy if I used it more often and if I actually sent the letters that pile up in my e-mail drafts and my snail mail outbox… but at least it’s a step in the right direction.</p>

  <p>If you want to know who you have or haven’t talked to in a while, you need to do two things. First, you need to keep track of when you talked to people. Second, you need to generate reports.</p>

  <p>To be able to quickly add contact notes to BBDB records, add the following to your 
    <i>~/.emacs</i>:
  </p>

  <h3>ch6-bbdb-ping.el:</h3>

  <pre>
(define-key bbdb-mode-map 
    <span class="org-string">&quot;z&quot;</span> &apos;wicked/bbdb-ping-bbdb-record)
(
    <span class="org-keyword">defun</span> 
    <span class="org-function-name">wicked/bbdb-ping-bbdb-record</span> (bbdb-record text 
    <span class="org-type">&amp;optional</span> date regrind)
  
    <span class="org-doc">&quot;Adds a note for today to the current BBDB record.
Call with a prefix to specify date.
BBDB-RECORD is the record to modify (default: current).
TEXT is the note to add for DATE.
If REGRIND is non-nil, redisplay the BBDB record.&quot;</span>
  (interactive (list (bbdb-current-record t)
                     (read-string 
    <span class="org-string">&quot;Notes: &quot;</span>)
                     
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Reading date - more powerful with Planner, but we&apos;ll make do if necessary
</span>                     (
    <span class="org-keyword">if</span> (
    <span class="org-keyword">featurep</span> &apos;
    <span class="org-constant">planner</span>)
                         (
    <span class="org-keyword">if</span> current-prefix-arg (planner-read-date) (planner-today))
                       (
    <span class="org-keyword">if</span> current-prefix-arg
                           (read-string 
    <span class="org-string">&quot;Date (YYYY.MM.DD): &quot;</span>)
                         (format-time-string 
    <span class="org-string">&quot;%Y.%m.%d&quot;</span>)))
                     t))
  (bbdb-record-putprop bbdb-record
                       &apos;contact
                       (concat date 
    <span class="org-string">&quot;: &quot;</span> text 
    <span class="org-string">&quot;\n&quot;</span>
                               (or (bbdb-record-getprop bbdb-record &apos;contact))))
  (
    <span class="org-keyword">if</span> regrind
      (
    <span class="org-keyword">save-excursion</span>
        (set-buffer bbdb-buffer-name)
        (bbdb-redisplay-one-record bbdb-record)))
  nil)

  </pre>

  <p>You can then use 
    <bold>z</bold> in BBDB buffers to add a quick note to the “contact” field of the current record. The date is automatically noted. You can create a note for a specific date by calling 
    <kbd>C-u wicked/bbdb-ping-bbdb-record</kbd> with a prefix argument. For convenience, the suggested configuration binds this to “z”, because it was one of the few unbound keys I could find.  Use this after you meet, call, or e-mail people, and write down a short note about the conversation you had. You might find these notes useful later on.
  </p>

  <p>If you met a number of people at an event in the past and you have Planner installed and loaded, you can use 
    <kbd>planner-timewarp</kbd> to set the effective date to another date. To return to today, use 
    <kbd>M-x planner-timewarp nil</kbd>.
  </p>

  <p>To automatically add a datestamped copy of sent e-mail subjects to people’s BBDB records, add the following to your 
    <i>~/.gnus</i>:
  </p>

  <h3>ch6-bbdb-message-add-subject.el:</h3>

  <pre>
(
    <span class="org-keyword">defun</span> 
    <span class="org-function-name">wicked/message-add-subject-to-bbdb-record</span> ()
  
    <span class="org-doc">&quot;Add datestamped subject note for each person this message has been sent to.&quot;</span>
  (
    <span class="org-keyword">let*</span> ((subject (concat (format-time-string 
    <span class="org-string">&quot;%Y.%m.%d&quot;</span>)
                          
    <span class="org-string">&quot;: E-mail: &quot;</span> (message-fetch-field 
    <span class="org-string">&quot;Subject&quot;</span>) 
    <span class="org-string">&quot;\n&quot;</span>))
         (bbdb-get-addresses-headers
          (list (assoc &apos;recipients bbdb-get-addresses-headers)))
         records)
    (setq records
          (bbdb-update-records
           (bbdb-get-addresses nil gnus-ignored-from-addresses &apos;gnus-fetch-field)
           nil nil))
    (mapc (
    <span class="org-keyword">lambda</span> (rec)
            (bbdb-record-putprop rec
                                 &apos;contact
                                 (concat subject
                                         (or
                                          (bbdb-record-getprop rec &apos;contact)
                                          
    <span class="org-string">&quot;&quot;</span>))))
          records)))
(add-hook &apos;message-send-hook &apos;wicked/message-add-subject-to-bbdb-record)

  </pre>

  <p>Now that you have the data, how can you use it to filter? Add the following to your 
    <i>~/.emacs</i>:
  </p>

  <h3>ch6-bbdb-show-only-no-contact-since.el:</h3>

  <pre>
(
    <span class="org-keyword">defun</span> 
    <span class="org-function-name">wicked/bbdb-show-only-no-contact-since</span> (date 
    <span class="org-type">&amp;optional</span> reverse records)
  
    <span class="org-doc">&quot;Show only people who haven&apos;t been pinged since DATE or at all.
If REVERSE is non-nil, show only the people you&apos;ve contacted on or since DATE.
Call with a prefix argument to show only people you&apos;ve contacted on or since DATE.&quot;</span>
  (interactive (list
                (
    <span class="org-keyword">if</span> (
    <span class="org-keyword">featurep</span> &apos;
    <span class="org-constant">planner</span>)
                    (planner-read-date)
                  (read-string 
    <span class="org-string">&quot;Date (YYYY.MM.DD): &quot;</span>))
                current-prefix-arg (or bbdb-records (bbdb-records))))
  (
    <span class="org-keyword">let</span> (new-records
        last-match
        timestamp
        omit
        notes)
    (
    <span class="org-keyword">while</span> records
      
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Find the latest date mentioned in the entry
</span>      (
    <span class="org-keyword">let</span> ((timestamp (wicked/bbdb-last-date
                        (
    <span class="org-keyword">if</span> (vectorp (car records))
                            (car records)
                          (caar records)))))
        (
    <span class="org-keyword">if</span> (
    <span class="org-keyword">if</span> reverse
                
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Keep if contact is &gt;= date
</span>                (null (string&lt; timestamp date))
              
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Keep if date &gt; contact
</span>              (string&gt; date timestamp))
            (add-to-list &apos;new-records (
    <span class="org-keyword">if</span> (vectorp (car records))
                            (car records)
                          (caar records)) t)))
      (setq records (cdr records)))
    (bbdb-display-records new-records)))

(
    <span class="org-keyword">defun</span> 
    <span class="org-function-name">wicked/bbdb-last-date</span> (rec)
  
    <span class="org-doc">&quot;Return the most recent date for REC or nil if none.
Dates should be in the form YYYY.MM.DD.  The first date in the
notes field and the first date in the contact field are used, so
dates should be in reverse chronological order.&quot;</span>
  (
    <span class="org-keyword">let*</span> ((wicked/date-regexp
          
    <span class="org-string">&quot;\\&lt;</span>
    <span class="org-string">
      <span class="regexp-grouping-backslash">\\</span>
    </span>
    <span class="org-string">
      <span class="regexp-grouping-construct">(</span>
    </span>
    <span class="org-string">[1-9][0-9][0-9][0-9]</span>
    <span class="org-string">
      <span class="regexp-grouping-backslash">\\</span>
    </span>
    <span class="org-string">
      <span class="regexp-grouping-construct">)</span>
    </span>
    <span class="org-string">\\.</span>
    <span class="org-string">
      <span class="regexp-grouping-backslash">\\</span>
    </span>
    <span class="org-string">
      <span class="regexp-grouping-construct">(</span>
    </span>
    <span class="org-string">[0-9][0-9]?</span>
    <span class="org-string">
      <span class="regexp-grouping-backslash">\\</span>
    </span>
    <span class="org-string">
      <span class="regexp-grouping-construct">)</span>
    </span>
    <span class="org-string">\\.</span>
    <span class="org-string">
      <span class="regexp-grouping-backslash">\\</span>
    </span>
    <span class="org-string">
      <span class="regexp-grouping-construct">(</span>
    </span>
    <span class="org-string">[0-9][0-9]?</span>
    <span class="org-string">
      <span class="regexp-grouping-backslash">\\</span>
    </span>
    <span class="org-string">
      <span class="regexp-grouping-construct">)</span>
    </span>
    <span class="org-string">\\&gt;&quot;</span>)
         
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Get the first date mentioned in the notes field
</span>         (notes-date
          (or (and (string-match wicked/date-regexp (or (bbdb-record-notes rec) 
    <span class="org-string">&quot;&quot;</span>))
                   (match-string 0 (or (bbdb-record-notes rec) 
    <span class="org-string">&quot;&quot;</span>)))
              
    <span class="org-string">&quot;0000.00.00&quot;</span>))
         
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Get the first date mentioned in the contact field
</span>         (contact-date
          (or (and (string-match wicked/date-regexp (or (bbdb-record-getprop rec &apos;contact) 
    <span class="org-string">&quot;&quot;</span>))
                   (match-string 0 (or (bbdb-record-getprop rec &apos;contact) 
    <span class="org-string">&quot;&quot;</span>)))
              
    <span class="org-string">&quot;0000.00.00&quot;</span>)))
    
    <span class="org-comment-delimiter">;; </span>
    <span class="org-comment">Compare the two dates
</span>    (or (
    <span class="org-keyword">if</span> (string&lt; notes-date contact-date) contact-date notes-date)
        
    <span class="org-string">&quot;0000.00.00&quot;</span>)))

  </pre>

  <p>To generate a report, use 
    <kbd>M-x wicked/bbdb-show-only-no-contact-since</kbd> and specify the date.  These functions are much easier to use with Planner’s date-handling functions. Planner can read dates like “-1” (yesterday), “-7fri” (seven Fridays ago), “2” (the second of this month), “1.2” (January 2 in this year), and “2007.01.02” (January 2, 2007).
  </p>

  <p>You can also flip the filter by using the universal prefix argument (\{\{C-u M-x wicked/bbdb-show-only-no-contact-since\}\}) to show only the people you’ve contacted since a certain date. This is good for knowing the size of your active network. Because the filter works on displayed records, you can combine it to find all the people you talked to last year but not this year. You can also combine it with other filters to find all the people you’ve marked as friends, but who you haven’t talked to in three months. Then you can send a personalized e-mail or make a phone list, and get back in touch. And that’s how you keep track of your contact dates!</p>

</div><p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2008%2F03%2Fwicked-cool-emacs-bbdb-keeping-track-of-contact-dates%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Crazy Emacs: Personalized signatures with random taglines</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2006/10/crazy-emacs-personalized-signatures-with-random-taglines/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2006-10-02T23:05:00Z</updated>
    <published>2006-10-02T19:05:00Z</published>
    <category term="bbdb" />
<category term="emacs" />
		<id>https://sachachua.com/blog/?p=3904</id>
		<content type="html"><![CDATA[<p>Of course, that naturally leads to the crazy idea: &#8220;What if I can<br>
personalize my signatures?&#8221; Knowing that Paul Lussier is an Emacs geek, I can reward him for reading all the way<br>
to the bottom of my message&#8230; ;)</p>
<pre class="example">
(defun sacha/gnus-personalize-signature ()
  "Personalizes signature based on BBDB signature field.
BBDB signature field should be a lambda expression.
First person with a custom signature field gets used."
  (let* ((bbdb-get-addresses-headers
          (list (assoc 'recipients bbdb-get-addresses-headers)))
         (records (bbdb-update-records
                   (bbdb-get-addresses
                    nil
                    gnus-ignored-from-addresses 'gnus-fetch-field)
                   nil
                   nil))
         signature)
    (while (and records (not signature))
      (when (bbdb-record-getprop (car records) 'signature)
        (setq signature
              (eval (read (bbdb-record-getprop (car records)
                                               'signature)))))
      (setq records (cdr records)))
    (or signature t)))
(setq-default message-signature 'sacha/gnus-personalize-signature)
</pre>
<p>So then all I have to do is add the following field to his record:</p>
<pre class="example">
      signature: (concat "Sacha Chua - Emacs geek
                 What crazy idea can I help you hack next?
                 Random Emacs symbol: "
                 (sacha/random-tagline
                  "~/.taglines.random-emacs-symbols"))
</pre>
<p>Emacs. One crazy idea at a time. Now I can use this to select random<br>
information, like my favorite networking books or a list of my<br>
upcoming events&#8230;</p>

<p>Random Emacs symbol: sort-coding-systems-predicate &#8211; Variable: If non-nil, a predicate function to sort coding systems.</p>
<p>You can <a href="https://sachachua.com/blog/2006/10/crazy-emacs-personalized-signatures-with-random-taglines/#comment">view 1 comment</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2006%2F10%2Fcrazy-emacs-personalized-signatures-with-random-taglines%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Crazy idea for Emacs: Random Emacs taglines</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2006/10/crazy-idea-for-emacs-random-emacs-taglines/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2006-10-02T22:34:00Z</updated>
    <published>2006-10-02T18:34:00Z</published>
    <category term="emacs" />
		<id>https://sachachua.com/blog/?p=3905</id>
		<content type="html"><![CDATA[<p>Would anyone happen to know of a way to select a random symbol with a<br>
description?</p>
<p>Actually. Hmm.</p>
<pre class="example">
(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 " - "))
</pre>
<p>Et voila! Random Emacs taglines together with the code:</p>
<pre class="example">
(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")))))
</pre>

<p>Random Emacs symbol: eshell-remove-entries &#8211; Function: From PATH, remove all of the given FILES, perhaps interactively.</p>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2006%2F10%2Fcrazy-idea-for-emacs-random-emacs-taglines%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Emacs BBDB: Prioritize exact matches</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2006/09/emacs-bbdb-prioritize-exact-matches/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2006-09-29T23:54:00Z</updated>
    <published>2006-09-29T19:54:00Z</published>
    <category term="bbdb" />
<category term="emacs" />
		<id>https://sachachua.com/blog/?p=3890</id>
		<content type="html"><![CDATA[<p>I often include people's names in my notes on other people, such as<br>
when I'm tracking who introduced me to whom. The following code<br>
modifies BBDB's behavior to put exact matches for name, company, or<br>
network address above matches for notes.</p>
<pre class="example">
(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)
</pre>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2006%2F09%2Femacs-bbdb-prioritize-exact-matches%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Emacs: BBDB rapid serial visualization</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2006/09/emacs-bbdb-rapid-serial-visualization/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2006-09-28T19:35:00Z</updated>
    <published>2006-09-28T15:35:00Z</published>
    <category term="bbdb" />
<category term="emacs" />
		<id>https://sachachua.com/blog/?p=3882</id>
		<content type="html"><![CDATA[<p>And because it's good to quickly flash through records once in a while<br>
to refresh my memory&#8230;</p>
<pre class="example">
(defvar sacha/bbdb-rapid-serial-visualization-delay
 1
 "*Number of seconds to wait between records.
Set to 0 to wait for input.")

(defun sacha/bbdb-rapid-serial-visualization ()
  "Breeze through everyone's name and notes."
  (interactive)
  (window-configuration-to-register ?a)
  ;; Copy the currently visible records
  (let ((records bbdb-records)
        (default-size (face-attribute 'default :height))
        (new-size 400)
        (continue t))
    (set-face-attribute 'default nil :height new-size)
    (pop-to-buffer (get-buffer-create "BBDB-Serial"))
    (delete-other-windows)
    (while (and records continue)
      (insert (bbdb-record-name (caar records))
              "\n\n"
              (or (car (bbdb-record-net (caar records))) "No e-mail")
              "\n\n"
              (or (bbdb-record-notes (caar records)) "")
              (make-string 50 ?\n))
      (goto-char (point-min))
      (sit-for sacha/bbdb-rapid-serial-visualization-delay)
      (setq records (cdr records)))
    (set-face-attribute 'default nil :height default-size)
    (when continue
      (jump-to-register ?a))))
</pre>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2006%2F09%2Femacs-bbdb-rapid-serial-visualization%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry>
</feed>