読者です 読者をやめる 読者になる 読者になる

EmacsでTernのサードパーティ製プラグインを使おうとしたら Cannot find module と怒られた

Emacs Tern

Ternが補完できるオブジェクト

最近、Ternを使い始めて、 EmacsJavascriptのコーディングがすこぶる快適になりつつあります。

ただ、Ternがデフォルトで補完できる型は、
組み込みオブジェクト以外では一部のライブラリ(jQueryとか)のオブジェクトに限られ、
それ以外の型を補完できるようにするには、型情報をTernに知らせる必要があるようです。(※)

※ 英語に自信ないので間違ってるかも

Google Maps API の型情報を提供してくれるプラグイン

で。最近、 Google Maps API を使ってコーディングした時、
そのオブジェクトも補完できるようにしたいと思って調べたら、
Google Maps API の型情報を追加してくれるプラグインを見つけました。

https://github.com/angelozerr/tern.googleapi

Ternにプラグインを認識させる

プロジェクトルート(.tern-projectがあるフォルダ)に
プラグインファイル(今回は、上記リポジトリのplugin/gmaps_3.17.js)を置き、
.tern-projectに、以下のように記述すれば良いとのこと。

{
    "plugins": {
        "gmaps_3.17": {}
    }
}

プラグインがrequireしているモジュールが見つからない

これで補完できるのかなとウキウキしながら、コーディングを始めたら、
以下のように Cannot find module と怒られてしまいました...

Error: Cannot find module 'tern/lib/infer'

そもそも、JavascriptやNode.jsでの外部ファイルの読み込みの仕組み自体に無知でしたが、
調べてみると、NODE_PATHも正しく設定されているし、プラグインの記述も正しそうだとわかりました。

Ternのプロセスに環境変数が引き継がれてなかった

Emacsから生成されるTernのプロセスにNODE_PATHの値が引き継がれてなかったです。
GUIEmacsでは有名な問題で、exec-path-from-shellを使って解決できます。

今回の場合、以下の設定を追加することで、
無事 Google Maps API のオブジェクトが補完できるようになりました。

(add-to-list 'exec-path-from-shell-variables "NODE_PATH")

ちなみに

Ternにプラグインとして認識させるには、
"tern-プラグイン名"というパッケージ名のnpmパッケージを作成することでも可能です。
今回みたいに、共通で使えるプラグインの場合、そっちの方がスマートじゃないかと。

以下の手順で、
該当のプラグインファイルだけのnpmパッケージをインストールして
Ternに認識させることができました。

/tmp$ mkdir gmaps_3.17
/tmp$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (tmp) tern-gmaps_3.17
version: (0.0.0) 
description: 
entry point: (index.js) gmaps_3.17.js
test command: 
git repository: 
keywords: 
author: 
license: (BSD) 
About to write to /tmp/package.json:

{
  "name": "tern-gmaps_3.17",
  "version": "0.0.0",
  "description": "",
  "main": "gmaps_3.17.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "BSD"
}


Is this ok? (yes) 
/tmp$ mv package.json gmaps_3.17/
/tmp$ git clone https://github.com/angelozerr/tern.googleapi.git
Cloning into 'tern.googleapi'...
remote: Counting objects: 521, done.
remote: Total 521 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (521/521), 1.00 MiB | 269 KiB/s, done.
Resolving deltas: 100% (51/51), done.
/tmp$ cp tern.googleapi/plugin/gmaps_3.17.js gmaps_3.17/
/tmp$ cd gmaps_3.17/
/tmp/gmaps_3.17$ npm install -g
tern-gmaps_3.17@0.0.0 /home/hiroaki/.nvm/v0.10.15/lib/node_modules/tern-gmaps_3.17
/tmp/gmaps_3.17$