Files
cthulhu/internal/review/document_navigation_reader.go
hjozwiak 7eef6e8be6 Make web document navigation survive live-updating pages
Recreated-node caret recovery, tolerant caret validation, per-root retained document trees and cursors across tab switches, document warm-up, coalesced structural moves, and hung-provider fast-fail with an 'Application is not responding' announcement.
2026-11-03 10:34:55 -05:00

65 lines
2.2 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package review
import (
"context"
"git.stormux.org/storm/cthulhu/internal/atspi"
)
// One move owns this wrapper. Validate the path before native reads so stale
// range lengths and omitted candidates trigger recovery without consuming a
// different object's text or advancing the retained cursor.
type navigationTextReader struct {
DocumentCaretReader
tree *atspi.AccessibleTree
document atspi.DocumentSnapshot
anchor int
readObjects []atspi.ObjectReference
}
func (reader *navigationTextReader) ReadDocumentCaret(ctx context.Context, object atspi.ObjectReference, offset int, unit atspi.DocumentCaretUnit) (atspi.DocumentCaretText, error) {
index, found := reader.document.Index(object.ID())
if !found || reader.document.Nodes[index].Reference != object {
return atspi.DocumentCaretText{}, atspi.ErrStaleObject
}
if err := reader.tree.ValidateDocumentCaretRange(ctx, reader.document, reader.anchor, index); err != nil {
return atspi.DocumentCaretText{}, err
}
text, err := reader.DocumentCaretReader.ReadDocumentCaret(ctx, object, offset, unit)
if err == nil {
reader.readObjects = append(reader.readObjects, object)
}
return text, err
}
func (reader *navigationTextReader) did_read(object atspi.ObjectReference) bool {
for _, read := range reader.readObjects {
if read == object {
return true
}
}
return false
}
// Character blocks belong to one word command and never survive an event or
// recapture. Native spelling and actual word boundaries still use normal reads.
type navigationCharacterReader struct {
*navigationTextReader
source DocumentCharacterReader
}
func (reader *navigationCharacterReader) ReadDocumentCharacters(ctx context.Context, object atspi.ObjectReference, offset int) (atspi.DocumentCaretText, error) {
index, found := reader.document.Index(object.ID())
if !found || reader.document.Nodes[index].Reference != object {
return atspi.DocumentCaretText{}, atspi.ErrStaleObject
}
if err := reader.tree.ValidateDocumentCaretRange(ctx, reader.document, reader.anchor, index); err != nil {
return atspi.DocumentCaretText{}, err
}
return reader.source.ReadDocumentCharacters(ctx, object, offset)
}