header

2013年12月15日日曜日

Emacsのキーバインドをひとつのキーに複数登録する拡張mykie.elの紹介

2014/1/5 blog更新しました
mykie.el version 0.1.1になりました。

  • :err,:C-u&err,:region&errのキーワード指定ができるようになりました。
    flycheck か flymake のエラーがあるときに使えます。
  • どのキーワードを優先するかをキーバインド登録時の順番でかえることができ
    るようになりました。(デフォルトではオフです)
  • 上の変更に伴いキーワード分岐のための条件のデータ構造が変わりました。
    独自に条件を追加している人は注意が必要です。

※過去の更新は下の方に移動しました

この記事は.emacs Advent Calender 15日目の記事です。


mykie.elとは

Emacsはそれ自体が拡張できるのが特徴ですが、拡張をどんどんいれていくと使
えるキーバインドがなくなってしまうのが問題です。
そこで今回の拡張mykie.elの出番です。
この拡張は通常の呼び出しと、リージョン選択時、bol(beginning of line),
eol(end of line), C-u(prefix-argument)を押した時などで、呼び出す関数を
変更できます。
※もともとはmine.elという名前でしたが、改名しました。


インストール

MELPA に登録したのでM-x package-install RET mykieでインストールできる
と思います。もし MELPA をpackage-archivesに登録してなければ以下を
スクラッチバッファなどで評価してから試してください。

(add-to-list 'package-archives
'("melpa" . "http://melpa.milkbox.net/packages/"))
(package-initialize)

el-getをつかっているなら以下のコードを評価して、M-x el-get-install RET
mykie でインストールできると思います。

(push '(:name mykie-el
:type git
:url "https://github.com/yuutayamada/mykie-el.git"
:load-path ("./"))
el-get-sources)

使い方

インストールができたら、init.el などで設定します。
下は簡単な設定例

(require 'mykie) ; <- これはmykie.elを読み込む必須の設定
;; major-modeのキーをglobal-mapのmykieの同じキーで上書きするかどうか
;; の設定。上書きしますが元のメジャーモードキーは:default用の関数に
;; 登録されるのでそのまま使用できます。
;; defaultではnilとなっています。
(setq mykie:use-major-mode-key-override t)
;; 上のメジャーモードの上書き設定の変更や条件の変更をおこなった場合は
;; ↓これを呼び出してください。
(mykie:initialize)
;; mykie:set-keysの第一引数にnilを指定するとグローバルマップに登録され
;; ます。emacs-lisp-mode-mapと指定して特定のメジャーモードを指定することも
;; できます。
(mykie:set-keys nil
"C-a"
:default '(beginning-of-line)
:C-u 'mark-whole-buffer
"C-e"
:default '(end-of-line)
:C-u '(message "Hello")
;; ... You can add more keybinds
)

上の例はC-a, C-eキーにC-uを押した時の設定を追加します。

関数の指定方法についてですが、上の例のようにリスト形式で渡す方法のほかに
'query-replace-regexpのようにシンボルで渡すこともできます。
この場合そのシンボルがインタラクティブだった場合、call-interactively
関数からそのシンボルを呼び出します。(コマンドとして実行する)


指定できるキーワード引数

以下は現在の指定できるキーワード引数です。
最新の情報は GitHubのmykieのページ に書くつもりなので、そのうち情報が古
くなるかもしれません。

KEYWORDDESCRIPTION
:defaultデフォルトで呼ばれる
:C-uC-uをそのキーを押す前に押すと呼ばれる
:C-u*NC-uをN回押したとき呼ばれる
:M-Nメタ(alt)キー + [0-9] の数字を押した時に呼ばれる
:regionリージョン選択中に呼ばれる
:region&C-u:regionと:C-u条件をみたすとよばれる
:repeat同じコマンドを同じポイントで行うと呼ばれる
:bolpbeginning of line で呼ばれる
:eolpend of line で呼ばれる
:bobpbeginning of buffer でよばれる
:eobpend of buffer で呼ばれる
:C-u&bolp:C-uと:bolpの条件をみたすとよばれる
:C-u&eolp:C-uと:eolpの条件をみたすとよばれる
:C-u&bobp:C-uと:bobpの条件をみたすとよばれる
:C-u&eobp:C-uと:eobpの条件をみたすとよばれる
:email現在のポイントが e-mail だと呼ばれる
:C-u&email:C-uと:emailの条件をみたすとよばれる
:url現在のポイントが URL だと呼ばれる
:C-u&url:C-uと:urlの条件をみたすとよばれる
:MAJOR-MODEemacs-lisp-mode などの 現在の major-modeに マッチ
:C-u&MAJOR-MODE:C-uと:MAJOR-MODEの条件をみたすとよばれる
:region&MAJOR-MODE:regionと:MAJOR-MODEの条件をみたすとよばれる
:progプログラミングモードに関連したバッファだと呼ばれる
:C-u&prog:C-uと:progの条件をみたすとよばれる
:region&prog:regionと:progの条件をみたすとよばれる
:errflymakeかflycheckのエラーがあるときよばれる
:C-u&err:C-uと:errの条件をみたすとよばれる
:region&err:regionと:errの条件をみたすとよばれる
:minibuffミニバッファでよばれる
:readonlyread only だとよばれる
:commentコメントか文字列だとよばれる

以下は関数の指定ではなく、違う効果を持ったキーワード引数

KEYWORDvalueDESCRIPTION
 (example)<30>
:cloneKEY as stringClone mykie's functions to other KEY. this function is convenient if you use Emacs either situation terminal and GUI. Because terminal Emacs can't use partial keybind such as C-;, this keyword can clone same functions to another key without :default function. For example: :clone ";" (<- if you want to clone origin key to ";")
 "a" 
:deactivate-regionsymboldeactivate selecting region after mykie executed command. You can specify this t, 'region, 'region&C-u.
 t 
:region-handle-flagsymbolDo copying or killing before command executing. This function is convenience if you want to use kill-ring's variable. But there is mykie:region-str variable that always store region's strings.
 'copy or 'kill 

:cloneはself-insert-commandに:default以外のコマンドを引き継ぐ設定です。
どういう時に便利かというと、ターミナルではC-;は使えないので";"キーで
リージョンに指定した関数を使いたいというときに便利です。
ユーザー側からすると同じキーバインドが使えてると錯覚できます。
(:defaultの設定は使えないですが)

(mykie:set-keys nil
"C-;"
:default 'newline-and-indent
:region 'comment-dwim
:clone ";")

その他の例

mykie.el のページにキーワードを使った例がありますのでそちらを参考にして
ください。


メジャーモードのキーの上書き

最近のmykie.elに実装された機能ですので不具合があるかもしれません。

(setq mykie:use-major-mode-key-override t)
(mykie:initialize)

のようにしないと設定されないので、つかいたくなければ上記の設定をしない
でください。

mykie:use-major-mode-key-overrideには 'both, 'global, 'self or t が設
定できます。'bothはself-insert-commandとglobal-mapのC-jなどのprefix付
きのコマンド両方、'globalは C-j などのprefix付きのglobal-mapのコマンド、
'self と t は self-insert-command(a-z0-9など) を意味します。

目的: major-modeのキーバインドにmykieのglobal-mapのキーを埋め込もうというの
が目的です。

;; You can specify 'both or 'global 'self or t to
;; mykie:use-major-mode-key-override.
;; 'both means use overriding major-mode keys both case.
;; 'global means use overriding major-mode keys by global-map's keys
;; without self-insert-command keys.
;; 'self means use overriding major-mode keys by self-insert-command keys.
;; if you set nil, then don't overriding major-mode key.
;; `mykie:use-major-mode-key-override' is nil by default.
(setq mykie:use-major-mode-key-override 'both)
(mykie:initialize)
(mykie:set-keys nil ; <- nil means registering global-map
"C-w" :default 'tetris :C-u '(message "C-u+C-w"))

;; self-insert-commandはこの方法で登録する
(mykie:set-keys 'with-self-key
"1" :region 'sort-lines
"2" :region 'align
"3" :region 'query-replace
"c" :C-u '(message "C-u+c"))

この機能を使うとき、self-insert-commandとそれ以外を意識する必要があります。
a-zや0-9などがself-insert-commandです。
それ以外というのは、global-mapのC-jやC-wなどprefix付きのコマンドです。
上の例はどちらの場合でも上書きする例です。

-Magitなど一部のパッケージでは、自動で上書きしてくれません。
僕のコードがバグってました。最新のmykie.elではmagitでも上書きしてくれ
ます。ごめんなさい

もしそういう場合どうしても上書きしたい場合、以下のようにしてください。
シンボル名でモード名を指定してください。

個別のモードごとに上書きの設定をしたい場合:

;; specify major-mode name as symbol.
(mykie:attach-mykie-func-to 'emacs-lisp-mode)

逆に MagitのようにC-uを押すと動作を変えるので上書きしてほしくない場合
以下のようにできます。

(setq mykie:major-mode-ignore-list '(magit-status-mode))

特定のキーだけに指定したい場合以下の指定方法もあります。

;; You can use below configuration. This way can specify specific keybind
;; only. you enjoy playing tetris.
(mykie:set-keys nil ; <- nil means registering global-map
"C-t"
:default 'tetris :C-u '(message "C-u+C-t")
:ignore-major-modes '(emacs-lisp-mode)
:ignore-major-modes '(diff-minor-mode))

個別のキーバインドごとにキーワード引数の優先度の設定をする

これはVersion 0.1.1からの機能です。
mykie:use-lazy-order 変数にtを設定することで、キーバインドごとにどの
キーワード引数を優先するか設定できます。

(setq mykie:use-lazy-order t)
(mykie:set-keys nil
\"C-0\"
:default '(message \"hi\")
:C-u*2 '(message \"howdy\")
:C-u '(message \"hello\")
:C-u*3 '(message \"hey\") ; <- you can't see
\"C-1\"
:default '(message \"hi\")
:C-u*3 '(message \"howdy\")
:C-u '(message \"hello\")
:C-u*2 '(message \"hey\")) ; <- you can't see

上の例はC-u*2とC-u*3の順番が入れ変わっているのがわかると思います。
これを実際試してみると、コメントでyou can't seeとかいてある行の
キーワード引数の関数は呼ばれないのがわかると思います。
つまり、C-u*3 or C-u*2 より:C-uを優先しているということです。
(上にある方を優先します。:default以外)
Mykie.el にはいくつか指定できるキーワード引数がありますが、
どれを優先するかはその時々で変わると思いこの機能を実装しました。
たとえば、エラーがでていてコメントのurlを参照しようとするとき
:err と :url をどちらを優先するかとか(ちょっと強引ですかね?)
あとは既存のキーバインドに追加するとき、その部分だけみれば実装の条件を
確認する必要がないというメリットがあります。

注意点としては、条件の評価は、リージョン -> C-u -> それ以外(normal)
という順番で見ていきます。
なので:C-uより下にリージョン系の関数を配置してもリージョンがアクティブであれば
リージョンの条件を先に見に行きます。


条件の変更

今まではmykie.el側で用意した条件の説明でしたが、自分で定義したいときも
あるかもしれません。
Version 0.1.1 から条件データ構造が変わりました。
(個別のキーバインドごとにキーワード引数の優先度の設定をする機能を実装するため)
以前の指定方法の最初のリスト要素に以降の条件が返すキーワード(state)を
入れます。
それとregion系,prefix-arg系に条件を登録するときは、(region-active-p)
やcurrent-prefix-argで確認する必要はなくなりました。

(setq mykie:before-user-normal-conditions
'((:skk-active :skk-on)
;; ↓は :skk-active か :skk-on を返す関数
(mykie:get-skk-state)
;; もっと条件を追加できます
;; (when t :something)
;; この場合 (:skk-active :skk-on) は
;; (:skk-active :skk-on :something)
;; になります。
))
(mykie:initialize)

他にもmykie:before-user-XXX-conditionsまたは
mykie:after-user-XXX-conditionsがあります。XXXがnormal, region,
prefix-argにかわります。

注意: mykie:use-lazy-orderがnon-nilだった場合はこのbeforeとafterに
意味はなくなりキーバインドを登録する時の順番が優先されます。


おすすめ関数とちょっと便利な C-j キーの例

標準の関数で便利な関数です。参考程度に

  • 非リージョン系
    delete-trailing-whitespace
    delete-blank-lines
    just-one-space
    ispell-word
  • リージョン系
    align
    sort-lines
    fill-region

下はちょっと便利にしたC-jの例です。

(global-set-key (kbd "C-j")
'(lambda ()
(interactive)
(mykie
:default '(progn
;; 無駄なスペースを削除
(delete-trailing-whitespace)
(case major-mode
(org-mode (org-return-indent))
(t (newline-and-indent))))
;; 行末で C-u+C-j で fill-region を実行する
:C-u&eolp '(fill-region (point-at-bol) (point-at-eol))
:region 'query-replace-regexp)))

おわり

region系の関数を開いてるキーにいれるだけでもまぁまぁ便利なのでよかった
らつかってください。以上 mykie.el の紹介でした。
明日は r_takaishi さんです。

更新履歴
2013/12/27ブログ&mykie.el(v0.1.0)更新しました

  • major-modeキーバインドのmykieの関数での上書きができるようになりました。
  • mykie:set-keys関数でまとめてキーバインドを登録できるようになりました。
  • 指定できるキーワードがいっぱい増えました。

2013/12/19ブログ&mykie.el(v0.0.6)更新しました

  • mykieのキーワード引数に:M-N(NがM-[0-9]の押した数字にかわります。)が
    指定できるようになりました。
    ShingoFukuyama さんpull-requestありがとうございます。
  • Version 0.0.6からmykie:get-C-u-keywordが:M-Nも返すようになったので
    mykie:get-prefix-arg-stateという名前になりました。
    自分でmykie:conditions変数を変更している場合注意が必要です。
  • last-commandが正常に保存されず winner-(undo|redo)不具合がでる件
    @rubikitch から pull-request いただきました。いつもありがとうございます。
    ↓これは勘違いしました。すいません↓
    mykie:loop中ではlast-commandが正常に保存されないバグが直りました。

2013/12/18ブログ&mykie.el(v0.0.5)更新しました

  • mykieのキーワード引数に:C-u*N(NはC-uを押した数が入ります)が指定でき
    るようになりました。
    これに伴い記事も若干修正

2013/12/17ブログ&mykie.el(v0.0.4)更新しました

  • mykie:condition変数で条件指定の順番や内容を変更できるようになりました。
    詳細は優先度の変更や条件のカスタマイズの項をご覧ください。
  • 一部のキーワードの引数の名前がみじかくなりました。
    (:default&bolp -> :bolpなど)
    詳細は優先度の変更や条件のカスタマイズの項をご覧ください。
  • @rubikitch さんからリージョン指定を解除する pull-request いただきました。
    ありがとうございます!(それとリファクタリングも)
    詳細はリージョンのマークを解除するをご覧ください。

2013/12/27 blog更新
変更が結構あるので最新のmykie.elの情報にあわせるようにしました。
以前設定したものが使えなくなるような変更は入っていません(たぶん)。
以前より簡単に設定できる(defunとかinteractiveいらない)ようになったので
ぜひ試してみてください!


2013年11月28日木曜日

Emacsで Go lang の設定

Golangについて興味がわいたのでEmacs環境を整えた。
ここ を参考にした。

他web上の情報とか:
Tour of go
Effective go
http://www.golang-book.com/
Go(wikipedia)

install

Hg でリポジトリからとってきた。
hg branches でブランチがみえるので、最新っぽいgo1.2rc2を入れた
(http://golang.org/doc/devel/release.html でリリース情報がみれる)
でも最近はgoenvなんてあるらしいのでそちらのほうがおすすめ。たぶん。

cd $HOMEhg clone https://go.googlecode.com/hg/ go
cd $HOME/go/src
hg update go1.2rc2
bash all.bash

goのパッケージ

godefで関数ジャンプ、gocodeはautocomplete(たぶん)するためにいれた。
godocはなんかエラーがでて、ぐぐったらgodocのバージョンが古いらしく
godocを削除して入れ直せと書いてあったので入れなおしている。

go get -u github.com/nsf/gocode
go get -u code.google.com/p/rog-go/exp/cmd/godef
go get -u code.google.com/p/go.tools/cmd/godoc

elispの設定

go-modeはgithub版のほうが新しそうなので、github版のものをつかう
go-autocomplete.elは下の場所にあったのでパスをとおしただけ。

$HOME/go/src/pkg/github.com/nsf/gocode/emacs/go-autocomplete.el

go-eldocという、eldocのGo版があったのでこれもインストールした。
flycheckは古いEmacsだとはいってないかも..

(require 'go-mode)
(require 'go-autocomplete)
(require 'go-eldoc)
(require 'flycheck)
(defconst tab-width 2)

(add-hook 'go-mode-hook
'(lambda()
(flycheck-mode t)
(go-eldoc-setup)))

;; 後述
(defvar my/go-packages (go-packages))

(defun my/go-before-save-function ()
(when (equal major-mode 'go-mode)
;; flycheckのエラー時 & org-modeのsrc buffer内の場合は
;; なにもしない
(unless (and (bound-and-true-p flycheck-current-errors)

(org-src-edit-buffer-p))
(gofmt-before-save)
;; 後述
(setq my/go-packages (go-packages)))))

(add-hook 'before-save-hook 'my/go-before-save-function)

パッケージ名のあと大文字にするのがルールのようなので、
fmt.のあとなど大文字にキャピタライズする設定、
よくわかってないので、go-packages変数にマッチする場合だけ
大文字にする。上のコードで後述と書いてある部分は、
go-packagesの読込が(少しだけ)時間がかかるので、
変数に保存するようにした。
コメントfaceと文字列のfaceの時は無視する。

(defadvice self-insert-command  (around ad-capitalize-method-head-in-go activate)
ad-do-it
(my/go-capitalize-auto))

(defun my/go-capitalize-auto ()
(when (equal major-mode 'go-mode)
(lexical-let
((current-face (nth 1 (text-properties-at (1- (point))))))
(if (and (equal "." (char-to-string (char-before (1- (point)))))
(case current-face
(font-lock-string-face)
(font-lock-comment-face)
(t t)))
(when (my/go-package-p)
(capitalize-word -1))))))

(defun my/go-package-p ()
"Return non-nil, if pre word is package."
(interactive)
(if (loop with pre-word = (save-excursion
(backward-char 2)
(word-at-point))
for pkg in my/go-packages
for subpkg = (when pkg (nth 1 (split-string pkg "/")))
if (and (stringp pkg)
(or (string-match (format "^%s$" pkg) pre-word)
(string-match (format "^%s$" subpkg) pre-word)))
do (return (or subpkg pkg)))
t
nil))

helmでGoの関数表示するelispパッケージ作った。

helmで関数名で検索したかったのでつくった。
godoc packge名の出力をfuncとtypeで正規表現で抜き出して
保存する非常にダサいコード。(もっとGo力があれば…)
https://github.com/yuutayamada/helm-go
名前はもしかしたらhelm-go-functionsとかに変更するかもしれない。
必要なもの:
helm.el, helm-ag-r.el, go-mode.el
ag(the silver searcher), godoc
helm-ag-rのインストールは ここ を参考にしてください。

helm-goはel-getとかで適当にインストールしたら後の設定は

(require 'helm-go)
(define-key go-mode-map (kbd "C-c C-d") 'helm-go)

のような感じで初回はhelm-go-save-documentsで関数名とtype名を保存する必
要があります。

おわり

全然つかいこなせてないけどGolangたのしいです。


2013年11月25日月曜日

festivalを無限再生(Golang利用)

Lookup.elで調べた英単語をファイル保存しているけど、helmでヒストリー表示
するくらいしかしてなかったのでgolangを利用してずっと再生するようにして
みた。通常のfestivalだと大量の文章だと合成音声を作るのに時間がかかるので
改行で分割して、ちょっとずつ再生するようにしています。
ここ からクローンできます。
名前はgofestivalにしました。(なんか楽しそう)

インストール

cd INSTALL_DIRECTORY
git clone https://github.com/yuutayamada/gofestival.git
cd gofestival
go install

使い方

$GOBINを設定していた場合そこにgofestivalでインストールされるので、
ファイル指定するか(絶対パスのみ。。)

$GOBIN/gofestival -file FILENAME

または
文字列指定してください。

$GOBIN/gofestival -text "hello"

ずっと再生したい場合は、-loopオプションをつけてください。
(止めるときは、C-cかC-dで)

$GOBIN/gofestival -text "hello" -loop

終わり

初めてGolangでちょっとしたものをつくりましたが、channelとか面白いと
思いました。


2013年11月5日火曜日

org trelloの導入

Trello をEmacsからつかう拡張の org-trello.el を導入した。
なんか忘れそうなのでメモ。

インストール

たぶんpackage installでインストールしたほうが楽(githubのページみるかぎり)
だけどなぜかel-getでインストールした。

(push '(:name org-trello
:type git
:url "https://github.com/ardumont/org-trello.git"
:depends (dash request elnode cl-lib json elnode esxml))
el-get-sources)

設定

普通のorg-modeとtrelloがまざるのはいやなので~/trello以下の場合
trello-modeになるようにした。

(require 'org-trello nil 'noerror)
(setq *ORGTRELLO-PROXY-PORT* 9876)
(add-hook 'org-mode-hook
'(lambda ()
(when (string-match
(expand-file-name "~/trello/") buffer-file-name)
(org-trello-mode))))

キーバインドとか

API keyの取得C-c o i
ボードをリストから選択C-c o I
同期 orgからtrelloへC-c o s
同期 trelloからorgへC-c o S
killC-c o k
kill allC-c o K
Clean up all setupC-c o x
insert deadlineC-c C-d
create boardC-c o b

最初にAPI keyの取得はmustやらないと使えない。
次にC-c o Iでボード選択これはweb側で事前に作ってたから
もし作ってなかった場合はどうなるかよくわからない。
たぶんC-c o bのorg-trello/create-boardコマンド
を使うのかと思うけど使ってない。


2013年11月1日金曜日

flycheckのエラーをpopup.elのツールチップで表示するflycheck-tip.elをリリ ースしました。

golangを最近勉強し始めました。flycheckは標準でgoに対応しているらしいの
インストールしましたがflycheckのエラー表示が僕はあまり好きではないので、
popup.elのtooltipで表示できるようにしました。
MELPAにアップロードしたので、M-x list-packagesやpackage-installから
flycheck-tipでインストールできます。

el-getでインストールしたい場合は

(push
'(:name flycheck-tip
:depends (flycheck) ; popupがない場合はpopupも追加する
:type github
:pkgname "yuutayamada/flycheck-tip")
el-get-sources)

を評価したあとM-x el-get-install flycheck-tipでインストールできると思
います。dependsのpopup.elはauto-complete.elがすでにインストールされてい
ればその中に同封されているので必要ありません。

設定

MELPAからインストールした場合,require節は必要ないかもしれません。
キーバインドの設定は適当に行ってください。

(require 'flycheck-tip)
(define-key your-prog-map (kbd "C-c C-n") 'flycheck-tip-cycle)
(define-key your-prog-map (kbd "C-c C-n") 'flycheck-tip-cycle-reverse)

イメージ

注意

このプログラムはタイマー後にminibufferに出るメッセージとpopup-tipででる
メッセージが重複するので flycheck-show-error-at-point 関数を無効化し
ます。もしこの挙動が嫌な場合は、以下の設定で元に戻せます。

(setq flycheck-tip-avoid-show-func nil)

2013年10月31日木曜日

Emacsでlookupで検索したのをfestivalでしゃべらせる

英語を辞書で調べたら発音がききたいので調べていた。
espeak は英語ぽくなかたったので、 festival を利用することにした。

参考サイト:
https://help.ubuntu.com/community/TextToSpeech
http://www.cstr.ed.ac.uk/projects/festival/manual/festival_toc.html

インストール

apt-getでインストールした。festvox-us1は女性の声でしゃべってくれる。
別にむさいおっさん声でいいなら必要ない。
女性の声もちょっとおばさんっぽいけど。。。

apt-get install festival festvox-us1

コマンドラインからつかう

echo "hello" | festival --tts

でも使えるし

$ festival
festival> ...

Festival コマンドで起動してあとから

(SayText "hi")

とかできる。
この時

(voice.list)

で使用可能な声のリストを見れる。
コマンドがなんかlispっぽい

Emacsから使う

festival.elパッケージをel-getからインストールした。
下のような設定をしているけどまだ使いはじめたばかりで変えるかもしれない。
取り敢えず注意点として、文字列内に"を含んでるとだめだった。
下の設定を使うには mine.el パッケージが必要です。(宣伝)
my/festivalコマンドはコマンドを実行する位置によってミニバッファから文
字列を指定して実行したり、フォーマットしたリージョン文字列をfestivalに
渡したりする。
my/festival-read-bufferはbufferの文字列をfestivalに読ませる。

(eval-when-compile (require 'cl))
(require 'festival)

(defvar my/festival-ignore-list
'(("[^0-9$a-zA-Z'\.#\+=\-]" . " ")
("###" . " ")
("\+\+\+" . " ")
("===" . " ")))

(defun my/festival (&optional txt)
"Read by festival from TXT or user input.
If user was selected region then pass region's words to festival."
(interactive)
(if (festivalp)
(let* ((replace
(lambda (from to str)
(replace-regexp-in-string from to str)))
(format
(lambda (text)
(loop with result = text
for (from . to) in my/festival-ignore-list
do (setq result (funcall replace from to result))
finally return result)))
(say
(lambda (text)
(festival-say (funcall format text)))))
(mine
:default '(funcall say (or txt (read-string "say: " (word-at-point))))
:region-handle-flag 'copy
:region '(funcall say mine:region-str)))
(festival-start)))

(defun my/festival-read-buffer ()
"Read from buffer's strings by using festival."
(interactive)
(mine
:default '(my/festival (buffer-string))
:C-u '(progn ; reset
(festival-stop)
(sleep-for 3)
(my/festival (buffer-string)))))

僕のlookup.elの呼び出し関数は、下のような感じなので(これもmine.elを…)

(defun my/lookup (&optional word)
"Search English word's meaning from my dictionaries.
If user specified WORD then search form it."
(interactive)
(let* ((ask (lambda ()
(read-string "lookup: " (word-at-point)))))
(if word
(lookup-pattern word)
(mine
;; カーソルの単語かユーザーに問い合わせする
:default '(lookup-pattern (or (word-at-point) (funcall ask)))
;; C-uを押してからの場合ユーザーに問い合わせした単語から調べる
:C-u '(lookup-pattern (funcall ask))
;; リージョン選択してる時はリージョンから
:region-handle-flag 'copy
:region '(lookup-pattern mine:region-str)))))

下のようなadvice関数を追加すれば、辞書検索するついでにその単語をしゃべ
るようにできました。

(defadvice lookup-pattern  (around ad-say-by-festival activate)
"Speak text that user is searching."
(condition-case error
(my/festival (ad-get-arg 0))
(error))
ad-do-it)

起動しなくなったら

メッセージを保存してなかったので不確かなのですが、

`(festival_warranty)'  segmentation fault (core dumped)

のようなメッセージがでてfestivalを起動できなくなったことがありました。
どちらが影響していたかわからないのでですが
~/.festival_historyと/var/crash/_usr_bin_festival.1000.crashを
削除したら起動できるようになりました。(自分用忘れぼうし)


2013年10月24日木曜日

Emacsでキーバインド不足を解消するmine.elの紹介

mine.el -> mykie.el に改名しました。 (後日記事を書く予定です)
自分用に作ったキーバインド定義を簡単に行なうelisp拡張です。
Emacsを使っていると拡張elispを入れるたびにキーバインドがたりなくなる思
いをしてないでしょうか?
mine.elは通常使うキーバインドの他にC-uを押したときやregionを選択中などの
コマンドを簡単に登録できます。

書き方

結局elispを書くんですが、こんな感じになります。

(defun mine:C-t ()
(interactive)
(mine
:default '(message "普通におしたらよばれるよ")
:C-u '(message "C-uをおしたらよばれるよ")
:region '(message "region選択してるときよばれるよ")))

これを普通にelispでかくと

(defun mine:C-t ()
(interactive)
(if (region-active-p)
(region-function)
(if current-prefix-arg
(do-default)
(C-u-function))))

こんな感じであんまりかわりません。(だめじゃん)

さすがにこれだけじゃあれなのでこんなかんじにもかけます。
※ 2013/10/25 :region&prefix-arg -> :region&C-uに変わりました。ご注意を

(defun mine:example ()
(interactive)
(mine
:default '(message "普通におしたらよばれるよ")
:C-u '(message "C-uをおしたらよばれるよ")
:region-handle-flag 'copy ; you can specify 'kill too.
:region&C-u '(message "region選択+C-u押したらよばれるよ")
:region '(message "region選択してるときよばれるよ")))

リージョン選択してなおかつC-uを押してた場合の関数を追加できます。

さらに(まだあるのかよ)以下のようによびだすこともできます。

(defun mine:example2 ()
(interactive)
(mine
:default '(message "普通におしたらよばれるよ")
:default&bolp '(message "beginning-of-lineでよばれるよ")
:default&eolp '(message "end-of-lineでよばれるよ")
:repeat '(message "同じポイントで呼び出したらよばれるよ")
:C-u&bolp '(message "C-u+beginning-of-lineでよばれるよ")
:C-u&eolp '(message "C-u+end-of-lineでよばれるよ")
:region '(message "region選択してるときよばれるよ")))

リピートは同じ位置で呼び出したときにdefaultのかわりに呼び出される関数です。
default,C-u&bolp,eolpはそれぞれメッセージ文にあるとおりです。

これらの条件を普通に書くのはめんどくさいというか、条件分岐が多くなって
大変なのでこのような拡張を書きました。

インストール

まだ開発段階というかもうちょっと機能を増やそうと思っているので
MELPAには登録していません。(上で紹介した機能は たぶんかわりません
キーワード引数の:region&prefix-argが:region&C-uにかわりました。)
興味がある方は GitHub からダウンロードしてください。
el-getからダウンロードしたい場合は以下のようにするといいと思います。

(push '(:name mine-el
:type git
:url "https://github.com/yuutayamada/mine-el.git")
el-get-sources)

簡単な例です。Emacsの標準の機能の組み合わせなので
mine.elをインストールすればそのままコピペで使えると思います。
注意点として、関数はリストで渡す必要があります。

(defun mine:C-j ()
"This is example function for C-j, but you can use this function"
(interactive)
(mine
:default '(progn
(delete-trailing-whitespace)
(case major-mode
(org-mode (org-return-indent))
(t (newline-and-indent))))
:C-u&eolp '(fill-region (point-at-bol) (point-at-eol))
:region-handle-flag 'copy
:region&C-u '(mine:query-replace-regexp)
:region '(mine:browse-url)))

まとめ

  • メリット
    簡単にひとうのキーに複数のキーバインドが登録できる。
  • デメリット
    内容をあらわす関数名をつけるのが難しい。
    機能が多いと忘れる(えっ意味な..)

おわり

よかったらつかってください。


2013年10月23日水曜日

lookup.el2を導入した

EPWING形式の英英辞書のCOBUILD辞書をもっていて以前lookup.elで参照できる
ようにしていたけど、Ubuntuさんがお亡くなりになってすっかりインストール
するのを忘れていた。
インストールしようとしたら、どうやらlookup2というものがあるらしいので
GitHub からこちらをインストールした。

lookup2: https://github.com/lookup2/lookup2
lookupのサイト: http://lookup.sourceforge.net/

Installation

僕はel-getからダウンロードしました。

(push '(:name lookup
:build ("sh autogen.sh && ./configure && make")
:type github
:url "https://github.com/lookup2/lookup2.git")
el-get-sources)

lispディレクトリ以下にパスを通す必要があるかも

Configurations

COBUILDの辞書はそのままでは使えなかったので、ここ の情報をたよりに変換
した(結構前なので記憶が曖昧だけど)。
下は僕の設定です。my/lookup関数の部分は mine.el というpackageを利用して
います。普通にキー入力したとき、リージョン選択時、C-uを押した時で挙動
を変えています。

(eval-when-compile (require 'cl))

(load "lookup-autoloads")
(require 'lookup)
(require 'lookup-vars)

(setq
lookup-search-agents
'((ndeb "~/Dropbox/dict/EPWING/COBUILD" :alias "cobuild"))
lookup-default-dictionary-options '((:stemmer . stem-english)))
(lookup)

(require 'mine)
(defun my/lookup ()
"Search English word's meaning from my dictionaries."
(interactive)
(lexical-let ((ask (lambda ())
(read-string "lookup: " (word-at-point))))
(mine
:default '(lookup-pattern (or (word-at-point) (funcall ask)))
:repeat '(lookup-pattern (funcall ask))
:C-u '(lookup-pattern (funcall ask))
:region '(lookup-region (region-beginning) (region-end)))))

(global-set-key (kbd "C-x l") 'my/lookup)

感想

GnusとMewでlookupを起動したときウィンドウの構成を破壊しなくなった。
いい感じに表示してくれてすごく気に入っている。


Gnusの設定とRSSの登録

Emacs24に移行したときか記憶が定かで無いけど Gnus が使えなくなっていて、
(Mewを使うようになった)放置していたけどとりあえず復活できた。
理由としてはMewは日本語系のメールアドレス,Gnusは英語系としたかったので

参考にしたサイト:
http://d.hatena.ne.jp/khiker/20110508/gnus
http://www.emacswiki.org/emacs/GnusEncryptedAuthInfo
http://www.emacswiki.org/emacs/GnusGmail

パスワードの入力はだるいので、authinfo.gpgファイルに保存して使うようにした。
注意: 下記の設定はGmailで利用することしか考えていません。

Emacs側の設定

使う場合はmy/gnus-mail-address変数とnntp-authinfo-file変数を適当に
設定してください。複数の gmail アドレスを利用する場合は
my/gnus-secondary-addresses変数も設定してください。これには
my/gnus-mail-addressを含める必要はありません。

(eval-when-compile (require 'cl))

(defconst my/gnus-mail-address ""
"User gmail address.")
;; if you have foo@gmail.com and bar@gmail.com
(defconst my/gnus-secondary-addresses '("foo" "bar"))
(defconst nntp-authinfo-file "~/.authinfo.gpg")

(lexical-let ((dir "~/gnus/"))
(defconst gnus-directory dir)
(defconst gnus-article-save-directory dir)
(defconst gnus-kill-files-directory dir)
(defconst gnus-cache-directory (concat dir "cache/"))
(defconst gnus-dribble-directory (concat dir "dribble/")))

(require 'gnus)
(require 'gnus-start)
(require 'gnus-setup)
(require 'gnus-agent)
(require 'gnus-util)
(require 'gnus-art)
(require 'starttls)
(require 'smtpmail)
(require 'nnir)

(eval-when-compile (require 'cl))

;; gnu-topic-mode by default
(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)

;; apply diff face
(add-hook 'gnus-article-prepare-hook
'(lambda ()
(my/mail-apply-diff-faces)))

(let ((coding 'utf-8))
(setq gnus-article-save-coding-system coding
gnus-agent-file-coding-system coding
gnus-ding-file-coding-system coding
gnus-cache-coding-system coding
gnus-cache-overview-coding-system coding))

(setq password-cache-expiry t
gnus-use-mailcrypt nil
gnus-use-cache t
gnus-cache-enter-articles '(ticked dormant read unread)
gnus-cache-remove-articles nil
gnus-cacheable-groups "^nnimap"
gnus-check-new-newsgroups nil
gnus-posting-styles `((".*" (name ,user-full-name)))
;; treats wide character
gnus-use-correct-string-widths t
;; Do not ask online or not.
gnus-agent-go-online t
;; do not goto cursor to unread group.
gnus-group-goto-unread nil
;; show also user-agent.
gnus-visible-headers (concat gnus-visible-headers "\\|^User-Agent")
;; extra headers to parse.
gnus-extra-headers '(To Newsgroups X-Newsreader
Content-Type CC User-Agent Gnus-Warning)
nnmail-extra-headers gnus-extra-headers
;; If member of thread that includes new article has old
;; article, grab old articles to display thread.
gnus-fetch-old-headers t
gnus-parameters
'(("INBOX"
(gnus-use-adaptive-scoring nil)
(gnus-use-scoring nil)
(visible . t)
(display . all)
(modeline-notify . t))))

(defun my/gnus-gen-template (host-and-user-name)
"Generate nnimap template.
HOST-AND-USER-NAME is utilize to get user information from authinfo file."
`(nnimap ,host-and-user-name
(nnimap-user ,host-and-user-name)
(nnimap-address "imap.gmail.com")
(nnimap-server-port 993)
(nnimap-authinfo-file ,nntp-authinfo-file)
(nnimap-stream ssl)))

(lexical-let* ((user (car (split-string my/gnus-mail-address "@gmail.com")))
(smtp "smtp.gmail.com"))
(setq gnus-select-method (my/gnus-gen-template user)
smtpmail-smtp-user user
message-send-mail-function 'smtpmail-send-it
send-mail-function 'smtpmail-send-it
smtpmail-default-smtp-server smtp
smtpmail-smtp-server smtp
smtpmail-smtp-service 587
;; Cc: and Bcc: to header of message-mode.
message-default-mail-headers "Cc: \nBcc: \n"))

(defun my/gnus-add-select-methods (mail-addresses)
"Add MAIL-ADDRESSES to `gnus-secondary-select-methods."
(lexical-let ((addresses mail-addresses))
(loop for address in mail-addresses
do (add-to-list 'gnus-secondary-select-methods
(my/gnus-gen-template address)))))

(unless gnus-secondary-select-methods
(my/gnus-add-select-methods my/gnus-secondary-addresses))

ちなみに.gnusが自動で読みこまれるらしいけど僕は他のコードと置き場所が
違くなるのが嫌なので以下のように設定してます。

(defadvice gnus
(around ad-load-init-file activate)
"Load init file for `gnus."
(require 'init_gnus)
ad-do-it)

authinfo.gpg

nntp-authinfo-fileで指定したgpgファイルに以下を設定する。
太字の部分USERはもしgmailアドレスがuser@gmail.comなら user
となります。PASSWORDはそのままパスワードを入れてください。
ちなみにmachineの部分はhostとも書けます。

machine USER login USER password PASSWORD port 993
machine smtp.gmail.com login USER password PASSWORD port 587

複数の gmail メールアドレスを登録した場合はそのアドレスの設定も追加し
てください。下の例は my/gnus-secondary-addressesに'("foo" "bar")を設定した
場合です。

machine foo login foo password PASSWORD port 993
machine bar login bar password PASSWORD port 993

その他の設定

Gnus で authfile を使うための設定いらないのもあるかも

  • gpgの設定
    apt-getでpinentryをインストールする必要がある
    ~/.gnupg/gpg-agent.confに以下を追加
    Mewユーザーの人はpinentry-programに /mew/bin/mew-pinentry へのパスで
    いいと思います。
    pinentry-program /usr/bin/pinentry-x11
    no-grab
    default-cache-ttl 36000
  • ~/.gnupg/gpg.confも追加
    use-agent
  • gpg2の鍵生成
    もし生成してなければ
    gpg2 --gen-key

    で生成できる

  • 自動起動に追加
    自動起動の設定で以下を入れる。(バッククオートです)
    eval `gpg-agent --daemon`

実行

M-x gnusでgnusを呼び出せます。
呼び出したら"^"を押して {nnimap:USER} (opened) のところでReturnした後
"u"で読み込む箇所にマークする。終わったら"q"を2回押して最初のbufferへ

RSSフィードの登録Group bufferで"G R"キーを押すとRSSフィードを登録でき
る。とりあえずハッカーニュースを登録した。
(http://feeds.feedburner.com/hacker-news-feed-50)


2013年10月15日火曜日

Emacsからagで検索してhelmで表示するhelm-ag-rの紹介

MELPAにアップロードだけして全然紹介してなかった ag(silver-searcher) で
検索して表示helmでおこなうhelm-ag-rについて紹介したいと思います。
でもその前に他の ag or helm関係で似ているパッケージの紹介をします。

helm-ag : 僕がhelm-ag-rを作るにあたって参考にさせていただきました。
実行すると"ag –nocolor –nogroup -S -C 2 -U "のような行が
minibuffer にでて検索ワードを入力します。結果をhelmでしぼりこみ検索できます。

ag.el : これはM-x grepコマンドがありますがそれの ag 版という印象です。
ただこれはgrepコマンドでgrep-editやgrep-a-lotなどを使っていると、
そちらと互換がとれてないので僕は使いませんでした。
(これの解決方は下の方に書いてます。)

なぜ車輪の再発明をしたのか?

helm-ag に不満があったわけではなく、自分がつくった elisp で
google-contacts,shell-history, gitのコミット履歴をhelmで表示する elisp
があったのですがそれらが結構似たような構造だったので似てる部分をあつめて
パッケージにしたかったというのがありました。これは Shell script で
grep 検索した結果を helm で表示するものでした。helm-ag-rはgrepだったもの
を ag に置きかえて作りました。
(でもgoogle-contactsは検索するたび問い合わせるのでちょっと遅い。。
まぁmewだと一度メール送ったらアドレスリストに乗るので必要ないですよね?)

特徴

  • helm-ag.el だと最初に検索ワードを入れて ag の結果を表示していましたが、
    helm-ag-rはhelmの絞込み minibuffer 入力時に検索します。(なんていうん
    ですかね?) なのでtypoして間違えた結果がでても、C-kしてすぐに入力しな
    おせます。そのかわり文字入力のたびにagを起動するのでちょっとアレな感
    じです。 (helm-input-idle-delay変数を指定することで対処可能ですが)
    -> 変数で指定するようにしました。詳しくは設定の項をみてください。
  • カスタマイズがshell-scriptを知っていればば簡単にできます。詳細はカス
    タマイズの項を参照してください(いいすぎかも。。)

機能

デフォルトで以下の検索コマンドがあります。

helm-ag-r-current-file現在のファイルから検索
helm-ag-r-from-git-repogit管理のリポジトリから検索
helm-ag-r-shell-history$HISTFILEで指定されているファイルからshell-history を表示する。
helm-ag-r-git-logsgit logから検索
helm-ag-r-google-contacts-listgoogle-contactsからメールアドレスを検索、挿入※

※google-clをインストールしている必要があります。まだ個人で複数メールア
ドレスをもっている場合には対応していません。

インストール

MELPAにアップロードしたので、list-packagesコマンドでhelm-ag-rを探すか
package-installコマンドでインストールできます。
GitHubからインストールしたい場合は ここから できます。

設定

検索時は文字を入力するたびに ag で検索するので、候補が多すぎると感じる
場合は、helm-ag-r-input-idle-delayやhelm-ag-r-requires-pattern変数など
を調整すると良いと思います。helm-ag-r-option-listはagのオプションを指
定します。C-oで次のオプションに移動します。 C-rで逆にオプションを移動
します。

(require 'helm-ag-r)
(setq
helm-ag-r-google-contacts-user "your gmail address"
helm-ag-r-google-contacts-lang "ja_JP.UTF-8"
helm-ag-r-option-list '("-S -U --hidden"
"-S -U -g")
;; 不安定な場合以下の項目を調整すれば
;; よくなるかもしれません
helm-ag-r-requires-pattern 3 ; 文字数以上入力してから検索
helm-ag-r-input-idle-delay 0.5 ; 検索をdelay後からおこなう
helm-ag-r-use-no-highlight t ; ハイライト無効化
helm-ag-r-candidate-limit 1000) ; 候補の上限を設定

使い方

普通のhelm系コマンドと同じように使えるのですが、
helm-ag-r-option-listで指定したオプションをC-oで切替えることができます。

カスタマイズ

shell-scriptを知っていれば簡単にカスタマイズできると書きましたが、実際
にはactionを定義するのに elisp の知識も多少必要になります。
下のはshell-historyをみるための関数定義です。
helm-ag-r-pype 関数の第一引数部分がshell scriptでその結果がhelmの候補として
表示されます。第二引数部分はhelmのソースの定義です。ここで指定したもの
は元のhelm-ag-rのソースを上書きします。下の例ではactionを上書きしていま
す。(helmではsource(s)という用語で何を検索してどのように表示するかなど
の情報をhelm-xxx-c-sourceというリストを指定する慣習?があります。)

(defun helm-ag-r-shell-history ()
"Search shell history(I don't make sure without zsh)"
(interactive)
(helm-ag-r-pype
"tac ~/.zsh_history | sed 's/^: [0-9]*:[0-9];//'"
'((action . (lambda (line)
(case major-mode
(term-mode (term-send-raw-string line))
(t (insert line))))))))

こんな感じで設定します。何か便利な関数ができたらpull requestくれるとよ
ろこびます。

おまけ grep-edit, grep-a-lot を ag でつかう

クリアコードさんの記事 Emacs実践入門 - おすすめEmacs設定2012 から
grepの設定を参考にしています。
grep-edit, grep-a-lot自体の設定は省略しています。(僕の設定は
grep-a-lot , grep-edit で公開しています)
ag がインストールされてない場合はそのまま grep コマンドを利用します。

(require 'grep)
(eval-when-compile (require 'cl))
(require 'thingatpt)

;; Use ag if the command was exist, otherwise use grep
(defvar grep-command-before-query
(if (zerop (shell-command "which ag"))
"ag --nogroup -a -S "
;; Recursive grep by -r
"grep -nH -r -e "))

(defun grep-default-command ()
(lexical-let*
((grep-read-from-point
(let ((grep-command-before-target
(concat grep-command-before-query
(shell-quote-argument (grep-tag-default)))))
(cons (if buffer-file-name
(concat grep-command-before-target
" *."
(file-name-extension buffer-file-name))
(concat grep-command-before-target " ."))
(1+ (length grep-command-before-target))))))
grep-read-from-point))

(setq grep-command (cons (concat grep-command-before-query "\"\" .")
(+ (length grep-command-before-query) 2)))

grep-command-before-queryとかなんだかわかってないんですが(helm-apropos
ででてこない..)設定するとM-x grepコマンドが指定したコマンドに変えられます。
それと ag コマンドを –nogroup オプションにすることで、grepと同じ形式
で出力できるので、ag で検索してるんだけど普通に grep コマンドで検索で
きます。なので、、、grep-a-lotとgrep-editをgrepで使えるならそのまま
使えるというわけです。

終わり

大したものではありませんがよかったら使ってください。


2013年10月14日月曜日

Emacsで英語の文章書くのに便利なgrammar.elを導入した

Emacswikiで grammar.el の紹介記事をみて、これはいいと思い導入しました。
英語の文章の間違ったところをハイライトして教えてくれる。ただインストー
ルするのにちょっと苦労したので記事として残しておこうとおもいます。
(僕がC++とかmakeに詳しくないだけかもしれないですが…)

Snapshot

Installation

Ubuntu12.02をつかっていますが、aptでインストールできるlink-grammarでは
間違った箇所のハイライトをよけいなところまでしていたので、最新のものを
インストールすることをおすすめします。

cd ~/
mkdir -p grammar
cd grammar
# download link-grammar
svn co http://svn.abisource.com/link-grammar/trunk link-gram

link-grammarのビルド
README をみるのが一番いいとおもいますが、一応自分の例を書いておきます。
(pacoについては ここ とか参考になるかもしれません。)

sh autogen.sh
CC=clang CXX=clang++ ./configure --disable-java-bindings && make
sudo paco -D make install
sudo ldconfig

僕はel-getを利用しているので下のように設定したあと

(push '(:name grammar
:type svn
:url "http://bcui-emacs.googlecode.com/svn/trunk/grammar")
el-get-sources)

M-x el-get-install grammarでインストールしました。

普通にsubversionでインストールするには下のような感じ


svn co http://bcui-emacs.googlecode.com/svn/trunk/grammar

grammar.elを使うにはgrammar.ccをコンパイルする必要がありますが、添付の
makefileではうまくコンパイルできませんでした。(僕は…)
なので以下のような感じでコンパイルしました。(autorg というところで
grammar.elをコンパイルしていたのでそれを参考にしました。)

#!/bin/bash # -*- coding: utf-8; mode: sh; -*-
# GRAMMAR_DIR=grammar.elがあるディレクトリを指定してください
GRAMMAR_DIR=${HOME}/.emacs.d/el-get/grammar
# LINK=link-grammarがあるディレクトリ指定してください
LINK=${HOME}/grammar/link-gram

if test -d ${GRAMMAR_DIR}; then
LG=${LINK}/link-grammar
LIBLINK=${LG}/.libs/liblink-grammar.a
cd ${GRAMMAR_DIR}
# clangがなければg++でも行けると思います(未確認)
clang++ -O3 grammar.cc -I${LINK} -I${LG} ${LIBLINK} -o grammar
fi

コンパイルが終わったらgrammar.elの設定

(require 'grammar)
(setq grammar-program-name "作成したgrammarへのパス")
;; 黒背景だとデフォルトの色はみずらかったので変更
(set-face-attribute 'grammar-error-face nil
:background "#Ff6347"
:foreground "#000000")
(define-key grammar-mode-map (kbd "C-M-g") nil)
(global-set-key (kbd "C-\]") 'grammar-check)

上の設定だとC-]でそのポイントより前の grammar チェックをおこなってくれ
ます。デフォルトだとC-M-gにgrammar-checkをバインドしてくれるので消しています。

下の設定はgrammar-modeの間は, '.?' を押したタイミングで
grammar-checkを呼び出すようにしています。書いてからきずいたけど
grammar-delay-commandってやつで設定するのがgrammar.elの普通の使い方の
気がしてきた。
でも英語の文章書いてて文が完結するのが".?"のタイミングだと思うからこ
れでいいや。(自分が後悔しないための必死の言い訳。。。)
ちなみにlink-grammarをインストールするとコマンドラインからlink-parser
コマンドも使えるようになってこれで、どんな感じにパースするかみれる。

(eval-when-compile (require 'cl))
(defconst my/grammar-default-sentence-end ".?\"!。ぁ-んァ-ン")
(defvar my/grammar-sentence-end-local nil)

(defun* my/grammar-check (&optional (sentence ""))
(interactive)
(unless (bound-and-true-p my/grammar-sentence-end-local)
(my/grammar-set-sentence-end))
(let ((sentence-end
(format "[%s%s]" my/grammar-sentence-end-local sentence)))
(grammar-check)))

(defun my/grammar-check-auto ()
(when (and (bound-and-true-p grammar-mode)
(not (bobp))
(case (char-before)
(46 t) ; dot
(63 t)) ; question
(case major-mode
(org-mode (not (org-in-src-block-p)))
(t t)))
(if (not (bound-and-true-p my/programming-mode)
(my/grammar-check)
(my/grammar-check-for-programming))))

(defun my/grammar-set-sentence-end ()
(setq-local my/grammar-sentence-end-local
(format "%s%s"
my/grammar-default-sentence-end
(replace-regexp-in-string " " "" comment-start))))

(defun my/grammar-check-for-programming ()
(lexical-let ((current-face (nth 1 (text-properties-at (1- (point))))))
(when (or (equal 'font-lock-string-face current-face)
(equal 'font-lock-comment-face current-face))
(my/grammar-check))))

(defadvice self-insert-command
(around ad-check-grammar-automatically activate)
ad-do-it
(my/grammar-check-auto))

プログラミングしている時の文字列の入力やコメントの入力時にも使えるように、
my/grammar-check-for-programming関数がありますが、そんなにつかってない
ので動作は期待しないでください。。。
もし使いたい場合は下のようにして、モードフックごとに
my/programming-mode変数が設定されるようにしてください。

(defvar programing-hooks
'(emacs-lisp-mode-hook
ruby-mode-hook))

(loop for hook in programing-hooks
do (add-hook hook
'(lambda ()
(setq-local my/programming-mode t))))

終わり

とりあえずこんな感じでつかっています。三単現のsとか複数系とか自分の忘
れやすいところを気づかせてくれるので結構便利と思います。

Land of Lisp

2013年10月11日金曜日

EmacsでのScala用の設定を公開してみる

Emacs上でのScala関係の設定がたまってきたので公開してみる。
いい設定があったらおしえてください。他にもこの辺りが参考になるかもしれません。

eMacsでscaLaを書く準備
EmacsでつくるScala開発環境 後編(ENSIME)

設定

scala-mode2利用している。

(require 'scala-mode2)
(add-to-list 'auto-mode-alist
'("\\.\\(scala\\|sbt\\)\\'" . scala-mode))

個人的な設定,,,日本語キーボードだけどmayu(窓使いの憂鬱)で英語配列にしているのでコロンがシフトを押さないといけないため入れかえる。Rubyとかのほとんどセミコロンを使わない言語で利用してもいいかも

;; Reverse ; and :
(let ((map scala-mode-map))
(define-key map (kbd ";") (lambda () (interactive) (insert ":")))
(define-key map (kbd ":") (lambda () (interactive) (insert ";"))))

Buffer上のコードを実行するのに関数をつくった。
apt-get install scalaでscalaコマンドの-eオプションを使えるのが条件
あとpopwinも必要。さりげにRubyとPerlにも対応している。

(require 'popwin)
(defun my/execute-from-current-file (&optional statement)
"Execute current buffer's program."
(interactive)
(lexical-let*
((original-buffer (current-buffer))
(code (shell-quote-argument (or statement (buffer-string))))
(program (case major-mode
(ruby-mode "ruby -e ")
(cperl-mode "perl -e ")
(scala-mode "scala -e ")))
(command (concat program code))
(buffer (get-buffer-create "*execute program*")))
(save-current-buffer
(popwin:popup-buffer
buffer :stick t :width (/ (frame-width) 2) :position :right)
(erase-buffer))
(start-process
"emacs-program" buffer "/bin/sh" "-c" command)
(switch-to-buffer-other-window original-buffer)))

(define-key scala-mode-map (kbd "C-c C-c") 'my/execute-from-current-file)

自動的にクラス名とか型名の最初の一文字を大文字にする。

;; Capitalize type word automatically
(defun my/scala-capitalize-auto ()
(when (equal major-mode 'scala-mode)
(lexical-let*
((current-face (nth 1 (text-properties-at (1- (point)))))
(type-face-p
(lambda () (equal font-lock-type-face current-face)))
(anonymous-func-type-p
(lambda ()
(and
(string-match "^ ?+def " (thing-at-point 'line))
(equal "=" (char-to-string (char-before (- (point) 3))))
(equal ">" (char-to-string (char-before (- (point) 2)))))))
(bracket-p
(lambda ()
(and (string-match "\\[" (char-to-string (char-before (1- (point)))))
(string-match "]" (char-to-string (char-after (point))))))))
(when (and (not (bobp))
(not (equal font-lock-string-face current-face))
(or (funcall type-face-p)
(and (not (equal font-lock-constant-face current-face))
(or (funcall anonymous-func-type-p)
(funcall bracket-p)))))
(let ((case-fold-search nil))
(unless (string-match "[A-Z]" (word-at-point))
(capitalize-word -1)))))))

(defadvice self-insert-command
(around ad-capitalize-type-word-in-scala activate)
ad-do-it
(my/scala-capitalize-auto))

multi-term上でのsbtの以下の挙動を修正する。

  • C-hで文字がきえない
  • C-aで行の頭に戻れない

パッケージにしたので、el-getとかで適当にダウンロードしてください。

cd somewhere
git clone https://github.com/yuutayamada/sbtp.git

sbtp-prompt-string変数はsbtコマンドを実行したあとの"> "とかの文字を
設定してください。(デフォルトで"> "なので 変更してなければ必要ありません)
2013/10/05 追記:
起動が遅いのでsbtのconsoleから利用するようにしました。
コマンドを実行するとEmacsのstart-processでsbt consoleを起動します。
起動後sbtp-console-sendでミニバッファから入力またはリージョン選択して
いればそのリージョンを実行します。
sbt-console-send-lineコマンドはその行を評価します。print文を使わなくても
res1: ~~ とかなんとかでて評価されるます。
sbtp-lang変数はデフォルトではLANG変数が設定されます。
もし日本語の表示がおかしい場合は設定するとなおるかもしれません。
C-uを押してから各sbt-consoleコマンドを実行すると、 :reset をしてからコ
マンドを実行します。
(ensimeでもC-c C-v sでensime-sbt-switchというコマンドがありますが、これ
はensime必須なので)

(require 'sbtp)
(setq sbtp-prompt-string "> "
sbtp-lang "ja_JP.UTF-8")
(define-key term-raw-map (kbd "C-a") 'sbtp-begging-of-line)
(define-key scala-mode-map (kbd "C-c C-p") 'sbtp-console-send)
(define-key scala-mode-map (kbd "C-c C-l") 'sbtp-console-send-line)

ensime 関係:

.ensime があったら自動でコネクトする

(require ensime)
(setq ensime-auto-connect 'always)
(add-hook 'scala-mode-hook '(lambda ()
(when (my/file-exists-p ".ensime")
(ensime-scala-mode-hook))))

(defun my/file-exists-p (file)
"Search file recursively until home directory"
(let* ((filename (concat "./" file))
(home "~/")
(home-p (lambda (dir1 filename)
(equal
(expand-file-name dir1)
(expand-file-name
(file-name-directory filename))))))
(while (not (or (file-exists-p filename)
(funcall home-p home filename)))
(setq filename (concat "../" filename)))
(when (file-exists-p filename)
(expand-file-name filename))))

Error箇所にジャンプする。popup packageが必要です

(eval-when-compile (require 'cl))
(require 'ensime)
(require 'popup)

(defvar my/ensime-current-error-number 0
"A number already searched error")

(defun my/ensime-goto-next-error ()
(interactive)
(when (equal major-mode 'scala-mode)
(lexical-let*
((errors (ensime-all-notes))
(convert-error-format
(lambda (err)
(loop for i from 0 to (length err) by 2
for key = (nth i err)
for value = (nth (1+ i) err)
if (< i (1- (length err)))
collect (cons key value))))
(converted-err-list
(loop for error in errors
for formatted-error = (funcall convert-error-format error)
if (equal buffer-file-name (assoc-default :file formatted-error))
collect formatted-error))
(move
(lambda (err-list)
(let* ((err (nth my/ensime-current-error-number err-list))
(err-point (1+ (assoc-default :beg err))))
(goto-char err-point)
(popup-tip (assoc-default :msg err)
:point err-point))))
(save-err-point
(lambda ()
(setq-local my/ensime-current-error-number
(if (equal my/ensime-current-error-number
(1- (length converted-err-list)))
0
(1+ my/ensime-current-error-number))))))
(when converted-err-list
(funcall move converted-err-list)
(funcall save-err-point)))))

終わり

Scalaのコード書くのにIntelliJとかIDEのほうがよさそうだけど、Emacsしか
使うきがないので仲間を増やすために設定を公開してみました。
何かナウい設定があったらおしえてください。


Popular Posts

Blogger templates

Blogger news