ElispでPHPを使い、Emacsをもっとフロントエンド仕様に。

こんにちは福山です。最近、他の人のパソコンを操作する機会が多くて、英字キーボード&emacs慣れした指がフリーズします。

本題ですが、emacsのelispでphpを使いたかったんです。でもネット上ではRubyやPerlの情報しかなく、PHP on Elisp な情報を必死でサルベージしました。lispもshellも最近始めたので、もっとスマートな方法があったらぜひ教えてください。

選択範囲を使う方法

[C-x d] でdiredを起動し設定ファイルの場所を開きます。
私の場合は、Dropboxに設定ファイルを置いています。Dropboxは使わなくても大丈夫です。
~(チルダ) は MacとUbuntuの場合、ユーザー名のフォルダになります。
~/Dropbox/config/.emacs.d

diredのバッファ(ウィンドウ)で キーボードから[+] を押すと新しいフォルダを作れます。ここでは、binというフォルダを作ります。
~/Dropbox/config/.emacs.d/bin

diredから先ほど作ったbinに移動します。
[C-x C-f] でファイルを作ります。test という名前にしましょう。
~/Dropbox/config/.emacs.d/bin/test

testの内容を書きます。#!/usr/bin/phpはインタプリタと呼ばれる最初に書くお約束で、これはphpだぞ!と宣言します。

中身さえ読み込めればいいので、ファイルの拡張子はphpでもtxtでも何もなくても構いません。

test

#!/usr/bin/php
<?php
$selected = file_get_contents('php://stdin');
echo '<div>'.$selected.'</div>';

emacsの設定ファイルであるinit.el(emacs.el)にtestのコードを呼び出す設定を書きます。

elisp

(defun wrapDiv ()
  "Divで包むよー"
  (interactive)
  (save-excursion
    ;; testまでのパス
    (shell-command-on-region (point) (mark) "~/Dropbox/config/.emacs.d/bin/test" nil t)))
;; この関数をキーボードショートカットに登録
(global-set-key (kbd "C-c 5") 'wrapDiv)

しかし、このままではtestファイルに権限がかかっているので、diredから変更します。
~/Dropbox/config/.emacs.d/bin/
と移動しtestの行で[Shift + m]を打ちます。
Change mode of test to:
と出るので続けて 755 と入力してエンター(return)すると権限を変更できます。

(追記:こちらの方がいいですね。[emacs]保存時実行属性付与

さあこれで設定OK、関数をC-x C-eで評価する、あるいはEmacsを再起動させると使えようになります。
文字列を[C-Space]( or [C-@]) でマークし、[C-c 5]と打つと選択範囲がdivタグで囲まれます。

shell-command-on-region を使う方法は、これはこれでいいのですが、いちいち選択範囲で囲むのがめんどいって人には次の方法をおススメします。

選択範囲で囲まない方法

先ほどと同じようにphp用のファイルを作ります。
~/Dropbox/config/.emacs.d/bin/test2
test2としました。内容を書きます。受け取り方が先ほどと違います。

test2という名前のファイル

#!/usr/bin/php
<?php
echo '<span>'.$_SERVER['argv'][1].'</span>';

そしてemacsの設定ファイルであるinit.el(emacs.el)にtestのコードを呼び出す設定を書きます。

elisp

(defun wrapSpan ()
  "Spanでつつむよー"
  (interactive)
  (let
      (innerText bounds)
    (setq innerText (thing-at-point 'word))
    (setq bounds (bounds-of-thing-at-point 'word))
    (save-excursion
      ;; 元の文字列を削除
      (delete-region (car bounds) (cdr bounds))
      ;; phpコードからの戻り値を挿入
      (insert
       (shell-command-to-string
        (concat
         "~/Dropbox/config/.emacs.d/bin/test2"
         " "
         innerText)))
      )))
(global-set-key (kbd "C-c 3") 'wrapSpan)

shell-command-to-stringを使いました。
対象の一区切りの文字列を、test2は$_SERVER['argv'][1]で受け取ります。

thing-at-point というのが便利です。
カーソルが単語の末尾あるいはその途中のどこかにあれば、その単語を丸ごと引数としてphpに渡す事ができます。'wordを使い文字列だけ取得しましたが、ファイルパスを取得する 'filename などいろいろあります。

画像をbase64に変換する

そんでもって実用編

前回の画像サイズを取得してimgタグを出力する方法も、このPHPを使った方法で出来ますね。

今回は、Emacsで画像ファイルをbase64のデータスキームに変換してみます。

パスの上にカーソルを持っていって、[C-c 6]で変換展開するようにします。

init.el (emacs.el) に記載する内容

(defun toBase64 ()
  "Convert image file to Base64 DataScheme"
  (interactive)
  (let
    (img-file-path bounds)
    (setq img-file-path (thing-at-point 'filename))
    (setq bounds (bounds-of-thing-at-point 'filename))
    (save-excursion
      (delete-region (car bounds) (cdr bounds))
      (insert
       (shell-command-to-string
        (concat
         "~/Dropbox/config/.emacs.d/bin/toBase64"
         " "
         img-file-path)))
      )))
(global-set-key (kbd "C-c 6") 'toBase64)
base64という名前のファイルを作ります。

上記したパーミッション変更を忘れずに。


#!/usr/bin/php
<?php
$path = $_SERVER["argv"][1];
$mine = '';
if(preg_match('/.(jpeg|jpg)z/i',$path)){ $mine = 'jpeg'; }
    elseif(preg_match('/.gifz/i',$path)){ $mine = 'gif'; }
    elseif(preg_match('/.pngz/i',$path)){ $mine = 'png'; }
    elseif(preg_match('/.icoz/i',$path)){ $mine = 'x-icon'; }
    else{ exit(); }
$binary = file_get_contents($path);
echo 'data:image/'.$mine.';base64,'.base64_encode($binary);

ブログ用に選択範囲のコードをエスケープする

htmlspecialcharsを使わないでやってみます。

init.el (emacs.el) に記載する内容

(defun toBlog ()
  "Escape code for blog"
  (interactive)
  (save-excursion
    ;; testまでのパス
    (shell-command-on-region (point) (mark) "~/Dropbox/config/.emacs.d/bin/toBlog" nil t)))
;; この関数をキーボードショートカットに登録
(global-set-key (kbd "C-c \") 'toBlog)
toBlogという名前のファイル

#!/usr/bin/php
<?php
$selected = file_get_contents('php://stdin');
$escape = preg_replace('/&/','&amp;',$selected);
$escape = preg_replace('/</','&lt;',$escape);
$escape = preg_replace('/>/','&gt;',$escape);
$escape = preg_replace('/"/','&quot;',$escape);
$escape = preg_replace('/'/','&#039;',$escape);
$escape = preg_replace('/t/','    ',$escape);
echo $escape;

めっちゃスッキリした!