view redit-client-sm.el @ 3:52cb18a3c1e2

fix
author shinobu
date Tue, 08 Aug 2006 16:11:56 +0900
parents 2b212e5a0009
children ee15fce75335
line wrap: on
line source

;	$Id$
;  (defcustom redit-client-program "/usr/home/masasi/work/redit/reditclient"
;(defcustom redit-client-program "/usr/home/shinobu/redit/demo/reditclient"
 (defcustom redit-client-program "/Users/dot/work/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


(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.")

(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")
(defvar redit-write-command          "6") ; insert
(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")

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

(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)
	; emacs が終了すると redit-client-process も終了する
	(process-kill-without-query redit-client-process)
	; フック変数 before-change-functions, after-change-functions
	;をバッファローカルにする
	(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 (host)
  "Allow this Emacs process to be a remote-edit session manager 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: "))
      (let ((process-connection-type nil))
	(setq redit-client-process
	      (start-process "reditclient" nil redit-client-program host))
	(set-process-sentinel redit-client-process 'redit-client-sentinel)
	(set-process-filter redit-client-process 'redit-client-process-filter)
	(set-process-coding-system redit-client-process
				   'raw-text 'raw-text)
	(process-kill-without-query redit-client-process)
	; (make-local-hook 'before-change-functions)
	; (make-local-hook 'after-change-functions)
    ; FIXME
    ; concat my hostname this command?
    (process-send-string 
        redit-client-process
        (concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-join-command) 0 0 (gen-seqno) 0 0) "\n")))
    (error "redit-client connection refused.")))

 ; (redit-get-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
            (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.")
    )
  )

 ;(defun redit-put-command ()
 ; "put"
 ; (interactive)
 ; )
 (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 (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-put-command) redit-client-session-id redit-client-editor-id (gen-seqno) 0 0) session-name "\n"))
    (error "invalid session name.")
    )
  )

 (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.")
    )
  )

 (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" (string-to-number 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 にwriteコマンドとバッファ番号、行番号、行の内容を送り、
; Ack を受け取る
(defun redit-client-write-line (linenum isnewline)
  (if redit-client-process
      (save-excursion
		(let ((beginp) (endp) (length))
	  ; write lines on server buffer
		  (setq beginp
				(progn (goto-line linenum) (point)))
		  (setq endp
				(progn (goto-line linenum) (end-of-line) (point)))
		  (setq length (- endp beginp))
      ; redit-client-process へ command_num + buffer_num + line_num + string
	  ;の文字列を送る
		  (process-send-string 
		   redit-client-process
		   (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"))
		  ;(while (eq nil
		  ;			 (accept-process-output redit-client-process 1)))))
          ))
    (error "redit-client is not running.")))

; linenum で指定した行の削除命令を redit-client-process に送信する
(defun redit-client-delete-line (linenum)
  (if redit-client-process
      (save-restriction
	; write lines on server buffer
	(process-send-string
	 redit-client-process
	 (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"))
	;(while (eq nil
	;	   (accept-process-output redit-client-process 1))))
    )
    (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-write-line line nil)
    (process-send-string
     redit-client-process
     (concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-close-command) redit-client-session-id redit-client-editor-id (gen-seqno) linenum 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 も同じ
  (remove-hook 'before-change-functions
	       'redit-client-before-change-function t)
  (remove-hook 'after-change-functions
	       'redit-client-after-change-function t)
  ; (message string) ;; add
  (let ((command (rep-get-command-from-pkt string)))
    ;command がどの命令かを判断し、対応した処理をする。case みたい
    (cond 
     ; FIXME
     ((if (= command (string-to-number redit-open-ack))
      (if (/= redit-client-editor-id)
	  (redit-client-exec-open-ack string))))

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

     ((if (= command (string-to-number redit-write-still-ack))
      (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	  (redit-client-exec-write-still-ack string))))

     ((if (= command (string-to-number 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 (string-to-number redit-close-ack))
      (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	  (redit-client-exec-close-ack string))))

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

     ; from Session Manager

     ; join ack (editor id)
     ; editor id を保存する
     ((if (= command (string-to-number redit-join-ack-command))
	  (redit-client-exec-join string)))

     ; put ack (editor id)
     ; session id を保存する
     ((if (= command (string-to-number redit-put-ack-command))
	  (redit-client-exec-put string)))

     ; select ack
     ; TODO : start editing
     ((if (= command (string-to-number redit-select-ack-command))
        (progn 
            (add-hook 'before-change-functions
	                  'redit-client-before-change-function t t)
            (add-hook 'after-change-functions
	                  'redit-client-after-change-function t t))))

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

     ; delete line
     ((if (= command (string-to-number redit-delete-line-command))
      (if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	  (redit-server-exec-delete-line string))))
     ((string-equal 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)
  ; check delete
  ; (message "call redit-client-before-change-function")
  (let ((beginl (real-count-lines begin)) ; begin の行番号
	(endl (real-count-lines end))     ; end の行番号
	(currline)) ; currline = nil
    ;前回の編集した行と今回の編集した行が違っていたら前回の編集をサーバへ送る
    (if (not (= beginl redit-client-previous-edited-line))
	(redit-client-write-line redit-client-previous-edited-line nil))
    (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)
  ; check insert
  ; (message "call redit-client-after-change-function")
  (let ((beginl (real-count-lines begin))
	(endl (real-count-lines end))
	(currline))
    (if (< beginl endl)
	(progn
	  (setq currline beginl)
	  ; beginlからendlまで挿入命令を出す
	  (while (< currline endl)
	    (progn (redit-client-write-line beginl nil);t)
		   (setq currline (+ currline 1))))))
    (setq redit-client-previous-edited-line endl)))

; 引き数で与えられた string (line_num + text_data) から
; 指定された行を削除し、そこに text_data を挿入する
(defun redit-client-exec-write-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
	  (end-of-buffer)
	  (newline)))
    (goto-line linenum)
    (delete-region (progn (beginning-of-line) (point)); 行の頭から
		   (progn (end-of-line) (point)))     ; 行の末尾まで削除
    (insert text))) ; テキストデータを挿入

; 引き数 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-pk 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)
      (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-write-still-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))
  (insert (rep-get-text-from-pkt string)))

; 引き数で与えられた string から Session IDを取得する
(defun redit-client-exec-put (string)
  (setq redit-client-session-id (rep-get-session-id-from-pkt string)))

; プロセスの状態を見て、対応したメッセージを表示
(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)
    (string-to-number (substring pkt 0 10)))
(defun rep-get-session-id-from-pkt (pkt)
    (string-to-number (substring pkt 10 20)))
(defun rep-get-editor-id-from-pkt (pkt)
    (string-to-number (substring pkt 20 30)))
(defun rep-get-sequence-id-from-pkt (pkt)
    (string-to-number (substring pkt 30 40)))
(defun rep-get-line-number-from-pkt (pkt)
    (string-to-number (substring pkt 40 50)))
(defun rep-get-text-size-from-pkt (pkt)
    (string-to-number (substring pkt 50 60)))
(defun rep-get-text-from-pkt (pkt)
    (substring pkt 60))