nano-rope-0.1.0.0: src/Data/Text/NanoRope/Measured.hs
-- |
-- Module : Data.Text.NanoRope.Measured
-- Copyright : (c) 2026 goolord
-- License : MIT
--
-- A persistent text rope with a custom monoidal 'Measure' cached alongside
-- the built-in t'Metrics'. Use "Data.Text.NanoRope" when the built-in byte,
-- code point, UTF-16, and newline counts are enough.
--
-- Import this module qualified:
--
-- > import Data.Text.NanoRope.Measured (Rope, Measure (..), Unit (..))
-- > import qualified Data.Text.NanoRope.Measured as Rope
--
-- Offsets are zero-based, clamped to the document, and rounded down to code
-- point boundaries. Ranges are half-open. Only @\\n@ starts a new line;
-- 'Chars' counts code points, not grapheme clusters or display columns.
--
-- Complexity bounds use /n/ for the document's byte length. They assume
-- constant-time measure combination, linear-time chunk measurement, and an
-- evaluated tree. Reads, including 'measure', may first apply buffered
-- input; 'null', 'length', 'lineCount', and 'metrics' do not force it.
--
-- = Example: counting tabs
--
-- A tab count is independent of chunk boundaries and grows monotonically,
-- so it supports prefix searches. Enable @OverloadedStrings@ for this example.
--
-- > import qualified Data.Text as T
-- >
-- > newtype Tabs = Tabs Int deriving (Eq, Ord, Show)
-- >
-- > instance Semigroup Tabs where Tabs a <> Tabs b = Tabs (a + b)
-- > instance Monoid Tabs where mempty = Tabs 0
-- >
-- > instance Measure Tabs where
-- > measureChunk = Tabs . T.count "\t"
-- >
-- > document :: Rope Tabs
-- > document = Rope.fromText "a\tb\tc\td"
-- >
-- > tabCount = Rope.measure document -- Tabs 3
-- > beforeThirdTab = Rope.toText (fst (Rope.splitWhere (\_ n -> n >= Tabs 3) document))
-- > -- "a\tb\tc"
--
-- See 'Measure' for the laws every annotation must satisfy.
module Data.Text.NanoRope.Measured
( -- * Ropes
Rope
, Measure (..)
-- * Units and metrics
, Unit (..)
, Metrics (..)
, count
-- * Construction
, empty
, singleton
, fromText
, fromLazyText
-- * Deconstruction
, toText
, toLazyText
, toString
, toChunks
, foldrChunks
, foldlChunks'
, chunkAt
-- * Output
, hPutUtf8
, writeFileUtf8
-- * Queries
, null
, length
, lineCount
, metrics
, measure
-- * Combining and breaking
, append
, splitAt
, take
, drop
, slice
, sliceText
-- * Editing
, insert
, delete
, replace
-- * Lines
, getLine
, lines
-- * Converting between units
-- $conversions
, metricsAt
, convert
-- * Positions
, Position (..)
, splitAtPosition
, metricsAtPosition
, metricsAtLineAndPosition
, metricsToPosition
, offsetToPosition
, positionToOffset
-- * Searching by measure
, splitWhere
, metricsWhere
, remeasure
) where
import Data.Text.NanoRope.Internal
import Prelude ()
-- $conversions
-- Prefix t'Metrics' describe a location in all four units. Obtain them with
-- 'metricsAt', 'metricsAtPosition', or 'metricsWhere', then use 'count' for
-- absolute offsets. Reusing the metrics avoids repeating the lookup for
-- each unit. 'metricsToPosition' also looks up the line start to calculate
-- a column; use metrics from the same rope.