-- | Queries on trees annotated with position information. module Annotations.F.Positional ( selectByRange, selectByPos, validBounds, repairBy, repair, moveSelection ) where import Annotations.Bounds import Annotations.ExploreHints import Annotations.F.Fixpoints import Annotations.F.Zipper import Annotations.F.Annotated import Data.Foldable (Foldable, toList) import Data.Ord import Data.List import Data.Maybe import Control.Applicative -- | Find the deepest node whose bounds match the given range. See 'rangeInBounds'. selectByRange :: Foldable f => Range -> AnnFix Bounds f -> Maybe (Zipper (AnnFix Bounds f)) selectByRange range@(left, _) = listToMaybe . reverse . explore hints where hints bounds@(Bounds _ (ir, _)) = ExploreHints { matchHere = range `rangeInBounds` bounds , exploreDown = range `rangeInRange` outerRange bounds , exploreRight = left >= ir } -- | Find the deepest node whose bounds contain the given position. selectByPos :: Foldable f => Int -> AnnFix Bounds f -> Maybe (Zipper (AnnFix Bounds f)) selectByPos pos = findLeftmostDeepest (posInRange pos . innerRange) -- | Find all selections in the tree and return their bounds. The tree is traversed in preorder. Consequently, the bounds are returned in lexicographical order. validBounds :: Foldable f => AnnFix Bounds f -> [Bounds] validBounds (In (Ann b f)) = b : concatMap validBounds (toList f) -- | @repairBy cost tree range@ finds the the closest valid text selection to @range@, where ''closest'' is determined by the specified cost function. repairBy :: (Foldable f, Ord dist) => (Range -> Range -> dist) -> AnnFix Bounds f -> Range -> Bounds repairBy cost tree range = head (sortOn (cost range . innerRange) (validBounds tree)) -- | Defined as @'repairBy' 'distRange'@. repair :: Foldable f => AnnFix Bounds f -> Range -> Bounds repair = repairBy distRange sortOn :: Ord b => (a -> b) -> [a] -> [a] sortOn = sortBy . comparing -- | Move around in a tree according to the 'Nav', expressed in tree selections. Although a 'Range' is required as input, a 'Bounds' is returned, providing information about all the valid text selections that would select the particular tree node. moveSelection :: Foldable f => AnnFix Bounds f -> Nav -> Range -> Maybe Bounds moveSelection tree (Nav nav) range = (rootAnn . zFocus) <$> (selectByRange range tree >>= nav)