エンジニアリングにはほど遠い

iPhoneアプリとかサイトとかをつくっていくブログです。

Emacs emacsclientを何気なく使いつつclientで開いた地点をdefault directoryにする

Emacs 起動した場所をdefault directoryにするをemacsclientでもやりたいということで。
これをするとVimとほぼ変わらない起動速度を得ることができました。

Emacsはserverを立ち上げておいて、emacsclientというコマンドで素早く立ち上げることができます。ちょっと細かい解説は省略しますが、-aオプションはserverが立ち上がっていたらそれを使用、そうでなければ立ち上げて使用という便利なオプションです。

参考:
emacsclientを使ってEmacsの起動を速くする

で、自分はemacsclientした場所をルートにしてファイルを開いたりしたかったので、それをした時のフックを探してたんですが、ありました。server-visit-hookです。使いたいフックは大体提供されているので嬉しいですね。

.zshrc

alias e='export EMACS_PWD=`pwd`; emacsclient -a ""'
alias ekill='emacsclient -e "(kill-emacs)"'

init.el

(defun set-default-pwd ()
  (let* ((working-dir (getenv "EMACS_PWD")))
    (if working-dir
        (setq default-directory working-dir))))

(add-hook 'server-vist-hook 'set-default-pwd)
(add-hook 'find-file-hook 'set-default-pwd)

emacsclientだと何となくキーボードの設定が若干変わったり(元に戻る?)するっぽいのでその辺も調整したいところ。

Emacs 起動した場所をdefault directoryにする

自分がVimに慣れているというのもあって、常に起動した場所からファイルを参照したい。
以下で実現。

  • 起動時に現在地を環境変数に保存
  • ファイル呼び出しのたびにそこをデフォルトディレクトリに指定

.zshrc

alias e='export EMACS_PWD=`pwd`; emacs'

init.el

(add-hook 'find-file-hook
          '(lambda ()
             (let* ((working-dir (getenv "EMACS_PWD")))
               (if working-dir
                   (setq default-directory working-dir)))))

起動時のパスをどこかに保存すれば環境変数使わなくていいと思うけど、とりあえずこれで。

追記: 2015-07-27
これをやるとgit管理しているプロジェクトで

Args out of range: #("Git@" 0 4 (help-echo "Locally added file under the Git version control system")), 0, 7

と怒られる。default directoryを書き換えているのでgitのpathとズレが起きて変なことになっているっぽい。ここを参考にして.zshrcを以下に変更した。

alias e='export EMACS_PWD=`pwd`; export GIT_DIR="`pwd`/.git"; /usr/local/bin/emacs'

Emacs 24からの標準テーマフレームワークで設定

Emacs 24からは標準でカラーテーマを設定する機能があるみたいですが、その設定でハマったのでメモ。
複数の設定方法で設定できるのもEmacs初心者の自分には厄介でした。

まずEmacs24以降の標準用color-themeなのか、以前のcolor-theme用のものかで設定ファイルのフォーマットが違います。なので、どっち用に作られたものなのか把握して合わせる必要があります。 24以降の標準のものは後ろに意図的に-themeと付けられている気がします。

  • ELPA, MELPAなどで配布されているもの
    • これはインストールした後にrequireすれば反映されます。一番簡単。
(el-get-bundle zenburn-theme)
(require 'zenburn-theme)
  • .elファイルで入手するもの
    • 配布サービスで入手できないものや自作のものなどはこっちを使うと思います。
    • 自分はここを使わせてもらって自作しました。
; ファイルを置くディレクトリを決める
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes")

; そこに"*****-theme.el"という名前でテーマファイルを置く
; ~/.emacs.d/themes/zenburn-theme.el
; M-x load-theme の候補に出てくるようになる

; "*-theme.el"の最後に書かれている(provide-theme '****)と同じものを(load-theme '**** t)する
(load-theme 'zenburn t)

ていうか切り替えたりしないのであればあればinit.elに色をベタ書きの方がいいんじゃないかとか思ったり。それも自分が決めた設定だし。

Evil tab機能を使えるようにする

自分はVimでよく:tabe とかってやってファイルを開くのでこの機能は是非使えるようにしたい。

evil-tabsというプラグインを作ってくれている方がいました。

これはelscreenというプラグインキーバインドをラップしてる感じです。 el-getで入れました。

(el-get-bundle evil)
(el-get-bundle elscreen)
(el-get-bundle evil-tabs)

これで以下をやれば完了です。

(require 'evil-tabs)
(global-evil-tabs-mode t)

すると、Vimと同じような操作でタブが作れるではないですか!!!すごい。これはすごい。
しかし、一つ不満点が。Vimと同じようにタブが一つの時はタブの表示をしないようにしたい。
できれば画面には余計な情報は載せたくない派なので。

elscreen.elを見ていると、使えそうな関数が提供されていました。

; elscreen.el
(defvar elscreen-screen-update-hook nil)
(defun elscreen-run-screen-update-hook ()
  (when elscreen-frame-confs
    (elscreen-notify-screen-modification-suppress
     (run-hooks 'elscreen-screen-update-hook)))
  (remove-hook 'post-command-hook 'elscreen-run-screen-update-hook))

elscreen-screen-update-hookに実行したい関数を入れておくとタブ更新の際に実行してくれるみたいです。
elscreen-one-screen-pという、タブ(screen)が1個の時はtを返す関数もあるので、以下のように書けました。

(add-hook 'elscreen-screen-update-hook
          '(lambda ()
             (setq elscreen-display-tab (if (elscreen-one-screen-p) nil t))))

これでタブが1個の時はタブバーが表示されなくなりました。
Emacsはプログラミングで設定している感覚が強くて楽しいですね。

Evil auto-completeが出た状態で普通にノーマルステートに戻る

LISP系言語をやっていくならEmacsなんでしょうが、何度も挫折を味わってきました。 Evilはそんな僕に力を与えてくれたんです。

Evilは素晴らしいですね。いざとなったらEmacsに戻れますし、EmacsVimが載っていてどっちも動かせるという感じです。

でも、少し良くないことも起きました。auto-completeで補完が出ている状態でC-[しても、画面最下部にESCという文字が出て動けません。 で、設定をしてみました。

(define-key ac-completing-map (kbd "C-[") 'evil-normal-state)

auto-completeは補完が出ている最中のキーマッピングを提供してくれているので、そこに対象のキー操作と対応する関数を書けばよいです。
とても分かりやすい!
SchemeClojureに触れる前はむしろわかりずらすぎと思ってinit.elはそっ閉じしていたのですが、今はちゃんとソースコードとして読めました。

課題としては、ssh接続のlinux上で起動したemacsで、カーソルを変える関数を実行した時にiTerm2だと変化を見ることができない、ということですね。 該当のエスケープシーケンスを飛ばすことができればいいみたいなんですがちょっとわかりませんでした。

Objective-C キャメル・スネークケース変換

超小ネタです。toSnakeCaseは最初の文字が大文字のものにはちゃんと対応していません。

NSString+CamelSnake.m

#import "NSString+CamelSnake.h"

@implementation NSString (CamelSnake)

- (NSString *)toCamelCase
{
    NSMutableString *result = [NSMutableString new];
    [[self componentsSeparatedByString:@"_"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [result appendString:idx == 0 ? obj : [obj capitalizedString]];
    }];
    return (NSString *)result;
}

- (NSString *)toSnakeCase
{
    NSMutableString *result = [self mutableCopy];
    NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: @"[A-Z]"
                                                                           options:0
                                                                             error:nil];
    [regex replaceMatchesInString:result
                          options:0
                            range:NSMakeRange(0, [result length])
                     withTemplate:@"_$0"];
    return [result lowercaseString];
}

@end

Ruby 条件式で代入をした際のうっかり

ちょっとうっかりしたのでメモ。
変数に値を代入して、その値を判定した後に変数を使いたい場合

[1] pry(main)> if a = 1 > 0
[1] pry(main)*   p a
[1] pry(main)* end
true
=> true
[2] pry(main)> if (a = 1) > 0
[2] pry(main)*   p a
[2] pry(main)* end
1
=> 1

前者はaに 1 > 0 の結果を代入する事になるのでaはtrue
後者はaに1を代入した上で、条件判定に入っている

ifなしで見ればこうなるのは自然な事ですけどね。