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.
65 lines
2.2 KiB
Go
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)
|
|
}
|