{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
module Unison.LSP.VFS.CompletionPrefix
( PosPrefixInfo (..),
getCompletionPrefix,
)
where
import Data.Char (isAlphaNum, isUpper)
import Data.Maybe
import Data.Text qualified as T
import Data.Text.Utf16.Rope.Mixed qualified as Rope
import Language.LSP.Protocol.Types qualified as J
import Language.LSP.VFS
data PosPrefixInfo = PosPrefixInfo
{
PosPrefixInfo -> Text
fullLine :: !T.Text,
PosPrefixInfo -> Text
prefixModule :: !T.Text,
PosPrefixInfo -> Text
prefixText :: !T.Text,
PosPrefixInfo -> Position
cursorPos :: !J.Position
}
deriving (Int -> PosPrefixInfo -> ShowS
[PosPrefixInfo] -> ShowS
PosPrefixInfo -> String
(Int -> PosPrefixInfo -> ShowS)
-> (PosPrefixInfo -> String)
-> ([PosPrefixInfo] -> ShowS)
-> Show PosPrefixInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PosPrefixInfo -> ShowS
showsPrec :: Int -> PosPrefixInfo -> ShowS
$cshow :: PosPrefixInfo -> String
show :: PosPrefixInfo -> String
$cshowList :: [PosPrefixInfo] -> ShowS
showList :: [PosPrefixInfo] -> ShowS
Show, PosPrefixInfo -> PosPrefixInfo -> Bool
(PosPrefixInfo -> PosPrefixInfo -> Bool)
-> (PosPrefixInfo -> PosPrefixInfo -> Bool) -> Eq PosPrefixInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PosPrefixInfo -> PosPrefixInfo -> Bool
== :: PosPrefixInfo -> PosPrefixInfo -> Bool
$c/= :: PosPrefixInfo -> PosPrefixInfo -> Bool
/= :: PosPrefixInfo -> PosPrefixInfo -> Bool
Eq)
getCompletionPrefix :: (Monad m) => J.Position -> VirtualFile -> m (Maybe PosPrefixInfo)
getCompletionPrefix :: forall (m :: * -> *).
Monad m =>
Position -> VirtualFile -> m (Maybe PosPrefixInfo)
getCompletionPrefix pos :: Position
pos@(J.Position UInt
l UInt
c) (VirtualFile Int32
_ Int
_ Rope
ropetext) =
Maybe PosPrefixInfo -> m (Maybe PosPrefixInfo)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe PosPrefixInfo -> m (Maybe PosPrefixInfo))
-> Maybe PosPrefixInfo -> m (Maybe PosPrefixInfo)
forall a b. (a -> b) -> a -> b
$ PosPrefixInfo -> Maybe PosPrefixInfo
forall a. a -> Maybe a
Just (PosPrefixInfo -> Maybe PosPrefixInfo)
-> PosPrefixInfo -> Maybe PosPrefixInfo
forall a b. (a -> b) -> a -> b
$ PosPrefixInfo -> Maybe PosPrefixInfo -> PosPrefixInfo
forall a. a -> Maybe a -> a
fromMaybe (Text -> Text -> Text -> Position -> PosPrefixInfo
PosPrefixInfo Text
"" Text
"" Text
"" Position
pos) (Maybe PosPrefixInfo -> PosPrefixInfo)
-> Maybe PosPrefixInfo -> PosPrefixInfo
forall a b. (a -> b) -> a -> b
$ do
let lastMaybe :: [a] -> Maybe a
lastMaybe [] = Maybe a
forall a. Maybe a
Nothing
lastMaybe [a]
xs = a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ [a] -> a
forall a. HasCallStack => [a] -> a
last [a]
xs
let curRope :: Rope
curRope = (Rope, Rope) -> Rope
forall a b. (a, b) -> a
fst ((Rope, Rope) -> Rope) -> (Rope, Rope) -> Rope
forall a b. (a -> b) -> a -> b
$ Word -> Rope -> (Rope, Rope)
Rope.splitAtLine Word
1 (Rope -> (Rope, Rope)) -> Rope -> (Rope, Rope)
forall a b. (a -> b) -> a -> b
$ (Rope, Rope) -> Rope
forall a b. (a, b) -> b
snd ((Rope, Rope) -> Rope) -> (Rope, Rope) -> Rope
forall a b. (a -> b) -> a -> b
$ Word -> Rope -> (Rope, Rope)
Rope.splitAtLine (UInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral UInt
l) Rope
ropetext
let beforePos :: Text
beforePos = Rope -> Text
Rope.toText (Rope -> Text) -> ((Rope, Rope) -> Rope) -> (Rope, Rope) -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Rope, Rope) -> Rope
forall a b. (a, b) -> a
fst ((Rope, Rope) -> Text) -> (Rope, Rope) -> Text
forall a b. (a -> b) -> a -> b
$ Word -> Rope -> (Rope, Rope)
Rope.charSplitAt (UInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral UInt
c) Rope
curRope
curWord <-
if
| Text -> Bool
T.null Text
beforePos -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
""
| HasCallStack => Text -> Char
Text -> Char
T.last Text
beforePos Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ' -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
""
| Bool
otherwise -> [Text] -> Maybe Text
forall {a}. [a] -> Maybe a
lastMaybe (Text -> [Text]
T.words Text
beforePos)
let parts =
(Char -> Bool) -> Text -> [Text]
T.split (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.') (Text -> [Text]) -> Text -> [Text]
forall a b. (a -> b) -> a -> b
$
(Char -> Bool) -> Text -> Text
T.takeWhileEnd (\Char
x -> Char -> Bool
isAlphaNum Char
x Bool -> Bool -> Bool
|| Char
x Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (String
"._'" :: String)) Text
curWord
case reverse parts of
[] -> Maybe PosPrefixInfo
forall a. Maybe a
Nothing
(Text
x : [Text]
xs) -> do
let modParts :: [Text]
modParts =
(Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Bool -> Bool
not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isUpper (Char -> Bool) -> (Text -> Char) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack => Text -> Char
Text -> Char
T.head) ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$
[Text] -> [Text]
forall a. [a] -> [a]
reverse ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$
(Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null) [Text]
xs
modName :: Text
modName = Text -> [Text] -> Text
T.intercalate Text
"." [Text]
modParts
let curLine :: Text
curLine = (Char -> Bool) -> Text -> Text
T.dropWhileEnd (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n') (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ Rope -> Text
Rope.toText Rope
curRope
PosPrefixInfo -> Maybe PosPrefixInfo
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (PosPrefixInfo -> Maybe PosPrefixInfo)
-> PosPrefixInfo -> Maybe PosPrefixInfo
forall a b. (a -> b) -> a -> b
$ Text -> Text -> Text -> Position -> PosPrefixInfo
PosPrefixInfo Text
curLine Text
modName Text
x Position
pos