Posts Tagged ‘Emacs’

Improving avy-goto-char-timer

Posted: September 17, 2015 in Emacs
Tags: ,

Until recently, the avy command avy-goto-char-timer read one char or two chars if the latter was typed within avy-timeout-seconds.  This command is the one I use most frequently of all avy commands (you can say, it’s basically the only one I use except for avy-goto-line), so I’ve improved it with this commit and this pull request which I guess will be accepted anytime soon.

What these two commits implement:

  1. Now avy-goto-char-timer may read one or as many chars as you want given that the respective next char is typed within avy-timeout-seconds.
  2. DEL deletes the last character from the input, so that you can fix typos easily without having to abort and invoke avy-goto-char-timer once again.
  3. RET immediately stops reading more chars without waiting for another avy-timeout-seconds.
  4. Most importantly: the matches of the current input chars are immediately highlighted to give you direct visual feedback.

Here’s a screencast of the new behavior.

avy-5

BTW, thanks a lot for camcorder.el, Artur!

UPDATE: The PR has already been merged, so you can now use this feature by updating your avy copy from MELPA.

When using Emacs on a larger screen where Emacs’ frame is split into multiple windows, you sometimes wish there was some simple way to rearrange which buffer is shown in which window.  Of course, you can do that by moving through your windows and using switch-to-buffer and friends but that’s not really convenient.

So here’s a command which lets you use drag one buffer from one window to the other. The effect is that the buffers of the start and target window are swapped.

(defun th/swap-window-buffers-by-dnd (drag-event)
  "Swaps the buffers displayed in the DRAG-EVENT's start and end
window."
  (interactive "e")
  (let ((start-win (cl-caadr drag-event))
        (end-win   (cl-caaddr drag-event)))
    (when (and (windowp start-win)
               (windowp end-win)
               (not (eq start-win end-win))
               (not (memq (minibuffer-window)
                          (list start-win end-win))))
      (let ((bs (window-buffer start-win))
            (be (window-buffer end-win)))
        (unless (eq bs be)
          (set-window-buffer start-win be)
          (set-window-buffer end-win bs))))))

Bind it to some mouse drag event and have fun. For example, I use (global-set-key (kbd "<C-S-drag-mouse-1>") #'th/swap-window-buffers-by-dnd) so that drag’n’drop with the left mouse button and control and shift pressed is bound to the command above.

Some weeks ago, I’ve tried out company-mode. While I’ve been really satisfied with its UI and completion machinery, I still stopped using it because when simply navigating using the arrow keys in some code buffer, the company-popup frequently opened without me noticing and my next <up> or <down> keystroke changed my buffer although I just wanted to move point.

So today I’ve thought I should go ahead and add an option to company-mode to trigger completion only if the last command has been an editing command. And guess what, that feature is already there. :-)

(setq company-begin-commands '(self-insert-command))

That will make the company completion trigger only if the last command has been self-insert-command. You can add as many commands as you like there. The default value is t which means to always trigger company completion after company-idle-delay.

With this setting and a very short company-idle-delay (0.3 secs), company-mode now works great for me.

After reading this post on the MasteringEmacs blog I gave eshell a try and I like it. On this other post he shows how to implement completion with pcomplete that is automatically used by eshell.

Without further ado, here are completions for git, bzr, and hg. The git completion is basically his completion with some improvements. For example, it completes all git commands by parsing the output of git help --all.

;;**** Git Completion

(defun pcmpl-git-commands ()
  "Return the most common git commands by parsing the git output."
  (with-temp-buffer
    (call-process-shell-command "git" nil (current-buffer) nil "help" "--all")
    (goto-char 0)
    (search-forward "available git commands in")
    (let (commands)
      (while (re-search-forward
	      "^[[:blank:]]+\\([[:word:]-.]+\\)[[:blank:]]*\\([[:word:]-.]+\\)?"
	      nil t)
	(push (match-string 1) commands)
	(when (match-string 2)
	  (push (match-string 2) commands)))
      (sort commands #'string<))))

(defconst pcmpl-git-commands (pcmpl-git-commands)
  "List of `git' commands.")

(defvar pcmpl-git-ref-list-cmd "git for-each-ref refs/ --format='%(refname)'"
  "The `git' command to run to get a list of refs.")

(defun pcmpl-git-get-refs (type)
  "Return a list of `git' refs filtered by TYPE."
  (with-temp-buffer
    (insert (shell-command-to-string pcmpl-git-ref-list-cmd))
    (goto-char (point-min))
    (let (refs)
      (while (re-search-forward (concat "^refs/" type "/\\(.+\\)$") nil t)
	(push (match-string 1) refs))
      (nreverse refs))))

(defun pcmpl-git-remotes ()
  "Return a list of remote repositories."
  (split-string (shell-command-to-string "git remote")))

(defun pcomplete/git ()
  "Completion for `git'."
  ;; Completion for the command argument.
  (pcomplete-here* pcmpl-git-commands)
  (cond
   ((pcomplete-match "help" 1)
    (pcomplete-here* pcmpl-git-commands))
   ((pcomplete-match (regexp-opt '("pull" "push")) 1)
    (pcomplete-here (pcmpl-git-remotes)))
   ;; provide branch completion for the command `checkout'.
   ((pcomplete-match "checkout" 1)
    (pcomplete-here* (append (pcmpl-git-get-refs "heads")
			     (pcmpl-git-get-refs "tags"))))
   (t
    (while (pcomplete-here (pcomplete-entries))))))

;;**** Bzr Completion

(defun pcmpl-bzr-commands ()
  "Return the most common bzr commands by parsing the bzr output."
  (with-temp-buffer
    (call-process-shell-command "bzr" nil (current-buffer) nil "help" "commands")
    (goto-char 0)
    (let (commands)
      (while (re-search-forward "^\\([[:word:]-]+\\)[[:blank:]]+" nil t)
	(push (match-string 1) commands))
      (sort commands #'string<))))

(defconst pcmpl-bzr-commands (pcmpl-bzr-commands)
  "List of `bzr' commands.")

(defun pcomplete/bzr ()
  "Completion for `bzr'."
  ;; Completion for the command argument.
  (pcomplete-here* pcmpl-bzr-commands)
  (cond
   ((pcomplete-match "help" 1)
    (pcomplete-here* pcmpl-bzr-commands))
   (t
    (while (pcomplete-here (pcomplete-entries))))))

;;**** Mercurial (hg) Completion

(defun pcmpl-hg-commands ()
  "Return the most common hg commands by parsing the hg output."
  (with-temp-buffer
    (call-process-shell-command "hg" nil (current-buffer) nil "-v" "help")
    (goto-char 0)
    (search-forward "list of commands:")
    (let (commands
	  (bound (save-excursion
		   (re-search-forward "^[[:alpha:]]")
		   (forward-line 0)
		   (point))))
      (while (re-search-forward
	      "^[[:blank:]]\\([[:word:]]+\\(?:, [[:word:]]+\\)*\\)" bound t)
	(let ((match (match-string 1)))
	  (if (not (string-match "," match))
	      (push (match-string 1) commands)
	    (dolist (c (split-string match ", ?"))
	      (push c commands)))))
      (sort commands #'string<))))

(defconst pcmpl-hg-commands (pcmpl-hg-commands)
  "List of `hg' commands.")

(defun pcomplete/hg ()
  "Completion for `hg'."
  ;; Completion for the command argument.
  (pcomplete-here* pcmpl-hg-commands)
  (cond
   ((pcomplete-match "help" 1)
    (pcomplete-here* pcmpl-hg-commands))
   (t
    (while (pcomplete-here (pcomplete-entries))))))

A quick pop-up shell for emacs

Posted: October 12, 2011 in Emacs, Emacs Lisp
Tags:

Some file managers provide a shortcut to quickly embed some terminal that’s initialized with the current directory as cwd. Wouldn’t that be cool to have in emacs?

For example, you edit some file, and then you want to quickly commit it. Using the code below, you can do that like this:

  • Edit the file and save: C-x C-s
  • Popup a shell: F12
  • Check changes and commit (using svn):
    $ svn diff | colordiff
    $ svn ci -m "some changes" RET
  • Close the popup shell again: F12
  • Here’s the code:

    (defvar th-shell-popup-buffer nil)
    
    (defun th-shell-popup ()
      "Toggle a shell popup buffer with the current file's directory as cwd."
      (interactive)
      (unless (buffer-live-p th-shell-popup-buffer)
        (save-window-excursion (shell "*Popup Shell*"))
        (setq th-shell-popup-buffer (get-buffer "*Popup Shell*")))
      (let ((win (get-buffer-window th-shell-popup-buffer))
    	(dir (file-name-directory (or (buffer-file-name)
    				      ;; dired
    				      dired-directory
    				      ;; use HOME
    				      "~/"))))
        (if win
    	(quit-window nil win)
          (pop-to-buffer th-shell-popup-buffer nil t)
          (comint-send-string nil (concat "cd " dir "\n")))))
    
    (global-set-key (kbd "<f12>") 'th-shell-popup)

    Using that, hitting F12 will popup a *Popup Shell* buffer, initialized in the directory containing the file your are currently editing. If the current buffer is not associated with a file, then the shell’s cwd is your HOME directory.

    Hitting F12 while the popup shell buffer is visible will hide it again. So you can use it as a simple toggle.

    The code ensures that the same shell buffer is reused over and over again (unless you kill it), so that you don’t end up with hundredth of them.

    And here’s a screenshot:

    UPDATES:

    • 2011-10-13 08:03: Made it work for dired buffers.
    • 2012-02-24 14:41: Use quit-window instead of delete-window.

    I’ve switched to another tiling window manager called awesome and it’s really like its name suggests. It’s more a window manager framework that can be programmed in lua to create a window manager that does exactly what you want it to do.

    Awesome has a lua library called naughty for displaying notifications. Now my idea was that whenever my mouse pointer enters the region of the textbox widget that shows the current date and time in a wibox, a popup should show the agenda for this week and dispose automatically when I move the mouse away.

    Ok, so here’s the code on the emacs side. It assures that the file /tmp/org-agenda.txt always contains a plain-text export of the current org agenda. It’s created once on emacs startup and after each change to any org agenda file it’ll be updated.

    ;; update agenda file after changes to org files
    (defun th-org-mode-init ()
      (add-hook 'after-save-hook 'th-org-update-agenda-file t t))
    
    (add-hook 'org-mode-hook 'th-org-mode-init)
    
    ;; that's the export function
    (defun th-org-update-agenda-file (&optional force)
      (interactive)
      (save-excursion
        (save-window-excursion
          (let ((file "/tmp/org-agenda.txt"))
            (org-agenda-list)
            (org-write-agenda file)))))
    
    ;; do it once at startup
    (th-org-update-agenda-file t)

    And here’s the lua code on the awesome side:

    -- the current agenda popup
    org_agenda_pupup = nil
    
    -- do some highlighting and show the popup
    function show_org_agenda ()
       local fd = io.open("/tmp/org-agenda.txt", "r")
       if not fd then
          return
       end
       local text = fd:read("*a")
       fd:close()
       -- highlight week agenda line
       text = text:gsub("(Week%-agenda[ ]+%(W%d%d?%):)", "<span style="text-decoration: underline;">%1</span>")
       -- highlight dates
       text = text:gsub("(%w+[ ]+%d%d? %w+ %d%d%d%d[^n]*)", "%1")
       -- highlight times
       text = text:gsub("(%d%d?:%d%d)", "%1")
       -- highlight tags
       text = text:gsub("(:[^ ]+:)([ ]*n)", "%1%2")
       -- highlight TODOs
       text = text:gsub("(TODO) ", "<strong>%1</strong> ")
       -- highlight categories
       text = text:gsub("([ ]+%w+:) ", "%1 ")
       org_agenda_pupup = naughty.notify(
          { text     = text,
            timeout  = 999999999,
            width    = 600,
            position = "bottom_right",
            screen   = mouse.screen })
    end
    
    -- dispose the popup
    function dispose_org_agenda ()
       if org_agenda_pupup ~= nil then
          naughty.destroy(org_agenda_pupup)
          org_agenda_pupup = nil
       end
    end
    
    mydatebox = widget({ type = "textbox", align = "right" }) -- shows the date
    mydatebox.mouse_enter = show_org_agenda
    mydatebox.mouse_leave = dispose_org_agenda
    
    -- after that the mydatebox is added to some wibox, of course...

    And that’s how it looks like.

    The Org Agenda in an Awesome/Naughty popup

    The Org Agenda in an Awesome/Naughty popup

    I use Carsten Dominik’s great emacs package org-mode for project planning, appointments and TODOs (org-mode homepage). One cool feature is that it integrates nicely with the remember package. So when an idea comes into my mind I need to remember for later, I hit M-x org-remember RET which presents me a buffer with a custom template, I type my note and hit C-c C-c. The note is filed then and the buffer is gone, so there’s really no interruption.

    One cool thing is that org-remember automatically inserts a link to the “thing” you had open when adding the note. This “thing” could be a info buffer, a mail, a usenet article, an image,…

    I thought it would be nice to use that facility to make TODO-bookmarks like “TODO read that great blog entry!” from inside my browser conkeror. And so I did.

    Here’s the emacs side of the code (put it in your ~/.emacs):

    (setq org-remember-templates 
          '(
            ;; lot of other templates...
            ("TODO"    ?t "* TODO %?
      :PROPERTIES:
      :created: %U
      :link: %a
      :END:
      %i")))
    
    (defun th-org-remember-conkeror (url)
      (interactive "s")
      (org-remember nil ?t)
      (save-excursion
        (insert "\n\n  [[" url "]]"))
      (local-set-key (kbd "C-c C-c")
             (lambda ()
               (interactive)
               (org-ctrl-c-ctrl-c)
               (delete-frame nil t))))

    And that’s the conkeror side (put it in your ~/.conkerorrc):

    function org_remember(url, window) {
        var cmd_str = 'emacsclient -c --eval \'(th-org-remember-conkeror "' + url + '")\'';
        if (window != null) {
        window.minibuffer.message('Issuing ' + cmd_str);
        }
        shell_command_blind(cmd_str);
    }
    
    interactive("org-remember", "Remember the current url with org-remember",
            function (I) {
            org_remember(I.buffer.display_URI_string, I.window);
            });

    This code may require emacs 23, I’m not too sure. What you need in every case is a running emacs server, so that you can connect to it with emacsclient.

    UPDATE: Now I use org-protocol instead of something home-brewn.

    Here’s the updated code for your ~/.conkerorrc. On the emacs side nothing is needed anymore.

    function org_remember(url, title, text, window) {
        var eurl = encodeURIComponent(url);
        var etitle = encodeURIComponent(title);
        var etext = encodeURIComponent(text);
        var cmd_str = "emacsclient -c org-protocol://remember://" + eurl + "/" + etitle + "/" + etext; 
        window.minibuffer.message("Issuing " + cmd_str);
        shell_command_blind(cmd_str);
    }
    
    interactive("org-remember", "Remember the current url with org-remember",
            function (I) {
              org_remember(I.buffer.display_uri_string,
                           I.buffer.document.title,
                           I.buffer.top_frame.getSelection(),
                           I.window);
            });