branch: externals/cm-mode
commit cb81f822e9ecce931bb489c99ed3eb9de6f15981
Author: Joost Kremers <[email protected]>
Commit: Joost Kremers <[email protected]>

    Recognize and honour author tags in additions and deletions.
---
 README.md  |  6 +++++-
 cm-mode.el | 57 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/README.md b/README.md
index 3c4b16f0ac..96afeb78f6 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,8 @@ Activating `cm-mode` provides key bindings to insert these 
markup tags and thus
 
 The commands to delete or substitute text operate on the region. The command 
to insert a comment can be used with an active region, in which case the text 
in the region will be highlighted. It can also be used inside an existing 
markup to add a comment to it. If it is used anywhere else, it just adds a lone 
comment. The commands for inserting and substituting text and for inserting a 
comment all put point at the correct position, so you can start typing right 
away.
 
+The commands for adding and deleting text combine additions/deletions that are 
adjacent: if you make a new addition next to an existing one, the cursor is 
simply moved into the addition tag. Similarly, if you delete text adjacent to 
an existing deletion, the deleted text is moved into the tag.
+
 Note: the [CriticMarkup spec](http://criticmarkup.com/spec.php) says you 
should avoid putting newlines in CriticMarkup tags and you should always wrap 
Markdown tags completely. These are wise precautions for `cm-mode` as well.
 
 ## Follow changes mode ##
@@ -30,7 +32,9 @@ Note: the [CriticMarkup 
spec](http://criticmarkup.com/spec.php) says you should
 
 Comments can be used to keep track of who made a particular change. If you 
want to do this automatically, you can set the variable `cm-author` to an 
identifier. When this variable is set, its value is automatically added as a 
comment to every change you make, preceded by `@`. If you explicitly make a 
comment with `C-c * c`, the value of `cm-author` is inserted at the beginning 
of the comment.
 
-The variable `cm-author` can be set globally through Customize (or with 
`setq-default` in your init file). This sets the global value. You can override 
this global value in a particular buffer by setting a buffer-local value. There 
are two ways to do this: you can use `C-c * C`, which will only set the value 
for the current session, or you can use a file-local (or directory-local) 
variable, which makes sure the value is set every time the file is loaded. 
(Note: if you use [Pandoc](http:/ [...]
+The variable `cm-author` can be set globally through Customize (or with 
`setq-default` in your init file). This sets the global value. You can override 
this global value in a particular buffer by setting a buffer-local value. There 
are two ways to do this: you can use `C-c * C`, which will only set the value 
for the current session, or you can use a file-local (or directory-local) 
variable, which makes sure the value is set every time the file is loaded. 
(Note: if you use [Pandoc](http:/ [...]
+
+If `cm-author` is set, a new addition or deletion that is adjacent to an 
existing one is not combined with it if it has a different author tag. This way 
you can add changes to a text that already has changes from another author and 
still keep track of who did what. Note that this *only* works for changes that 
have a comment with an author tag. If the existing addition/deletion does not 
have an author tag, any addition/deletion made adjacent to it is simply 
combined with it.
 
 
 ## Navigating changes ##
diff --git a/cm-mode.el b/cm-mode.el
index 7b7d689079..ba2e6ffccc 100644
--- a/cm-mode.el
+++ b/cm-mode.el
@@ -54,6 +54,7 @@
 ;; - key bindings to insert CriticMarkup.
 ;; - 'follow changes' mode: automatically record changes to the buffer.
 ;; - accept/reject changes interactively.
+;; - automatically add author tag.
 ;; - navigation to move between changes.
 ;;
 ;;
@@ -83,8 +84,6 @@
 ;; ----
 ;;
 ;; - Commands to accept or reject all changes in one go.
-;; - Do not combine two adjacent additions/deletions if the author is
-;;   different.
 ;; - Mouse support?
 
 ;;; Code:
@@ -346,10 +345,13 @@ combined with it, even if point is right outside it. 
(That avoids
 having two additions adjacent to each other.) If it is another
 kind of markup, and point is inside the curly braces, we make
 sure point is not in the delimiter before adding text."
-  (unless (or (cm-addition-p (cm-expand-change change))
-              (cm-point-inside-change-p change))
-    (cm-insert-markup 'cm-addition))
-  (cm-move-into-markup 'cm-addition))
+  (setq change (cm-expand-change change))
+  (if (or (cm-point-inside-change-p change)
+              (and (cm-addition-p change)
+                   (cm-has-current-author-p change)))
+      (cm-move-into-markup 'cm-addition)
+    (cm-insert-markup 'cm-addition)
+    (cm-move-into-markup 'cm-addition t)))
 
 (defun cm-make-deletion (text &optional backspace)
   "Reinsert TEXT into the buffer and add deletion markup if necessary.
@@ -359,10 +361,11 @@ If BACKSPACE is T, the deletion was done with the 
backspace key;
 point will then be left before the deletion markup."
   ;; TODO: we should check whether the text to be deleted contains part of
   ;; a change.
-  (let ((change (cm-markup-at-point)))
+  (let ((change (cm-expand-change (cm-markup-at-point))))
     (unless (cm-point-inside-change-p change)
       (save-excursion
-        (if (not (cm-deletion-p (cm-expand-change change)))
+        (if (not (and (cm-deletion-p change)
+                      (cm-has-current-author-p change)))
             (cm-insert-markup 'cm-deletion text)
           (cm-move-into-markup 'cm-deletion)
           (insert text)))
@@ -471,13 +474,16 @@ delimiter, do not move. Return T if point has moved."
                                len))))
     (/= pos (point))))
 
-(defun cm-move-into-markup (type)
+(defun cm-move-into-markup (type &optional backwards)
   "Make sure point is inside the delimiters of TYPE.
 Point is either moved forward if at an opening delimiter or
 backward if at a closing delimiter. When moving backward, point
 is moved past a comment if the change before the comment is of
-TYPE."
-  (unless (cm-move-past-delim (second (assq type cm-delimiters)))
+TYPE.
+
+If BACKWARDS is T, only try moving backwards."
+  (unless (and (not backwards)
+               (cm-move-past-delim (second (assq type cm-delimiters))))
     (if (and (not (eq type 'cm-comment))
              (cm-comment-p (cm-markup-at-point t)))
         (cm-forward-markup 'cm-comment -1))
@@ -631,6 +637,35 @@ outside of them. The latter counts as being AT a change."
        (> (point) (third change))
        (< (point) (fourth change))))
 
+(defun cm-extract-comment (change)
+  "Extract the comment from CHANGE."
+  (let ((bdelim (regexp-quote (second (assq 'cm-comment cm-delimiters))))
+        (edelim (regexp-quote (third (assq 'cm-comment cm-delimiters))))
+        (text (second change)))
+    (if (string-match (concat bdelim "\\(.*?\\)" edelim) text)
+        (match-string 1 text))))
+
+(defun cm-extract-author (change)
+  "Extract the author tag of CHANGE.
+The author tag should start with an `@' sign, should not contain
+any spaces and should be at the start of the comment part of
+CHANGE. The return value is the author tag without `@', or NIL if
+CHANGE has no comment part or a comment without an author."
+  (let ((comment (cm-extract-comment change)))
+    (if (and comment
+             (string-match "^@\\([^[:space:]]*\\).*?$" comment))
+        (match-string 1 comment))))
+
+(defun cm-has-current-author-p (change)
+  "Return T if the user is the author of CHANGE.
+The user is considered the author of CHANGE if the author tag of
+CHANGE matches `cm-author'; if CHANGE has no author; or if
+`cm-author' is NIL."
+  (let ((author (cm-extract-author change)))
+    (or (not cm-author)
+        (not author)
+        (string= author cm-author))))
+
 (defun cm-expand-change (change)
   "Expand CHANGE with a following comment or, if a comment, with a preceding 
change.
 If CHANGE is a comment, check if there's another change preceding

Reply via email to