@@ -32,6 +32,7 @@ module Ide.PluginUtils
3232 handleMaybe ,
3333 handleMaybeM ,
3434 throwPluginError ,
35+ unescape ,
3536 )
3637where
3738
@@ -43,10 +44,12 @@ import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE)
4344import Data.Algorithm.Diff
4445import Data.Algorithm.DiffOutput
4546import Data.Bifunctor (Bifunctor (first ))
47+ import Data.Char (isPrint , showLitChar )
48+ import Data.Functor (void )
4649import qualified Data.HashMap.Strict as H
47- import Data.List (find )
4850import Data.String (IsString (fromString ))
4951import qualified Data.Text as T
52+ import Data.Void (Void )
5053import Ide.Plugin.Config
5154import Ide.Plugin.Properties
5255import Ide.Types
@@ -57,6 +60,9 @@ import Language.LSP.Types hiding
5760 SemanticTokensEdit (_start ))
5861import qualified Language.LSP.Types as J
5962import Language.LSP.Types.Capabilities
63+ import qualified Text.Megaparsec as P
64+ import qualified Text.Megaparsec.Char as P
65+ import qualified Text.Megaparsec.Char.Lexer as P
6066
6167-- ---------------------------------------------------------------------
6268
@@ -255,3 +261,34 @@ pluginResponse :: Monad m => ExceptT String m a -> m (Either ResponseError a)
255261pluginResponse =
256262 fmap (first (\ msg -> ResponseError InternalError (fromString msg) Nothing ))
257263 . runExceptT
264+
265+ -- ---------------------------------------------------------------------
266+
267+ type TextParser = P. Parsec Void T. Text
268+
269+ -- | Unescape printable escape sequences within double quotes.
270+ -- This is useful if you have to call 'show' indirectly, and it escapes some characters which you would prefer to
271+ -- display as is.
272+ unescape :: T. Text -> T. Text
273+ unescape input =
274+ case P. runParser escapedTextParser " inline" input of
275+ Left _ -> input
276+ Right strs -> T. pack strs
277+
278+ -- | Parser for a string that contains double quotes. Returns unescaped string.
279+ escapedTextParser :: TextParser String
280+ escapedTextParser = concat <$> P. many (outsideStringLiteral P. <|> stringLiteral)
281+ where
282+ outsideStringLiteral :: TextParser String
283+ outsideStringLiteral = P. someTill (P. anySingleBut ' "' ) (P. lookAhead (void (P. char ' "' ) P. <|> P. eof))
284+
285+ stringLiteral :: TextParser String
286+ stringLiteral = do
287+ inside <- P. char ' "' >> P. manyTill P. charLiteral (P. char ' "' )
288+ let f ' "' = " \\\" " -- double quote should still be escaped
289+ -- Despite the docs, 'showLitChar' and 'showLitString' from 'Data.Char' DOES ESCAPE unicode printable
290+ -- characters. So we need to call 'isPrint' from 'Data.Char' manually.
291+ f ch = if isPrint ch then [ch] else showLitChar ch " "
292+ inside' = concatMap f inside
293+
294+ pure $ " \" " <> inside' <> " \" "
0 commit comments