Support text-2.0#392
Conversation
|
Thanks for the pr. For things like this i miss some sort of benchmark here. We could use the hls benchmark making ghcide use this pr though. |
|
Nice! Will |
Could you point me to a specific benchmark please?
Of course, it is compatible with both already. |
Sure, it is the benchmark we use for ghcide: https://github.com/haskell/haskell-language-server/blob/9c2bc32875ae0bb6871d7cc06547da129cf4ff5f/ghcide/ghcide.cabal#L449 in our ci |
| (before, after) = Rope.splitAt start str | ||
| after' = Rope.drop len after | ||
| (before, after) = fromJust $ Rope.splitAtPosition finish str | ||
| (before', _) = fromJust $ Rope.splitAtPosition start before |
There was a problem hiding this comment.
What's the best way to handle malformed input here? Since LSP counts by UTF-16 code units (not code points), it is possible that split happens in the middle of a code point, in which case text-lines returns Nothing.
(rope-utf16-splay in such situation silently extends split region until the end of a code point)
There was a problem hiding this comment.
Nope. To be honest I have never really paid attention to issues of splits in the middle of code points, assuming the client will send meaningful input, since they are managing a cursor over the text and sending its positions.
There was a problem hiding this comment.
We could throw. Maybe blowing up with an informative error message is the most sensible thing to do here.
There was a problem hiding this comment.
I would be happy with either:
- Throwing, based on judging this to be very unlikely.
- Returning
Nothing, and propagating that up to the exported functions. InchangeFromClientVFSwe can drop the change and log, as is done in the existingNothingbranch.
Thoughts @alanz ?
There was a problem hiding this comment.
Alternatively, we can merge this with the fromJust and experiment with removing it with @Bodigrim out of the loop :)
There was a problem hiding this comment.
Looking more at the code, I think we could push these failures right the way up to the LSP message response, which is probably the right thing to do, but I think we should do it separately. So my vote goes for "leave fromJust for now and remove subsequently", which I volunteer to look into.
There was a problem hiding this comment.
I think this is unlikely to happen in practice, so leaving as is would likely be safe, as is pushing it up to an LSP response. For the latter it at least makes us aware it is happening, and hopefully that will show up in initial developer dogfooding, or not at all.
| $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (fromIntegral l) ropetext | ||
| let beforePos = T.take (fromIntegral c) curLine | ||
| let curRope = fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (fromIntegral l) ropetext | ||
| beforePos <- Rope.toText . fst <$> Rope.splitAt (fromIntegral c) curRope |
There was a problem hiding this comment.
BTW this fixes an existing bug: T.take counts code points, while LSP requires counting UTF-16 code units.
|
Benchmarks adapted from https://github.com/ollef/rope-bench/blob/main/bench/Bench.hs show that |
|
@Bodigrim do you feel motivated to maintain |
|
I think I'm beyond the point of no return :) and will release and maintain it anyway. More benchmarks, now with a realistic payload mimicking |
|
Hard to argue with those performance numbers. 👍 |
michaelpj
left a comment
There was a problem hiding this comment.
LGTM, so long as we resolve the question of what to do with malformed input.
| let modParts = dropWhile (not . isUpper . T.head) | ||
| $ reverse $ filter (not .T.null) xs | ||
| modName = T.intercalate "." modParts | ||
| curLine <- headMaybe $ T.lines $ Rope.toText curRope |
There was a problem hiding this comment.
Why not use Rope.lines? Won't that be faster?
There was a problem hiding this comment.
curRope is already a single line, but it likely includes an enclosing \n. The only purpose of headMaybe . T.lines . Rope.toText is to strip this \n. I've changed it to a more explicit version.
| new = applyChange (fromString orig) | ||
| $ J.TextDocumentContentChangeEvent (Just $ J.mkRange 2 1 2 5) (Just 4) "" | ||
| lines (Rope.toString new) `shouldBe` | ||
| lines (T.unpack $ Rope.toText new) `shouldBe` |
There was a problem hiding this comment.
I think these could all be
| lines (T.unpack $ Rope.toText new) `shouldBe` | |
| Rope.lines new `shouldBe` |
| (before, after) = Rope.splitAt start str | ||
| after' = Rope.drop len after | ||
| (before, after) = fromJust $ Rope.splitAtPosition finish str | ||
| (before', _) = fromJust $ Rope.splitAtPosition start before |
There was a problem hiding this comment.
I would be happy with either:
- Throwing, based on judging this to be very unlikely.
- Returning
Nothing, and propagating that up to the exported functions. InchangeFromClientVFSwe can drop the change and log, as is done in the existingNothingbranch.
Thoughts @alanz ?
Closes #391.
This is a draft, need to handle
fromJustmore gracefully (and publishtext-lineson Hackage).I tried adapting
rope-utf16-splaytotext-2.0, but did not succeed: the changes required are too radical to its API. Hence a switch totext-lines.