Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

217 lines (193 sloc) 9.56 KB
;;; helm-external.el --- Run Externals commands within Emacs with helm completion.
;; Copyright (C) 2012 Thierry Volpiatto <>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <>.
;;; Code:
(eval-when-compile (require 'cl))
(require 'helm)
(defgroup helm-external nil
"External related Applications and libraries for Helm."
:group 'helm)
(defcustom helm-raise-command nil
"A shell command to jump to a window running specific program.
Need external program wmctrl.
This will be use with `format', so use something like \"wmctrl -xa %s\"."
:type 'string
:group 'helm-external)
(defcustom helm-c-external-programs-associations nil
"Alist to store externals programs associated with file extension.
This variable overhide setting in .mailcap file.
e.g : '\(\(\"jpg\" . \"gqview\"\) (\"pdf\" . \"xpdf\"\)\) "
:type '(alist :key-type string :value-type string)
:group 'helm-external)
(defcustom helm-c-default-external-file-browser "nautilus"
"Default external file browser for your system.
Directories will be opened externally with it when
opening file externally in `helm-find-files'.
Set to nil if you do not have external file browser
or do not want to use it.
Windows users should set that to \"explorer.exe\"."
:group 'helm-external
:type 'string)
;;; Internals
(defvar helm-external-command-history nil)
(defvar helm-c-external-commands-list nil
"A list of all external commands the user can execute. If this
variable is not set by the user, it will be calculated
(defun helm-c-external-commands-list-1 (&optional sort)
"Returns a list of all external commands the user can execute.
If `helm-c-external-commands-list' is non-nil it will
return its contents. Else it calculates all external commands
and sets `helm-c-external-commands-list'."
(if helm-c-external-commands-list
(setq helm-c-external-commands-list
with paths = (split-string (getenv "PATH") path-separator)
with completions = ()
for dir in paths
when (and (file-exists-p dir) (file-accessible-directory-p dir))
for lsdir = (loop for i in (directory-files dir t)
for bn = (file-name-nondirectory i)
when (and (not (member bn completions))
(not (file-directory-p i))
(file-executable-p i))
collect bn)
append lsdir into completions
finally return (if sort (sort completions 'string-lessp) completions)))))
(defun helm-run-or-raise (exe &optional file)
"Generic command that run asynchronously EXE.
If EXE is already running just jump to his window if `helm-raise-command'
is non--nil.
When FILE argument is provided run EXE with FILE.
In this case EXE must be provided as \"EXE %s\"."
(lexical-let* ((real-com (car (split-string (replace-regexp-in-string
"%s" "" exe))))
(proc (if file (concat real-com " " file) real-com)))
(if (get-process proc)
(if helm-raise-command
(shell-command (format helm-raise-command real-com))
(error "Error: %s is already running" real-com))
(when (loop for i in helm-c-external-commands-list thereis real-com)
(message "Starting %s..." real-com)
(if file
proc nil (format exe (shell-quote-argument
(if (eq system-type 'windows-nt)
(helm-w32-prepare-filename file)
(start-process-shell-command proc nil real-com))
(get-process proc)
#'(lambda (process event)
(when (and (string= event "finished\n")
(not (helm-c-get-pid-from-process-name real-com)))
(shell-command (format helm-raise-command "emacs")))
(message "%s process...Finished." process))))
(setq helm-c-external-commands-list
(cons real-com
(delete real-com helm-c-external-commands-list))))))
(defun helm-get-mailcap-for-file (filename)
"Get the command to use for FILENAME from mailcap files.
The command is like <command %s> and is meant to use with `format'."
(let* ((ext (file-name-extension filename))
(mime (when ext (mailcap-extension-to-mime ext)))
(result (when mime (mailcap-mime-info mime))))
;; If elisp file have no associations in .mailcap
;; `mailcap-maybe-eval' is returned, in this case just return nil.
(when (stringp result) result)))
(defun helm-get-default-program-for-file (filename)
"Try to find a default program to open FILENAME.
Try first in `helm-c-external-programs-associations' and then in mailcap file
if nothing found return nil."
(let* ((ext (file-name-extension filename))
(def-prog (assoc-default ext helm-c-external-programs-associations)))
(cond ((and def-prog (not (string= def-prog "")))
(concat def-prog " %s"))
((and helm-c-default-external-file-browser
(file-directory-p filename))
(concat helm-c-default-external-file-browser " %s"))
(t (helm-get-mailcap-for-file filename)))))
(defun helm-c-open-file-externally (file)
"Open FILE with an external program.
Try to guess which program to use with `helm-get-default-program-for-file'.
If not found or a prefix arg is given query the user which tool to use."
(let* ((fname (expand-file-name file))
(collection (helm-c-external-commands-list-1 'sort))
(def-prog (helm-get-default-program-for-file fname))
(real-prog-name (if (or helm-current-prefix-arg (not def-prog))
;; Prefix arg or no default program.
"Program: " collection
:must-match t
:name "Open file Externally"
:del-input nil
:history helm-external-command-history)
;; Always prompt to set this program as default.
(setq def-prog nil))
;; No prefix arg or default program exists.
(replace-regexp-in-string " %s\\| '%s'" "" def-prog)))
(program (concat real-prog-name " %s")))
(unless (or def-prog ; Association exists, no need to record it.
;; Don't try to record non--filenames associations (e.g urls).
(not (file-exists-p fname)))
"Do you want to make `%s' the default program for this kind of files? "
(helm-aif (assoc (file-name-extension fname)
(setq helm-c-external-programs-associations
(delete it helm-c-external-programs-associations)))
(push (cons (file-name-extension fname)
"Program (Add args maybe and confirm): " real-prog-name))
(customize-save-variable 'helm-c-external-programs-associations
(helm-run-or-raise program file)
(setq helm-external-command-history
(cons real-prog-name
(delete real-prog-name
(loop for i in helm-external-command-history
when (executable-find i) collect i))))))
(defun helm-c-run-external-command (program)
"Preconfigured `helm' to run External PROGRAM asyncronously from Emacs.
If program is already running exit with error.
You can set your own list of commands with
(interactive (list
"RunProgram: "
(helm-c-external-commands-list-1 'sort)
:must-match t
:del-input nil
:name "External Commands"
:history helm-external-command-history)))
(helm-run-or-raise program)
(setq helm-external-command-history
(cons program (delete program
(loop for i in helm-external-command-history
when (executable-find i) collect i)))))
(provide 'helm-external)
;; Local Variables:
;; byte-compile-warnings: (not cl-functions obsolete)
;; coding: utf-8
;; indent-tabs-mode: nil
;; byte-compile-dynamic: t
;; End:
;;; helm-external ends here
Jump to Line
Something went wrong with that request. Please try again.