使用“文件名:行”语法打开文件的行

问题描述 投票:0回答:14

很多时候,编译错误会以

file:line
语法显示。

直接复制粘贴以在右行打开文件就好了。

Emacs 已经有一些模式可以在缓冲区中处理这个问题(编译模式、iirc),但我希望可以从 shell 命令行获得此功能,因为我在 emacs 之外的大部分时间都使用标准 shell。

知道如何调整 emacs 来学习

file:line
语法以在
file
行打开
line
吗? (显然,如果磁盘上确实存在
file:line
,最好打开它)

bash emacs
14个回答
58
投票

您可以使用 emacsclient 来完成此操作。例如在新框架中打开第 4 行第 3 列的 FILE:

emacsclient +4:3 FILE

省略

:3
即可在第 4 行打开文件。


20
投票

我的

.emacs
中有以下内容,但我没有发现它像我想象的那样有用。

;; Open files and goto lines like we see from g++ etc. i.e. file:line#
;; (to-do "make `find-file-line-number' check if the file exists")
(defun find-file-noselect--advice-around-with-line-number (orig-fun filename &rest args)
  (save-match-data
    (let* ((matched (string-match "^\\(.*\\):\\([0-9]+\\):?$" filename))
           (line-number (and matched
                             (match-string 2 filename)
                             (string-to-number (match-string 2 filename))))
           (filename (if matched (match-string 1 filename) filename))

           ;; Call the underlying function
           (buf (apply orig-fun filename args)))

      (when line-number
        (with-current-buffer buf
          ;; goto-line is for interactive use
          (goto-char (point-min))
          (forward-line (1- line-number))))

      ;; Always need to return the buffer for other things to use
      buf)))
;; By advising find-file-noselect instead of find-file we also get this
;; behaviour in lots of other places, e.g. opening files from emacsclient.
(advice-add #'find-file-noselect :around #'find-file-noselect--advice-around-with-line-number)

14
投票

我建议在你的 emacs 配置中添加以下代码:

(defadvice server-visit-files (before parse-numbers-in-lines (files proc &optional nowait) activate)
  "looks for filenames like file:line or file:line:position and reparses name in such manner that position in file"
  (ad-set-arg 0
              (mapcar (lambda (fn)
                        (let ((name (car fn)))
                          (if (string-match "^\\(.*?\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?$" name)
                              (cons
                               (match-string 1 name)
                               (cons (string-to-number (match-string 2 name))
                                     (string-to-number (or (match-string 3 name) "")))
                               )
                            fn))) files))
  )

现在您可以直接从命令行打开带有行号的文件,如下所示:

emacsclient filename:linenumber:position

附注我希望我的回答还不算太晚。


12
投票

这是我的尝试。调用原始的 find-file-at-point

(defun find-file-at-point-with-line()
  "if file has an attached line num goto that line, ie boom.rb:12"
  (interactive)
  (setq line-num 0)
  (save-excursion
    (search-forward-regexp "[^ ]:" (point-max) t)
    (if (looking-at "[0-9]+")
         (setq line-num (string-to-number (buffer-substring (match-beginning 0) (match-end 0))))))
  (find-file-at-point)
  (if (not (equal line-num 0))
      (goto-line line-num)))

9
投票

您可以使用 bash 脚本:

#! /bin/bash
file=$(awk '{sub(/:[0-9]*$/,"")}1' <<< "$1")
line=$(awk '{sub(/^.*:/,"")}1' <<< "$1")
emacs --no-splash "+$line" "$file" &

如果您为

openline
调用此脚本,并且收到错误消息,例如

Error: file.cpp:1046

你可以做

openline file.cpp:1046

打开第 1046 行

file.cpp
中的
Emacs
..


8
投票

Ivan Andrus 不错的查找文件建议的另一个版本,它同时执行行号和可选列号,正如您在节点和咖啡脚本错误中看到的那样:

;; Open files and go places like we see from error messages, i e: path:line:col
;; (to-do "make `find-file-line-number' work for emacsclient as well")
;; (to-do "make `find-file-line-number' check if the file exists")
(defadvice find-file (around find-file-line-number
                             (path &optional wildcards)
                             activate)
  "Turn files like file.js:14:10 into file.js and going to line 14, col 10."
  (save-match-data
    (let* ((match (string-match "^\\(.*?\\):\\([0-9]+\\):?\\([0-9]*\\)$" path))
           (line-no (and match
                         (match-string 2 path)
                         (string-to-number (match-string 2 path))))
           (col-no (and match
                        (match-string 3 path)
                        (string-to-number (match-string 3 path))))
           (path (if match (match-string 1 path) path)))
      ad-do-it
      (when line-no
        ;; goto-line is for interactive use
        (goto-char (point-min))
        (forward-line (1- line-no))
        (when (> col-no 0)
          (forward-char (1- col-no)))))))

7
投票

Emacs 25 不再使用

defadvice
参考

所以这是更新为新语法的版本:

(defun find-file--line-number (orig-fun filename &optional wildcards)
  "Turn files like file.cpp:14 into file.cpp and going to the 14-th line."
  (save-match-data
    (let* ((matched (string-match "^\\(.*\\):\\([0-9]+\\):?$" filename))
           (line-number (and matched
                             (match-string 2 filename)
                             (string-to-number (match-string 2 filename))))
           (filename (if matched (match-string 1 filename) filename)))
      (apply orig-fun (list filename wildcards))
      (when line-number
        ;; goto-line is for interactive use
        (goto-char (point-min))
        (forward-line (1- line-number))))))

(advice-add 'find-file :around #'find-file--line-number)

如果您从 emacs 内部调用打开文件(C-x C-f),这可以工作,但从命令行不再工作,当您从命令行调用它时,emacs 25 似乎不使用

find-file
,我不知道如何调试这种事情。


3
投票

您谈论了粘贴以打开文件(我假设您的意思是在 emacs 内的查找文件提示符下)以及从命令行执行某些操作。如果您想复制和粘贴,那么您需要执行类似 Ivan 在 defadvice 中展示的操作。如果您想从命令行执行某些操作,可以执行以下操作。我根据一年前使用 emacs:// URI 处理程序(在 Firefox 中使用)所做的事情进行了改编:

将其放入您的 .emacs 文件中:

(defun emacs-uri-handler (uri)
  "Handles emacs URIs in the form: emacs:///path/to/file/LINENUM"
  (save-match-data
    (if (string-match "emacs://\\(.*\\)/\\([0-9]+\\)$" uri)
        (let ((filename (match-string 1 uri))
              (linenum (match-string 2 uri)))
          (while (string-match "\\(%20\\)" filename)
            (setq filename (replace-match " " nil t filename 1)))
          (with-current-buffer (find-file filename)
            (goto-line (string-to-number linenum))))
      (beep)
      (message "Unable to parse the URI <%s>"  uri))))

然后在您的路径中创建一个 shell 脚本(我将其称为“emacsat”):

#!/bin/bash
emacsclient --no-wait -e "(emacs-uri-handler \"emacs://$1/${2:-1}\")"

DOS 批处理脚本看起来类似,但我不知道如何设置默认值(尽管我很确定你可以做到)。

如果您也想与 Firefox 集成,请参阅如何配置 Firefox 以在某些链接上运行 emacsclientw? 以获取更多说明。


2
投票

为了返回42的代码,添加了列号支持,并清理了存在列号和查找行号的情况。

;; find file at point, jump to line no.
;; ====================================

(require 'ffap)

(defun find-file-at-point-with-line (&optional filename)
  "Opens file at point and moves point to line specified next to file name."
  (interactive)
  (let* ((filename (or filename (if current-prefix-arg (ffap-prompter) (ffap-guesser))))
         (line-number
          (and (or (looking-at ".* line \\(\[0-9\]+\\)")
                   (looking-at "[^:]*:\\(\[0-9\]+\\)"))
               (string-to-number (match-string-no-properties 1))))
         (column-number
          (or 
           (and (looking-at "[^:]*:\[0-9\]+:\\(\[0-9\]+\\)")
                (string-to-number (match-string-no-properties 1)))
           (let 'column-number 0))))
    (message "%s --> %s:%s" filename line-number column-number)
    (cond ((ffap-url-p filename)
           (let (current-prefix-arg)
             (funcall ffap-url-fetcher filename)))
          ((and line-number
                (file-exists-p filename))
           (progn (find-file-other-window filename)
                  ;; goto-line is for interactive use
                  (goto-char (point-min))
                  (forward-line (1- line-number))
                  (forward-char column-number)))
          ((and ffap-pass-wildcards-to-dired
                ffap-dired-wildcards
                (string-match ffap-dired-wildcards filename))
           (funcall ffap-directory-finder filename))
          ((and ffap-dired-wildcards
                (string-match ffap-dired-wildcards filename)
                find-file-wildcards
                ;; Check if it's find-file that supports wildcards arg
                (memq ffap-file-finder '(find-file find-alternate-file)))
           (funcall ffap-file-finder (expand-file-name filename) t))
          ((or (not ffap-newfile-prompt)
               (file-exists-p filename)
               (y-or-n-p "File does not exist, create buffer? "))
           (funcall ffap-file-finder
                    ;; expand-file-name fixes "~/~/.emacs" bug sent by CHUCKR.
                    (expand-file-name filename)))
          ;; User does not want to find a non-existent file:
          ((signal 'file-error (list "Opening file buffer"
                                     "no such file or directory"
                                     filename))))))

1
投票

我对

find-file-at-point
函数进行了一些重写。

如果行号匹配,文件将在另一个窗口中打开,并且课程器将放置在该行中。如果没有行号匹配,则执行 ffap 通常执行的操作...

;; find file at point, jump to line no.
;; ====================================

(require 'ffap)

(defun find-file-at-point-with-line (&optional filename)
  "Opens file at point and moves point to line specified next to file name."
  (interactive)
  (let* ((filename (or filename (ffap-prompter)))
     (line-number
      (and (or (looking-at ".* line \\(\[0-9\]+\\)")
           (looking-at ".*:\\(\[0-9\]+\\):"))
       (string-to-number (match-string-no-properties 1)))))
(message "%s --> %s" filename line-number)
(cond ((ffap-url-p filename)
       (let (current-prefix-arg)
     (funcall ffap-url-fetcher filename)))
      ((and line-number
        (file-exists-p filename))
       (progn (find-file-other-window filename)
          (goto-line line-number)))
      ((and ffap-pass-wildcards-to-dired
        ffap-dired-wildcards
        (string-match ffap-dired-wildcards filename))
       (funcall ffap-directory-finder filename))
      ((and ffap-dired-wildcards
        (string-match ffap-dired-wildcards filename)
        find-file-wildcards
        ;; Check if it's find-file that supports wildcards arg
        (memq ffap-file-finder '(find-file find-alternate-file)))
       (funcall ffap-file-finder (expand-file-name filename) t))
      ((or (not ffap-newfile-prompt)
       (file-exists-p filename)
       (y-or-n-p "File does not exist, create buffer? "))
       (funcall ffap-file-finder
        ;; expand-file-name fixes "~/~/.emacs" bug sent by CHUCKR.
        (expand-file-name filename)))
      ;; User does not want to find a non-existent file:
      ((signal 'file-error (list "Opening file buffer"
                 "no such file or directory"
                 filename))))))

如果您有旧版本的 ffap (2008),您应该更新您的 emacs 或应用 另一个小补丁...

--- Emacs/lisp/ffap.el
+++ Emacs/lisp/ffap.el
@@ -1170,7 +1170,7 @@ which may actually result in an url rather than a filename."
          ;; remote, you probably already have a connection.
          ((and (not abs) (ffap-file-exists-string name)))
          ;; Try stripping off line numbers; good for compilation/grep output.
-         ((and (not abs) (string-match ":[0-9]" name)
+         ((and (string-match ":[0-9]" name)
                (ffap-file-exists-string (substring name 0 (match-beginning 0)))))
          ;; Try stripping off prominent (non-root - #) shell prompts

1
投票

这是一个 zsh 函数,如果您将其放入您的

.zshrc
文件中,它就可以工作。

因为我通常在 zsh 中运行代码,这就是我看到错误的地方。感谢 @sanityinc 的 emacs 部分。只是觉得这应该在谷歌上。


emn () {
blah=$1
filen=(${(s.:.)blah})
/Applications/Emacs.app/Contents/MacOS/Emacs +$filen[2] $filen[1] &
}

emn /long/stack/error.rb:123

一样使用

1
投票

我修改了 ivan-andrus

defadvice
,因此它可以与 emacsclient 一起使用:

(defadvice find-file-noselect (around find-file-noselect-at-line
                                      (filename &optional nowarn rawfile wildcards)
                                      activate)
  "Turn files like file.cpp:14 into file.cpp and going to the 14-th line."
  (save-match-data
    (let* ((matched (string-match "^\\(.*\\):\\([0-9]+\\):?$" filename))
           (line-number (and matched
                             (match-string 2 filename)
                             (string-to-number (match-string 2 filename))))
           (filename (if matched (match-string 1 filename) filename))
           (buffer-name ad-do-it))
      (when line-number
        (with-current-buffer buffer-name
          (goto-char (point-min))
          (forward-line (1- line-number)))))))

0
投票

我经常使用这个,我用 command-line-1 制作的:

;;;; Open files and goto lines like we see from g++ etc. i.e. file:line#
(defun command-line-1--line-number (orig-fun args)
  (setq new-args ())
  (dolist (f args)
    (setq new-args
          (append new-args
                  (if (string-match "^\\(.*\\):\\([0-9]+\\):?$" f)
                      (list (concat "+" (match-string 2 f))
                            (match-string 1 f))
                    (list f)))))
  (apply orig-fun (list new-args)))

(advice-add 'command-line-1 :around #'command-line-1--line-number)

0
投票

失败时出现错误消息的另一种解决方案

(defun find-file-at-point-with-line ()
  "Like `find-file-at-point' but use line num, if any, to go to that line
 Example: boom.rb:12"

  (interactive)

  (let ((filename (thing-at-point 'filename))
        (line nil))
    
    (unless filename
      (error "Cannot find file at point"))
    
    ;; Extract line number 
    (let ((line-str-pos (string-match ":\\([0-9]+\\)$" filename)))
      (when line-str-pos
        (setq line (string-to-number (match-string 1 filename)))
        (setq filename (substring filename 0 line-str-pos))))
   
    ;; Open file and jump to line (if any)
    (unless (file-exists-p filename)
      (error "File does not exist: %s" filename))
    (find-file filename)
    (when line 
      (goto-line line))))
© www.soinside.com 2019 - 2024. All rights reserved.