Archive for the ‘Emacs Lisp’ Category

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.

Advertisements

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.