Mercurial > hg > RemoteEditor > emacs
changeset 0:a897604a9280
Initial revision
author | yasumura |
---|---|
date | Mon, 28 Nov 2005 18:38:29 +0900 |
parents | |
children | 632716a4bb5a 2b212e5a0009 |
files | redit-client-sm.el |
diffstat | 1 files changed, 555 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redit-client-sm.el Mon Nov 28 18:38:29 2005 +0900 @@ -0,0 +1,555 @@ +; $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 "/home/dot/work/rep/emacs/edit/reditclient" + "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.") + +; 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-get-command "43") +(defvar redit-put-command "45") +(defvar redit-select-command "47") +(defvar redit-register-command "49") +(defvar redit-deregister-command "51") +(defvar redit-quit-command "53") + +(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" (string-to-number redit-join-command) 0 (gen-seqno) 0 0)))) + (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:")) + (process-send-string + redit-client-process + (concat (format "%10d%10d%10d%10d%10d" (string-to-number redit-select-command) (string-to-number redit-client-editor-id) (gen-seqno) 0 0) + session-name)) + (error "invalid session name.") + ) + ) + + (defun redit-put-command () + "put" + (interactive) + ) + + (defun redit-register-command (session-name) + "register" + (interactive "P") + (if (setq session-name (read-string "session name:")) + (process-send-string + redit-client-process + ; FIXME "%10d%10d%10d%10d%10d" ? + (concat (format "%10d%10d%10d%10d%10d" (string-to-number redit-register-command) (string-to-number redit-client-editor-id) (gen-seqno) 0 0) + session-name)) + (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" (string-to-number redit-read-command) (string-to-number 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" (string-to-number redit-write-command) (string-to-number redit-client-editor-id) (gen-seqno) linenum length) + (buffer-substring beginp endp) + (if (eq isnewline t) + "\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" (string-to-number redit-delete-line-command) (string-to-number redit-client-editor-id) (gen-seqno) linenum 0))) + (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" (string-to-number redit-close-command) (string-to-number redit-client-editor-id) (gen-seqno) linenum 0))))) + +; 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 (string-to-number (rep-get-command-from-pkt string)))) + ;command がどの命令かを判断し、対応した処理をする。case みたい + (cond + ; FIXME + ((string-equal command (string-to-number redit-open-ack)) + (if (not redit-client-editor-id) + (redit-client-exec-open-ack string))) + + ((string-equal command (string-to-number redit-read-ack)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-read-ack string))) + + ((string-equal command (string-to-number redit-write-still-ack)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-write-still-ack string))) + + ((string-equal command (string-to-number redit-delete-line-ack)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-delete-line-ack string))) + + ((string-equal command (string-to-number redit-close-ack)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-close-ack string))) + + ((string-equal command (string-to-number redit-write-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-write-line string))) + + ; from Session Manager + ; join(editor id) + ; editor idを保存する + ((string-equal command (string-to-number redit-join-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-command-from-pkt string))) + (redit-client-exec-join string))) + ; get + ((string-equal command (string-to-number redit-get-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-get string))) + ; put + ((string-equal command (string-to-number redit-put-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-put string))) + ; select + ((string-equal command (string-to-number redit-select-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-select string))) + ; register + ((string-equal command (string-to-number redit-register-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-register string))) + ; deregister + ((string-equal command (string-to-number redit-deregister-command)) + (if (= redit-client-editor-id + (string-to-number (rep-get-editor-id-from-pkt string))) + (redit-client-exec-deregister string))) + + ; delete line + ((string-equal command (string-to-number redit-delete-line-command)) + (if (= redit-client-editor-id + (string-to-number (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 + (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 + (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 (string-to-number (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 (string-to-number (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 (string-to-number (rep-get-editor-id-from-pkt string))) + (linenum (string-to-number (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 (string-to-number (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 (string-to-number (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 (string-to-number (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 から Session IDを取得する +(defun redit-client-exec-join (string) + (setq redit-client-editor-id (rep-get-editor-id-from-pkt string)) + ; FIXME : open another buffer? + (insert (rep-get-text-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-editor-id-from-pkt (pkt) + (string-to-number (substring pkt 10 20))) +(defun rep-get-sequence-id-from-pkt (pkt) + (string-to-number (substring pkt 20 30))) +(defun rep-get-line-number-from-pkt (pkt) + (string-to-number (substring pkt 30 40))) +(defun rep-get-text-size-from-pkt (pkt) + (string-to-number (substring pkt 40 50))) +(defun rep-get-text-from-pkt (pkt) + (substring pkt 50))