changeset 57:91236a2c6e2d

*** empty log message ***
author gongo
date Wed, 27 Aug 2008 16:06:20 +0900
parents 3bbcdd5060a6
children 4d087b06929a
files ChangeLog redit-client-sm.el
diffstat 2 files changed, 328 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Aug 26 20:45:22 2008 +0900
+++ b/ChangeLog	Wed Aug 27 16:06:20 2008 +0900
@@ -1,3 +1,232 @@
+2008-08-27  Wataru MIYAGUNI  <gongo@cr.ie.u-ryukyu.ac.jp>
+
+	* redit-client-sm.el (redit-client-after-change-function): memo
+	insert/delete 発行判別の説明
+
+	バッファで編集が行われたとき、次の二つの hook が反応します。
+
+	- before-change-functions
+	  バッファが変更される『直前』に呼ばれる。
+	  引数には変更される予定の範囲の begin point と end point を受け取る。
+
+	- after-change-functions
+	  バッファが変更された『直後』に呼ばれる。
+	  引数には変更された範囲の begin point と end point 、
+	  そして二つの間にあった文字列の長さ length を受け取る。
+
+	例えば
+
+	  1: hoge (n: は n行目 を表す)
+
+	というバッファがあったとする。そこで、hoge の後ろで改行を入れてみると
+
+	  1: hoge
+	  2: 
+
+	となる。この時の before, after が受け取る begin と end の行数は
+
+	  before: begin=1 end=1
+	  after : begin=1 end=2
+
+	また、hoge の後ろに文字を追加すると
+
+	  1: hogea
+
+	この時は以下のようになる。
+
+	  before: begin=1 end=1
+	  after : begin=1 end=1
+
+	次に、以下のようなバッファがあるとする
+
+	  1: huge
+	  2: sanma
+
+	この時、2行目の先頭で backspace (DEL) をすると
+
+	  1: hugesanma
+
+	となる。この時の begin/end は以下のようになる。
+
+	  before: begin=1 end=2
+	  after : begin=1 end=1
+
+	以上の動作を複数行でやってみる。
+
+	  1: a
+	  2: b
+	  3: c
+
+	というバッファで、1〜3を C-Space, M-w, C-y というカット&ペーストで
+	3行目の c の直後で行う。
+
+	  1: a
+	  2: b
+	  3: ca
+	  4: b
+	  5: c
+
+	  before: begin=3 end=3
+	  after : begin=3 end=5
+	  before: begin=3 end=5
+	  after : begin=3 end=5
+	  before: begin=5 end=5
+	  after : begin=5 end=5
+
+	C-y (yank) を行うと、何故か 3回呼ばれる。(要ソース読み)
+	次に
+
+	  1: a
+	  2: b
+	  3: ca
+	  4: b
+	  5: c
+
+	というバッファがあるとして、5行目 から 2行目のbの直後までを
+	C-Space,C-W でカットする。すると
+
+	  1: a
+	  2: b
+
+	  before: begin=2 end=5
+	  after : begin=2 end=2
+
+	となる。
+
+	以上のことをふまえて、以下の法則を見つけた(気がする)
+
+	if (before-begin == after-end) {
+	    (before-begin == before-end) {
+	        if (previous-edited-line == before-end) {
+	            // 前回と違う行を編集しているので
+	            // previous-edited-line を insert
+                    // before-end を次の previous-edited-line にする。
+	        } else {
+     	            // 同じ行での編集なので何もしない
+	        }
+	    } else {
+	        // before-begin < line <= before-end を delete (改行コードも)
+	        // before-begin を insert
+	        // #1
+	    }
+	} else {
+	    // 1. 編集前と後でバッファの最終行が変更されていれば、
+	    //    その差だけ改行を insert で before-begin に送る
+	    // #2
+
+	
+	    // 2. 1. で得られた差の数値を、
+	    //     previous-edited-line が before-begin より下であれば
+	    //     previous-edited-line に 加える。
+	    // #3
+	
+	    // 3. before-begin > previous-edited-line
+	    //    && after-end < previous-edited-line
+	    //    の時、previous-edited-line を insert
+	
+	    // 4. before-begin <= line <= after-end までを insert
+	}
+
+	- #1
+	before-begin を delete でない理由ですが、
+	例えばこんな感じ。まあ上にもあるけど
+
+	  1: a
+	  2: b
+
+	ってあって、b の先頭で DEL すると
+
+	  1: ab
+
+	になる。このとき、before-end である 2 行目は削除されるわけですが
+	before-begin である 1 行目は 変更されている。
+	なので、before-begin だけは insert を行っているというわけ。
+
+	  1:
+	  2:
+
+	で、2行目で DEL だと
+
+	  1:
+
+	になるわけで、この場合は変更してないので送る必要はないが、
+	そこまで判別するのもアレなので、今は同じく送るようにしている。
+	実害ないし。無駄なのが送られるってのはアレだが。
+	
+	- #2
+	改行を入れる必要性について。
+	例えば
+
+	  1: a
+	  2: b
+	  3: c
+	  4: d
+
+	ってあったとき、1〜3をカットし、3の直後にペースとしたとすると
+
+	  1: a
+	  2: b
+	  3: ca
+	  4: b
+	  5: c
+	  6: d
+
+	ってなるのが理想的なわけです。しかし、改行を入れてないと
+
+	  1: a
+	  2: b
+	  3: ca
+	  4: b
+	  5: c
+
+	と、もともと 4 行目にあった d が上書きされてしまう。
+	exec-insert-line では、バッファ範囲外の line number に
+	insert 命令が来ると、自動的にその行まで改行を入れるように
+	記述してあります。なので、この場合だと、変更があったのは 3〜5なので
+	相手側では 5 行目までは行が確保されるわけ。しかし、insert そのものは、
+	line number の文字列を消去して書き直す、
+	上書きみたいなものなので、こんなことがおこったと。
+	ですから、予め改行しておいて下の行をずらしておき、
+	書く予定の行を確保しておく。
+	イメージ的には、改行いれた後は以下の感じ。
+	最終行が 4 から 6 に変更されてるので、受け取り側には
+	改行を 2 個送って、そのあと変更部分 (3〜5) を送る
+
+	改行のみ
+	  1: a
+	  2: b
+	  3: c
+	  4: 
+	  5: 
+	  6: d
+	insert
+	  1: a
+	  2: b
+	  3: ca
+	  4: b
+	  5: c
+	  6: d
+
+	#3
+	差の分を previous-edited-line に加えているのは、
+	改行によって行数がずれているからです。
+	before-begin より上の行であれば影響されないので無視。
+
+	=======
+
+	以上。
+	これでいいんじゃないかと思いました。
+	現在この感じで実装していますが、今のところちゃんと動いてます(きっと)
+	yank で before/after が 3回も呼ばれているのは謎です。
+	ちらっとソース読んでみたんですが。。。。ふーんみたいな(何
+	今のままだと無駄に3回 insert が呼ばれてしまうので
+	optimize 的な意味では、ちゃんと yank を理解すべき。
+	今は、正確に適切な行が insert/delete されているかを
+	確かめる状況なので、ひとまず置いておく。
+
+	上の条件式で、「もっとコンパクトにできる」「意味不明」ってのがあるかも
+	誰かヘルプみー
+	
 2008-08-26  Wataru MIYAGUNI  <gongo@cr.ie.u-ryukyu.ac.jp>
 
 	* redit-client-sm.el (redit-client-delete-line): TODO
--- a/redit-client-sm.el	Tue Aug 26 20:45:22 2008 +0900
+++ b/redit-client-sm.el	Wed Aug 27 16:06:20 2008 +0900
@@ -50,6 +50,9 @@
 (defvar redit-client-previous-edited-line 1
   "Current cursor line of remote-edit client.")
 
+(defvar redit-client-previous-max-line 1
+  "The max line in the current buffer")
+
 (defvar redit-client-after-edited-line 1
   "Current cursor line of remote-edit client.")
 
@@ -70,13 +73,13 @@
 (defconst redit-process-port 8766
   "Default port that REP use")
 
-(defvar redit-client-begin-line-before-func nil
+(defvar redit-client-before-begin-line nil
   "Begin edited line of redit-client-before-change-function")
-(defvar redit-client-end-line-before-func nil
+(defvar redit-client-before-end-line nil
   "End edited line of redit-client-before-change-function")
-(defvar redit-client-begin-line-after-func nil
+(defvar redit-client-after-begin-line nil
   "Begin edited line of redit-client-after-change-function")
-(defvar redit-client-end-line-after-func nil
+(defvar redit-client-after-end-line nil
   "End edited line of redit-client-after-change-function")
 
 
@@ -268,8 +271,10 @@
 (defun redit-get-text-from-pkt (pkt &optional _siz)
   (let ((size) (text) (offset redit-command-offset-txt))
     (setq size (if (null _siz) (redit-get-text-size-from-pkt pkt) _siz))
-    (setq text (substring pkt offset (+ offset size)))
-    (decode-coding-string text redit-string-coding)))
+    (if (= size 0) ""
+      (progn
+	(setq text (substring pkt offset (+ offset size)))
+	(decode-coding-string text redit-string-coding)))))
 
 ;;;;;;;;;;;;;;;;;;
 ;; User Command ;;
@@ -370,18 +375,24 @@
 ;; linenumで指定された行の編集をサーバへ送る
 ;; redit-client-process に insert コマンドと
 ;; バッファ番号、行番号、行の内容を送り、 Ack を受け取る
-(defun redit-client-insert-line (linenum isnewline)
+;; _text が指定されている場合、バッファのlinenumの文字列ではなく
+;; _text で指定された文字列を送信する
+(defun redit-client-insert-line (linenum isnewline &optional _text)
   (if redit-client-process
       (save-excursion
 	(let ((beginp) (endp) (text) (text-size) (packet) (sinfo))
-	  (setq beginp
-		(progn (goto-line linenum) (beginning-of-line) (point)))
-	  (setq endp
-		(progn (goto-line linenum) (end-of-line) (point)))
-	  (setq text
-		(concat (encode-coding-string
-			 (buffer-substring beginp endp) redit-string-coding)
-			(if (eq isnewline t) "\n")))
+	  (if (null _text)
+	      (progn
+		(setq beginp
+		      (progn (goto-line linenum) (beginning-of-line) (point)))
+		(setq endp
+		      (progn (goto-line linenum) (end-of-line) (point)))
+		(setq text
+		      (concat (encode-coding-string
+			       (buffer-substring beginp endp)
+			       redit-string-coding)
+			      (if (eq isnewline t) "\n"))))
+	    (setq text _text))
 	  (setq text-size (string-bytes text))
 
 	  (setq sinfo (redit-sinfo-get-buf2sinfo (buffer-name)))
@@ -415,8 +426,12 @@
 			redit-client-editor-id (gen-seqno) linenum
 			5 "55555"))
 	  
-	  (process-send-string redit-client-process packet)))
-	(error "redit-client is not running.")))
+	  (process-send-string redit-client-process packet))
+	(message (format
+		  "delete-line-command: %d %d %d %d %d %d %s"
+		  redit-delete-line-command redit-client-session-id
+		  redit-client-editor-id 0 linenum 5 "55555")))
+    (error "redit-client is not running.")))
 
 ;; redit-client-process へcloseコマンドを送る
 (defun redit-client-close ()
@@ -455,8 +470,6 @@
 	       'redit-client-before-change-function t)
   (remove-hook 'after-change-functions
 	       'redit-client-after-change-function t)
-
-  (message (format "redit-client-process-filter: %s" string))
     
   (let (command textsize text allsize cursize)
     ;; 前回に余った奴があれば、それを前方追加する
@@ -589,39 +602,67 @@
 ;; begin と end には変更前の変更部分の始まりと終わりの point が入る
 (defun redit-client-before-change-function (begin end)
   ;; (message "before-change-function")
-  (setq redit-client-begin-line-before-func (real-count-lines begin))
-  (setq redit-client-end-line-before-func (real-count-lines end)))
+  (setq redit-client-before-begin-line (real-count-lines begin))
+  (setq redit-client-before-end-line (real-count-lines end))
+  (setq redit-client-previous-max-line (real-count-lines (point-max)))
+  )
 
 ;; after-change-functions に hook される
 ;; バッファが変更された直後に呼ばれる
 ;; begin と end には変更後の変更部分の始まりと終わりの point が入る
 (defun redit-client-after-change-function (begin end length)
   ;; (message "after-change-function")
-  (let (endl)
-    (setq redit-client-begin-line-after-func (real-count-lines begin))
-    (setq redit-client-end-line-after-func (real-count-lines end))
+  (let ((endl) (cur-max-line) (max-line-diff) (prev-edit-line))
+    (setq redit-client-after-begin-line (real-count-lines begin))
+    (setq redit-client-after-end-line (real-count-lines end))
+    (setq cur-max-line (real-count-lines (point-max)))
+    (setq prev-edit-line redit-client-previous-edited-line)
 
-    (if (and 
-	 (= redit-client-begin-line-before-func
-	    redit-client-end-line-after-func)
-	 (not (= redit-client-begin-line-before-func
-		 redit-client-end-line-before-func)))
+    ;; 詳しくは ChangeLog [2008-08-27] を見て
+    (if (= redit-client-before-begin-line
+	   redit-client-after-end-line)
+	;; 同じ行での編集
+	(if (= redit-client-before-begin-line
+	       redit-client-before-end-line)
+	    ;; 前回編集した所と違う行を編集していれば、
+	    ;; 前回編集した行を insert する
+	    (if (not (= redit-client-after-end-line
+			redit-client-previous-edited-line))
+		(redit-client-insert-line
+		 redit-client-previous-edited-line nil))
+	  (progn
+	    ;; 行の削除が行われた
+	    (setq endl redit-client-before-end-line)
+	    (while (> endl redit-client-before-begin-line)
+	      (redit-client-delete-line endl)
+	      (setq endl (1- endl)))
+	    (redit-client-insert-line
+	     redit-client-before-begin-line nil)))
       (progn
-	;; delete
-	(setq endl redit-client-end-line-before-func)
-	(while (> endl redit-client-begin-line-before-func)
-	  (redit-client-delete-line endl)
-	  (setq endl (1- endl)))
-	(redit-client-insert-line redit-client-begin-line-before-func nil))
-      (progn
-	;; insert
-	(setq endl redit-client-end-line-after-func)
-	(while (>= endl redit-client-begin-line-before-func)
+	;; 行の追加が行われた
+
+	;; 改行の追加
+	(setq max-line-diff (- cur-max-line redit-client-previous-max-line))
+	(if (> max-line-diff 0)
+	    (progn
+	      (if (> prev-edit-line redit-client-before-begin-line)
+		  (setq prev-edit-line (+ prev-edit-line max-line-diff)))
+	      (let (line-feed)
+		(setq line-feed (make-string max-line-diff ?\n))
+		(redit-client-insert-line redit-client-before-begin-line
+					  nil line-feed))))
+	(if (not (is-value-in-range redit-client-before-begin-line
+				    prev-edit-line
+				    redit-client-after-end-line))
+	    (redit-client-insert-line prev-edit-line nil)
+	  )
+	(setq endl redit-client-after-end-line)
+	(while (>= endl redit-client-before-begin-line)
 	  (redit-client-insert-line endl nil)
 	  (setq endl (1- endl))))
       )
-
-    (setq redit-client-previous-edited-line endl)))
+    
+    (setq redit-client-previous-edited-line redit-client-after-end-line)))
 
 ;; 引き数で与えられた string (line_num + text_data) から
 ;; 指定された行を削除し、そこに text_data を挿入する
@@ -661,17 +702,20 @@
 
 ;; 引き数 string (line_num + text_data) で指定された行を削除する
 (defun redit-client-exec-delete-line (string)
-  (let ((linenum (redit-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)))
-  )
+  (let ((diff) (linenum (redit-get-line-number-from-pkt string)))
+    (setq diff (goto-line linenum))
+    (if (= diff 0)
+	(progn
+	  ;; 行頭から末尾までのテキストを削除
+	  (delete-region (progn (beginning-of-line) (point))
+			 (progn (end-of-line) (point)))
+	  ;; 指定された行自体を削除
+	  ;; 指定された行番号は別の行の番号を表すことになる
+	  (if (= (point) (point-max))
+	      (delete-backward-char 1)
+	    (delete-char 1)))
+      )
+  ))
 
 ;; 何用?相手が開いてるファイルを取得するのかな?
 ;; read みたいなもん?
@@ -949,35 +993,6 @@
 ;;  (print redit-client-send-queue))
 ;;
 
-;;
-;; insert-line-to-buffer.el
-;;   指定したバッファの、指定した行に、指定した文字列を挿入。
-;;   バッファの最後尾行が指定した行に足りない場合、その数だけ改行する
-;;
-;; buffer : 編集するバッファ(名)
-;; lineno : 編集するbufの行番号
-;; string : lineno行目に挿入する文字列
-(defun insert-line-to-buffer (buffer lineno string)
-  "Insert STRING at line LINENO of BUFFER.
-The line LINENO is deleted, and STRING is inserted."
-  (let (curlineno)
-    (save-excursion
-      (set-buffer (get-buffer-create buffer))
-      (goto-line lineno) ;; 指定行番号へ移動
-      (setq curlineno (real-count-lines (point))) ;; 現在の行番号
-
-      (if (> lineno curlineno)
-	  ;; buffer の 最後の行番号が、指定した lineno に足りない場合、
-	  ;; その行数だけ改行し、その行へ移動する。
-	  ;; newline なので、下のようにテキストを削除する必要は無い
-	  (progn (end-of-line)
-		 (newline (- lineno curlineno))
-		 (goto-line lineno))
-	
-	;; 行頭から末尾までのテキストを削除
-	(delete-region (progn (beginning-of-line) (point))
-		       (progn (end-of-line) (point)))
-	)
-
-      ;; 新しい行を挿入
-      (insert string))))
+(defun is-value-in-range (min value max)
+  (if (and (<= min value) (<= value max))
+      t nil))
\ No newline at end of file