8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Eshellで「Text is read-only」が出る問題を回避する/複数行プロンプトを使いやすくする

Last updated at Posted at 2013-01-29

Eshell、つかってますか。いーしぇる。
わたしはEshellをつかっています。

Windowsだけ使って生きてきてるので、いまいちシェルの使い方もわからずでしたが、とにかく触ってみようとおもって使い始めたのがbashさんでした。
しかしどうもいまいち操作感がしっくりこなかったのでEmacs Shell上で動かすようにしてみて、そこからしばらくしてEshellに乗り換えた形です。
最初はなにこれって思いましたが、少しずつカスタムしてみたらすごく手に馴染むようになったのでまるでEmacsみたいなシェルでした。じわじわくる。
まだまだ便利な使い方がわからないのでしばらく使いこなせるようにがんばります。

本題です。
Eshellをカスタマイズしてたら突然「Text is read-only」とかいうエラーメッセージを出しはじめてEshellが閉じられなくなったことはありませんか。
Emacs本体を閉じることもできなくなってとってもイライラさせてくれるこの問題をなおしたのでここに書いておきます。

こいつをInit.elにドーン!

;; 許されざるText is read-onlyを回避する
(defadvice eshell-get-old-input (after eshell-read-only-korosu activate)
  (setq ad-return-value (substring-no-properties ad-return-value)))

表面上で問題回避しようとするとどうやってもどこかしらに穴がでちゃったので根本からつぶしちゃいました。
この問題、eshell-prompt-functionをいじってプロンプトを複数行にしたときなどによく発生します。

本来EshellはReturnキーを押したときカーソルのある行をそのままコマンドラインにドーンしてくれるんですが、プロンプトのある行でReturnしたときはプロンプト部をスルーするように作られてます。
そのときのプロンプト探しの正規表現がeshell-prompt-regexpですね。

実はこのeshell-prompt-regexpをつかう関数がちょっと曲者で、__プロンプトを1行単位でしかマッチング判定しない__ようにできてます。なにそれクソい。
で、複数行プロンプトには当然マッチしなくて、プロンプトにマッチしないとなると、今度はそのプロンプト文自体をコマンドラインにドーンしちゃうんですが、それをやると上の問題が発生しちゃいます。プロンプト文の持つread-only属性が原因だったようです。

というわけで、なんとかしてプロンプト行をコマンドラインに入れないようにしようといろいろ画策したんですが、結局無理っぽかったので問題原因そのものを潰しました。これで何やってももんだいないよやったね。
たぶんこれで不具合とかは出ないと思いますが、まだテストしてないので……。お悩みの方はどうぞ。

以下メモ。/おまけ「Eshellプロンプトを複数行にしたときの動作を快適にする」

上の問題を回避しようといろいろやった時の残りカスです。
が、使い物にはなります。

とりあえずコード。
例のごとくInit.elかどこかにドーンする。

(custom-set-variables
 ;; eshellのプロンプトフォーマットを指定
 '(eshell-prompt-function
   (lambda ()
     (concat
      "[ " (format-time-string "%Y/%m/%d %H:%M") " | "
      (user-login-name) "@" (system-name) " ]\n"
      "[" (abbreviate-file-name (eshell/pwd)) "]\n"
      (if (= (user-uid) 0) "#" "$")
      " ")))
 ;; プロンプトにマッチングする正規表現を指定
 ;; 複数行プロンプトを使う場合はそれぞれ1行毎にマッチングするように書くこと
 '(eshell-prompt-regexp "^\\(\\[[^]\n]+\\]\\|[$#] \\)"))

;; 追加設定
(defcustom eshell-prompt-regexp-lastline "^[#$] "
  "複数行プロンプトの最終行にマッチする正規表現を指定する"
  :type 'regexp
  :group 'eshell-prompt)

;; 複数行プロンプトでもスキップが正常に動作するようにする
(defadvice eshell-skip-prompt (around eshell-skip-prompt-ext activate)
  (if (looking-at eshell-prompt-regexp)
      (re-search-forward eshell-prompt-regexp-lastline nil t)))

上から順に。

eshell-prompt-functionは書いてあるとおり、プロンプトのフォーマットを指定します。
上の記述だと、

[じかん | ろぐいん名@ほすと名]
[作業でぃれくとり]
$

という形ですね。

2つ目。eshell-prompt-regexpはプロンプトにマッチする正規表現を指定します。
大事なのは、プロンプトの全ての行に1行ずつマッチするようにORる必要があることです。
上の設定だと、
[...]$/# の行それぞれにマッチするようになってます。
こう書けばプロンプト部が全部きちんとプロンプトとして扱われるようになりますが、
弊害として、プロンプトの上でC-a(eshell-bol)を押すと

[じかん | ろぐいん名@ほすと名]■ ←ここにカーソルが来るようになっちゃいます。
[...

なので、改造。3つ目。
eshell-prompt-regexp-lastline変数を新しく作りました。
複数行プロンプトの最後の行にマッチするように書きます。
defcustomは趣味なのでdefvarでもいい。setqはバイトコンパイラさんにおこられるからダメ。

4つめ。プロンプト上のカーソル移動関数eshell-skip-promptを上書きして、
複数行プロンプトの時も正しくスキップが働くようにしてみました。
eshell-prompt-regexpeshell-prompt-regexp-lastlineが正しく設定されていればちゃんと動くはずです。
これでeshell-bol実行時、$/#の後ろにカーソルが来るようになります。
例によってこれでなんか不具合出るかもしれないけど、そんなことはしらない。

以上メモおわり。

ここまでやってようやく最初の一歩を踏み出した感あるEshellさん底が知れない。
でもいじる楽しみがたくさんあるのは面白いし楽しい。
みなさんもEshellを使ってみてはいかがでしょうか。

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?