;;; my-magit-encoding.el --- magit 1.2.0用文字化け対策コード ;; Copyright (C) 2013 AKIYAMA Kouhei ;; Author: AKIYAMA Kouhei ;; Keywords: ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; 概要: ;; magitにおいて、 ;; 異なる文字符号化方式のファイルが混在しても文字化けせずに表示でき、かつ、 ;; 部分的なstageといったmagitの素晴らしい機能もそのまま使えるようにします。 ;; ;; 具体的には、 ;; - magitがgit diffを実行したときのgitからの出力 ;; - magitがgit applyを実行するときのgitへの入力 ;; を差分の各ファイルパート毎に文字符号を変換します。 ;; ;; 各ファイルの文字符号化方式は git check-attr encoding <ファイル名> で ;; 取得します。 ;; したがって、あらかじめどのファイルがどの文字符号化方式であるか ;; を.gitattributesファイル等で指定しておく必要があります。 ;; ;; 詳しい処理内容: ;; - magit-cmd-outputをdefadviceでフックして、git diffが実行されたとき、 ;; その出力をファイルパート毎にdecode-coding-regionを呼び出して ;; 各ファイルの文字符号化方式からバッファの文字符号化方式へ変換します。 ;; 最初はmagit-wash-diff-sectionの時点で変換していたのですが、 ;; 全体の構造を把握するのが面倒で不安だったので、実行直後に変換するように ;; magit-cmd-outputをフックしました。 ;; - magit-run*をdefadviceでフックして、git applyを実行する前にinputバッファの ;; 文字符号化方式をdiffのときとは逆になるようにencode-coding-regionを ;; 使用して変換します。 ;; ;;; Code: ;; coding-system utils (defun my-magit-encoding-name-to-coding-system (encoding-string) "文字符号化方式を表す文字列をCODING-SYSTEM(シンボル)へ変換します。" (let ((cs (intern (downcase encoding-string)))) (if (coding-system-p cs) cs nil))) (defun my-magit-file-encoding (file) "fileで指定されたファイルの文字符号化方式を返します。git check-attr encodingを使用します。" (let ((check-attr-result (magit-git-string "check-attr" "encoding" file))) (if (string-match "encoding: \\([^ ]+\\)" check-attr-result) (match-string-no-properties 1 check-attr-result)))) (defun my-magit-file-coding-system (file) "fileで指定されたファイルのCODING-SYSTEMを返します。git check-attr encodingを使用します。" (my-magit-encoding-name-to-coding-system (my-magit-file-encoding file))) ;; common diff/apply (defun my-magit-git-command-in-args (args) "リストargsの中の-で始まらない最初の要素を返します。" (while (and args (let* ((str (car args))) (or (= (length str) 0) (string= (substring str 0 1) "-")))) (setq args (cdr args))) (car args) ) (defun my-magit-git-command-is (args cmd) (string= (my-magit-git-command-in-args args) cmd)) (defun my-magit-convert-coding-each-file-diff (fun-coding-region) "カレントバッファにdiffの出力があるものとして、その各ファイルパートに対してfun-coding-regionを適用します。ファイルのcoding-systemはmy-magit-file-coding-systemで取得します。" (goto-char (point-min)) (let ((current-file nil) (current-cs nil) (prev-file-end nil)) ;; for each file (while (re-search-forward "^diff --git a/\\(.+\\) b/\\(.+\\)$" nil t) (beginning-of-line) (if (and prev-file-end current-cs) (funcall fun-coding-region prev-file-end (point) current-cs)) (setq prev-file-end (point)) (setq current-file (match-string-no-properties 1)) (setq current-cs (my-magit-file-coding-system current-file)) ;;(message "file:%s encoding:%s" current-file current-cs) (next-line)) ;; last file (goto-char (point-max)) (if (and prev-file-end current-cs) (funcall fun-coding-region prev-file-end (point) current-cs))) ) ;; diff (defun my-magit-decode-coding-region (beg end cs) (let ( ;; gitからの出力をデコードしたときに使用したCODING-SYSTEMを特定する。 ;; 特定できなければ、とりあえずutf-8にしておく。 (git-decoding-system (or (car (find-operation-coding-system 'call-process "git")) 'utf-8))) ;; begからendの範囲をバイト列に戻してからcsでデコードし直す。 (decode-coding-region beg (+ beg (encode-coding-region beg end git-decoding-system)) cs)) ) (defadvice magit-cmd-output (after my-magit-cmd-output-encoding-fix (cmd args)) (if (my-magit-git-command-is args "diff") (setq ad-return-value (with-temp-buffer (insert ad-return-value) ;;(my-magit-convert-coding-each-file-diff #'decode-coding-region) (my-magit-convert-coding-each-file-diff #'my-magit-decode-coding-region) (buffer-string))))) (ad-activate 'magit-cmd-output) ;; apply (defadvice magit-run* (before my-magit-run* (cmd-and-args &optional logline noerase noerror nowait input)) (if (my-magit-git-command-is args "apply") (with-current-buffer input (my-magit-convert-coding-each-file-diff #'encode-coding-region)))) (ad-activate 'magit-run*) (provide 'my-magit-encoding) ;;; my-magit-encoding.el ends here