使文件名/行号在 Emacs gud 缓冲区中可链接

发布于 2024-08-18 10:25:27 字数 540 浏览 6 评论 0原文

我通过 gud 缓冲区在 Python 中的测试用例上运行 pdb。当我的测试用例中出现堆栈跟踪/失败时,它看起来像这样:

FAIL: test_foo_function (__main__.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/testfoo.py", line 499, in test_foo_function
    self.assertEqual('foo', 'foo')

我希望能够使行如下:

File "test/testfoo.py", line 499, in test_foo_function

可单击并转到 testfoo.py 中的第 499 行。

(编辑)python 模式列表上的人引导我找到了 pdbtrack,并且我能够让它在那里工作。请参阅下面的答案...

I'm running pdb on my testcases in Python through the gud buffer. When I get a stacktrace/failure in my testcase, it looks like this:

FAIL: test_foo_function (__main__.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/testfoo.py", line 499, in test_foo_function
    self.assertEqual('foo', 'foo')

I'd love to be able to make the line(s) like:

File "test/testfoo.py", line 499, in test_foo_function

clickable and take be to line 499 in testfoo.py.

(edit) The folks on the python-mode list led me to pdbtrack and I was able to get it to work there. See answer below...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一梦等七年七年为一梦 2024-08-25 10:25:28

感谢 Gerard BI 的提示,我明白了。我是从 pdbtrack (shell) 而不是纯 pdb 执行此操作,但我相信它应该适用于两者。您需要启用compilation-shell-minor-mode。并在 .emacs 中包含以下代码:

;; if compilation-shell-minor-mode is on, then these regexes
;; will make errors linkable
(defun matt-add-global-compilation-errors (list)
  (dolist (x list)
    (add-to-list 'compilation-error-regexp-alist (car x))
    (setq compilation-error-regexp-alist-alist
      (cons x
            (assq-delete-all (car x)
                             compilation-error-regexp-alist-alist)))))

(matt-add-global-compilation-errors
 `(
   (matt-python ,(concat "^ *File \\(\"?\\)\\([^,\" \n    <>]+\\)\\1"
                    ", lines? \\([0-9]+\\)-?\\([0-9]+\\)?")
           2 (3 . 4) nil 2 2)
   (matt-pdb-stack ,(concat "^>?[[:space:]]*\\(\\([-_./a-zA-Z0-9 ]+\\)"
                       "(\\([0-9]+\\))\\)"
                       "[_a-zA-Z0-9]+()[[:space:]]*->")
              2 3 nil 0 1)
   (matt-python-unittest-err "^  File \"\\([-_./a-zA-Z0-9 ]+\\)\", line \\([0-9]+\\).*" 1 2)
   )
 )

(defun matt-set-local-compilation-errors (errors)
  "Set the buffer local compilation errors.

Ensures than any symbols given are defined in
compilation-error-regexp-alist-alist."
  (dolist (e errors)
     (when (symbolp e)
      (unless (assoc e compilation-error-regexp-alist-alist)
        (error (concat "Error %s is not listed in "
                       "compilation-error-regexp-alist-alist")
               e))))
  (set (make-local-variable 'compilation-error-regexp-alist)
       errors))

然后,您可以使用标准编译模式导航来压缩错误堆栈跟踪。

Thanks to a hint by Gerard B I figured it out. I'm doing this from pdbtrack (shell) instead of pure pdb, but it should work in both I believe. You need to enable compilation-shell-minor-mode. And have the following code in your .emacs:

;; if compilation-shell-minor-mode is on, then these regexes
;; will make errors linkable
(defun matt-add-global-compilation-errors (list)
  (dolist (x list)
    (add-to-list 'compilation-error-regexp-alist (car x))
    (setq compilation-error-regexp-alist-alist
      (cons x
            (assq-delete-all (car x)
                             compilation-error-regexp-alist-alist)))))

(matt-add-global-compilation-errors
 `(
   (matt-python ,(concat "^ *File \\(\"?\\)\\([^,\" \n    <>]+\\)\\1"
                    ", lines? \\([0-9]+\\)-?\\([0-9]+\\)?")
           2 (3 . 4) nil 2 2)
   (matt-pdb-stack ,(concat "^>?[[:space:]]*\\(\\([-_./a-zA-Z0-9 ]+\\)"
                       "(\\([0-9]+\\))\\)"
                       "[_a-zA-Z0-9]+()[[:space:]]*->")
              2 3 nil 0 1)
   (matt-python-unittest-err "^  File \"\\([-_./a-zA-Z0-9 ]+\\)\", line \\([0-9]+\\).*" 1 2)
   )
 )

(defun matt-set-local-compilation-errors (errors)
  "Set the buffer local compilation errors.

Ensures than any symbols given are defined in
compilation-error-regexp-alist-alist."
  (dolist (e errors)
     (when (symbolp e)
      (unless (assoc e compilation-error-regexp-alist-alist)
        (error (concat "Error %s is not listed in "
                       "compilation-error-regexp-alist-alist")
               e))))
  (set (make-local-variable 'compilation-error-regexp-alist)
       errors))

Then you can use standard compile mode navigation to zip through the error stack trace.

情何以堪。 2024-08-25 10:25:28

我认为您想要自定义的是compilation-parse-errors-filename-function,它是一个接受文件名并返回要显示的文件名的修改版本的函数。这是一个缓冲区局部变量,因此您应该在每个将显示 python 错误的缓冲区中设置它(可能有一个合适的钩子可以使用,我没有安装 python 模式,所以我无法查找它)。您可以使用 propertize 返回输入文件名的一个版本,该版本充当加载实际文件的超链接。 propertytize 在 elisp 手册中有详细记录。

如果compilation-parse-errors-filename-function没有被调用,那么您需要将一个列表添加到compilation-error-regexp-alist-alist(这确实是alist-alist,而不是一个拼写错误),它是模式名称的列表,后跟用于匹配错误的正则表达式,以及错误正则表达式匹配中的匹配行号、文件名等信息的数字索引。

I think what you want to customize is compilation-parse-errors-filename-function, which is a function that takes a filename, and returns a modified version of the filename to be displayed. This is a buffer local variable, so you should set it in each buffer that will be displaying python errors (there is probably an appropriate hook to use, I do not have python mode installed so I cannot look it up). You would use propertize to return a version of the input filename that acts as a hyperlink to load the actual file. propertize is well documented in the elisp manual.

If compilation-parse-errors-filename-function is not getting called, then you want to add a list to compilation-error-regexp-alist-alist (that does say alist-alist, that is not a typo) which is a list of mode names followed by regular expressions to match errors, and numerical indexes of the matching line number, filename etc. info in the error regexp match.

锦欢 2024-08-25 10:25:28

添加贾斯汀的答案:

我的 slime 配置中有以下内容,它应该从 clojure 堆栈跟踪跳转到文件和行。

不幸的是,我必须承认它目前对我来说实际上不起作用 - 该函数无法找到正确的文件 - 但据我所知,这应该可以通过更改 project-root 的方式来修复 是定义的,或者是通过更改文件系统上我的项目的结构来定义的(我只是没有时间或意愿去研究它)。

不过,它确实提出了一个很好的观点,在大多数像这样的功能中,以通用和可移植的方式找出项目根有点棘手。在本例中,我们依赖于 src 目录,但这可能不适合您的 python 项目。

因此,从 Justin 离开的地方开始,您应该能够从下面的函数中获取一些提示,并从测试用例错误中解析文件名和行号,创建指向行号的链接,并使用 编译-parse-errors-filename-functionpropertize 使 gud 缓冲区中的行成为链接。

如果您确实可以使用它,请添加您自己问题的答案。我想很多人都会觉得它很有用。

  (defun slime-jump-to-trace (&optional on)
    "Jump to the file/line that the current stack trace line references.
    Only works with files in your project root's src/, not in dependencies."
    (interactive)
    (save-excursion
      (beginning-of-line)
      (search-forward-regexp "[0-9]: \\([^$(]+\\).*?\\([0-9]*\\))")
      (let ((line (string-to-number (match-string 2)))
            (ns-path (split-string (match-string 1) "\\."))
            (project-root (locate-dominating-file default-directory "src/")))

        (find-file (format "%s/src/%s.clj" project-root
                           (mapconcat 'identity ns-path "/")))
        (goto-line line))))

我还应该提到,我从网络上的某个地方复制了这个函数,但我不记得 URL。它似乎来自 Phil Hagelberg 的(技术)优秀的 Emacs 入门套件。

Adding to Justin's answer:

I have the following in my slime config which is supposed to jump to a file and line from a clojure stack trace.

Unfortunately I must admit that it doesn't actually work for me at the moment - the function isn't able to find the correct file - but as far as I can tell, that should be fixable by changing how project-root is defined or by changing the structure of my projects on the filesystem (I just haven't had the time or inclination to look into it).

It does bring up a good point though, in most functinality like this it's a bit tricky to figure out the project root in a generic and portable fashion. In this case we rely on a src directory, but that's probably not appropriate for your python projects.

So following on from where Justin left off, you should be able to take some tips from the function below and parse the file name and line numbers from the test case error, create a link to the line number, and use the compilation-parse-errors-filename-function and propertize to make the line in the gud buffer a link.

If you do get it to work, please add an answer to your own question. I think a lot of people would find it useful.

  (defun slime-jump-to-trace (&optional on)
    "Jump to the file/line that the current stack trace line references.
    Only works with files in your project root's src/, not in dependencies."
    (interactive)
    (save-excursion
      (beginning-of-line)
      (search-forward-regexp "[0-9]: \\([^$(]+\\).*?\\([0-9]*\\))")
      (let ((line (string-to-number (match-string 2)))
            (ns-path (split-string (match-string 1) "\\."))
            (project-root (locate-dominating-file default-directory "src/")))

        (find-file (format "%s/src/%s.clj" project-root
                           (mapconcat 'identity ns-path "/")))
        (goto-line line))))

I should also mention that I copied this function from somewhere on the web, but I can't remember the URL. It seems to be from Phil Hagelberg's (technomancy) excellent Emacs starter kit.

恍梦境° 2024-08-25 10:25:28

这是一个跳转到跟踪中指定的文件和行的函数。

如果您将您的观点放在回溯的“文件”行的任何位置,
该函数将找到该文件并将点移动到另一个窗口中指定行的前面:

FAIL: test_foo_function (__main__.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/testfoo.py", line 499, in test_foo_function
    self.assertEqual('foo', 'foo')
(defun my-jump-to-file-from-python-error ()
  "Jump to line in file specified by a Python traceback."
  (interactive)
  (let* ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
         file
         number)

    (string-match "^ *File \\(\"?\\)\\([^,\" \n    <>]+\\)\\1, lines? \\([0-9]+\\)-?\\([0-9]+\\)?" line)

    (setq file (match-string 2 line))
    (setq number (match-string 3 line)) ; match is string, not numeric

    (cond ((and file number)
           (find-file-other-window file)
           (with-current-buffer (get-buffer (file-name-nondirectory file))
             (goto-char (point-min))
             (forward-line (1- (string-to-number number))))))))

我刚刚从@matt harrison 的响应中复制了正则表达式。如果您更改它,我建议使用 regexp-builder 并按 Cc Ce 进行 reb-enter-subexp-mode。这允许您循环遍历子表达式并知道文件和行号使用哪个匹配。

Here's a function to jump to the file and line specified in a trace.

If you put your point anywhere on the "File" line of the traceback,
the function will find the file and move the point to the front of the specified line in another window:

FAIL: test_foo_function (__main__.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/testfoo.py", line 499, in test_foo_function
    self.assertEqual('foo', 'foo')
(defun my-jump-to-file-from-python-error ()
  "Jump to line in file specified by a Python traceback."
  (interactive)
  (let* ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
         file
         number)

    (string-match "^ *File \\(\"?\\)\\([^,\" \n    <>]+\\)\\1, lines? \\([0-9]+\\)-?\\([0-9]+\\)?" line)

    (setq file (match-string 2 line))
    (setq number (match-string 3 line)) ; match is string, not numeric

    (cond ((and file number)
           (find-file-other-window file)
           (with-current-buffer (get-buffer (file-name-nondirectory file))
             (goto-char (point-min))
             (forward-line (1- (string-to-number number))))))))

I just copied the regex from @matt harrison's response. If you change it, I recommend using regexp-builder and pressing C-c C-e for reb-enter-subexp-mode. This is allows you to cycle through sub-expressions and know which match to use for the file and line number.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文