From 75563bf71e6df356a5ae77a93152fcf913378107 Mon Sep 17 00:00:00 2001 From: Carsten Dominik Date: Sun, 21 Mar 2010 18:10:00 +0100 Subject: [PATCH] Allow regexps in org-file-apps to capture link parameters using groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Jan Böker. Jan writes: > What is this? > ============= > > This patch changes the way extension regexps in `org-file-apps' are > handled. Instead of against the file name, the regexps are now matched > against the whole link, and you can use grouping to extract link > parameters which you can then use in a command string to be executed. > > For example, to allow linking to PDF files using the syntax > file:/doc.pdf::, you can add the following entry to > org-file-apps: > > Extension: \.pdf::\([0-9]+\)\' > Command: evince "%s" -p %1 > > In a command string to be executed, the parameters can be referenced > using %1, %2, etc. Lisp forms can access them using (string-match n link). > > > Where to get it? > ================ > Either apply the patch by hand or > > git pull git://github.com/jboecker/org-mode.git org-file-apps-parameters > > > What's next? / Feedback > ======================= > > - Find the bugs. Since this messes with links, a central concept of Org, > I probably have missed some edge cases; so please test this and > report if it works for you. > > I also appreciate any feedback on code quality or the design decisions > made. I am learning elisp along the way, so you may be able to write > some changes in a more idiomatic and/or elegant way. > > - Add a mechanism for org-mode modules to add default values to > org-file-apps, similar to the variables org-file-apps-defaults-*. > This could be used by modules to define their own extensions to the > syntax of file: links. > > - Modify org-docview.el to use this and deprecate the docview: link syntax. > > > What does it (intentionally) break? > =================================== > > This patch introduces a backwards-incompatible change. If LINE or SEARCH > is given, the file is no longer guaranteed to open in emacs: if IN-EMACS > is nil and an entry in org-file-apps matches, that takes precedence. > > A grep of the lisp/ and contrib/ directories showed that no code in the > org-mode distribution was relying on this behaviour; whereever LINE or > SEARCH is given, IN-EMACS is also set to t. > > I decided against adding an additional parameter because that would be > redundant; the original link as seen by org-open-at-point can be > reconstructed from PATH, LINE and SEARCH. > > I am not that sure if this is the right way to do this, but it seems to > break as little as possible while hopefully avoiding to add too much > complexity. --- lisp/ChangeLog | 10 ++++++++ lisp/org.el | 64 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index b7ee6478f..82768cbd0 100755 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,13 @@ +2010-03-21 Jan Böcker + + * org.el (org-open-file): Allow regular expressions in + org-file-apps to capture link parameters using groups. In a + command string to be executed, the parameters can be referenced + using %1, %2, etc. Lisp forms can access them using + (match-string n link). + (org-apps-regexp-alist): Adopt the created regexp, as this is now + matched against a file: link instead of the file name. + 2010-03-21 Carsten Dominik * org-crypt.el (org-reveal-start-hook): Add a decryption function diff --git a/lisp/org.el b/lisp/org.el index b7235287b..60191f5a2 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -1463,9 +1463,10 @@ you can use this variable to set the application for a given file extension. The entries in this list are cons cells where the car identifies files and the cdr the corresponding command. Possible values for the file identifier are - \"regex\" Regular expression matched against the file name. For backward - compatibility, this can also be a string with only alphanumeric - characters, which is then interpreted as an extension. + \"regex\" Regular expression matched against the file: link. For + backward compatibility, this can also be a string with only + alphanumeric characters, which is then interpreted as an + extension. `directory' Matches a directory `remote' Matches a remote file, accessible through tramp or efs. Remote files most likely should be visited through Emacs @@ -1494,9 +1495,13 @@ Possible values for the command are: does define this command, but you can overrule/replace it here. string A command to be executed by a shell; %s will be replaced - by the path to the file. + by the path to the file. If the file identifier is a regex, + %n will be replaced by the match of the nth match group. sexp A Lisp form which will be evaluated. The file path will - be available in the Lisp variable `file'. + be available in the Lisp variable `file', the link itself + in the Lisp variable `link'. If the file identifier is a regex, + the original match data will be restored, so subexpression + matches are accessible using (match-string n link). For more examples, see the system specific constants `org-file-apps-defaults-macosx' `org-file-apps-defaults-windowsnt' @@ -9007,10 +9012,12 @@ With a double C-c C-u prefix arg, Org tries to avoid opening in Emacs and to use an external application to visit the file. Optional LINE specifies a line to go to, optional SEARCH a string to -search for. If LINE or SEARCH is given, the file will always be -opened in Emacs. +search for. If LINE or SEARCH is given, but IN-EMACS is nil, it will +be assumed that org-open-file was called to open a file: link, and the +original link to match against org-file-apps will be reconstructed +from PATH and whichever of LINE or SEARCH is given. + If the file does not exist, an error is thrown." - (setq in-emacs (or in-emacs line search)) (let* ((file (if (equal path "") buffer-file-name (substitute-in-file-name (expand-file-name path)))) @@ -9022,10 +9029,19 @@ If the file does not exist, an error is thrown." file)) (a-m-a-p (assq 'auto-mode apps)) (dfile (downcase file)) + ;; reconstruct the original file: link from the PATH, LINE and SEARCH args + (link (cond ((and (eq line nil) + (eq search nil)) + file) + (line + (concat file "::" (number-to-string line))) + (search + (concat file "::" search)))) + (dlink (downcase link)) (old-buffer (current-buffer)) (old-pos (point)) (old-mode major-mode) - ext cmd) + ext cmd link-match-data) (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\.gz\\)$" dfile) (setq ext (match-string 1 dfile)) (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\)$" dfile) @@ -9037,8 +9053,15 @@ If the file does not exist, an error is thrown." (t (setq cmd (or (and remp (cdr (assoc 'remote apps))) (and dirp (cdr (assoc 'directory apps))) - (assoc-default dfile (org-apps-regexp-alist apps a-m-a-p) - 'string-match) + ;; if we find a match in org-file-apps, store the match + ;; data for later + (let ((match (assoc-default dlink (org-apps-regexp-alist + apps a-m-a-p) + 'string-match))) + (if match + (progn (setq link-match-data (match-data)) + match) + nil)) (cdr (assoc ext apps)) (cdr (assoc t apps)))))) (when (eq cmd 'system) @@ -9068,6 +9091,18 @@ If the file does not exist, an error is thrown." (shell-quote-argument (convert-standard-filename file))) t t cmd))) + ;; Replace "%1", "%2" etc. in command with group matches from regex + (save-match-data + (let ((match-index 1) + (number-of-groups (- (/ (length link-match-data) 2) 1))) + (set-match-data link-match-data) + (while (<= match-index number-of-groups) + (let ((regex (concat "%" (number-to-string match-index))) + (replace-with (match-string match-index dlink))) + (while (string-match regex cmd) + (setq cmd (replace-match replace-with t t cmd)))) + (setq match-index (+ match-index 1))))) + (save-window-excursion (start-process-shell-command cmd nil cmd) (and (boundp 'org-wait) (numberp org-wait) (sit-for org-wait)) @@ -9080,7 +9115,9 @@ If the file does not exist, an error is thrown." (if search (org-link-search search)))) ((consp cmd) (let ((file (convert-standard-filename file))) - (eval cmd))) + (save-match-data + (set-match-data link-match-data) + (eval cmd)))) (t (funcall (cdr (assq 'file org-link-frame-setup)) file))) (and (org-mode-p) (eq old-mode 'org-mode) (or (not (equal old-buffer (current-buffer))) @@ -9110,7 +9147,8 @@ be opened in Emacs." nil (if (string-match "\\W" (car x)) x - (cons (concat "\\." (car x) "\\'") (cdr x))))) + (cons (concat "\\." (car x) "\\(::.*\\)?\\'") + (cdr x))))) list)) (if add-auto-mode (mapcar (lambda (x) (cons (car x) 'emacs)) auto-mode-alist))))