yi-rope-0.2.0.0: A rope data structure used by Yi

LicenseGPL-2
Maintaineryi-devel@googlegroups.com
Stabilityexperimental
Portabilityportable
Safe HaskellSafe-Inferred
LanguageHaskell2010
Extensions
  • BangPatterns
  • OverloadedStrings
  • TypeSynonymInstances
  • FlexibleInstances
  • MultiParamTypeClasses

Yi.Rope

Contents

Description

This module defines a rope data structure for use in Yi. This specific implementation uses a fingertree over Text.

In contrast to our old implementation, we can now reap all the benefits of Text: automatic unicode handling and blazing fast implementation on underlying strings. This frees us from a lot of book-keeping. We don't lose out on not using ByteString directly because the old implementation encoded it into UTF8 anyway, making it unsuitable for storing anything but text.

Synopsis

Documentation

data YiString Source

A YiString is a FingerTree with cached column and line counts over chunks of Text.

Instances

Eq YiString 
Show YiString 
IsString YiString 
Binary YiString

To serialise a YiString, we turn it into a regular String first.

NFData YiString 

Conversions to YiString

fromText :: Text -> YiString Source

Converts a Text into a YiString using defaultChunkSize-sized chunks for the underlying tree.

fromText' :: Int -> Text -> YiString Source

This is like fromText but it allows the user to specify the chunk size to be used. Uses defaultChunkSize if the given size is <= 0.

Conversions from YiString

toText :: YiString -> Text Source

Consider whether you really need to use this!

toReverseText :: YiString -> Text Source

Spits out the underlying string, reversed.

Note that this is actually slightly faster than manually unrolling the tree from the end, reverseing each chunk and concating, at least with -O2 which you really need to be using with Text anyway.

List-like functions

null :: YiString -> Bool Source

Checks if the given YiString is actually empty.

empty :: YiString Source

Creates an empty YiString.

take :: Int -> YiString -> YiString Source

Takes the first n given characters.

drop :: Int -> YiString -> YiString Source

Drops the first n characters.

length :: YiString -> Int Source

Length of the whole underlying string.

Amortized constant time.

reverse :: YiString -> YiString Source

Reverse the whole underlying string.

This involves reversing the order of the chunks as well as content of the chunks. We use a little optimisation here that re-uses the content of each chunk but this exposes a potential problem: after many transformations, our chunks size might become quite varied (but never more than the default size), perhaps we should periodically rechunk the tree to recover nice sizes?

countNewLines :: YiString -> Int Source

Count the number of newlines in the underlying string. This is actually amortized constant time as we cache this information in the underlying tree.

lines :: YiString -> [YiString] Source

This is like lines' but it does *not* preserve newlines.

Specifically, we just strip the newlines from the result of lines'.

This behaves slightly differently than the old split: the number of resulting strings here is equal to the number of newline characters in the underlying string. This is much more consistent than the old behaviour which blindly used ByteStrings split and stitched the result back together which was inconsistent with the rest of the interface which worked with number of newlines.

lines' :: YiString -> [YiString] Source

Splits the YiString into a list of YiString each containing a line.

Note that in old implementation this allowed an arbitrary character to split on. If you want to do that, manually convert toText and use splitOn to suit your needs. This case is optimised for newlines only which seems to have been the only use of the original function.

The newlines are preserved so this should hold:

'toText' . 'concat' . 'lines'' ≡ 'toText'

but the underlying structure might change: notably, chunks will most likely change sizes.

splitAt :: Int -> YiString -> (YiString, YiString) Source

Splits the string at given character position.

If position <= 0 then the left string is empty and the right string contains everything else.

If position >= length of the string then the left string contains everything and the right string is empty.

Implementation note: the way this works is by splitting the underlying finger at a closest chunk that goes *over* the given position (see split). This either results in a perfect split at which point we're done or more commonly, it leaves as few characters short and we need to take few characters from the first chunk of the right side of the split. We do precisely that.

All together, this split is only as expensive as underlying split, the cost of splitting a chunk into two and the cost consing and snocing one chunk to each string. As the chunks are short, the split fairly cheap and cons/snoc constant time, this turns out pretty fast all together.

splitAtLine :: Int -> YiString -> (YiString, YiString) Source

Splits the underlying string before the given line number. Zero-indexed lines.

Splitting at line <= 0 gives you an empty string. Splitting at n > 0 gives you the first n lines.

Also see splitAtLine'.

concat :: [YiString] -> YiString Source

Concat a list of YiStrings.

IO