github.com/aretext/aretext@v1.3.0/state/undo_test.go (about)

     1  package state
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  
     8  	"github.com/aretext/aretext/clipboard"
     9  	"github.com/aretext/aretext/locate"
    10  	"github.com/aretext/aretext/syntax"
    11  )
    12  
    13  func TestUndoAndRedo(t *testing.T) {
    14  	state := NewEditorState(100, 100, nil, nil)
    15  
    16  	// Make some edits with undo checkpoints.
    17  	BeginUndoEntry(state)
    18  	InsertRune(state, 'a')
    19  	InsertRune(state, 'b')
    20  	InsertRune(state, 'c')
    21  	CommitUndoEntry(state)
    22  
    23  	BeginUndoEntry(state)
    24  	InsertNewline(state)
    25  	InsertRune(state, 'd')
    26  	CommitUndoEntry(state)
    27  
    28  	BeginUndoEntry(state)
    29  	DeleteToPos(state, func(params LocatorParams) uint64 {
    30  		return locate.PrevCharInLine(params.TextTree, 1, false, params.CursorPos)
    31  	}, clipboard.PageDefault)
    32  	CommitUndoEntry(state)
    33  
    34  	// Verify state before undo.
    35  	assert.Equal(t, uint64(4), state.documentBuffer.cursor.position)
    36  	assert.Equal(t, "abc\n", state.documentBuffer.textTree.String())
    37  
    38  	// Undo the deletion.
    39  	Undo(state)
    40  	assert.Equal(t, uint64(5), state.documentBuffer.cursor.position)
    41  	assert.Equal(t, "abc\nd", state.documentBuffer.textTree.String())
    42  
    43  	// Undo the newline and insertion of 'd'
    44  	Undo(state)
    45  	assert.Equal(t, uint64(3), state.documentBuffer.cursor.position)
    46  	assert.Equal(t, "abc", state.documentBuffer.textTree.String())
    47  
    48  	// Redo the newline and insertion of 'd'
    49  	Redo(state)
    50  	assert.Equal(t, uint64(5), state.documentBuffer.cursor.position)
    51  	assert.Equal(t, "abc\nd", state.documentBuffer.textTree.String())
    52  
    53  	// Undo again.
    54  	Undo(state)
    55  	assert.Equal(t, uint64(3), state.documentBuffer.cursor.position)
    56  	assert.Equal(t, "abc", state.documentBuffer.textTree.String())
    57  
    58  	// Undo last change.
    59  	Undo(state)
    60  	assert.Equal(t, uint64(0), state.documentBuffer.cursor.position)
    61  	assert.Equal(t, "", state.documentBuffer.textTree.String())
    62  }
    63  
    64  func TestUndoDeleteLinesWithIndentation(t *testing.T) {
    65  	state := NewEditorState(100, 100, nil, nil)
    66  
    67  	// Insert some lines with indentation.
    68  	BeginUndoEntry(state)
    69  	InsertTab(state)
    70  	InsertRune(state, 'a')
    71  	InsertRune(state, 'b')
    72  	InsertNewline(state)
    73  	InsertTab(state)
    74  	InsertRune(state, 'c')
    75  	InsertNewline(state)
    76  	InsertRune(state, 'd')
    77  	CommitUndoEntry(state)
    78  
    79  	// Delete second-to-last line, which is indented.
    80  	BeginUndoEntry(state)
    81  	MoveCursor(state, func(p LocatorParams) uint64 { return locate.StartOfLineNum(p.TextTree, 1) })
    82  	DeleteLines(state, func(p LocatorParams) uint64 { return p.CursorPos }, false, false, clipboard.PageDefault)
    83  	CommitUndoEntry(state)
    84  
    85  	// Verify state before undo.
    86  	assert.Equal(t, uint64(4), state.documentBuffer.cursor.position)
    87  	assert.Equal(t, "\tab\nd", state.documentBuffer.textTree.String())
    88  
    89  	// Undo the deletion.
    90  	// Expect the cursor to land on the start of the restored line AFTER indentation.
    91  	Undo(state)
    92  	assert.Equal(t, uint64(8), state.documentBuffer.cursor.position)
    93  	assert.Equal(t, "\tab\n\tc\nd", state.documentBuffer.textTree.String())
    94  }
    95  
    96  func TestUndoMultiByteUnicodeWithSyntaxHighlighting(t *testing.T) {
    97  	state := NewEditorState(100, 100, nil, nil)
    98  	SetSyntax(state, syntax.LanguageGo)
    99  
   100  	// Insert multi-byte UTF-8 runes.
   101  	BeginUndoEntry(state)
   102  	for _, r := range "丂丄丅丆丏 ¢ह€한" {
   103  		InsertRune(state, r)
   104  	}
   105  	CommitUndoEntry(state)
   106  
   107  	// This used to trigger a panic when retokenizing because the
   108  	// deleted rune count was incorrect.
   109  	Undo(state)
   110  	assert.Equal(t, "", state.documentBuffer.textTree.String())
   111  
   112  	Redo(state)
   113  	assert.Equal(t, "丂丄丅丆丏 ¢ह€한", state.documentBuffer.textTree.String())
   114  }
   115  
   116  func TestUnsavedChanges(t *testing.T) {
   117  	state := NewEditorState(100, 100, nil, nil)
   118  
   119  	// Initially no unsaved changes.
   120  	assert.False(t, state.documentBuffer.undoLog.HasUnsavedChanges())
   121  
   122  	// Make some edits with undo checkpoints.
   123  	BeginUndoEntry(state)
   124  	InsertRune(state, 'a')
   125  	InsertRune(state, 'b')
   126  	InsertRune(state, 'c')
   127  	CommitUndoEntry(state)
   128  
   129  	// Now there are unsaved changes.
   130  	assert.True(t, state.documentBuffer.undoLog.HasUnsavedChanges())
   131  }