github.com/aretext/aretext@v1.3.0/locate/paragraph.go (about)

     1  package locate
     2  
     3  import (
     4  	"io"
     5  
     6  	"github.com/aretext/aretext/text"
     7  	"github.com/aretext/aretext/text/segment"
     8  )
     9  
    10  // NextParagraph locates the start of the next paragraph after the cursor.
    11  // Paragraph boundaries occur at empty lines.
    12  func NextParagraph(tree *text.Tree, pos uint64) uint64 {
    13  	reader := tree.ReaderAtPosition(pos)
    14  	segmentIter := segment.NewGraphemeClusterIter(reader)
    15  	seg := segment.Empty()
    16  	var prevWasNewlineFlag, nonNewlineFlag bool
    17  	var offset, prevOffset uint64
    18  	for {
    19  		err := segmentIter.NextSegment(seg)
    20  		if err == io.EOF {
    21  			// End of document.
    22  			return pos + prevOffset
    23  		} else if err != nil {
    24  			panic(err)
    25  		}
    26  
    27  		if seg.HasNewline() {
    28  			if prevWasNewlineFlag && nonNewlineFlag {
    29  				// An empty line is a paragraph boundary.
    30  				// Choose the first one after we see a non-newline.
    31  				break
    32  			}
    33  			prevWasNewlineFlag = true
    34  		} else {
    35  			nonNewlineFlag = true
    36  			prevWasNewlineFlag = false
    37  		}
    38  
    39  		prevOffset = offset
    40  		offset += seg.NumRunes()
    41  	}
    42  	return pos + offset
    43  }
    44  
    45  // PrevParagraph locates the start of the first paragraph before the cursor.
    46  // Paragraph boundaries occur at empty lines.
    47  func PrevParagraph(tree *text.Tree, pos uint64) uint64 {
    48  	reader := tree.ReverseReaderAtPosition(pos)
    49  	segmentIter := segment.NewReverseGraphemeClusterIter(reader)
    50  	seg := segment.Empty()
    51  	var prevWasNewlineFlag, nonNewlineFlag bool
    52  	var offset uint64
    53  	for {
    54  		err := segmentIter.NextSegment(seg)
    55  		if err == io.EOF {
    56  			// Start of the document.
    57  			return 0
    58  		} else if err != nil {
    59  			panic(err)
    60  		}
    61  
    62  		if seg.HasNewline() {
    63  			if prevWasNewlineFlag && nonNewlineFlag {
    64  				// An empty line is a paragraph boundary.
    65  				return pos - offset
    66  			}
    67  			prevWasNewlineFlag = true
    68  		} else {
    69  			prevWasNewlineFlag = false
    70  			nonNewlineFlag = true
    71  		}
    72  
    73  		offset += seg.NumRunes()
    74  	}
    75  }