Hatena::ブログ(Diary)

てつじんにっき

2011-08-23

[][] CoffeeScript + Guard::CoffeeScript + Emacs + Flymakeで「Errno::ENOENT: No such file or directory」が出ないように

最近、GuardでTitanium+CoffeeScriptの開発を快適に – ひげろぐを参考にGuard::CoffeeScriptを入れ、coffeescriptでflymakeする - Kentaro Kuribayashi's blogのFlymakeの設定を入れてCoffeeScriptTitaniumでのアプリ作成を試してます。

Guard::CoffeeScriptでファイル変更の監視・コンパイルをしてると

ERROR: Guard::CoffeeScript failed to achieve its <run_on_change>, exception was:
Errno::ENOENT: No such file or directory - coffee/app_flymake.coffee

というエラーが発生して頻繁にGuardが落ちる。

Flymake用の一時ファイルをGuardが検知し、コンパイルしようとするが、すぐに削除されるのでエラーになっているようだ。

そのまま参考になるruby-modeのflymakeでguardが誤動作しないようにする - むしゃくしゃしてやったを参考に、 flymake-create-temp-inplace を flymake-create-temp-with-folder-structure に変更し、同一ディレクトリ内でなく、tempディレクトリにflymake用のファイルを作成するようにした。

以下、diff

(setq flymake-coffeescript-err-line-patterns
  '(("\\(Error: In \\([^,]+\\), .+ on line \\([0-9]+\\).*\\)" 2 3 nil 1)))

(defconst flymake-allowed-coffeescript-file-name-masks
  '(("\\.coffee$" flymake-coffeescript-init)))

(defun flymake-coffeescript-init ()
  (let* ((temp-file (flymake-init-create-temp-buffer-copy
+                     'flymake-create-temp-with-folder-structure))
-                     'flymake-create-temp-inplace))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    (list "coffee" (list local-file))))

(defun flymake-coffeescript-load ()
  (interactive)
  (defadvice flymake-post-syntax-check (before flymake-force-check-was-interrupted)
    (setq flymake-check-was-interrupted t))
  (ad-activate 'flymake-post-syntax-check)
  (setq flymake-allowed-file-name-masks
        (append flymake-allowed-file-name-masks
                flymake-allowed-coffeescript-file-name-masks))
  (setq flymake-err-line-patterns flymake-coffeescript-err-line-patterns)
  (flymake-mode t))

(add-hook 'coffee-mode-hook 'flymake-coffeescript-load)

メモ

例えば/Users/foo/local/app/titanium/example/coffee/app.coffeeでエラーがある場合、エラー表示は以下のようになる。

[3] Error: In ../../../../../../../private/var/folders/OQ/OQ*************************/-Tmp-/Users/foo/local/app/titanium/example/coffee/app.coffee, Parse error on line 3: Unexpected 'TERMINATOR'

この/private/var/folders/..というのは何だろう?と見てみると、自分の環境では環境変数TMPDIR, TMP, TEMPを見ているようでmacではこうなってるみたい。

;; flymake.el -> flymake-create-temp-with-folder-structure
(defun flymake-create-temp-with-folder-structure (file-name prefix)
  (unless (stringp file-name)
    (error "Invalid file-name"))

  (let* ((dir       (file-name-directory file-name))
         ;; Not sure what this slash-pos is all about, but I guess it's just
         ;; trying to remove the leading / of absolute file names.
	 (slash-pos (string-match "/" dir))
	 (temp-dir  (expand-file-name (substring dir (1+ slash-pos))
                                      (flymake-get-temp-dir))))

    (file-truename (expand-file-name (file-name-nondirectory file-name)
                                     temp-dir))))

;; flymake.el -> flymake-get-temp-dir
(defalias 'flymake-get-temp-dir
  (if (fboundp 'temp-directory)
      'temp-directory
    (lambda () temporary-file-directory)))

;; files.el -> temporary-file-directory
(defcustom temporary-file-directory
  (file-name-as-directory
   (cond ((memq system-type '(ms-dos windows-nt))
	  (or (getenv "TEMP") (getenv "TMPDIR") (getenv "TMP") "c:/temp"))
	 (t
	  (or (getenv "TMPDIR") (getenv "TMP") (getenv "TEMP") "/tmp"))))
  "The directory for writing temporary files."
  :group 'files
  :initialize 'custom-initialize-delay
  :type 'directory)

参考

2011-08-14

[][] Emacsphp-modeでalignする その2

4年越しですが Emacsのphp-modeでalignする - てつじんにっき で書いていたものの挙動が気になったので修正し、requireして使うよう改良してphp-align.elという名称でgithubにあげてみました。

no title


話が変わりますが最近php-modeは https://github.com/rradonic/php-mode を使っています。オリジナルに比べて色々と機能追加やバグ修正されているようです。*1

と、していたら https://github.com/ejmr/php-mode の方がEmacs同梱になるというのをブコメで知りました。

b:id:tomoya emacs, php

PHP5.4 対応版。php-mode-version-number は 1.5.1 になってる。と思ったら、あっという間に 1.6 へ。そして Emacs 同梱へ http://comments.gmane.org/gmane.emacs.devel/142507 2011/07/26

はてなブックマーク - ejmr/php-mode - GitHub

こちらも5.4対応など色々と機能追加されているようです。

乗り換えようかな。

*1:5.4のtraitも簡単なパッチを送ったらマージしてくれました

2011-06-10

[][] anything-project.elでSymfony2

anything-project.elにSymfony2用のプロジェクトを定義してみた。

(ap:add-project
 :name 'symfony2
 :look-for 'ap:symfony2-root-detector
 :grep-extensions '("\\.php" "\\.twig" "\\.yml")
 :exclude-regexp  '("/cache/" "/logs/"))
(defun ap:symfony2-root-detector (files)
  (ap:all-files-exist '("app" "bin" "src" "vendor" "web") files))

使ってみて、M-x anything-project-grepした時にcacheディレクトリなどが対象になって少し困ったので、ap:add-project-exという関数の追加とap:build-grep-commandを上書きして:grep-ignore-dirsというキーワード引数で除外するディレクトリを定義できるようにした。*1

最終的に以下のような感じ。

(require 'anything-project)

(defun* ap:add-project-ex (&key name look-for (include-regexp ".*") (exclude-regexp nil) (exclude-directory-regexp nil) (grep-extensions nil) (grep-ignore-dirs nil))
  (ap:add-project
   :name name
   :look-for look-for
   :include-regexp include-regexp
   :exclude-regexp exclude-regexp
   :exclude-directory-regexp exclude-directory-regexp
   :grep-extensions grep-extensions)
  (nconc (cdr (assq name ap:projects))
         (list (cons :grep-ignore-dirs grep-ignore-dirs))))

(defun ap:get-grep-extra-options (key)
  (let ((grep-ignore-dirs (ap:get-project-data key :grep-ignore-dirs)))
    (mapconcat 'identity (mapcar (lambda (dir)
                                   (concat "--ignore-dir=" dir))
                                 grep-ignore-dirs) " ")))

(defun ap:build-grep-command (key)
  (let ((grep-extensions (ap:get-grep-extensions key))
        (ack-command (ap:get-ack-command))
        (grep-extra-options (ap:get-grep-extra-options key))
        (xargs-command (ap:get-xargs-command))
        (egrep-command (ap:get-egrep-command)))
    (concat
     ack-command " -afG " grep-extensions
     " "
     grep-extra-options
     " | "
     xargs-command
     " "
     egrep-command " -Hin "
     "%s")))

;; PHP Symfony2
(ap:add-project-ex
 :name 'symfony2
 :look-for 'ap:symfony2-root-detector
 :grep-extensions '("\\.php" "\\.twig" "\\.yml")
 :grep-ignore-dirs '("cache" "logs")    ; これを新たに追加
 :exclude-regexp  '("/cache/" "/logs/"))
(defun ap:symfony2-root-detector (files)
  (ap:all-files-exist '("app" "bin" "src" "vendor" "web") files))

そしてsymfony2.elが欲しくなってきた。

*1emacs lisp知識不足でdefunしたけどdefadviceで出来るものなのかな?

2009-06-23

[]ターミナル上のEmacsにペーストしたい

普段、PuTTY上でEmacsを使っているのですが、そのEmacsにコード等をペーストしたいときに、

S-Insert等でそのままペーストしても、キー入力として送られるので、自分の環境だといろいろ問題が起きてました。

  • global-mapのC-mに'newline-and-indentを設定してるので、コードを貼り付けたときに階段状に余計なインデントがされてしまう。
  • key-chordで設定したキーの文字列が含まれるとkey-chordが起動してしまう。("jk"とか)
  • undo一発で元の状態に戻せるようにしたい。

いろいろ調べてみたのですが、良い解決方法が見つけられなかったので

今までは我慢するか、vimを起動して:set pasteをしてたのですが、

やっぱりEmacsにペーストしたい!!ってことで、ちまちま調べながらelispを書いてみました。


undoの部分はundo-groupを使うようにしました。load-path上に置いておきます。

http://www.mahalito.net/~harley/elisp/undo-group.el

term-paste-mode.el

;; http://www.mahalito.net/~harley/elisp/undo-group.el
(require 'undo-group)

(defvar term-paste-mode-map
  (let ((map (make-keymap))
        (i ? ))
    (while (< i ?~) ;; 取りあえずスペース 〜 ~までself-insert-commandをセットして上書く
      (define-key map (char-to-string i) 'self-insert-command)
      (setq i (1+ i)))
    (define-key map "\C-m" 'newline)
    map))

(defcustom term-paste-mode-on-hook nil
  "Hook to run when term-paste-mode is activated."
  :group 'term-paste
  :type 'hook)

(defcustom term-paste-mode-off-hook nil
  "Hook to run when term-paste-mode is deactivated."
  :group 'term-paste
  :type 'hook)

(define-minor-mode term-paste-mode
  "Minor mode for pasting from any terminal applications."
  :lighter " Paste"
  :group 'term-paste
  (cond (term-paste-mode
         ;; minor-mode-mapの優先順位を上げる
         (setq minor-mode-map-alist
               (cons (cons 'term-paste-mode term-paste-mode-map)
                     (delete (assq 'term-paste-mode minor-mode-map-alist) minor-mode-map-alist)))
         (undo-group-boundary)
         (run-hooks 'term-paste-mode-on-hook))
        (t
         (run-hooks 'term-paste-mode-off-hook)
         )))

(provide 'term-paste-mode)

.emacs

(require 'term-paste-mode)

(add-hook 'term-paste-mode-on-hook
          (lambda ()
            (key-chord-mode 0)
            ))
(add-hook 'term-paste-mode-off-hook
          (lambda ()
            (key-chord-mode 1)
            ))
(defalias 'p 'term-paste-mode)

M-x pでterm-paste-modeをtoggleさせます。

  1. M-x p でterm-paste-modeをonに
  2. S-Insertで貼り付け
  3. M-x p でterm-paste-modeをoffに

元に戻す場合は

M-x undo-group

ちゃんと動かなかったらすみません。もっと良い方法あるかなあ。

追記 2009-10-01

githubにあげました。http://github.com/tetsujin/term-paste-mode

2009-05-27

[] faceがnilのときだけsmartchr

ストリングのリストを引数にとって割り当てられたキーを連続して押すと順番に入力するコマンド関数を返す関数 - IMAKADO::BLOG

が便利そうだったので今日から導入してみました。

コメントとか、文字列の中では起動しないほうが自分は嬉しかったので、

faceがnilのときだけ起動するように、条件判定をかますようにしてみました。

;; http://d.hatena.ne.jp/IMAKADO/20080913/1221328814
;; 関数名だけsmartchrに変更させていただきました。
(eval-when-compile (require 'cl))
(defun smartchr (list-of-string)
  (lexical-let ((los list-of-string)
                (last-word "")
                (count 0))
    (lambda ()
      (interactive)
      (if (eq this-command real-last-command)
          (incf count)
        (setq count 0))
      (when (>= count (length los))
        (setq count 0))
      (let ((word (nth count los)))
        (when (eq this-command real-last-command)
          (delete-backward-char (length last-word)))
        (setq last-word word)
        (insert word)))))

(defun smartchr-if (list-of-string condition)
  (lexical-let ((f (smartchr list-of-string))
                (c condition))
    (lambda (arg)
      (interactive "p")
      (if (and (= arg 1) (funcall c))
          (funcall f)
        (self-insert-command arg)))))

(defmacro smartchr-if-face (list-of-string list-of-face)
  `(smartchr-if ,list-of-string
                (lambda ()
                  (memq (get-text-property (point) 'face)
                        ,list-of-face))))

(define-key php-mode-map (kbd "=") (smartchr-if-face '("=" " = "  " == " " === ") '(nil)))

追記 2009-05-29

  • C-u前置引数があればsmartchrは起動しないようにした。(無意識に結構使ってたみたいで不便だった)