yi-editor/yi — pull request #605 by Fuuzetsu (2014-09-17 21:43:40+00:00)
Title: Migrate most of Yi to YiString/Text combo.

Below is the commentary of the changes. This commit is large and
encompasses a lot but due to the nature of changes, small commits were
often not viable. If there was something obvious to fix then I did it
there and then too rather than putting it off for later.

YiString is a new rope implementation implemented in the ‘yi-rope’
package. It allows us to delete the Data.Rope module from src/library
which I did. The fundamental difference is that it is a rope implemented
over strict Text rather than over (lazy) ByteString. Benchmarks show
nice speedup and better memory efficiency. More benchmarks showed that
the chunk size of 1200 is more suited for working with larger text sizes
such as whole buffers. These benchmarks are disjoint from Yi core itself
which may mean that in practice, chunk size might need tweaking.

Using Text frees us from having to deal with encoding. Old
implementation converted back and forth a huge amount because any
meaningful operation had to be done over String or Char, we never want
to work with Word8. These conversions made using a rope pointless as
we'd probably get better milage just keeping a String around with cached
length. This brings me onto the rest of the changes. `yi-rope` needs few
extra functions (`span`, `break`, Hashable instance) to replace some
more Text usage but it's quite usable as it is.

Having removed the need for constant conversions, we are now able to
refactor the rest of the code to take advantage. Ideally we want to keep
things as YiString for as long as possible to not incur conversion
penalties. I initially made everything work over Text and then had a
second pass at it, converting to YiString. This was quite a lot of work.
Note that there are exceptions where it might still be faster to convert
to Text: if we are doing something over a string we have to force
and map over anyway, it is probably faster (and more convenient) to just
convert to Text and work with it that way. Text is pretty damn fast so
this still works out much better than String. I'll try to give a very
short comment on each module I touched in hopes that when someone
inevitably git-blame's, they'll be able to at least get a basic idea why
the change is in this commit. Some changes were trivial: Haddock header
includes, import cleanups, reformatting to 80C, de-shadowing, removing
extra pragmas, fixing warnings…. These are just documented here as
‘style’. Lastly, I have removed the Copyright field from few of the
Haddock headers. The reason is that they are not consistent: most
modules are missing them and when they are present, they only have
authors from many years ago and none after. The choice was to either
track down all the authors and years they touched the files (a ton of
work) or to remove the field all together. If anyone is interested,
git-blame the file. Also see AUTHORS.

Spread out through the modules are a bunch of more TODOs such as places
where we should probably keep something as YiString rather than
converting to Text or places where benchmarking needs to make the call.
There are a couple of places where top level types have been fixed to
YiString or Text but they do conversion internally. This makes it
implement Text/YiString native versions of their guts. Places where
conversions to String are still done are scarce but still present, such
is when working with filepaths or external processes. Where Text worked
fine from the first pass, I did not change it specifically but in some
places it might be worthwhile to do so if it causes conversions.

`cabal test` passes and I haven't noticed any bugs I might have caused
but there are a lot of changes so I have no doubt something sneaked in.
Please report if you see anything wrong.

Data.Rope: deleted, use Yi.Rope from `yi-rope` instead

System.CanonicalizePath: Turned a bunch of stuff to work over Text. It
talks with System.FilePath which works over String and character-based
processing is done here which means that YiString would just incur extra
penalties. We should move to `system-filepath` package anyway which uses
its own type all together rather than an alias for String.

Yi: style only, but we should remove the `Data.Prototypes` export now
that it's in a separate package, `oo-prototypes`.

Yi.Boot: Follow changes from other modules.

Yi.Buffer.Highlevel: String-returning functions got the boot in favour
of YiString. Also many of the duplicate functions (over String and Rope)
are gone and instead only YiString is exposed. `bufInfoPercent` field is
now Text.

Yi.Buffer.Implementation: Similar to `Yi.Buffer.Highlevel`.
Unfortunatelly, there is still the problem that we need a String stream
for things like Vty. That String version is now under `getStream'`.
Also, `mem` is now exposed so that we `elemsB` can directly return
buffer content rather than stupidly taking `maxBound` from 0.

Yi.Buffer.Indent: `yi-rope` needs `break` and `span` to get rid of the
Text conversion inside the keyword functions.

Yi.Buffer.Misc: Get rid of a few duplicate functions that only did
conversions. Add `streamB'` for the same reason as
`Yi.Buffer.Implementation.getStream'`. modeName and modeModeLine are now
Text.

Yi.Buffer.Region: Most notably, `replaceRegionClever` needs an
implementation for `getGroupedDiff` as the existing TODO stated. At the
moment we have to convert to and from String to call it. A benchmark may
show that it's actually faster to replace the whole region than
calculate the diff as splicing into the YiString should not be that
expensive.

Yi.Buffer.TextUnit: You'll notice `genBoundary` still uses String. An
easy way to see just how ineffecient this makes it is to hold M-q for a
bit with the emacs keymap and look at the profiling. The reason is its
use of `streamB'`. An optimisation should be fairly easy here however,
unlike in the Vty scenario.

Yi.Buffer.Undo: fix on of many badly placed Haddock comments, allowing
docs to build. All other such changes won't be commented on.

Yi.Command: The minibuffers now run on Text so we just follow the
changes here. It's unclear if YiString gives us much benefit here as we
need to convert to String anyway to shell commands.

Yi.Completion: Now runs on Text as we need to do a bunh of filtering and
prefixing in here anyway.

Yi.Config.Default: `defaultPublishedActions` still run on String as
`yi-rope` needs a Hashable instance first.

Yi.Config.Simple: Mostly style

Yi.Core: Follows changes to Text

Yi.Debug: debug functions now work over Text as most everything else is
now either Text or YiString.

Yi.Dired: Follow other module changes but nothing fundamental changed
otherwise.

Yi.Editor: `fst` of Status is now list of Text. Extra CPP for Typeable.

Yi.Eval: Mostly just follow changes. Raises a problem as we use
regex-tdfa which uses String. There is `regex-tdfa-text` on Hackage
which we should probably use even though it's just a thin wrapper.

Yi.File: Mostly adapt to Text.

Yi.History: Replace fields with Text but I feel YiString would do better
here.

Yi.Hoogle: Now works over YiString

Yi.IReader: Just followed changes. I have no idea why this works over
ByteString to begin with so I did not touch it. Needs investigating.

Yi.Interact: accepted spits out Text now.

Yi.Keymap.Cua: work over YiString

Yi.Keymap.Emacs: Surprisingly barely any changes, mostly imports.

Yi.Keymap.Emacs.KillRing: Kllring now works over YiString which saves
conversions on every M-w and C-y.

Yi.Keymap.Emacs.Utils: Follow minibuffer and Tag changes.

Yi.Keymap.Vim: accomodate for EventString change from String to a
newtype over Text.

Yi.Keymap.Vim.Common: EventString and OperatorName are now newtypes over
Text instead of String aliases. This was a lot of work because none of
the Vim modules actually used the aliases making it quite a chase.
Newtyping this exposed a couple of places where conversion from
EventString to OperatorName was necessary. Perhaps this indicates a bug?
A notable thing here is that the command parsers all parse a spit out
String. We should instead use Attoparsec here and directly work and get
Text. We'd much better performance due to no type conversions and
because attoparsec is damn fast although I doubt speed was ever a
concern in those modules.

Yi.Keymap.Vim.Ex.Types: ExCommand now uses Text instead of String.

Yi.Keymap.Vim.InsertMap: VimState still works with String (for now) so
there are a lot of conversions here from EventString to String, for
VimBindingE/Y.

Yi.KillRing: String → YiString

Yi.MiniBuffer: now works over Text. This is an artifact of the first
pass but considering what we do with minibuffers, it may be better to
not migrate to YiString here: the strings manipulated are never huge and
we need to do splitting, matching and all that jazz so I suspect it's
more efficient to stick to Text here. There are some conversions for
functions dumping content to buffer, such as results of the typing
callback.

Yi.Misc: deals with filepaths a bit so some conversions are necessary
but looks like Text to the outside users.

Yi.Haskell.Dollarify: Now uses YiString

Yi.Search: searches are now over YiString which is nice due to replaces
but not nice because we need to unpack to String for regex.

Yi.Snippets: ValuedMark is now over YiString.

yi-contrib:

Only one notable change here: JP.hs was importing a file from inside
Yi's test directory. I pulled out the content imported and inlined it
into the config. The module should be moved out of test directory if
people want to use it. The definition inside look useful enough to do
that. Other changes are simply String → Text/YiString conversions +
style.

Summary: There are still quite a few ‘easy’ conversions to get us to the
90%. ‘The second 90%’ requires tests and benchmarks which is why I'm
submitting this before being completely satisified. I don't want to
diverge from the main branch too much and make it difficult to merge
later, especially if I start piling testing and other tools on top of
this.

GitHub note: I will merge this about a day after making the pull request unless objections are raised as I want to implement stuff on top and continue this work.