[Bf-blender-cvs] [366865dd020] master: Undo System: replace with simpler binary diffing buffer storage

Campbell Barton noreply at git.blender.org
Wed Jul 17 13:22:21 CEST 2019


Commit: 366865dd020904116086e6d9ec46b8f70c42cdd1
Author: Campbell Barton
Date:   Thu Jul 11 15:25:52 2019 +1000
Branches: master
https://developer.blender.org/rB366865dd020904116086e6d9ec46b8f70c42cdd1

Undo System: replace with simpler binary diffing buffer storage

Applying/undoing incremental changes didn't fit well when
mixed with periodic snapshots from mem-file undo.

This moves to a much simpler undo system.

- Uses array storage with de-duplication from `BLI_array_store`.
- Loads the buffer into existing text data,
  for better performance on large files.
- Has the advantage that Python operators can be supported
  since we don't depend on hard coded undo operations.

Solves T67045, T66695, T65909.

===================================================================

M	source/blender/blenkernel/BKE_text.h
M	source/blender/blenkernel/intern/text.c
M	source/blender/editors/include/ED_text.h
M	source/blender/editors/interface/interface_ops.c
M	source/blender/editors/space_text/text_autocomplete.c
M	source/blender/editors/space_text/text_ops.c
M	source/blender/editors/space_text/text_undo.c
M	source/blender/makesdna/DNA_text_types.h
M	source/blender/makesrna/intern/rna_text_api.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_text.h b/source/blender/blenkernel/BKE_text.h
index c78faa9dd18..6509788932c 100644
--- a/source/blender/blenkernel/BKE_text.h
+++ b/source/blender/blenkernel/BKE_text.h
@@ -30,7 +30,6 @@ extern "C" {
 struct Main;
 struct Text;
 struct TextLine;
-struct TextUndoBuf;
 
 void BKE_text_free_lines(struct Text *text);
 void BKE_text_free(struct Text *text);
@@ -49,8 +48,8 @@ void BKE_text_copy_data(struct Main *bmain,
                         const int flag);
 struct Text *BKE_text_copy(struct Main *bmain, const struct Text *ta);
 void BKE_text_make_local(struct Main *bmain, struct Text *text, const bool lib_local);
-void BKE_text_clear(struct Text *text, struct TextUndoBuf *utxt);
-void BKE_text_write(struct Text *text, struct TextUndoBuf *utxt, const char *str);
+void BKE_text_clear(struct Text *text);
+void BKE_text_write(struct Text *text, const char *str);
 int BKE_text_file_modified_check(struct Text *text);
 void BKE_text_file_modified_ignore(struct Text *text);
 
@@ -77,29 +76,26 @@ void txt_move_eol(struct Text *text, const bool sel);
 void txt_move_toline(struct Text *text, unsigned int line, const bool sel);
 void txt_move_to(struct Text *text, unsigned int line, unsigned int ch, const bool sel);
 void txt_pop_sel(struct Text *text);
-void txt_delete_char(struct Text *text, struct TextUndoBuf *utxt);
-void txt_delete_word(struct Text *text, struct TextUndoBuf *utxt);
-void txt_delete_selected(struct Text *text, struct TextUndoBuf *utxt);
+void txt_delete_char(struct Text *text);
+void txt_delete_word(struct Text *text);
+void txt_delete_selected(struct Text *text);
 void txt_sel_all(struct Text *text);
 void txt_sel_clear(struct Text *text);
 void txt_sel_line(struct Text *text);
 char *txt_sel_to_buf(struct Text *text, int *r_buf_strlen);
-void txt_insert_buf(struct Text *text, struct TextUndoBuf *utxt, const char *in_buffer);
-void txt_undo_add_op(struct Text *text, struct TextUndoBuf *utxt, int op);
-void txt_do_undo(struct Text *text, struct TextUndoBuf *utxt);
-void txt_do_redo(struct Text *text, struct TextUndoBuf *utxt);
-void txt_split_curline(struct Text *text, struct TextUndoBuf *utxt);
-void txt_backspace_char(struct Text *text, struct TextUndoBuf *utxt);
-void txt_backspace_word(struct Text *text, struct TextUndoBuf *utxt);
-bool txt_add_char(struct Text *text, struct TextUndoBuf *utxt, unsigned int add);
-bool txt_add_raw_char(struct Text *text, struct TextUndoBuf *utxt, unsigned int add);
-bool txt_replace_char(struct Text *text, struct TextUndoBuf *utxt, unsigned int add);
-void txt_unindent(struct Text *text, struct TextUndoBuf *utxt);
-void txt_comment(struct Text *text, struct TextUndoBuf *utxt);
-void txt_indent(struct Text *text, struct TextUndoBuf *utxt);
-void txt_uncomment(struct Text *text, struct TextUndoBuf *utxt);
-void txt_move_lines(struct Text *text, struct TextUndoBuf *utxt, const int direction);
-void txt_duplicate_line(struct Text *text, struct TextUndoBuf *utxt);
+void txt_insert_buf(struct Text *text, const char *in_buffer);
+void txt_split_curline(struct Text *text);
+void txt_backspace_char(struct Text *text);
+void txt_backspace_word(struct Text *text);
+bool txt_add_char(struct Text *text, unsigned int add);
+bool txt_add_raw_char(struct Text *text, unsigned int add);
+bool txt_replace_char(struct Text *text, unsigned int add);
+void txt_unindent(struct Text *text);
+void txt_comment(struct Text *text);
+void txt_indent(struct Text *text);
+void txt_uncomment(struct Text *text);
+void txt_move_lines(struct Text *text, const int direction);
+void txt_duplicate_line(struct Text *text);
 int txt_setcurr_tab_spaces(struct Text *text, int space);
 bool txt_cursor_is_line_start(struct Text *text);
 bool txt_cursor_is_line_end(struct Text *text);
@@ -125,10 +121,9 @@ enum {
   TXT_MOVE_LINE_DOWN = 1,
 };
 
-typedef struct TextUndoBuf {
-  char *buf;
-  int pos, len;
-} TextUndoBuf;
+/* Fast non-validating buffer conversion for undo. */
+char *txt_to_buf_for_undo(struct Text *text, int *r_buf_strlen);
+void txt_from_buf_for_undo(struct Text *text, const char *buf, int buf_len);
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 1d6de646255..b922aabc171 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -79,96 +79,24 @@
  *
  * Display
  * --
+ *
  * The st->top determines at what line the top of the text is displayed.
  * If the user moves the cursor the st containing that cursor should
  * be popped ... other st's retain their own top location.
- *
- * Undo
- * --
- * Undo/Redo works by storing
- * events in a queue, and a pointer
- * to the current position in the
- * queue...
- *
- * Events are stored using an
- * arbitrary op-code system
- * to keep track of
- * a) the two cursors (normal and selected)
- * b) input (visible and control (ie backspace))
- *
- * input data is stored as its
- * ASCII value, the opcodes are
- * then selected to not conflict.
- *
- * opcodes with data in between are
- * written at the beginning and end
- * of the data to allow undo and redo
- * to simply check the code at the current
- * undo position
  */
 
-/* Undo opcodes */
-
-enum {
-  /* Complex editing */
-  /* 1 - opcode is followed by 1 byte for ascii character and opcode (repeat)) */
-  /* 2 - opcode is followed by 2 bytes for utf-8 character and opcode (repeat)) */
-  /* 3 - opcode is followed by 3 bytes for utf-8 character and opcode (repeat)) */
-  /* 4 - opcode is followed by 4 bytes for unicode character and opcode (repeat)) */
-  UNDO_INSERT_1 = 013,
-  UNDO_INSERT_2 = 014,
-  UNDO_INSERT_3 = 015,
-  UNDO_INSERT_4 = 016,
-
-  UNDO_BS_1 = 017,
-  UNDO_BS_2 = 020,
-  UNDO_BS_3 = 021,
-  UNDO_BS_4 = 022,
-
-  UNDO_DEL_1 = 023,
-  UNDO_DEL_2 = 024,
-  UNDO_DEL_3 = 025,
-  UNDO_DEL_4 = 026,
-
-  /* Text block (opcode is followed
-   * by 4 character length ID + the text
-   * block itself + the 4 character length
-   * ID (repeat) and opcode (repeat)) */
-  UNDO_DBLOCK = 027, /* Delete block */
-  UNDO_IBLOCK = 030, /* Insert block */
-
-  /* Misc */
-  UNDO_INDENT = 032,
-  UNDO_UNINDENT = 033,
-  UNDO_COMMENT = 034,
-  UNDO_UNCOMMENT = 035,
-
-  UNDO_MOVE_LINES_UP = 036,
-  UNDO_MOVE_LINES_DOWN = 037,
-
-  UNDO_DUPLICATE = 040,
-};
-
 /***/
 
 static void txt_pop_first(Text *text);
 static void txt_pop_last(Text *text);
-static void txt_undo_add_blockop(Text *text, TextUndoBuf *utxt, int op, const char *buf);
 static void txt_delete_line(Text *text, TextLine *line);
-static void txt_delete_sel(Text *text, TextUndoBuf *utxt);
+static void txt_delete_sel(Text *text);
 static void txt_make_dirty(Text *text);
 
 /***/
 
 /**
- * Set to true when undoing (so we don't generate undo steps while undoing).
- *
- * Also use to disable undo entirely.
- */
-static bool undoing;
-
-/**
- * \note caller must handle `undo_buf` and `compiled` members.
+ * \note caller must handle `compiled` member.
  */
 void BKE_text_free_lines(Text *text)
 {
@@ -516,29 +444,17 @@ void BKE_text_make_local(Main *bmain, Text *text, const bool lib_local)
   BKE_id_make_local_generic(bmain, &text->id, true, lib_local);
 }
 
-void BKE_text_clear(Text *text, TextUndoBuf *utxt) /* called directly from rna */
+void BKE_text_clear(Text *text) /* called directly from rna */
 {
-  const bool undoing_orig = undoing;
-  undoing = (utxt == NULL);
-
   txt_sel_all(text);
-  txt_delete_sel(text, utxt);
-
-  undoing = undoing_orig;
-
+  txt_delete_sel(text);
   txt_make_dirty(text);
 }
 
-void BKE_text_write(Text *text, TextUndoBuf *utxt, const char *str) /* called directly from rna */
+void BKE_text_write(Text *text, const char *str) /* called directly from rna */
 {
-  const bool undoing_orig = undoing;
-  undoing = (utxt == NULL);
-
-  txt_insert_buf(text, utxt, str);
+  txt_insert_buf(text, str);
   txt_move_eof(text, 0);
-
-  undoing = undoing_orig;
-
   txt_make_dirty(text);
 }
 
@@ -1270,7 +1186,7 @@ bool txt_has_sel(Text *text)
   return ((text->curl != text->sell) || (text->curc != text->selc));
 }
 
-static void txt_delete_sel(Text *text, TextUndoBuf *utxt)
+static void txt_delete_sel(Text *text)
 {
   TextLine *tmpl;
   char *buf;
@@ -1288,12 +1204,6 @@ static void txt_delete_sel(Text *text, TextUndoBuf *utxt)
 
   txt_order_cursors(text, false);
 
-  if (!undoing) {
-    buf = txt_sel_to_buf(text, NULL);
-    txt_undo_add_blockop(text, utxt, UNDO_DBLOCK, buf);
-    MEM_freeN(buf);
-  }
-
   buf = MEM_mallocN(text->curc + (text->sell->len - text->selc) + 1, "textline_string");
 
   strncpy(buf, text->curl->line, text->curc);
@@ -1349,6 +1259,106 @@ void txt_sel_line(Text *text)
   text->selc = text->sell->len;
 }
 
+/* -------------------------------------------------------------------- */
+/** \name Buffer Conversion for Undo/Redo
+ *
+ * Buffer conversion functions that rely on the buffer already being validated.
+ *
+ * The only requirement for these functions is that they're reverse-able,
+ * the undo logic doesn't inspect their content.
+ *
+ * Currently buffers:
+ * - Always ends with a new-line.
+ * - Are not null terminated.
+ * \{ */
+
+/**
+ * Create a buffer, the only requirement is #txt_from_buf_for_undo can decode it.
+ */
+char *txt_to_buf_for_undo(Text *text, int *r_buf_len)
+{
+  int buf_len = 0;
+  for (const TextLine *l = text->lines.first; l; l = l->next) {
+    buf_len += l->len + 1;
+  }
+  char *buf = MEM_mallocN(buf_len, __func__);
+  char *buf_step = buf;
+  for (const TextLine *l = text->lines.first; l; l = l->next) {
+    memcpy(buf_step, l->line, l->len);
+    buf_step += l->len;
+    *buf_step++ = '\n';
+  }
+  *r_buf_len = buf_len;
+  return buf;
+}
+
+/**
+ * Decode a buffer from #txt_to_buf_for_undo.
+ */
+void txt_from_buf_for_undo(Text *text, const char *buf, int buf_len)
+{
+  const char *buf_end = buf + buf_len;
+  const char *buf_step = buf;
+
+  /* First re-use existing lines.
+   * Good for undo since it means in practice many operations re-use all
+   * except for the modified line. */
+  TextLine *l_src = text->lines.first;
+  BLI_listbase_clear(&text->lines);
+  while (buf_step != buf_end && l_src) {
+    /* New lines are ensured by #txt_to_buf

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list