header

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しか
使うきがないので仲間を増やすために設定を公開してみました。
何かナウい設定があったらおしえてください。


Emacsから org-mode で Blogger に記事を送るbpe.elがタグに対応しました。

googleコマンドをみてたら–tagsオプションがあったので指定できるように
しました。それとupdate時にbpe:use-real-post-when-updating変数にtを設定
することで、ドラフトではなくそのまま更新するようにしました。
既に利用されているかたは(もしいたら)、githubから利用しているとおもうので、
git pullしてConfiguratinの項の設定を加えてください。これからインストールしたい方は
melpaにも登録してみたので、M-x list-packagesコマンドからbpeを探してイ
ンストールできます。

  • そもそも bpe.el って、、、
    タイトルにはさらっと書いてますが、Emacsの olg-mode 形式のファイルから
    googleの Blogger にHTMLエクスポートしたものを送るプログラムです。

Requirement

googlecl というパッケージを利用しているので最初にインストールする必要が
あります。Ubuntuでは以下のようにインストールできました。

sudo apt-get install googlecl

Installation

Melpa からインストールする場合は M-x package-install bpe でインストールできます。GitHubからクローンしたい場合は ここから インストールできます。

Configuration

以下の設定を.emacsなどの設定ファイルに貼り付けてください。
bpe:lang変数は$LANG変数を参照するので最設定しないと文字ばけするかもし
れません。bpe:use-real-post-when-updating変数は記事をアップデートする
ときにドラフトではなくそのままポストします。(default nil)
bpe:no-ask変数はnon-nilだと更新時のy/nの問い合わせを出ないようにします。

(require 'bpe)
(setq bpe:account "your mail address on google blogger")
(setq bpe:blog-name "your blog name")
(setq bpe:no-ask t)
(define-key org-mode-map (kbd "C-c C-p") 'bpe:post-article)
;; Do not attach --draft option if updating
(setq bpe:use-real-post-when-updating t)
;; For Japanese, default is $LANG environment variable.
(setq bpe:lang "ja_JP.UTF-8")

Usage

.org形式のファイルを開いて以下のコメント行を貼り付けます。

#+TITLE: How to post my blog from Emacs and org-mode?
#+OPTIONS: toc:nil \n:nil num:nil
#+TAGS: Emacs Blog
#+AUTHOR: author-name

OPTIONSは必要はありませんが、toc(table of contents)の部分が表示される
のが嫌な場合は書いておいたほうがいいです。
TITLE行はブログのタイトルになり、TAGSはブログのタグになります。
(これが今回の追加した機能)

ポストするにはM-x bpe:post-articleコマンドか設定の項で入れたキーバイン
ドでできます。C-uを押してから実行した場合は同じ名前の記事を削除してから
投稿します。

使用環境

僕はOrg-mode 8.1.1(maint branch)を使用しています。
古いバージョンのorg-modeを利用していてもし動かなかった場合は、

(defadvice bpe:export-html
(around ad-use-old-export-func activate)
(bpe:export-html-old-version))

で動くかもしれません。(確認はしてないけど前は動いていたので)

問題

googleのBloggerサービスがHTML内に改行が入っていると自動で改行をいれてし
まうので、対処としてHTMLをminifyするコード(perlのパッケージを利用してる
だけ)を書きましたが,,,
org-mode内で<p></p>要素などの最初と最後に改行をいれていて、もうminifyす
るとかそんな問題じゃなかった。
というわけで現在はHTML出力したものに正規表現で改行を置換しています。な
ので文書内に<p>\nのような部分があった場合は改行が削除されてしまいます。


Popular Posts

Blogger templates

Blogger news