view redit-client-sm.el @ 31:ccd1ad69a852 rep_camp_20071111

*** empty log message ***
author gongo
date Fri, 21 Dec 2007 13:30:50 +0900
parents 9e43cec05dc5
children c8bac1eb3c9b
line wrap: on
line source

;; 	$Id$

;; not use
;;(defcustom redit-client-program "/Users/gongo/src/cr/REP_project/emacs/redit_client.pl"
;;  "The program to use as the remote-edit client."
;;  :group 'redit
;;  :type 'string)

;; 送受信されるコマンドは
;; 2 + 2 + 9 + data
;; command_number + buffer_number + line_number + data

(eval-when-compile (require 'cl))

(defvar redit-client-process nil
  "Remote-edit client process.")

(defvar redit-client-buffer nil
  "Remote-edit client's buffer.")

(defvar redit-client-buffer-name nil
  "Remote-edit client's buffer-name.")

(defvar redit-client-buffer-number nil
  "Remote-edit client's buffer-number.")

(defvar redit-client-editor-id "0"
  "Remote-edit client's editor-id.")

(defvar redit-client-session-id 0
  "Remote-edit client's session-id.")

;; editor local sequence number
(defvar redit-client-seqno 0
  "Remote-edit client's editor-id.")

(defvar redit-client-line-max-in-server nil
  "Value of point-max in server's buffer.")

(defvar redit-client-previous-edited-line 1
  "Current cursor line of remote-edit client.")

(defvar redit-client-after-edited-line 1
  "Current cursor line of remote-edit client.")

(defvar redit-client-previous-line 1
  "Behind cursor line of remote-edit client.")

(defvar redit-max-string-length 1004
  "Read-Write max length of string.")

;; REP が使用する文字コード
(defvar rep-string-encoding 'utf-8)

(defvar redit-open-command           "1")
(defvar redit-open-ack               2)
(defvar redit-read-command           3)
(defvar redit-read-ack               4)
(defvar redit-read-finish-ack        5) ;; obsolete
(defvar redit-insert-command         6)
(defvar redit-insert-ack             7)
;;(defvar redit-write-still-ack        "7")
;;(defvar redit-write-finish-ack       "8")
(defvar redit-delete-line-command    9)
(defvar redit-delete-line-ack       10)
(defvar redit-close-command         11)
(defvar redit-close-ack             12)
(defvar redit-replace-command       13)
(defvar redit-replace-ack           14)
(defvar redit-nop-command           15)

;; REP Session
(defvar redit-join-command           41)
(defvar redit-join-ack-command       42)
(defvar redit-get-command            43) ;; obsolete
(defvar redit-get-ack-command        44) ;; obsolete
(defvar redit-put-command            45)
(defvar redit-put-ack-command        46)
(defvar redit-select-command         47) ;; obsolete
(defvar redit-select-ack-command     48) ;; obsolete
(defvar redit-register-command       49) ;; obsolete
(defvar redit-register-ack-command   50) ;; obsolete
(defvar redit-deregister-command     51) ;; obsolete
(defvar redit-deregister-ack-command 52) ;; obsolete
(defvar redit-quit-command           53)
(defvar redit-quit-ack-command       54)

;; Queue struct
;; (defstruct Queue (front nil) (rear nil))
;; (setq redit-client-command-queue (make-Queue))
;; (setq redit-client-receive-queue (make-Queue))
;; (setq redit-client-send-queue (make-Queue))

;; line translated queue
;; (setq after-CQ (make-Queue))
;; (setq after-RQ (make-Queue))


;; Fix Me
;; 適当に作ってしまったので。
;; 一応コマンドは認識してます。
(defun redit-pack-int-loop (num count)
  (if (> count 0)
      (concat
       (redit-pack-int-loop (/ num 256) (- count 1))
       (char-to-string (% num 256)))))

(defun redit-pack-int (num)
  (redit-pack-int-loop num 4))

(defun redit-pack (cmd sid eid seq lno siz)
  (concat
   (redit-pack-int cmd) (redit-pack-int sid) (redit-pack-int eid)
   (redit-pack-int seq) (redit-pack-int lno) (redit-pack-int siz)))

(defun redit-unpack-int-loop (pkt pos count)
  (if (> count 1)
      (+
       (* (redit-unpack-int-loop pkt (- pos 1) (- count 1)) 256)
       (string-to-char (substring pkt (- pos 1) pos)))
    0
    ))

(defun redit-unpack-int (pkt pos)
  (redit-unpack-int-loop pkt (* pos 4) 4))


;; これ使ってる?
;; redit-join-command の仕事と被ってるっぽいのでコメント
;; (defun redit-client-start (host)
;;   "Allow this Emacs process to be a remote-edit client for client processes."
;;   (interactive "P")
;;   (if redit-client-process
;;       (progn
;; 	(set-process-sentinel redit-client-process nil)
;; 	(condition-case () ;; エラーが起こるとプロセスを消す
;; 	    (delete-process redit-client-process) (error nil))))
;;   (if (setq host (read-string "Hostname: ")) ;; minibufferでホスト名を入力させる
;;       (let ((process-connection-type nil))
;; 	;; クライアントプロセス(redit-client)内のプログラムをスタートさせる。
;; 	;; 引き数として host が与えられる
;; 	(setq redit-client-process
;; 	      (start-process "reditclient" nil redit-client-program host))
;; 
;; 	;; redit-client-process の状態が変化すると redit-client-sentinel を呼ぶ
;; 	(set-process-sentinel redit-client-process 'redit-client-sentinel)
;; 	;; redit-client-process からのデータ入力は redit-client-process-filter
;; 	;; を介する
;; 	(set-process-filter redit-client-process 'redit-client-process-filter)
;; 	;; redit-client-process からの入出力を 8-bit codes で取扱う。
;; 	;;(set-process-coding-system redit-client-process
;; 	;;'raw-text 'raw-text)
;; 	(set-process-coding-system redit-client-process
;; 				   rep-string-encoding rep-string-encoding)
;; 	;; emacs が終了すると redit-client-process も終了する
;; 	(set-process-query-on-exit-flag redit-client-process nil)
;; 
;; 	;; フック変数 before-change-functions, after-change-functions
;; 	;; をバッファローカルにする
;; 	;; not necessary since Emacs 21.1
;; 	;; (make-local-hook 'before-change-functions)
;; 	;; (make-local-hook 'after-change-functions)
;; 	
;; 	(message "Remote edit client started."))   (error "redit-client connection refused.")))
;; 
;; (redit-join-command ())


(defun redit-join-command ()
  "Allow this Emacs process to be a remote-edit session manager for client processes."
  (interactive)
  (let (bufname)
    (setq bufname (encode-coding-string
		   (buffer-name)
		   rep-string-encoding))
    (if redit-client-process
	(progn
	  (set-process-sentinel redit-client-process nil)
	  (condition-case () ;; 
	      (delete-process redit-client-process) (error nil))))
    
    (setq redit-client-process (open-network-stream
				"redit-client" nil "localhost" 8766))    

    ;; called when the process changes state
    (set-process-sentinel redit-client-process 'redit-client-sentinel)
    
    ;; called when the process receive packet
    (set-process-filter redit-client-process 'redit-client-process-filter)
    
    ;; //Code of the process input/output buffer is rep-string-encoding
    ;; //(set-process-coding-system redit-client-process rep-string-encoding rep-string-encoding)
    ;; 送受信するデータはバイナリで貰わないといけない。
    ;; utf-8 じゃなくてずれる可能性があるので
    (set-process-coding-system redit-client-process 'binary 'binary)
    
    ;; プロセスが生きてるとき、 emacs を終了しようとすると
    ;; 「processがうんたらで、本当に切ってもいいの y/n」
    ;; って聞かれる。nil てやっておくと聞かれない。
    ;; quit を入れるんなら t にして有効にすべきなんだろうか。
    ;; 今は nil で
    (set-process-query-on-exit-flag redit-client-process nil)
    
    ;; not necessary since Emacs 21.1
    ;; (make-local-hook 'before-change-functions)
    ;; (make-local-hook 'after-change-functions)
    
    (if (string= "*scratch*" bufname) 
	;; JOIN if buffer-name is "*scratch*"
	(process-send-string
	 redit-client-process
	 (concat
	  (redit-pack
	   redit-join-command
	   0 0 0 0 0)))
      ;; PUT if buffer-name is not "*scratch*"
      (process-send-string
       redit-client-process
       (concat
	(redit-pack
	 redit-put-command
	 0 0 0 0 (string-bytes bufname))
	(buffer-name)))
      )))
  
;; 'get' is obsolete command
;; (defun redit-get-command ()
;;   "get. alias redit-join-command and redit-select-command."
;;   (interactive)
;;   (redit-join-command)
;;   (redit-select-command ()))

;; select : select session
;; ユーザの入力を受け取って、selectコマンドを構成し、通信プロセスに渡す
;; (redit-select-command ()
(defun redit-select-command (session-name)
  "select session"
  (interactive "P")
  (if (setq session-name (read-string "session name:"))
      (progn 
	(setq redit-client-session-id (string-to-number session-name))
	(process-send-string 
	 redit-client-process
	 (redit-pack
	  redit-select-command redit-client-session-id redit-client-editor-id
	  (gen-seqno) 0 0))
	
	;;(concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-select-command) redit-client-session-id redit-client-editor-id (gen-seqno) 0 0) session-name "\n"))
	)
    (error "invalid session name.")
    )
)


;; put-command は、join-command 内で
;; ファイルを開いてない *scratch* の時に呼び出されるので
;; ここで定義する必要は無い
;;(defun redit-put-command (session-name)
;;  "put session"
;;  (interactive "P")
;;  (if (setq session-name (read-string "put session name:")) 
;;      (process-send-string 
;;       redit-client-process
;;       (concat
;;	(redit-pack
;;	 (string-to-number redit-put-command) 0 0 0 0
;;	 (string-bytes (encode-coding-string session-name rep-string-encoding)))
;;	session-name
;;	)
;;       )
;;    (error "invalid session name.")
;;    ))

;; 'register' is obsolete command
;; (defun redit-register-command (session-name)
;;   "register"
;;   (interactive "P")
;;   (if (setq session-name (read-string "session name:")) 
;;       (process-send-string 
;;        redit-client-process
;;        (concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-register-command) redit-client-session-id redit-client-editor-id (gen-seqno) 0 0) session-name "\n"))
;;     (error "invalid session name.")
;;     )
;;   )
;; 

;; 'deregister' is obsolete command
;;(defun redit-deregister-command ()
;;  "deregister"
;;  (interactive)
;; )

(defun redit-quit-command ()
  "quit"
  (interactive)
  )

(defun redit-client-open (file)
  (interactive "P")
  (if redit-client-process
      (if (setq file (read-string "Filename: "))
	  (progn
	    ;; redit-client-process の input に文字列を送信する
	    (process-send-string
	     redit-client-process
	     ;; redit-open-command (01) と file を連結させる
	     (concat redit-open-command file))
	    ;; redit-client-process から出力を得るまでループ
	    (while (eq nil (accept-process-output redit-client-process)))))
    (error "redit-client is not running.")))

;; redit-client-process にreadコマンドとバッファ番号、行番号を送り、
;; サーバからの出力を得る
(defun redit-client-read-line (linenum)
  (if redit-client-process
      (save-excursion
	(progn
	  (process-send-string
	   redit-client-process
	   (format "%10d%10d%10d%10d%10d%10d\n" redit-read-command redit-client-session-id redit-client-editor-id (gen-seqno) linenum 0))
	  (while (eq nil
		     (accept-process-output redit-client-process)))))
    (error "redit-client is not running.")))

;; linenumで指定された行の編集をサーバへ送る
;; redit-client-process に insert コマンドと
;;バッファ番号、行番号、行の内容を送り、 Ack を受け取る
(defun redit-client-insert-line (linenum isnewline)
  (if redit-client-process
      (save-excursion
	(let ((beginp) (endp) (length))
	  ;; insert lines on server buffer
	  (setq beginp
		(progn (goto-line linenum) (beginning-of-line) (point)))
	  (setq endp
		(progn (goto-line linenum) (end-of-line) (point)))
	  ;; (setq length (- endp beginp))
	  ;; rep-string-encoding に変換してから長さを求める
	  (setq length (string-bytes (encode-coding-string (buffer-substring beginp endp) rep-string-encoding)))
	  
	  ;; トークンが回ってくるのを待つ場合は、
	  ;; すぐに process-send-string せずに
	  ;; 編集情報を保存するだけにとどめる
	  ;;(enqueue
	  ;;redit-client-command-queue
	  ;;(concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-write-command) redit-client-session-id redit-client-editor-id (gen-seqno) linenum length)
	  ;;(buffer-substring beginp endp)
	  ;;(if (eq isnewline t) "\n")
	  ;;"\n"))
	  
	  ;; redit-client-process へ
	  ;; command_num + buffer_num + line_num + string
	  ;; の文字列を送る

	  (process-send-string
	   redit-client-process
	   (concat

	    ;; obsolete send string
	    ;;(format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-write-command) redit-client-session-id redit-client-editor-id (gen-seqno) linenum length)

	    (redit-pack
	     redit-insert-command
	     redit-client-session-id
	     redit-client-editor-id
	     (gen-seqno) linenum length)
	    
	    ;; (buffer-substring beginp endp)
	    ;; rep-string-encoding に変換
	    (encode-coding-string (buffer-substring beginp endp) rep-string-encoding)
	    (if (eq isnewline t) "\n")
	    "\n"))
	  (sleep-for 0.3)
	  ))
    (error "redit-client is not running.")))

;; linenum で指定した行の削除命令を redit-client-process に送信する
(defun redit-client-delete-line (linenum)
  (if redit-client-process
      (save-restriction

	;; トークンが回ってくるのを待つ場合は、
	;; すぐに process-send-string せずに
	;; 編集情報を保存するだけにとどめる
	;;(enqueue redit-client-command-queue
	;;(concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-delete-line-command) redit-client-session-id redit-client-editor-id (gen-seqno) linenum 0) "\n"))
	
	;; insert lines on server buffer
	(process-send-string
	 redit-client-process

	 (concat
	  ;; obsolete send string
	  ;; (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-delete-line-command) redit-client-session-id redit-client-editor-id (gen-seqno) linenum 0) "\n"))

	  (redit-pack
	   redit-delete-line-command
	   redit-client-session-id
	   redit-client-editor-id
	   (gen-seqno) linenum 0)))
	 (sleep-for 0.3)
	)
    (error "redit-client is not running.")))

;; redit-client-process へcloseコマンドを送る
(defun redit-client-close ()
  "Save File and exit Remote-edit client."
  (interactive)
  (let ((line redit-client-previous-line))
    (redit-client-insert-line line nil)
    (process-send-string
     redit-client-process
     (concat
      ;; obsolete send string
      ;; (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-close-command) redit-client-session-id redit-client-editor-id (gen-seqno) 0 0)
      (redit-pack
       redit-close-command
       redit-client-session-id
       redit-client-editor-id
       (gen-seqno) 0 0) "\n"))))

;; redit-client-process を終了させる
(defun redit-client-kill ()
  "Stop Remote-Edit client process."
  (interactive)
  (if redit-client-process
      (kill-process redit-client-process))
  (if redit-client-buffer
      (kill-buffer redit-client-buffer))
  (kill-all-local-variables))

;; proc == redit-client-process
;; string == redit-client-process からの output
(defun redit-client-process-filter (proc string)
  ;; バッファローカルなフックリスト before-change-functions から
  ;; redit-client-before-change-function を取り除く. after も同じ
  ;; proc からの出力に hook しないように
  (remove-hook 'before-change-functions
	       'redit-client-before-change-function t)
  (remove-hook 'after-change-functions
	       'redit-client-after-change-function t)
  
  (let ((command (rep-get-command-from-pkt string)))
    ;; command がどの命令かを判断し、対応した処理をする。case みたい
    (cond 
     ;; FIXME
     ((if (= command redit-open-ack)
	  (if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-open-ack string))))

     ((if (= command redit-read-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-read-ack string))))

     ((if (= command redit-insert-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-insert-ack string))))

     ((if (= command redit-delete-line-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-delete-line-ack string))))

     ((if (= command redit-close-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-close-ack string))))

     ((if (= command redit-insert-command)
	  (if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-insert-line string))))

     ;; from Session Manager
     ;; join ack (editor id)
     ((if (= command redit-join-ack-command)
	  ;;(message "redit-client-exec-join")
	  (redit-client-exec-join string)))

     ;; put ack (editor id)
     ((if (= command redit-put-ack-command)
	  (redit-client-exec-put string)))

     ;; select ack
     ;; TODO : start editing
     ;; obsolete
     ;;((if (= command redit-select-ack-command)
     ;;(redit-client-exec-select)))

     ;; get
     ;;((if (= command redit-get-command)
     ;;(if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
     ;;(redit-client-exec-get string))))

     ;; register
     ;;((if (= command redit-register-command)
     ;;(if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
     ;;(redit-client-exec-register string))))

     ;; deregister
     ;;((if (= command redit-deregister-command)
     ;;(if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
     ;;(redit-client-exec-deregister string))))

     ;; delete line
     ((if (= command redit-delete-line-command)
	  (if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-delete-line string))))
     ))

  (add-hook 'before-change-functions
	    'redit-client-before-change-function t t)
  (add-hook 'after-change-functions
	    'redit-client-after-change-function t t)
  )

;; window-scroll-functions に hook される。
;; window がスクロールする度に呼ばれる
(defun redit-client-update-function (window window-start)
  (if (equal (window-buffer) redit-client-buffer)
      (save-excursion
	(remove-hook 'before-change-functions
		     'redit-client-before-change-function t)
	(remove-hook 'after-change-functions
		     'redit-client-after-change-function t)
	;; read part.
	(while (and (= (point-max)
		       (window-end nil t))
		    (> redit-client-line-max-in-server
		       (real-count-lines (window-end nil t))))
	  ;; 		       (count-lines (window-end nil t))))
	  (redit-client-read-line (real-count-lines (window-end nil t))))
	
	(if (not (= (count-lines (point-min) (window-end nil t))
		    (real-count-lines (window-end nil t))))
	    (redit-client-read-line
	     (progn (goto-line redit-client-line-max-in-server)
		    (real-count-lines (point)))))
	
	(if (< redit-client-line-max-in-server
	       (real-count-lines (window-end nil t)))
	    (progn (goto-line (window-end nil t))
		   (delete-region
		    (progn (beginning-of-line) (backward-char) (point))
		    (progn (forward-char) (end-of-line) (point)))))
	
	(add-hook 'before-change-functions
		  'redit-client-before-change-function t t)
	(add-hook 'after-change-functions
		  'redit-client-after-change-function t t))))

;; currentp の位置の行数を返す
(defun real-count-lines (currentp)
  (+ (count-lines (point-min) currentp)
     (if (= (save-excursion (goto-char currentp) (current-column))
	    0)
	 1  0)))

(defun limited-save-excursion (&rest exprs)
  (let ((saved-point (point)))
    (while exprs
      (eval (car exprs))
      (setq exprs (cdr exprs)))
    (goto-char saved-point)))

;; before-change-functions に hook される
;; バッファに変更があるたびに呼ばれる。バッファが変更される前に呼ばれる。
;; begin と end には変更前の変更部分の始まりと終わりの point が入る
(defun redit-client-before-change-function (begin end)
  (let ((beginl (real-count-lines begin)) ;; begin の行番号
	(endl (real-count-lines end))	  ;; end の行番号
	(currline))			  ;; currline = nil

    ;; (message "before-change-function")

    ;; 前回の編集した行と今回の編集した行が
    ;; 違っていたら前回の編集をサーバへ送る
    (if (not (= beginl redit-client-previous-edited-line))
	(redit-client-insert-line redit-client-previous-edited-line nil))

    ;; 削除処理で起こる。
    ;; (ex. BackSpaceによる行そのものの削除, C-Space C-wなどのカット, ...)
    (if (< beginl endl)
	(progn
	  (setq currline endl)
	  ;; endlからbeginlまで行の削除命令を出す
	  (while (> currline beginl) 
	    (redit-client-delete-line endl)
	    (setq currline (- currline 1)))))))

;; after-change-functions に hook される
;; バッファに変更があるたびに呼ばれる。バッファが変更された後に呼ばれる
;; begin と end には変更後の変更部分の始まりと終わりの point が入る
(defun redit-client-after-change-function (begin end length)
  (let ((beginl (real-count-lines begin))
	(endl (real-count-lines end))
	(currline))

    ;; (message "after-change-function")

    ;; 挿入処理で起こる。
    ;; (ex. 改行, C-y などのペースト, ...)
    (if (< beginl endl)
	(progn
	  (setq currline beginl)
	  ;; beginlからendlまで挿入命令を出す
	  (while (<= currline endl)
	    (progn (redit-client-insert-line currline nil)
		   (setq currline (+ currline 1))))))
    (setq redit-client-previous-edited-line endl)))

;; 引き数で与えられた string (line_num + text_data) から
;; 指定された行を削除し、そこに text_data を挿入する
(defun redit-client-exec-insert-line (string)
  (let ((linenum (rep-get-line-number-from-pkt string)) ;; 行番号
	(text (rep-get-text-from-pkt string))) ;; テキストデータ
    (if (< (real-count-lines (point-max)) linenum)
	(progn
	  (call-interactively 'end-of-buffer) (newline)))
    (goto-line linenum)
    (delete-region (progn (beginning-of-line) (point)) ;; 行の頭から
		   (progn (end-of-line) (point))) ;; 行の末尾まで削除
    (insert text) ;; テキストデータを挿入
    (message (concat "recv = " text ", line = " (number-to-string linenum)))
    ))

;; 引き数 string (line_num + text_data) で指定された行を削除する
(defun redit-client-exec-delete-line (string)
  (let ((linenum (rep-get-line-number-from-pkt string)))
    (goto-line linenum)
    ;; 行頭から末尾までのテキストを削除
    (delete-region (progn (beginning-of-line) (point))
		   (progn (end-of-line) (point)))
    ;; 指定された行自体を削除
    ;; 指定された行番号は別の行の番号を表すことになる
    (if (= (point) (point-max))
	(delete-backward-char 1)
      (delete-char 1)))
  )

;; 引き数 string (buf_num + line_num + text_data) 
(defun redit-client-exec-open-ack (string)
  (save-excursion
    (let ((bufnum (rep-get-editor-id-from-pkt string))
	  (linenum (rep-get-line-number-from-pkt string)))
      (make-variable-buffer-local 'redit-client-buffer-name)
      (make-variable-buffer-local 'redit-client-editor-id)
      (setq redit-client-buffer-name
	    (rep-get-text-from-pkt string))
      (setq redit-client-buffer (get-buffer-create redit-client-buffer-name))
      ;; プロセスとバッファを関連づける
      (set-process-buffer redit-client-process redit-client-buffer)
      (switch-to-buffer redit-client-buffer) ;; windowのバッファを変更する
      (make-variable-buffer-local 'redit-client-previous-edited-line)
      (make-variable-buffer-local 'redit-client-after-edited-line)
      (make-variable-buffer-local 'redit-client-line-max-in-server)
      (make-variable-buffer-local 'redit-client-previous-line)
      (setq redit-client-line-max-in-server linenum)
      (setq redit-client-editor-id bufnum)
      (setq redit-client-previous-edited-line 1)
      (setq redit-client-after-edited-line 1)
      (setq redit-client-previous-line 1)

      ;;(add-hook 'before-change-functions
      ;;'redit-client-before-change-function t t)
      ;;(add-hook 'after-change-functions
      ;;'redit-client-after-change-function t t)

      ;; not necessary since Emacs 21.1
      ;; (make-local-hook 'window-scroll-functions)

      ;; window がスクロールする度に呼ばれる関数群
      (add-hook 'window-scroll-functions
		'redit-client-update-function t t))))

;; string (line_num + text_data) で指定された行に
;; text_data を挿入する
(defun redit-client-exec-read-ack (string)
  (save-excursion
    (let ((lines (rep-get-line-number-from-pkt string)))
      ;; redit-client-buffer をカレントバッファにする
      (set-buffer redit-client-buffer)
      (goto-line lines)
      (beginning-of-line) ;; 行の始めへ移動
      ;;      (delete-region (progn (beginning-of-line) (point))
      ;; 		     (progn (end-of-line) (point)))
      (insert (rep-get-text-from-pkt string))))) ;; テキストデータを挿入

;; redit-client-line-max-in-server に行番号を入れてる(だけ)
(defun redit-client-exec-insert-ack (string)
  (save-excursion
    (set-buffer redit-client-buffer)
    (let ((linenum (rep-get-line-number-from-pkt string )))
      (setq redit-client-line-max-in-server linenum))))

(defun redit-client-exec-delete-line-ack (string)
  (save-excursion
    (set-buffer redit-client-buffer)
    (let ((linenum (rep-get-line-number-from-pkt string)))
      (setq redit-client-line-max-in-server linenum))))

;; プロセスとバッファ、ローカル変数を削除する
(defun redit-client-exec-close-ack (string)
  (if redit-client-process
      (kill-process redit-client-process))
  (if redit-client-buffer
      (kill-buffer redit-client-buffer))
  (kill-all-local-variables))

;; 引き数で与えられた string から Editor IDを取得する
(defun redit-client-exec-join (string)
  (setq redit-client-editor-id (rep-get-editor-id-from-pkt string))
  (message (concat "your editor id = " (number-to-string redit-client-editor-id) "\n")))

;; //引き数で与えられた string から Session IDを取得する
;; 新仕様で、(put = ファイル有りjoin) ってことになって
;; editor id を返すことになったので、sid と共に eid もセーブする
(defun redit-client-exec-put (string)
  (setq redit-client-editor-id (rep-get-editor-id-from-pkt string))
  (setq redit-client-session-id (rep-get-session-id-from-pkt string))
  (message (concat "Your Editor ID = " (number-to-string redit-client-editor-id) "\n"))
  (message (concat "Session ID = " (number-to-string redit-client-session-id) "\n")))

;; SessionIDを名前とした、新しいバッファを開く
;; FIXME
;;   SessionIDではなく、Session Nameが望ましい?
;;   バッファを開いた後、Sessionで開いてるファイルの中身を表示するべき
(defun redit-client-exec-select ()
  (switch-to-buffer (get-buffer-create
		     (number-to-string redit-client-session-id))))

;; プロセスの状態を見て、対応したメッセージを表示
(defun redit-client-sentinel (proc msg)
  (cond ((eq (process-status proc) 'exit)
	 (message "Client subprocess exited"))
	((eq (process-status proc) 'signal)
	 (message "Client subprocess killed"))))

(defun gen-seqno ()
  "generate editor local sequence number."
  (setq redit-client-seqno (+ redit-client-seqno 1)))

(defun rep-get-command-from-pkt (pkt)
  (redit-unpack-int pkt 1))
(defun rep-get-session-id-from-pkt (pkt)
  (redit-unpack-int pkt 2))
(defun rep-get-editor-id-from-pkt (pkt)
  (redit-unpack-int pkt 3))
(defun rep-get-sequence-id-from-pkt (pkt)
  (redit-unpack-int pkt 4))
(defun rep-get-line-number-from-pkt (pkt)
  (redit-unpack-int pkt 5))
(defun rep-get-text-size-from-pkt (pkt)
  (redit-unpack-int pkt 6))
(defun rep-get-text-from-pkt (pkt)
  (let ((size (rep-get-text-size-from-pkt pkt)))
  (substring (decode-coding-string pkt 'euc-jp) 27 (+ 27 size))))

;; enqueue item in queue
;;  (defun enqueue (queue item)
;;    (let ((new-cell (list item)))
;;      (if (Queue-front queue)
;;  	;; 最終セルを書き換える
;;  	(setf (cdr (Queue-rear queue)) new-cell)
;;        ;; キューは空の状態
;;        (setf (Queue-front queue) new-cell))
;;      (setf (Queue-rear queue) new-cell)))

;; deque last queue
;;  (defun dequeue (queue)
;;    (if (Queue-front queue)
;;        (prog1
;;  	  (pop (Queue-front queue))
;;  	(unless (Queue-front queue)
;;  	  ;; キューは空になった
;;  	  (setf (Queue-rear queue) nil)))))

;; 
;;      USER
;;     |  i   |  r   |  d
;;  ---|--------------------
;; T  i | 0\+1 | 0\+1 | 0\+1
;; O ---|--------------------
;; K  r | +1\0 | 0\X  | i\X
;; E ---|--------------------
;; N  d | +1\0 | X\i  | X\X
;; 
(defun dequeue-all (queue)
  "clean queue"
  (while (Queue-front queue)
    (dequeue queue)))

(defun redit-line-translate-out (cque rque)
  "redit line translate for output"
  (let ((cc) (rc) (xcc) (xrc) (ccc) (crc) (cignore 0) (rignore 0))
    (setq xcc 0)
    (setq xrc 0)
    (setq cignore 0)
    (setq rignore 0)
    (while cque
      (setq cc (car cque))
      (while rque
	(setq rc (car rque))
	;; -------- translation ------------

	(if (< (+ (redit-get-line-from-queue cc) xcc) (+ (redit-get-line-from-queue rc) xrc))
	    (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (- xrc 1)))
	  (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq xrc (- xrc 1))))

	(if (> (+ (redit-get-line-from-queue cc) xcc) (+ (redit-get-line-from-queue rc) xrc))
	    (if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	  (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq xcc (- xcc 1))))

	(if (= (+ (redit-get-line-from-queue cc) xcc) (+ (redit-get-line-from-queue rc) xrc))
	    (if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	  (if (= (redit-get-command-from-queue rc) redit-replace-command)
	      (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (+ xrc 1)))
	    (if (= (redit-get-command-from-queue cc) redit-replace-command) (setq cignore 1))
	    (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq crc redit-insert-command) (setq cignore 1))))

	(if (= (redit-get-command-from-queue rc) redit-delete-line-command)
	    (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (+ (redit-get-line-from-queue rc) 1)))
	  (if (= (redit-get-command-from-queue cc) redit-replace-command) (setq cignore 1))
	  (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq cignore 1) (setq rignore 1)))

	;; -------- translation ------------

	(setq rque (cdr rque)))	;; while rque

      ;; ignore
      (if (= cignore 1)
	  ;; xxx
	  (setq cignore 0))
      (if (= rignore 1)
	  ;; xxx
	  (setq cignore 0))

      ;; -------- add after que ------------
      (enqueue after-CQ (concat (format "%2d%2d%9d" (redit-get-command-from-queue cc) (redit-get-uid-from-queue cc) (+ (redit-get-line-from-queue cc) xcc)) (redit-get-text-from-queue cc)))
      ;; -------- add after que ------------

      (setq cque (cdr cque))) ;; while cque
    )			      ;; let
  )			      ;; defun

(defun redit-line-translate-in (cque rque)
  "redit line translate for input"
  (let ((cc) (rc) (xcc) (xrc) (ccc) (crc) (cignore 0) (rignore 0))
    (setq xcc 0)
    (setq xrc 0)
    (setq cignore 0)
    (setq rignore 0)
    (while rque
      (setq rc (car rque))
      (setq cignore 0)
      (setq rignore 0)
      (while cque
	(setq cc (car cque))
	;; -------- translation ------------

	(if (and (= cignore 1) (= rignore 1))
	    (if (< (+ (redit-get-line-from-queue rc) xrc) (+ (redit-get-line-from-queue cc) xcc))
		(if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	      (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq xcc (- xcc 1))))

	  (if (> (+ (redit-get-line-from-queue rc) xrc) (+ (redit-get-line-from-queue cc) xcc))
	      (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (- xrc 1)))
	    (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq xrc (+ xrc 1))))

	  (if (= (+ (redit-get-line-from-queue rc) xrc) (+ (redit-get-line-from-queue cc) xcc))
	      (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xcc (+ xcc 1)))
	    (if (= (redit-get-command-from-queue cc) redit-replace-command)
		(if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	      (if (= (redit-get-command-from-queue rc) redit-replace-command) (setq cignore 1))
	      (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq crc redit-insert-command) (setq cignore 1))))
	  (if (= (redit-get-command-from-queue cc) redit-delete-line-command)
	      (if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ (redit-get-line-from-queue cc) 1)))
	    (if (= (redit-get-command-from-queue rc) redit-replace-command) (setq crc redit-insert-command) (setq cignore 1))
	    (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq cignore 1) (setq rignore 1))))

	;; -------- translation ------------

	(setq cque (cdr cque)))	;; while rque

      ;; ignore
      (if (= cignore 1)
	  ;; xxx
	  (setq cignore 0))
      (if (= rignore 1)
	  ;; xxx
	  (setq rignore 0))

      ;; -------- add after que ------------
      (enqueue after-RQ (concat (format "%2d%2d%9d" (redit-get-command-from-queue rc) (redit-get-uid-from-queue rc) (+ (redit-get-line-from-queue rc) xrc)) (redit-get-text-from-queue rc)))
      ;; -------- add after que ------------

      (setq rque (cdr rque))) ;; while cque
    )			      ;; let
  )			      ;; defun

;; for debug
(defun redit-client-print-command-queue ()
  "Print command queue."
  (interactive)
  ;;(message redit-client-command-queue))
  (let ((queue redit-client-command-queue))
    (while (Queue-front queue)
      (message (Queue-front queue))
      (setq queue (cdr (Queue-rear queue)))
      )
))

(defun redit-client-print-recive-queue ()
  "Print command queue."
  (interactive)
  (print redit-client-receive-queue))

(defun redit-client-print-send-queue ()
  "Print command queue."
  (interactive)
  (print redit-client-send-queue))

(defun redit-get-command-from-queue (queue)
  (nth 0 (Queue-front queue)))
(defun redit-get-line-from-queue (queue)
  (nth 1 (Queue-front queue)))
(defun redit-get-uid-from-queue (queue)
  (nth 2 (Queue-front queue)))
(defun redit-get-text-from-queue (queue)
  (nth 3 (Queue-front queue)))