Files

90 lines
3.2 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package review
import (
"context"
"time"
"git.stormux.org/storm/cthulhu/internal/atspi"
)
// ReadCurrentLine leaves both the native caret and review cursors untouched.
func (worker *Worker) ReadCurrentLine(browse bool, style FlatSpeech) error {
if style > FlatPhonetic {
return ErrInvalidDirection
}
select {
case worker.requests <- request{currentLine: true, currentLineBrowse: browse, flatSpeech: style,
documentEpoch: worker.documentEpoch.Load(), queuedAt: time.Now()}:
return nil
default:
return ErrRequestQueueFull
}
}
func (worker *Worker) read_current_line(ctx context.Context, cursor *DocumentCursor, browse bool) Result {
result := Result{CurrentLine: true, CurrentLineBrowse: browse, DocumentFocus: worker.focus()}
if !result.DocumentFocus.Valid() {
result.Err = ErrNoCurrent
return result
}
deadline, cancel := context.WithTimeout(ctx, worker.captureTimeout)
defer cancel()
if !browse {
reader, ok := worker.nativeCapturerFor(result.DocumentFocus).(interface {
ReadFocusedLine(context.Context, atspi.ObjectID) (atspi.DocumentContent, error)
})
if !ok {
result.Err = atspi.ErrNoDocumentCaret
return result
}
content, err := reader.ReadFocusedLine(deadline, result.DocumentFocus)
result.Err = err
result.DocumentCaretText, result.DocumentCaretRawText = content.Text, content.RawText
result.DocumentPosition.Snapshot = content.Snapshot
result.DocumentCaretContext = content.SourceContext
return result
}
// Start a private cursor at the saved browse position. Going to line start
// before reading avoids Say All's deliberate "rest of this line" behavior.
if cursor != nil {
cursor = &DocumentCursor{tree: cursor.tree, active: cursor.active, places: append([]documentPlace(nil), cursor.places...)}
_, result.Err = cursor.syncActionFocus(deadline, result.DocumentFocus)
} else {
result.Err = ErrNoCurrent
}
if documentNeedsCapture(result.Err) {
cursor, result.Err = worker.refreshDocumentCursor(deadline, cursor, result.DocumentFocus)
}
if result.Err != nil {
return result
}
reader, ok := worker.nativeCapturerFor(result.DocumentFocus).(DocumentCaretReader)
if !ok {
result.Err = atspi.ErrNoDocumentCaret
return result
}
_, cursor, result.Err = worker.readAllMove(deadline, cursor, reader, CaretLineStart, result.DocumentFocus)
if result.Err != nil {
return result
}
movement, cursor, err := worker.readAllMove(deadline, cursor, reader, CaretReadLine, result.DocumentFocus)
result.Err = err
result.DocumentPosition, result.DocumentCaretText = movement.Position, movement.Text
result.DocumentCaretRawText, result.DocumentCaretLine = movement.RawText, movement.Line
if err == nil {
// Retain the unfiltered ancestry from the tree that produced this
// movement, including after recapture. Missing metadata keeps export
// private without preventing the local current-line announcement.
if owner, contextErr := cursor.tree.DocumentContext(movement.Position.Object); contextErr == nil {
result.DocumentCaretContext = owner.Ancestors
}
}
if result.DocumentCaretText == "" && wholeCaretNode(movement.Position.Snapshot) {
result.DocumentCaretText = movement.Position.Snapshot.Name
}
return result
}