Emacs helm-kythe与Haskell交叉引用
2017-07-01 16:00:00 Author: maskray.me(查看原文) 阅读量:147 收藏

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

haskell-indexer

依赖

索引Hackage

1

2

3

4

5

6

7

8

9

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源并安装这两个包。

1

2

3

4

5

(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")

使用

1

2

(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做如下配置:

1

2

3

4

5

6

7

8

(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的例子。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

#!/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

fi

done

# 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

done

# 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目录可用:

1

2

3

4

5

6

7

8

% /opt/kythe/tools/kythe -api serving ls 'kythe:?'

proj/

% /opt/kythe/tools/kythe -api serving ls 'kythe:?path=proj'

a.c

% /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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

;; 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模式。


文章来源: http://maskray.me/blog/2017-07-01-emacs-helm-kythe-and-haskell-xrefs
如有侵权请联系:admin#unsafe.sh