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  }