16

Emacs helm-kythe与Haskell交叉引用

 4 years ago
source link: http://maskray.me/blog/2017-07-01-emacs-helm-kythe-and-haskell-xrefs
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Emacs helm-kythe与Haskell交叉引用

一直以来Haskell没有好用的工具支持goto-references,hasktagsfast-tags等能提供ctags风格的goto-definition,但不支持goto-references。最近Google开源的一个工具https://github.com/google/haskell-indexer(主要作者为Robin Palotai)提供了基于Kythe的交叉引用实现。

haskell-indexer

索引Hackage

git clone https://github.com/google/haskell-indexer
cd haskell-indexer
# 构建索引。如果需要编译不同Stackage lts版本,请修改`stack.yaml`。
./build-stack.sh /tmp/logs lens mtl
./build-stack.sh /tmp/logs mtlparse cpu
# http_server
./serve.sh /tmp/logs 127.0.0.1:8080

打开http://127.0.0.1:8080能看到Kythe的简陋网页前端。

注意事项:

  • Kythe v0.0.26里的/opt/kythe/tools/http_server --listen localhost不会监听IPv6 ::1。
  • haskell-indexer使用GHC API,需要ghc编译的命令行。当前找到所需源码及编译选项、依赖的方式是让build-stack.sh修改PATH环境变量,让stack build --system-ghc时使用自己指定的ghc wrapper。但有些时候stack build会复制~/.stack/precompiled/下的包而不是重新构建。需要一个更加可靠的获取ghc命令行的方式。目前如果发现某个包mtl-2.2.1: using precompiled package的话,可以删除~/.stack/precompiled/x86_64-linux/ghc-8.0.2/1.24.2.0/mtl-2.2.1/目录后再执行build-stack.sh

helm-kythe.el

我写了一个Emacs Helm扩展,调用tools/http_server的HTTP API实现交叉引用。

建议用某个Emacs包管理器加载https://github.com/MaskRay/emacs-helm-kytheload-pathhelm-kythe依赖dashhelm

如果只有一个vanilla Emacs的话,可以执行下面代码,添加melpa-stable源并安装这两个包。

(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/"))
(package-refresh-contents)
(package-list-packages) ;; 安装dash和helm
(load "/path/to/emacs-helm-kythe/helm-kythe.el")
(require 'helm-kythe)
(add-hook 'haskell-mode-hook 'helm-kythe-mode)

假设./build-stack.sh /tmp/logs mtl时安装了mtl-2.2.1

  • cabal get mtl-2.2.1
  • emacs mtl-2.2.1/Control/Monad/Cont/Class.hs

可以在mode line看到Kythe字样,如果显示为Kythe的话说明helm-kythe-apply-decorations没有执行成功,无法访问http://127.0.0.1:8080或者没有索引。

  • 设置了eldoc-documentation-function,把point移动到标识符上可以在minibuffer看到其定义的snippet
  • helm-kythe-find-definitions,默认绑定到C-c k d,跳转到定义
  • helm-kythe-find-references,默认绑定到C-c k r,跳转到引用
  • helm-kythe-imenu,默认绑定到C-c k i,显示当前文件顶层定义
  • helm-kythe-resume,默认绑定到C-c k l,打开最近访问的一个helm-kythe buffer

假设当前文件为/tmp/kan-extensions-5.0.2/src/Data/Functor/Contravariant/Coyoneda.hs,如果要跳转到的文件在当前Cabal包外,比如要跳到contravariant-1.4/目录,会尝试找kan-extensions-5.0.2/的兄弟目录,不存在的话再考虑helm-kythe-filesystem-path-to-corpus-root。如果你把Cabal .tar.gz解压到其他地方了,请设置这个变量。

效果

效果

C/C++

helm-kythe对于C/C++也适用。Emacs做如下配置:

(add-hook 'c-mode-hook 'helm-kythe-mode)
(add-hook 'c++-mode-hook 'helm-kythe-mode)
;; 把文件系统路径与Kythe path双向转换需要用的search paths
;; 对于 kythe:?path=proj/a.c 将会在 /tmp/c /tmp/d 下找 /tmp/c/proj/a.c 或 /tmp/d/proj/a.c,选择第一个存在的文件
;; /tmp/d/proj2/b.c 则会转换为 kythe:?path=proj2/b.c
;; 建立起双向映射
(setq helm-kythe-filesystem-path-to-corpus-root '(("/tmp/d" "corpus" "root") ("/tmp/c" "" "")))

目前Kythe 0.0.26的命令行工具非常难用,下面是索引/tmp/c/proj/a.c的例子。

#!/bin/zsh
export KYTHE_OUTPUT_DIRECTORY=kythe/compilations
export ENTRIES_DIR=kythe/entries
export SERVING_DIR=kythe/serving
cd /tmp/c
mkdir -p $KYTHE_OUTPUT_DIRECTORY $ENTRIES_DIR $SERVING_DIR
# extractor
/opt/kythe/extractors/cxx_extractor gcc proj/a.c -o proj/a
/opt/kythe/extractors/cxx_extractor gcc proj/b.c -o proj/b
# indexer
for i in $KYTHE_OUTPUT_DIRECTORY/*.kindex; do
if [[ $i =~ ([[:xdigit:]]+)\.kindex$ ]]; then
/opt/kythe/indexers/cxx_indexer --ignore_unimplemented $i > $ENTRIES_DIR/$match[1].entries
# dedup_stream + write_tables
for i in $ENTRIES_DIR/*.entries; do
#export GS_DIR=kythe/gs
#/opt/kythe/tools/dedup_stream < $i | /opt/kythe/tools/write_entries --graphstore leveldb:$GS_DIR
/opt/kythe/tools/dedup_stream < $i > $i.dedup
/opt/kythe/tools/write_tables --entries $i.dedup --out $SERVING_DIR
# http_server
/opt/kythe/tools/http_server --serving_table $SERVING_DIR --listen :8080 --public_resources /opt/kythe/web/ui

不运行tools/http_server,用命令行客户端tools/kythe检查生成得到的kythe/serving目录可用:

% /opt/kythe/tools/kythe -api serving ls 'kythe:?'
proj/
% /opt/kythe/tools/kythe -api serving ls 'kythe:?path=proj'
% /opt/kythe/tools/kythe -api serving node 'kythe:?path=proj/a.c'
kythe:?path=proj/a.c
/kythe/node/kind file
/kythe/text

可以参考我的Emacs配置https://github.com/MaskRay/Config/blob/master/home/.emacs.d

;; spacemacs|define-reference-handlers 是仿照 spacemacs|define-jump-handlers 写的
(spacemacs|define-reference-handlers haskell-mode)
(add-to-list 'spacemacs-jump-handlers-haskell-mode 'helm-kythe-find-definitions)
(add-to-list 'spacemacs-reference-handlers-haskell-mode 'helm-kythe-find-references)
(define-key evil-motion-state-map (kbd "C-,") 'spacemacs/jump-to-reference)
(define-key evil-motion-state-map (kbd "C-j") 'spacemacs/jump-to-definition)
(define-key evil-motion-state-map (kbd "C-t") 'my-xref-jump-backward)
(defun my-xref-jump-backward ()
(interactive)
(if (eq major-mode 'haskell-mode)
(helm-kythe-jump-backward)
(helm-gtags-pop-stack)))

一晃到七月了,不能再堕落了,开启CTF模式。

Share Comments


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK