<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7773847580486821558</id><updated>2012-01-28T09:54:59.634-05:00</updated><title type='text'>Scott Frazer's Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://scottfrazersblog.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://scottfrazersblog.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Scott Frazer</name><uri>http://www.blogger.com/profile/02727761079827984966</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://bp1.blogger.com/_-ce9aQ2RrYs/RxQAX_pY_QI/AAAAAAAAAA8/m0nwV63Ozh8/s200/ScottFrazer.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>4</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7773847580486821558.post-5555198998646819853</id><published>2010-01-06T16:34:00.097-05:00</published><updated>2010-01-07T10:41:32.428-05:00</updated><title type='text'>Emacs:  Filtered Buffer Switching</title><content type='html'>&lt;span style="font-size: large;"&gt;Emacs:&amp;nbsp; Filtered Buffer Switching &lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There are a lot of different ways to &lt;a href="http://www.emacswiki.org/emacs/CategoryBufferSwitching"&gt;switch buffers&lt;/a&gt;.&amp;nbsp; A lot.&amp;nbsp; I use four myself:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Toggle between the two most recently used buffers&lt;/li&gt;&lt;li&gt;Choose buffer by name&lt;/li&gt;&lt;li&gt;Choose buffer from a list&lt;/li&gt;&lt;li&gt;Flip sequentially through buffers&lt;/li&gt;&lt;/ol&gt;One thing I want in common between these is for them to all use a common subset of buffers.&amp;nbsp; For example, I don't want buffers that start with '*' considered when switching unless, say, the buffer is "*scratch*".&amp;nbsp; Many packages allow you to filter the buffer list using a function, so let's make one.&lt;br /&gt;&lt;br /&gt;I'll use three variables to do the filtering: 1) Buffer name regex's that should always be shown, 2) buffer name regex's that should never be shown (always overrides never), and 3) a boolean for if dired buffers should be shown.&amp;nbsp; The last one is because I use dired a lot and sometimes I want to see those buffers and sometimes I don't.&amp;nbsp; I'll write the function in an "ignore this buffer" way since the packages I use work that way, but it's easy enough to invert the sense if you want:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defvar&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;my-bs-always-show-regexps&lt;/span&gt; '(&lt;span style="color: darkcyan;"&gt;"\\*&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;scratch&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;|&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;info&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;|&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;grep&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;|&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;compilation&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;\\*"&lt;/span&gt;)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"*Buffer regexps to always show when buffer switching."&lt;/span&gt;)&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defvar&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;my-bs-never-show-regexps&lt;/span&gt; '(&lt;span style="color: darkcyan;"&gt;"^\\s-"&lt;/span&gt; &lt;span style="color: darkcyan;"&gt;"^\\*"&lt;/span&gt; &lt;span style="color: darkcyan;"&gt;"TAGS$"&lt;/span&gt;)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"*Buffer regexps to never show when buffer switching."&lt;/span&gt;)&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defvar&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;my-ido-ignore-dired-buffers&lt;/span&gt; t&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"*If non-nil, buffer switching should ignore dired buffers."&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #996633;"&gt;&lt;b&gt;my-bs-str-in-regexp-list&lt;/b&gt;&lt;/span&gt; (str regexp-list)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Return non-nil if str matches anything in regexp-list."&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((case-fold-search nil))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;catch&lt;/b&gt;&lt;/span&gt; '&lt;span style="color: deepskyblue;"&gt;&lt;b&gt;done&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (regexp regexp-list)&lt;br /&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; (string-match regexp str)&lt;br /&gt;          (&lt;span style="color: blue;"&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/span&gt; '&lt;span style="color: deepskyblue;"&gt;&lt;b&gt;done&lt;/b&gt;&lt;/span&gt; t))))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #996633;"&gt;&lt;b&gt;my-bs-ignore-buffer&lt;/b&gt;&lt;/span&gt; (name)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Return non-nil if the named buffer should be ignored."&lt;/span&gt;&lt;br /&gt;  (or (and (not (my-bs-str-in-regexp-list name my-bs-always-show-regexps))&lt;br /&gt;           (my-bs-str-in-regexp-list name my-bs-never-show-regexps))&lt;br /&gt;      (and my-ido-ignore-dired-buffers&lt;br /&gt;           (&lt;span style="color: blue;"&gt;&lt;b&gt;save-excursion&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;             (set-buffer name)&lt;br /&gt;             (equal major-mode 'dired-mode)))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is set up to ignore all buffers that start with a space or '*', except for scratch, info, grep, and compilation buffers.&amp;nbsp; Dired buffers are also ignored.&lt;br /&gt;&lt;br /&gt;The function to toggle between the two most recently used buffers is easy enough:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #996633;"&gt;&lt;b&gt;my-bs-toggle&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Toggle buffers, ignoring certain ones."&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;catch&lt;/b&gt;&lt;/span&gt; '&lt;span style="color: deepskyblue;"&gt;&lt;b&gt;done&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (buf (buffer-list))&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;unless&lt;/b&gt;&lt;/span&gt; (or (equal (current-buffer) buf)&lt;br /&gt;                  (my-bs-ignore-buffer (buffer-name buf)))&lt;br /&gt;        (switch-to-buffer buf)&lt;br /&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/span&gt; '&lt;span style="color: deepskyblue;"&gt;&lt;b&gt;done&lt;/b&gt;&lt;/span&gt; t)))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I use ido to switch buffers by name:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(setq ido-ignore-buffers '(my-bs-ignore-buffer))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I like &lt;a href="http://www.emacswiki.org/emacs/BufferSelection"&gt;bs&lt;/a&gt; for getting a list of buffers to choose from:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(setq bs-configurations&lt;br /&gt;      '((&lt;span style="color: darkcyan;"&gt;"all"&lt;/span&gt; nil nil nil nil nil)&lt;br /&gt;        (&lt;span style="color: darkcyan;"&gt;"files"&lt;/span&gt; nil nil nil (&lt;span style="color: blue;"&gt;&lt;b&gt;lambda&lt;/b&gt;&lt;/span&gt; (buf) (my-bs-ignore-buffer (buffer-name buf))) nil)))&lt;br /&gt;(setq bs-cycle-configuration-name &lt;span style="color: darkcyan;"&gt;"files"&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This sets up two bs configurations, one that shows all the buffers and one that only shows my subset.&amp;nbsp; Somewhat off-topic, I like bs but not the default look ... too much information.&amp;nbsp; Here's my simplified version:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(setq bs-mode-font-lock-keywords&lt;br /&gt;  (list&lt;br /&gt;   &lt;span style="color: #a8a8a8;"&gt;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Headers&lt;br /&gt;&lt;/span&gt;   (list &lt;span style="color: darkcyan;"&gt;"^[ ]+&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;[-M].*&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;$"&lt;/span&gt; 1 font-lock-keyword-face)&lt;br /&gt;   &lt;span style="color: #a8a8a8;"&gt;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Boring buffers&lt;br /&gt;&lt;/span&gt;   (list &lt;span style="color: darkcyan;"&gt;"^&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;.*\\*.*\\*.*&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;$"&lt;/span&gt; 1 font-lock-comment-face)&lt;br /&gt;   &lt;span style="color: #a8a8a8;"&gt;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Dired buffers&lt;br /&gt;&lt;/span&gt;   '(&lt;span style="color: darkcyan;"&gt;"^[ .*%]+&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;Dired.*&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;$"&lt;/span&gt; 1 font-lock-type-face)&lt;br /&gt;   &lt;span style="color: #a8a8a8;"&gt;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Modified buffers&lt;br /&gt;&lt;/span&gt;   '(&lt;span style="color: darkcyan;"&gt;"^[ .]+&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;\\*&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;"&lt;/span&gt; 1 font-lock-warning-face)&lt;br /&gt;   &lt;span style="color: #a8a8a8;"&gt;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Read-only buffers&lt;br /&gt;&lt;/span&gt;   '(&lt;span style="color: darkcyan;"&gt;"^[ .*]+&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;\\%&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;\\&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style="color: darkcyan;"&gt;"&lt;/span&gt; 1 font-lock-variable-name-face)))&lt;br /&gt;&lt;br /&gt;(setq bs-attributes-list&lt;br /&gt;      (quote ((&lt;span style="color: darkcyan;"&gt;""&lt;/span&gt; 2 2 left bs--get-marked-string)&lt;br /&gt;              (&lt;span style="color: darkcyan;"&gt;"M"&lt;/span&gt; 1 1 left bs--get-modified-string)&lt;br /&gt;              (&lt;span style="color: darkcyan;"&gt;"R"&lt;/span&gt; 2 2 left bs--get-readonly-string)&lt;br /&gt;              (&lt;span style="color: darkcyan;"&gt;""&lt;/span&gt; 2 2 left &lt;span style="color: darkcyan;"&gt;"  "&lt;/span&gt;)&lt;br /&gt;              (&lt;span style="color: darkcyan;"&gt;"Mode"&lt;/span&gt; 16 16 left bs--get-mode-name)&lt;br /&gt;              (&lt;span style="color: darkcyan;"&gt;""&lt;/span&gt; 2 2 left &lt;span style="color: darkcyan;"&gt;"  "&lt;/span&gt;)&lt;br /&gt;              (&lt;span style="color: darkcyan;"&gt;"Buffer"&lt;/span&gt; bs--get-name-length 30 left bs--get-name))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, to flip sequentially through buffers (like Alt-Tab in a window manager) I use &lt;a href="http://www.emacswiki.org/emacs/iflipb"&gt;iflipb&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(setq iflipb-boring-buffer-filter 'my-bs-ignore-buffer)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now no matter how I switch buffers I get a consistent set to choose from, and if I want to change that set I can do it in one place.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7773847580486821558-5555198998646819853?l=scottfrazersblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://scottfrazersblog.blogspot.com/feeds/5555198998646819853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7773847580486821558&amp;postID=5555198998646819853' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/5555198998646819853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/5555198998646819853'/><link rel='alternate' type='text/html' href='http://scottfrazersblog.blogspot.com/2010/01/emacs-filtered-buffer-switching.html' title='Emacs:  Filtered Buffer Switching'/><author><name>Scott Frazer</name><uri>http://www.blogger.com/profile/02727761079827984966</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://bp1.blogger.com/_-ce9aQ2RrYs/RxQAX_pY_QI/AAAAAAAAAA8/m0nwV63Ozh8/s200/ScottFrazer.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7773847580486821558.post-5103319277782920837</id><published>2009-12-21T11:39:00.001-05:00</published><updated>2009-12-21T11:40:56.932-05:00</updated><title type='text'>Emacs:  Named Desktop Sessions</title><content type='html'>&lt;span style="font-size: large;"&gt;Emacs:&amp;nbsp; Named Desktop Sessions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can use the &lt;a href="http://www.gnu.org/software/emacs/manual/html_node/emacs/Saving-Emacs-Sessions.html"&gt;desktop&lt;/a&gt; package to save the state of Emacs sessions, i.e. which files are open, where point is in them and so on.&amp;nbsp; This is cool, but it saves the information in the current directory, which can be inconvenient.&amp;nbsp; What would be better is if you could save the session by name, like "task-a" or "project-b".&lt;br /&gt;&lt;br /&gt;I originally wrote some elisp to do this in &lt;a href="http://stackoverflow.com/questions/847962/what-alternate-session-managers-are-available-for-emacs/849180#849180"&gt;answer&lt;/a&gt; to a StackOverflow question, but found it so useful myself that I expanded it to do more.&amp;nbsp; The code is below, but here are the commands:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;my-desktop-save -- Save the current session by name&lt;/li&gt;&lt;li&gt;my-desktop-save-and-clear -- Same as above, but clear out all the buffers so you start with a "clean" session&lt;/li&gt;&lt;li&gt;my-desktop-read -- Load a session by name&lt;/li&gt;&lt;li&gt;my-desktop-change -- Save the current session and load a different one&lt;/li&gt;&lt;li&gt;my-desktop-name -- Echo the current session name&lt;/li&gt;&lt;/ul&gt;One more thing this code does: when you exit Emacs it automatically saves the current state as "last-session".&amp;nbsp; Sometimes I fat finger "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;C-x C-c&lt;/span&gt;&lt;/b&gt;" and exit accidentally, or I need to restart Emacs or my window manager and this lets me get back to what I was doing quickly.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;The Code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;require&lt;/b&gt;&lt;/span&gt; '&lt;span style="color: deepskyblue;"&gt;&lt;b&gt;desktop&lt;/b&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defvar&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;my-desktop-session-dir&lt;/span&gt;&lt;br /&gt;  (concat (getenv &lt;span style="color: darkcyan;"&gt;"HOME"&lt;/span&gt;) &lt;span style="color: darkcyan;"&gt;"/.emacs.d/desktop-sessions/"&lt;/span&gt;)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"*Directory to save desktop sessions in"&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defvar&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;my-desktop-session-name-hist&lt;/span&gt; nil&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Desktop session name history"&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-save&lt;/b&gt;&lt;/span&gt; (&lt;span style="color: mediumseagreen;"&gt;&lt;b&gt;&amp;amp;optional&lt;/b&gt;&lt;/span&gt; name)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Save desktop by name."&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;unless&lt;/b&gt;&lt;/span&gt; name&lt;br /&gt;    (setq name (my-desktop-get-session-name &lt;span style="color: darkcyan;"&gt;"Save session"&lt;/span&gt; t)))&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; name&lt;br /&gt;    (make-directory (concat my-desktop-session-dir name) t)&lt;br /&gt;    (desktop-save (concat my-desktop-session-dir name) t)))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-save-and-clear&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Save and clear desktop."&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (call-interactively 'my-desktop-save)&lt;br /&gt;  (desktop-clear)&lt;br /&gt;  (setq desktop-dirname nil))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-read&lt;/b&gt;&lt;/span&gt; (&lt;span style="color: mediumseagreen;"&gt;&lt;b&gt;&amp;amp;optional&lt;/b&gt;&lt;/span&gt; name)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Read desktop by name."&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;unless&lt;/b&gt;&lt;/span&gt; name&lt;br /&gt;    (setq name (my-desktop-get-session-name &lt;span style="color: darkcyan;"&gt;"Load session"&lt;/span&gt;)))&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; name&lt;br /&gt;    (desktop-clear)&lt;br /&gt;    (desktop-read (concat my-desktop-session-dir name))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-change&lt;/b&gt;&lt;/span&gt; (&lt;span style="color: mediumseagreen;"&gt;&lt;b&gt;&amp;amp;optional&lt;/b&gt;&lt;/span&gt; name)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Change desktops by name."&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((name (my-desktop-get-current-name)))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; name&lt;br /&gt;      (my-desktop-save name))&lt;br /&gt;    (call-interactively 'my-desktop-read)))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-name&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Return the current desktop name."&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((name (my-desktop-get-current-name)))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; name&lt;br /&gt;        (message (concat &lt;span style="color: darkcyan;"&gt;"Desktop name: "&lt;/span&gt; name))&lt;br /&gt;      (message &lt;span style="color: darkcyan;"&gt;"No named desktop loaded"&lt;/span&gt;))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-get-current-name&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Get the current desktop name."&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; desktop-dirname&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((dirname (substring desktop-dirname 0 -1)))&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; (string= (file-name-directory dirname) my-desktop-session-dir)&lt;br /&gt;        (file-name-nondirectory dirname)))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-get-session-name&lt;/b&gt;&lt;/span&gt; (prompt &lt;span style="color: mediumseagreen;"&gt;&lt;b&gt;&amp;amp;optional&lt;/b&gt;&lt;/span&gt; use-default)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Get a session name."&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let*&lt;/b&gt;&lt;/span&gt; ((default (and use-default (my-desktop-get-current-name)))&lt;br /&gt;         (full-prompt (concat prompt (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; default&lt;br /&gt;                                         (concat &lt;span style="color: darkcyan;"&gt;" (default "&lt;/span&gt; default &lt;span style="color: darkcyan;"&gt;"): "&lt;/span&gt;)&lt;br /&gt;                                       &lt;span style="color: darkcyan;"&gt;": "&lt;/span&gt;))))&lt;br /&gt;    (completing-read full-prompt (and (file-exists-p my-desktop-session-dir)&lt;br /&gt;                                      (directory-files my-desktop-session-dir))&lt;br /&gt;                     nil nil nil my-desktop-session-name-hist default)))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-desktop-kill-emacs-hook&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Save desktop before killing emacs."&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; (file-exists-p (concat my-desktop-session-dir &lt;span style="color: darkcyan;"&gt;"last-session"&lt;/span&gt;))&lt;br /&gt;    (setq desktop-file-modtime&lt;br /&gt;          (nth 5 (file-attributes (desktop-full-file-name (concat my-desktop-session-dir &lt;span style="color: darkcyan;"&gt;"last-session"&lt;/span&gt;))))))&lt;br /&gt;  (my-desktop-save &lt;span style="color: darkcyan;"&gt;"last-session"&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;(add-hook 'kill-emacs-hook 'my-desktop-kill-emacs-hook)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7773847580486821558-5103319277782920837?l=scottfrazersblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://scottfrazersblog.blogspot.com/feeds/5103319277782920837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7773847580486821558&amp;postID=5103319277782920837' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/5103319277782920837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/5103319277782920837'/><link rel='alternate' type='text/html' href='http://scottfrazersblog.blogspot.com/2009/12/emacs-named-desktop-sessions.html' title='Emacs:  Named Desktop Sessions'/><author><name>Scott Frazer</name><uri>http://www.blogger.com/profile/02727761079827984966</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://bp1.blogger.com/_-ce9aQ2RrYs/RxQAX_pY_QI/AAAAAAAAAA8/m0nwV63Ozh8/s200/ScottFrazer.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7773847580486821558.post-2463155781520302381</id><published>2009-12-18T14:19:00.000-05:00</published><updated>2009-12-18T14:19:33.236-05:00</updated><title type='text'>Emacs: Using Bookmarked Directories</title><content type='html'>&lt;span style="font-size: large;"&gt;Emacs: Using Bookmarked Directories&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can use &lt;a href="http://www.gnu.org/software/emacs/manual/html_node/emacs/Bookmarks.html"&gt;bookmarks&lt;/a&gt; to mark positions in files by name and jump back to them later.&amp;nbsp; I don't use file bookmarks often, but if you set a bookmark while you're in a &lt;a href="http://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html"&gt;dired&lt;/a&gt; buffer it saves the directory location.&amp;nbsp; I work on projects with thousands of files in hundreds of directories so this is extremely useful to me.&amp;nbsp; I set bookmarks in the dozen or so directories I use all the time, and a few others in strategic directories that I can start from and navigate down.&lt;br /&gt;&lt;br /&gt;This post isn't about how to use bookmarks in general, you can find that elsewhere.&amp;nbsp; You set a bookmark using "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;C-x r m&lt;/span&gt;&lt;/b&gt;", and list your bookmarks using "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;C-x r l&lt;/span&gt;&lt;/b&gt;".&amp;nbsp; In the bookmark list you can rename, delete, etc. the bookmarks.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;In Dired&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that we have Ido acting a bit more &lt;a href="http://scottfrazersblog.blogspot.com/2009/12/emacs-better-ido-flex-matching.html"&gt;sensibly&lt;/a&gt;, let's use it to choose a bookmark in dired and switch to that directory (I am assuming here you already have Ido enabled).&amp;nbsp; I like things in most recently used (MRU) order, so when we pick a bookmark we'll also move it to the top of the bookmark list:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;require&lt;/b&gt;&lt;/span&gt; '&lt;span style="color: deepskyblue;"&gt;&lt;b&gt;bookmark&lt;/b&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-ido-bookmark-jump&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Jump to bookmark using ido"&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((dir (my-ido-get-bookmark-dir)))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; dir&lt;br /&gt;      (find-alternate-file dir))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-ido-get-bookmark-dir&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Get the directory of a bookmark."&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let*&lt;/b&gt;&lt;/span&gt; ((name (ido-completing-read &lt;span style="color: darkcyan;"&gt;"Use dir of bookmark: "&lt;/span&gt; (bookmark-all-names) nil t))&lt;br /&gt;         (bmk (bookmark-get-bookmark name)))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; bmk&lt;br /&gt;      (setq bookmark-alist (delete bmk bookmark-alist))&lt;br /&gt;      (push bmk bookmark-alist)&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((filename (bookmark-get-filename bmk)))&lt;br /&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; (file-directory-p filename)&lt;br /&gt;            filename&lt;br /&gt;          (file-name-directory filename))))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-ido-dired-mode-hook&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  (define-key dired-mode-map &lt;span style="color: darkcyan;"&gt;"$"&lt;/span&gt; 'my-ido-bookmark-jump))&lt;br /&gt;&lt;br /&gt;(add-hook 'dired-mode-hook 'my-ido-dired-mode-hook)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now in dired you press &lt;b&gt;'&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$&lt;/span&gt;&lt;/b&gt;' to choose a bookmarked directory to switch to.&amp;nbsp; I stole '&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$&lt;/span&gt;&lt;/b&gt;' away from dired-hide-subdir, which I never use, because it's a mnemonic device as I'll explain later.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;In Ido&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That's good for dired, but it would be nice to be able to use bookmarks to switch directories when you are doing a regular ido-find-file with "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;C-x C-f&lt;/span&gt;&lt;/b&gt;":&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;my-ido-use-bookmark-dir&lt;/b&gt;&lt;/span&gt; ()&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Get directory of bookmark"&lt;/span&gt;&lt;br /&gt;  (interactive)&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let*&lt;/b&gt;&lt;/span&gt; ((enable-recursive-minibuffers t)&lt;br /&gt;         (dir (my-ido-get-bookmark-dir)))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; dir&lt;br /&gt;      (ido-set-current-directory dir)&lt;br /&gt;      (setq ido-exit 'refresh)&lt;br /&gt;      (exit-minibuffer))))&lt;br /&gt;&lt;br /&gt;(define-key ido-file-dir-completion-map (kbd &lt;span style="color: darkcyan;"&gt;"$"&lt;/span&gt;) 'my-ido-use-bookmark-dir)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now when you are opening a file, you can type '&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$&lt;/span&gt;&lt;/b&gt;', choose a bookmark, then ido will restart the find-file from that location.&lt;br /&gt;&lt;br /&gt;It has a quirk that after ido gets reseated in the new directory you can't navigate up, only down.&amp;nbsp; I get around this by hitting "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;C-e&lt;/span&gt;&lt;/b&gt;", editing the path, then "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;RET&lt;/span&gt;&lt;/b&gt;" to go back to Ido.&amp;nbsp; It's somewhat annoying, but it's only a couple keystrokes, and I don't do it often enough to motivate me to try to fix it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;In Your Shell &lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that you're jumping around the file system in Emacs, you'll start to miss it in your terminal.&amp;nbsp; Let's have Emacs write the bookmarks out to a file as shell variables:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(setq bookmark-save-flag 1)&lt;br /&gt;(setq bookmark-sort-flag nil)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defadvice&lt;/b&gt;&lt;/span&gt; &lt;span style="color: brown;"&gt;&lt;b&gt;bookmark-write-file&lt;/b&gt;&lt;/span&gt; (after my-bookmark-to-shell activate)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Convert bookmarks to format bash and tcsh (yuck!) can use."&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; (filename)&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;with-temp-buffer&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (bmk bookmark-alist)&lt;br /&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; (listp (caadr bmk))&lt;br /&gt;            (setq filename (cdr (assoc 'filename (cadr bmk))))&lt;br /&gt;          (setq filename (cdr (assoc 'filename (cdr bmk)))))&lt;br /&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;unless&lt;/b&gt;&lt;/span&gt; (file-directory-p filename)&lt;br /&gt;          (setq filename (file-name-directory filename)))&lt;br /&gt;        (insert (car bmk) &lt;span style="color: darkcyan;"&gt;"="&lt;/span&gt; filename)&lt;br /&gt;        (delete-char -1)&lt;br /&gt;        (newline))&lt;br /&gt;      (write-file &lt;span style="color: darkcyan;"&gt;"~/.bashrc_bmk"&lt;/span&gt;)&lt;br /&gt;      (goto-char (point-min))&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;while&lt;/b&gt;&lt;/span&gt; (not (eobp))&lt;br /&gt;        (beginning-of-line)&lt;br /&gt;        (insert &lt;span style="color: darkcyan;"&gt;"set "&lt;/span&gt;)&lt;br /&gt;        (forward-line))&lt;br /&gt;      (write-file &lt;span style="color: darkcyan;"&gt;"~/.cshrc_bmk"&lt;/span&gt;))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I have it writing out in both bash and csh compatible formats because I have to use tcsh at work (SIGH).&amp;nbsp; Now in your .bashrc put:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: #7d26cd;"&gt;bmk_file&lt;/span&gt;=~/.bashrc_bmk&lt;br /&gt;&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; [ -f $&lt;span style="color: #7d26cd;"&gt;bmk_file&lt;/span&gt; ]; &lt;span style="color: blue;"&gt;&lt;b&gt;then&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;  . $&lt;span style="color: #7d26cd;"&gt;bmk_file&lt;/span&gt;&lt;br /&gt;fi&lt;br /&gt;&lt;span style="color: orchid;"&gt;&lt;b&gt;alias&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;bmk_reload&lt;/span&gt;=&lt;span style="color: darkcyan;"&gt;'. $bmk_file'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: orchid;"&gt;&lt;b&gt;alias&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;bmk_list&lt;/span&gt;=&lt;span style="color: darkcyan;"&gt;"sort $bmk_file | awk 'BEGIN { FS = "'"[ =]" }; { printf("%-25s%s\n", $1, $2) }'"'"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or in your .cshrc:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: orchid;"&gt;&lt;b&gt;set&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;bmk_file&lt;/span&gt;=~/.cshrc_bmk&lt;br /&gt;&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; ( -f $&lt;span style="color: #7d26cd;"&gt;bmk_file&lt;/span&gt; ) &lt;span style="color: orchid;"&gt;&lt;b&gt;source&lt;/b&gt;&lt;/span&gt; $&lt;span style="color: #7d26cd;"&gt;bmk_file&lt;/span&gt;&lt;br /&gt;&lt;span style="color: orchid;"&gt;&lt;b&gt;alias&lt;/b&gt;&lt;/span&gt; bmk_reload &lt;span style="color: darkcyan;"&gt;"source $bmk_file"&lt;/span&gt;&lt;br /&gt;&lt;span style="color: orchid;"&gt;&lt;b&gt;alias&lt;/b&gt;&lt;/span&gt; bmk_list &lt;span style="color: darkcyan;"&gt;"sort $bmk_file | awk 'BEGIN { FS = "'"[ =]" }; { printf("%-25s%s\n", $2, $3) }'"'"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now if you have a bookmark named "proj_a_inc" you can do things like "cd $proj_a_inc" or "cp $proj_a_inc/foo.h ."&amp;nbsp; This is why I chose "&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$&lt;/span&gt;&lt;/b&gt;" above to easily remember my bookmark key (and it usually isn't in a filename).&amp;nbsp; The bmk_reload command reloads your bookmarks in case you add one in Emacs, and bmk_list lists your bookmarks and the directories they point to.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7773847580486821558-2463155781520302381?l=scottfrazersblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://scottfrazersblog.blogspot.com/feeds/2463155781520302381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7773847580486821558&amp;postID=2463155781520302381' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/2463155781520302381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/2463155781520302381'/><link rel='alternate' type='text/html' href='http://scottfrazersblog.blogspot.com/2009/12/emacs-using-bookmarked-directories.html' title='Emacs: Using Bookmarked Directories'/><author><name>Scott Frazer</name><uri>http://www.blogger.com/profile/02727761079827984966</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://bp1.blogger.com/_-ce9aQ2RrYs/RxQAX_pY_QI/AAAAAAAAAA8/m0nwV63Ozh8/s200/ScottFrazer.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7773847580486821558.post-6445486151898647864</id><published>2009-12-16T17:07:00.000-05:00</published><updated>2009-12-16T17:07:25.580-05:00</updated><title type='text'>Emacs: Better Ido Flex-Matching</title><content type='html'>I've had this blog for a long time ... perhaps I should actually post things.&amp;nbsp; Most will be about Emacs; there are plenty of beginner-level Emacs blogs out there, so I'll lean towards the intermediate/advanced.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Better Ido Flex-Matching&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First up: Better &lt;a href="http://www.emacswiki.org/emacs/InteractivelyDoThings"&gt;Ido&lt;/a&gt; flex-matching.&amp;nbsp; Ido lets you switch buffers, choose files, and so on by presenting you with a list of choices and narrowing the list down as you type.&amp;nbsp; Flex-matching does "fuzzy" matching of your input to the list of items; i.e. you can just type pieces of what you want, and the pieces don't even need to be next to each other.&lt;br /&gt;&lt;br /&gt;This is great, but it has some quirks.&amp;nbsp; Suppose my list of items was ("chops" "scone" "close" "confide") and I typed "co".&amp;nbsp; Ido throws out "chops" and "close" -- even though they have 'c' followed by 'o' -- and only presents "scone" and "confide" since they have "co" in them.&amp;nbsp; Now I add 's' so the input is "cos" and Ido discards the previous, seemingly only, matches and switches to "chops" and "close".&amp;nbsp; It presents them in that order as well, even though I consider "close" to be a better match since the 'o' and 's' are next to each other.&lt;br /&gt;&lt;br /&gt;I found this behavior inconsistent and surprising, so I decided to change the algorithm.&amp;nbsp; If you want to try my version but aren't interested in how it works, skip the next section.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;The Algorithm&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The first thing to do is see if someone else has solved this problem.&amp;nbsp; Trying different searches for "string matching", you get a lot of hits on finding substrings, which isn't what I want, and a lot for &lt;a href="http://en.wikipedia.org/wiki/Levenshtein_distance"&gt;Levenshtein Distance&lt;/a&gt;.&amp;nbsp; The Levenshtein algorithm is close (there's even an &lt;a href="http://www.emacswiki.org/emacs/LevenshteinDistance"&gt;elisp implementation&lt;/a&gt;, and similarly &lt;a href="http://www.emacswiki.org/emacs/fuzzy-match.el"&gt;fuzzy-match&lt;/a&gt;), but it's more for things like spelling suggestions.&amp;nbsp; It will always match,&amp;nbsp; giving you a measure of how close the strings are.&amp;nbsp; They don't even have to have any of the same characters in them.&amp;nbsp; There are a couple academic papers also, but they had various problems as well.&amp;nbsp; I know what I want, I'll just write it myself!&lt;br /&gt;&lt;br /&gt;We need to calculate a correlation value between the input and each item; that is, how closely they match.&amp;nbsp; A correlation value of 'nil' will mean no match, and zero or higher will mean a match with higher values meaning better matches.&amp;nbsp; The correlation value will be increased by one for every pair of adjacent letters in the input that are adjacent in the item (only the first time counts).&amp;nbsp; A special case of adding one is if the first letters in both match, as if the beginning of the string could be considered a character.&amp;nbsp; For example, if the input is "cos" and the item is "close", it will get one point for both starting with 'c', and one point for "os" being adjacent in both for a total of two.&amp;nbsp; If the item was "chops", it would only get one point (starts with 'c' and has 'o' and 's', but none adjacent).&lt;br /&gt;&lt;br /&gt;A hash table is created from the input, with each character being the key and a descending list of the character's positions in the input as the value.&amp;nbsp; For example, if the input is "aba", the table would have a -&amp;gt; (2 0), b -&amp;gt; (1).&amp;nbsp; Why they need to be in descending order will be explained later.&amp;nbsp; Since the same input is used for each item, the hash table is only created once.&lt;br /&gt;&lt;br /&gt;For each item, we start with an empty (all nil value) vector with a length equal to that of the input.&amp;nbsp; We then go through the characters of the item in order, using each to index into the hash table and retrieve the character's corresponding locations in the input string.&amp;nbsp; We look in the vector at these locations, and if the preceding vector location is non-nil (or we are at the beginning of the vector) we fill in the vector location with a cons cell.&amp;nbsp; This is why they must be in descending order, otherwise you would incorrectly fill in locations where there were double letters.&amp;nbsp; The car of the cell is the index of the item where the character was found, and the cdr is the current correlation that we will carry along.&amp;nbsp; If the car of the preceding vector location (the previous character location in the item) is the current item character location minus one, we add one to the correlation.&amp;nbsp; When all vector locations have been filled in, we have a match and we save the correlation.&amp;nbsp; As more matches happen we keep the highest correlation value.&lt;br /&gt;&lt;br /&gt;I know I'm kind of skimming over things without a lot of explanation, but this post is already getting too detailed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;The Implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here's the implementation, woven into Ido with a variable to switch to the regular flex-matching if you want:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #996633;"&gt;&lt;b&gt;my-ido-fuzzy-match&lt;/b&gt;&lt;/span&gt; (str items)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Better ido fuzzy matching"&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((str-len (length str)))&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; (= str-len 0)&lt;br /&gt;        (reverse items)&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((char-lookup (make-hash-table &lt;span style="color: orchid;"&gt;&lt;b&gt;:test&lt;/b&gt;&lt;/span&gt; 'equal)))&lt;br /&gt;        &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Make hash table of all characters with their corresponding indexes&lt;br /&gt;&lt;/span&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((chars (split-string (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; ido-case-fold (downcase str) str) &lt;span style="color: darkcyan;"&gt;""&lt;/span&gt; t))&lt;br /&gt;              (idx 0)&lt;br /&gt;              elt)&lt;br /&gt;          (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (char chars)&lt;br /&gt;            (setq elt (gethash char char-lookup))&lt;br /&gt;            (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; elt&lt;br /&gt;                (push idx elt) &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;It's important that the indexes are in descending order&lt;br /&gt;&lt;/span&gt;              (setq elt (list idx)))&lt;br /&gt;            (puthash char elt char-lookup)&lt;br /&gt;            (setq idx (1+ idx))))&lt;br /&gt;        &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Go through all the items&lt;br /&gt;&lt;/span&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; (corr matches)&lt;br /&gt;          (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (item items)&lt;br /&gt;            (setq corr (my-ido-match-get-correlation str-len char-lookup (ido-name item)))&lt;br /&gt;            (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; corr&lt;br /&gt;              (push (cons item corr) matches)))&lt;br /&gt;          &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Sort matches and return&lt;br /&gt;&lt;/span&gt;          (mapcar 'car (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; ido-rotate&lt;br /&gt;                           matches&lt;br /&gt;                         (sort matches (&lt;span style="color: blue;"&gt;&lt;b&gt;lambda&lt;/b&gt;&lt;/span&gt; (x y) (&amp;gt; (cdr x) (cdr y)))))))))))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defun&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #996633;"&gt;&lt;b&gt;my-ido-match-get-correlation&lt;/b&gt;&lt;/span&gt; (str-len char-lookup item)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Get the correlation for this item"&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;let&lt;/b&gt;&lt;/span&gt; ((partial-matches (make-vector str-len nil))&lt;br /&gt;        (chars (split-string (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; ido-case-fold (downcase item) item) &lt;span style="color: darkcyan;"&gt;""&lt;/span&gt; t))&lt;br /&gt;        (char-idx 0)&lt;br /&gt;        elt-idxs corr prev-partial-match curr-partial-match)&lt;br /&gt;    (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (char chars)&lt;br /&gt;      (setq elt-idxs (gethash char char-lookup))&lt;br /&gt;      (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; elt-idxs&lt;br /&gt;        (&lt;span style="color: blue;"&gt;&lt;b&gt;dolist&lt;/b&gt;&lt;/span&gt; (elt-idx elt-idxs)&lt;br /&gt;          &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Current and previous partial matches&lt;br /&gt;&lt;/span&gt;          (setq curr-partial-match (aref partial-matches elt-idx))&lt;br /&gt;          (setq prev-partial-match (and (&amp;gt; elt-idx 0)&lt;br /&gt;                                        (aref partial-matches (1- elt-idx))))&lt;br /&gt;          &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Create a new partial match if necessary&lt;br /&gt;&lt;/span&gt;          (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; (and (not curr-partial-match)&lt;br /&gt;                     (or prev-partial-match (= elt-idx 0)))&lt;br /&gt;            (setq curr-partial-match&lt;br /&gt;                  (aset partial-matches elt-idx&lt;br /&gt;                        (cons char-idx (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; (and (= elt-idx 0) (= char-idx 0)) 1 0)))))&lt;br /&gt;          &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Set (match-position . correlation)&lt;br /&gt;&lt;/span&gt;          (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; curr-partial-match&lt;br /&gt;            (setcar curr-partial-match char-idx)&lt;br /&gt;            (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; prev-partial-match&lt;br /&gt;              (setcdr curr-partial-match&lt;br /&gt;                      (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; (= char-idx (1+ (car prev-partial-match)))&lt;br /&gt;                          (1+ (cdr prev-partial-match))&lt;br /&gt;                        (cdr prev-partial-match))))&lt;br /&gt;            &lt;span style="color: #a8a8a8;"&gt;;; &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Update final correlation&lt;br /&gt;&lt;/span&gt;            (&lt;span style="color: blue;"&gt;&lt;b&gt;when&lt;/b&gt;&lt;/span&gt; (= elt-idx (1- str-len))&lt;br /&gt;              (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; corr&lt;br /&gt;                  (setq corr (max corr (cdr curr-partial-match)))&lt;br /&gt;                (setq corr (cdr curr-partial-match)))))))&lt;br /&gt;      (setq char-idx (1+ char-idx)))&lt;br /&gt;    corr))&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defvar&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #7d26cd;"&gt;my-ido-use-fuzzy-match&lt;/span&gt; t&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"*Use my-ido-fuzzy-match for ido matching"&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: blue;"&gt;&lt;b&gt;defadvice&lt;/b&gt;&lt;/span&gt; &lt;span style="color: #996633;"&gt;&lt;b&gt;ido-set-matches-1&lt;/b&gt;&lt;/span&gt; (around my-ido-set-matches-1 activate)&lt;br /&gt;  &lt;span style="color: darkcyan;"&gt;"Choose between the regular ido-set-matches-1 and my-ido-fuzzy-match"&lt;/span&gt;&lt;br /&gt;  (&lt;span style="color: blue;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; my-ido-use-fuzzy-match&lt;br /&gt;      (setq ad-return-value (my-ido-fuzzy-match ido-text (ad-get-arg 0)))&lt;br /&gt;    ad-do-it))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7773847580486821558-6445486151898647864?l=scottfrazersblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://scottfrazersblog.blogspot.com/feeds/6445486151898647864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7773847580486821558&amp;postID=6445486151898647864' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/6445486151898647864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7773847580486821558/posts/default/6445486151898647864'/><link rel='alternate' type='text/html' href='http://scottfrazersblog.blogspot.com/2009/12/emacs-better-ido-flex-matching.html' title='Emacs: Better Ido Flex-Matching'/><author><name>Scott Frazer</name><uri>http://www.blogger.com/profile/02727761079827984966</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://bp1.blogger.com/_-ce9aQ2RrYs/RxQAX_pY_QI/AAAAAAAAAA8/m0nwV63Ozh8/s200/ScottFrazer.jpg'/></author><thr:total>5</thr:total></entry></feed>
