github.com/aretext/aretext@v1.3.0/state/locator.go (about) 1 package state 2 3 import ( 4 "github.com/aretext/aretext/locate" 5 "github.com/aretext/aretext/selection" 6 "github.com/aretext/aretext/syntax/parser" 7 "github.com/aretext/aretext/text" 8 ) 9 10 // LocatorParams are inputs to a function that locates a position in the document. 11 type LocatorParams struct { 12 TextTree *text.Tree 13 SyntaxParser *parser.P 14 CursorPos uint64 15 AutoIndentEnabled bool 16 TabSize uint64 17 } 18 19 func locatorParamsForBuffer(buffer *BufferState) LocatorParams { 20 return LocatorParams{ 21 TextTree: buffer.textTree, 22 SyntaxParser: buffer.syntaxParser, 23 CursorPos: buffer.cursor.position, 24 AutoIndentEnabled: buffer.autoIndent, 25 TabSize: buffer.tabSize, 26 } 27 } 28 29 // Locator is a function that locates a position in the document. 30 type Locator func(LocatorParams) uint64 31 32 // RangeLocator is a function that locates the start and end positions 33 // of a range in the document (for example, a word or selection). 34 // The (start, end] interval does NOT include the end position. 35 type RangeLocator func(LocatorParams) (uint64, uint64) 36 37 // SelectionEndLocator returns a locator for the end of a selection. 38 // For example, suppose a user has selected the first two lines of a document. 39 // When the user repeats an action for this selection (by using the "." command), 40 // the command repeats for the two lines starting at the *new* cursor location. 41 // The selection end locator determines the end of the selection at the new cursor. 42 // 43 // For linewise selections, it attempts to select the same number of lines. 44 // Example where cursor moves from line two to line three: 45 // 46 // abcd abcd 47 // [efg --> efg 48 // hij] [hij 49 // klm klm] 50 // 51 // For charwise selections it attempts to select down the same number of lines 52 // and over the same number of columns (grapheme clusters) on the final line 53 // of the selection. 54 // Example where the cursor moves from the second col in the first line 55 // to the third col in the second line: 56 // 57 // ab[cd abcd 58 // ef]g --> ef[g 59 // hij hi]j 60 // klm klm 61 func SelectionEndLocator(textTree *text.Tree, cursorPos uint64, selector *selection.Selector) Locator { 62 r := selector.Region(textTree, cursorPos) 63 switch selector.Mode() { 64 case selection.ModeNone: 65 return nil 66 case selection.ModeChar: 67 return charwiseSelectionEndLocator(textTree, r) 68 case selection.ModeLine: 69 return linewiseSelectionEndLocator(textTree, r) 70 default: 71 panic("Unrecognized selection mode") 72 } 73 } 74 75 func charwiseSelectionEndLocator(textTree *text.Tree, r selection.Region) Locator { 76 startLineNum := textTree.LineNumForPosition(r.StartPos) 77 endLineNum := textTree.LineNumForPosition(r.EndPos) 78 if startLineNum == endLineNum { 79 numGcFromStartToEnd := locate.NumGraphemeClustersInRange(textTree, r.StartPos, r.EndPos) 80 return func(p LocatorParams) uint64 { 81 return locate.NextCharInLine(p.TextTree, numGcFromStartToEnd, true, p.CursorPos) 82 } 83 } 84 85 numLinesDown := endLineNum - startLineNum 86 startOfLinePos := locate.StartOfLineAtPos(textTree, r.EndPos) 87 numGcPastStartOfLine := locate.NumGraphemeClustersInRange(textTree, startOfLinePos, r.EndPos) 88 return func(p LocatorParams) uint64 { 89 startOfLineBelowPos := locate.StartOfLineBelow(p.TextTree, numLinesDown, p.CursorPos) 90 if startOfLineBelowPos > p.CursorPos { 91 // Moved down a line, now move over. 92 return locate.NextCharInLine(p.TextTree, numGcPastStartOfLine, true, startOfLineBelowPos) 93 } else { 94 // On last line, so select to the end of the document. 95 return locate.NextLineBoundary(p.TextTree, true, p.CursorPos) 96 } 97 } 98 } 99 100 func linewiseSelectionEndLocator(textTree *text.Tree, r selection.Region) Locator { 101 startLineNum := textTree.LineNumForPosition(r.StartPos) 102 endLineNum := textTree.LineNumForPosition(r.EndPos) 103 numLinesDown := endLineNum - startLineNum 104 return func(p LocatorParams) uint64 { 105 pos := locate.StartOfLineBelow(p.TextTree, numLinesDown, p.CursorPos) 106 return locate.NextLineBoundary(p.TextTree, true, pos) 107 } 108 }