28

I think it would be super if, in my LaTeX editor, I could click on the name of package...

\usepackage{blob}

... and the corresponding style file, blob.sty, would be opened.

Do any LaTeX editors have this feature? I mainly use Aquamacs/AucTeX myself, but am interested in other editors too. I reckon it would be mildly challenging to implement, because blob.sty might be

  • in the current working directory,
  • in the system texmf directory, or
  • in the user's texmf directory.
  • It's easier than you think. (On a related note, always back up your .emacs. Also, don't spill honey on your computer.) There are environment variables for each of those paths, and of course the cwd is trivial to check. File-exists-p and file-readable-p come in handy. – Sean Allred May 9 '13 at 9:12
  • 2
    My editor (winedt 7) does it - not by default (the default behaviour is to open the documentation) but it is easy to connect a command to a doubleclick on a string. With miktex I'm using the built-in findtexmf --start file which uses kpsewhich to find the file. – Ulrike Fischer May 9 '13 at 9:35
5

Emacs development version now has this feature builtin (see this commit). This will go live in Emacs 25.1. You just have to issue M-x ffap RET. Below there is my old answer, for reference.


Starting from David's answer, I've written the following functions (they need AUCTeX).

(defun mg-TeX-kpsewhich-find-file (&optional name)
  "Visit file associated to NAME searching for it with kpsewhich.
If NAME is nil prompt for a file name.  If there is an active
region, use it as initial input.  When it is called with
\\[universal-argument] prefix, visit file in another window, in
the current one otherwise."
  (interactive)
  (if (executable-find "kpsewhich")
      (let* ((fun (if current-prefix-arg 'find-file-other-window 'find-file))
         (default-directory (TeX-master-directory))
         (name (or name (TeX-read-string
                 "File name: "
                 (if (TeX-active-mark)
                 (buffer-substring-no-properties
                  (region-beginning) (region-end))))))
         (file (replace-regexp-in-string
            "[\n\r]*\\'" ""
            (shell-command-to-string (concat "kpsewhich " name)))))
    (if (and (not (zerop (length file))) (file-exists-p file))
        (funcall fun file)
      (message (concat "File " name " not found."))))
    (message "Kpsewhich not available.")))
(define-key TeX-mode-map (kbd "C-c k") 'mg-TeX-kpsewhich-find-file)

(defun mg-LaTeX-find-file-at-point ()
  "Visit LaTeX file searching for it with kpsewhich.
File basename is guessed from text around point and its extension
is guessed from current macro.  When it is called with
\\[universal-argument] prefix, visit file in another window, in
the current one otherwise.

See also `mg-TeX-kpsewhich-find-file'."
  (interactive)
  (let* ((file-name-regexp "-~/A-Za-z0-9_.$#%:+")
     ;; Get filename at point.
     (name
      ;; Check whether character at point is a valid file name character.
      (if (string-match (concat "[" file-name-regexp "]")
                (string (char-after)))
          (save-excursion
             (skip-chars-backward file-name-regexp)
             (looking-at (concat "\\([" file-name-regexp "]+\\)"))
             (TeX-match-buffer 1))))
     ;; Get current macro once.
     (current-macro (TeX-current-macro))
     ;; Guess file extension based on current macro.
     (extension (cond
             ((or (equal "usepackage" current-macro)
              (equal "RequirePackage" current-macro)
              (equal "RequirePackageWithOptions" current-macro))
              ".sty")
             ((or (equal "documentclass" current-macro)
              (equal "documentstyle" current-macro)
              (equal "LoadClass" current-macro)
              (equal "LoadClassWithOptions" current-macro))
              ".cls")
             ((equal "include" current-macro) ".tex")
             ((equal "input" current-macro)
              ;; `input' macro accepts a file name with extension, in
              ;; that case use an empty but non-nil extension.
              (if (and name (file-name-extension name)) "" ".tex"))
             ((equal "bibliography" current-macro) ".bib")
             ((equal "addbibresource" current-macro) "")
             (t nil))))
    (if (and name extension)
    (mg-TeX-kpsewhich-find-file (concat name extension))
      (message "Cannot guess file name at point."))))
(define-key LaTeX-mode-map (kbd "C-c f") 'mg-LaTeX-find-file-at-point)

As you can see, mg-LaTeX-find-file-at-point (my key binding: C-c f) works not only with \usepackage but also with \RequirePackage{,WithOptions}, \document{class,style}, \LoadClass{,WithOptions}, \include, \input, \bibliogaphy, and \addbibresource. You can search for any (La)TeX file with kpsewhich using M-x mg-TeX-kpsewhich-find-file or C-c k, and if there is an active region it will be used as initial input. This is useful for macros not known by mg-LaTeX-find-file-at-point, e.g. \lstinputlisting: you mark input file name and search for it with C-c k.

  • Cool! :-) There's a similar question here if you're feeling ambitious... – John Wickerson May 29 '13 at 14:22
  • 1
    egreg's answer explains that probably there will never be an editor with a really reliable feature for doing that. If you want to blindly use texdef, you have to gather class and its options, all packages and their options: current stable AUCTeX version mixes class, packages and all options, it's really a mess to set them apart (development version of AUCTeX makes a better job but it's not enough). Moreover you have to guess TeX format (tex, latex, xetex, luatex...), maybe it could be done looking at TeX-engine variable, but I'm not sure. Hence, I don't believe it's worth trying, sorry. – giordano May 29 '13 at 14:58
  • Heh, fair enough :-) – John Wickerson May 29 '13 at 15:47
27

Stick point somewhere near

\usepackage{array,colortbl}

and two buffers will open up for array and colortbl packages if you do M-x getpackage which you can bind to a key of your choice

(defun getpackage ()
(interactive)
(search-backward "\\")
(re-search-forward "usepackage[^{}]*{" nil t)
(while (looking-at "\\s-*,*\\([a-zA-Z0-9]+\\)")
(re-search-forward "\\s-*,*\\([a-zA-Z0-9]+\\)" nil 1)
(save-excursion
  (find-file-other-window (replace-regexp-in-string "[\n\r ]*" "" (shell-command-to-string (concat "kpsewhich " (match-string 1) ".sty")))))))
  • Wow, awesome, thank you so much! It doesn't seem to work in the presence of package options, however. – John Wickerson May 9 '13 at 10:03
  • 2
    oh yes options, change age\\s-*{" to age[^{}]*{" so it skips over anything up the the first { rather than just skipping over space (i'll edit answer) – David Carlisle May 9 '13 at 10:09
  • Thanks. I made my own keyboard shortcut by adding the line (global-set-key (kbd "C-c p") 'getpackage) at the end. – John Wickerson May 9 '13 at 10:25
  • 2
    Oh no, an emacs answer. :) +1 though. – Paulo Cereda May 10 '13 at 10:15
  • @PauloCereda well what is the respone in vi-ish? – Frank Mittelbach May 14 '13 at 21:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.