emacs
在windows
下总是查找不到引用最近迷上了emacs
的TAGS
,因为它比使用clangd
阅读源代码来得更快,可能还更准确。但是现实情况是cygwin
上TAGS
用起来很好,windows
上只能用M-.
来查找定义,想要查找引用却始终失败。
更完整的*Messages*
的错误显示:
Searching d:/demos/emacs-28.2/src/... done
xref--not-found-error: No references found for: stack_bottom
TAGS
的方法是:在编译emacs
完成后,顺便运行一下make TAGS
即可。edebug
的一些快捷键:C-u C-M-x
-设置edebug
,C-M-x
-取消edebug
,SPC
-单步步过,b
-设置断点,i
-步入,o
-步出。xref-find-references
(M-?
)。xref
里没有我所相像的peek
,在查找到的结果窗口内按n
或者p
可以直接跳转而不会失去焦点。edebug
中想要查看某变量的值,将光标移到它的结尾,按C-x C-e
。edebug
上执行C-x C-e
出现Debugger entered--Lisp error: (void-variable cmd)
时,不要按q
,这会导致整个edebug
退出,而要按c
让它继续。edebug
上执行C-x C-e
出现省略号时,如下,按M-:
运行(setq edebug-print-length 200)
。"find -H d:/demos/emacs-28.2/src -type f \"-name\" \"..."
在xref-find-references
处启动edebug
,经过一堆无聊的步入步出操作后,最终定位了关键函数xref-references-in-directory
:
;;;###autoload
(defun xref-references-in-directory (symbol dir)
"Find all references to SYMBOL in directory DIR.
Return a list of xref values.
This function uses the Semantic Symbol Reference API, see
`semantic-symref-tool-alist' for details on which tools are used,
and when."
(cl-assert (directory-name-p dir))
(require 'semantic/symref)
(defvar semantic-symref-tool)
;; Some symref backends use `ede-project-root-directory' as the root
;; directory for the search, rather than `default-directory'. Since
;; the caller has specified `dir', we bind `ede-minor-mode' to nil
;; to force the backend to use `default-directory'.
(let* ((ede-minor-mode nil)
(default-directory dir)
;; FIXME: Remove CScope and Global from the recognized tools?
;; The current implementations interpret the symbol search as
;; "find all calls to the given function", but not function
;; definition. And they return nothing when passed a variable
;; name, even a global one.
(semantic-symref-tool 'detect)
(case-fold-search nil)
(inst (semantic-symref-instantiate :searchfor symbol
:searchtype 'symbol
:searchscope 'subdirs
:resulttype 'line-and-text)))
(xref--convert-hits (semantic-symref-perform-search inst)
(format "\\_<%s\\_>" (regexp-quote symbol)))))
如上,这里(semantic-symref-perform-search inst)
是关键代码,即:
(cl-defmethod semantic-symref-perform-search ((tool semantic-symref-tool-grep))
"Perform a search with Grep."
;; Grep doesn't support some types of searches.
(let ((st (oref tool searchtype)))
(when (not (memq st '(symbol regexp)))
(error "Symref impl GREP does not support searchtype of %s" st))
)
;; Find the root of the project, and do a find-grep...
(let* (;; Find the file patterns to use.
(rootdir (semantic-symref-calculate-rootdir))
(filepatterns (semantic-symref-derive-find-filepatterns))
(filepattern (mapconcat #'shell-quote-argument filepatterns " "))
;; Grep based flags.
(grepflags (cond ((eq (oref tool resulttype) 'file)
"-l ")
((eq (oref tool searchtype) 'regexp)
"-nE ")
(t "-nw ")))
(searchfor (oref tool searchfor))
(greppat (if (eq (oref tool searchtype) 'regexp)
searchfor
(semantic-symref-grep--quote-grep searchfor)))
;; Misc
(b (get-buffer-create "*Semantic SymRef*"))
(ans nil)
)
(with-current-buffer b
(erase-buffer)
(setq default-directory rootdir)
(let ((cmd (semantic-symref-grep-use-template
(directory-file-name (file-local-name rootdir))
filepattern grepflags greppat)))
(process-file semantic-symref-grep-shell nil b nil
shell-command-switch cmd)))
(setq ans (semantic-symref-parse-tool-output tool b))
;; Return the answer
ans))
如上,注意变量cmd
的值就是我想要的,semantic-symref-grep-shell
在不同平台有不同的值:
windows
为D:\Program Files\Emacs\emacs-28.2\libexec\emacs\28.2\x86_64-w64-mingw32\cmdproxy.exe
。cygwin
为/bin/bash
。因此在windows
平台,它会调用cmdproxy.exe -c "cmd"
来执行命令,那么这串命令到底是啥?来看这里打印cmd
的值为:
;; cygwin
"find -H /cygdrive/d/demos/emacs-28.2/src -type f -name \\*.\\[ch\\] -exec grep -nw -nH --null -e stack_bottom \\{\\} +"
;; windows
"find -H d:/demos/emacs-28.2/src -type f \"-name\" \"*.[ch]\" -exec grep -nw -nH --null \"stack_bottom\" \"{}\" \";\""
因此我在命令行里运行了一下试试,果然出错。所以这里问题其实就已经找到了,命令行里的find
调用的是windows
平台的,所以出错,替换成cygwin
的find
就可以了:
(defun semantic-symref-grep-use-template (rootdir filepattern flags pattern)
"Use the grep template expand feature to create a grep command.
ROOTDIR is the root location to run the `find' from.
FILEPATTERN is a string representing find flags for searching file patterns.
FLAGS are flags passed to Grep, such as -n or -l.
PATTERN is the pattern used by Grep."
;; We have grep-compute-defaults. Let's use it.
(grep-compute-defaults)
(let* ((semantic-symref-grep-flags flags)
(grep-expand-keywords semantic-symref-grep-expand-keywords)
(cmd (grep-expand-template
(if (memq system-type '(windows-nt ms-dos))
;; FIXME: Is this still needed?
;; grep-find uses '--color=always' on MS-Windows
;; because it wants the colorized output, to show
;; it to the user. By contrast, here we don't show
;; the output, and the SGR escapes get in the way
;; of parsing the output.
(replace-regexp-in-string "--color=always" ""
grep-find-template t t)
grep-find-template)
pattern
filepattern
rootdir)))
cmd))
所以这里要需要解决find
的路径问题,见上面代码,我发现在grep-find-template
变量中设置了find
,所以可以将该变量的find
替换成D:\cygwin64\bin\find.exe
。试了一下果真可以:
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(grep-find-template
"D:\\cygwin64\\bin\\find.exe -H <D> <X> -type f <F> -exec grep <C> -nH --null <R> \"{}\" \";\"")
'(inhibit-startup-screen t)
'(make-backup-files nil))
完美,打完收工!