{-# LANGUAGE DataKinds #-}

module Unison.LSP.Formatting where

import Control.Lens hiding (List)
import Data.Set qualified as Set
import Language.LSP.Protocol.Lens
import Language.LSP.Protocol.Message qualified as Msg
import Language.LSP.Protocol.Types
import Unison.Codebase.Editor.HandleInput.FormatFile qualified as Formatting
import Unison.Codebase.ProjectPath qualified as PP
import Unison.LSP.Conversions (lspToURange, uToLspRange)
import Unison.LSP.FileAnalysis (getFileAnalysis)
import Unison.LSP.FileAnalysis qualified as FileAnalysis
import Unison.LSP.Types
import Unison.Prelude

formatDocRequest :: Msg.TRequestMessage 'Msg.Method_TextDocumentFormatting -> (Either Msg.ResponseError (Msg.MessageResult 'Msg.Method_TextDocumentFormatting) -> Lsp ()) -> Lsp ()
formatDocRequest :: TRequestMessage 'Method_TextDocumentFormatting
-> (Either
      ResponseError (MessageResult 'Method_TextDocumentFormatting)
    -> Lsp ())
-> Lsp ()
formatDocRequest TRequestMessage 'Method_TextDocumentFormatting
m Either ResponseError (MessageResult 'Method_TextDocumentFormatting)
-> Lsp ()
respond = do
  [TextEdit]
edits <- Uri -> Maybe (Set Range) -> Lsp [TextEdit]
formatDefs (TRequestMessage 'Method_TextDocumentFormatting
m TRequestMessage 'Method_TextDocumentFormatting
-> Getting Uri (TRequestMessage 'Method_TextDocumentFormatting) Uri
-> Uri
forall s a. s -> Getting a s a -> a
^. (DocumentFormattingParams -> Const Uri DocumentFormattingParams)
-> TRequestMessage 'Method_TextDocumentFormatting
-> Const Uri (TRequestMessage 'Method_TextDocumentFormatting)
forall s a. HasParams s a => Lens' s a
Lens'
  (TRequestMessage 'Method_TextDocumentFormatting)
  DocumentFormattingParams
params ((DocumentFormattingParams -> Const Uri DocumentFormattingParams)
 -> TRequestMessage 'Method_TextDocumentFormatting
 -> Const Uri (TRequestMessage 'Method_TextDocumentFormatting))
-> ((Uri -> Const Uri Uri)
    -> DocumentFormattingParams -> Const Uri DocumentFormattingParams)
-> Getting Uri (TRequestMessage 'Method_TextDocumentFormatting) Uri
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TextDocumentIdentifier -> Const Uri TextDocumentIdentifier)
-> DocumentFormattingParams -> Const Uri DocumentFormattingParams
forall s a. HasTextDocument s a => Lens' s a
Lens' DocumentFormattingParams TextDocumentIdentifier
textDocument ((TextDocumentIdentifier -> Const Uri TextDocumentIdentifier)
 -> DocumentFormattingParams -> Const Uri DocumentFormattingParams)
-> ((Uri -> Const Uri Uri)
    -> TextDocumentIdentifier -> Const Uri TextDocumentIdentifier)
-> (Uri -> Const Uri Uri)
-> DocumentFormattingParams
-> Const Uri DocumentFormattingParams
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Uri -> Const Uri Uri)
-> TextDocumentIdentifier -> Const Uri TextDocumentIdentifier
forall s a. HasUri s a => Lens' s a
Lens' TextDocumentIdentifier Uri
uri) Maybe (Set Range)
forall a. Maybe a
Nothing
  Either ResponseError ([TextEdit] |? Null) -> Lsp ()
Either ResponseError (MessageResult 'Method_TextDocumentFormatting)
-> Lsp ()
respond (Either ResponseError ([TextEdit] |? Null) -> Lsp ())
-> ([TextEdit] -> Either ResponseError ([TextEdit] |? Null))
-> [TextEdit]
-> Lsp ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([TextEdit] |? Null) -> Either ResponseError ([TextEdit] |? Null)
forall a b. b -> Either a b
Right (([TextEdit] |? Null) -> Either ResponseError ([TextEdit] |? Null))
-> ([TextEdit] -> [TextEdit] |? Null)
-> [TextEdit]
-> Either ResponseError ([TextEdit] |? Null)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TextEdit] -> [TextEdit] |? Null
forall a b. a -> a |? b
InL ([TextEdit] -> Lsp ()) -> [TextEdit] -> Lsp ()
forall a b. (a -> b) -> a -> b
$ [TextEdit]
edits

formatRangeRequest :: Msg.TRequestMessage 'Msg.Method_TextDocumentRangeFormatting -> (Either Msg.ResponseError (Msg.MessageResult 'Msg.Method_TextDocumentRangeFormatting) -> Lsp ()) -> Lsp ()
formatRangeRequest :: TRequestMessage 'Method_TextDocumentRangeFormatting
-> (Either
      ResponseError (MessageResult 'Method_TextDocumentRangeFormatting)
    -> Lsp ())
-> Lsp ()
formatRangeRequest TRequestMessage 'Method_TextDocumentRangeFormatting
m Either
  ResponseError (MessageResult 'Method_TextDocumentRangeFormatting)
-> Lsp ()
respond = do
  let p :: DocumentRangeFormattingParams
p = TRequestMessage 'Method_TextDocumentRangeFormatting
m TRequestMessage 'Method_TextDocumentRangeFormatting
-> Getting
     DocumentRangeFormattingParams
     (TRequestMessage 'Method_TextDocumentRangeFormatting)
     DocumentRangeFormattingParams
-> DocumentRangeFormattingParams
forall s a. s -> Getting a s a -> a
^. Getting
  DocumentRangeFormattingParams
  (TRequestMessage 'Method_TextDocumentRangeFormatting)
  DocumentRangeFormattingParams
forall s a. HasParams s a => Lens' s a
Lens'
  (TRequestMessage 'Method_TextDocumentRangeFormatting)
  DocumentRangeFormattingParams
params
  [TextEdit]
edits <- Uri -> Maybe (Set Range) -> Lsp [TextEdit]
formatDefs (DocumentRangeFormattingParams
p DocumentRangeFormattingParams
-> Getting Uri DocumentRangeFormattingParams Uri -> Uri
forall s a. s -> Getting a s a -> a
^. (TextDocumentIdentifier -> Const Uri TextDocumentIdentifier)
-> DocumentRangeFormattingParams
-> Const Uri DocumentRangeFormattingParams
forall s a. HasTextDocument s a => Lens' s a
Lens' DocumentRangeFormattingParams TextDocumentIdentifier
textDocument ((TextDocumentIdentifier -> Const Uri TextDocumentIdentifier)
 -> DocumentRangeFormattingParams
 -> Const Uri DocumentRangeFormattingParams)
-> ((Uri -> Const Uri Uri)
    -> TextDocumentIdentifier -> Const Uri TextDocumentIdentifier)
-> Getting Uri DocumentRangeFormattingParams Uri
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Uri -> Const Uri Uri)
-> TextDocumentIdentifier -> Const Uri TextDocumentIdentifier
forall s a. HasUri s a => Lens' s a
Lens' TextDocumentIdentifier Uri
uri) (Set Range -> Maybe (Set Range)
forall a. a -> Maybe a
Just (Set Range -> Maybe (Set Range))
-> (Range -> Set Range) -> Range -> Maybe (Set Range)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Range -> Set Range
forall a. a -> Set a
Set.singleton (Range -> Maybe (Set Range)) -> Range -> Maybe (Set Range)
forall a b. (a -> b) -> a -> b
$ DocumentRangeFormattingParams
p DocumentRangeFormattingParams
-> Getting Range DocumentRangeFormattingParams Range -> Range
forall s a. s -> Getting a s a -> a
^. Getting Range DocumentRangeFormattingParams Range
forall s a. HasRange s a => Lens' s a
Lens' DocumentRangeFormattingParams Range
range)
  Either ResponseError ([TextEdit] |? Null) -> Lsp ()
Either
  ResponseError (MessageResult 'Method_TextDocumentRangeFormatting)
-> Lsp ()
respond (Either ResponseError ([TextEdit] |? Null) -> Lsp ())
-> ([TextEdit] -> Either ResponseError ([TextEdit] |? Null))
-> [TextEdit]
-> Lsp ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([TextEdit] |? Null) -> Either ResponseError ([TextEdit] |? Null)
forall a b. b -> Either a b
Right (([TextEdit] |? Null) -> Either ResponseError ([TextEdit] |? Null))
-> ([TextEdit] -> [TextEdit] |? Null)
-> [TextEdit]
-> Either ResponseError ([TextEdit] |? Null)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TextEdit] -> [TextEdit] |? Null
forall a b. a -> a |? b
InL ([TextEdit] -> Lsp ()) -> [TextEdit] -> Lsp ()
forall a b. (a -> b) -> a -> b
$ [TextEdit]
edits

-- | Format all definitions in a file.
formatDefs :: Uri -> Maybe (Set Range {- the ranges to format, if Nothing then format the whole file. -}) -> Lsp [TextEdit]
formatDefs :: Uri -> Maybe (Set Range) -> Lsp [TextEdit]
formatDefs Uri
fileUri Maybe (Set Range)
mayRangesToFormat =
  [TextEdit] -> Maybe [TextEdit] -> [TextEdit]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [TextEdit] -> [TextEdit])
-> Lsp (Maybe [TextEdit]) -> Lsp [TextEdit]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MaybeT Lsp [TextEdit] -> Lsp (Maybe [TextEdit])
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT do
    FileAnalysis {$sel:parsedFile:FileAnalysis :: FileAnalysis -> Maybe (UnisonFile Symbol Ann)
parsedFile = Maybe (UnisonFile Symbol Ann)
mayParsedFile, $sel:typecheckedFile:FileAnalysis :: FileAnalysis -> Maybe (TypecheckedUnisonFile Symbol Ann)
typecheckedFile = Maybe (TypecheckedUnisonFile Symbol Ann)
mayTypecheckedFile} <- Uri -> MaybeT Lsp FileAnalysis
getFileAnalysis Uri
fileUri
    ProjectPath
pp <- Lsp ProjectPath -> MaybeT Lsp ProjectPath
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Lsp ProjectPath
getCurrentProjectPath
    Config {Int
formattingWidth :: Int
$sel:formattingWidth:Config :: Config -> Int
formattingWidth} <- Lsp Config -> MaybeT Lsp Config
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Lsp Config
getConfig
    Lsp (Maybe [TextEdit]) -> MaybeT Lsp [TextEdit]
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (Lsp (Maybe [TextEdit]) -> MaybeT Lsp [TextEdit])
-> Lsp (Maybe [TextEdit]) -> MaybeT Lsp [TextEdit]
forall a b. (a -> b) -> a -> b
$
      (Maybe (UnisonFile Symbol Ann)
 -> Maybe (TypecheckedUnisonFile Symbol Ann)
 -> Lsp PrettyPrintEnvDecl)
-> Int
-> Absolute
-> Maybe (UnisonFile Symbol Ann)
-> Maybe (TypecheckedUnisonFile Symbol Ann)
-> Maybe (Set Range)
-> Lsp (Maybe [TextReplacement])
forall (m :: * -> *).
Monad m =>
(Maybe (UnisonFile Symbol Ann)
 -> Maybe (TypecheckedUnisonFile Symbol Ann)
 -> m PrettyPrintEnvDecl)
-> Int
-> Absolute
-> Maybe (UnisonFile Symbol Ann)
-> Maybe (TypecheckedUnisonFile Symbol Ann)
-> Maybe (Set Range)
-> m (Maybe [TextReplacement])
Formatting.formatFile (\Maybe (UnisonFile Symbol Ann)
uf Maybe (TypecheckedUnisonFile Symbol Ann)
tf -> Maybe (UnisonFile Symbol Ann)
-> Maybe (TypecheckedUnisonFile Symbol Ann)
-> Lsp PrettyPrintEnvDecl
forall a.
Maybe (UnisonFile Symbol a)
-> Maybe (TypecheckedUnisonFile Symbol a) -> Lsp PrettyPrintEnvDecl
FileAnalysis.ppedForFileHelper Maybe (UnisonFile Symbol Ann)
uf Maybe (TypecheckedUnisonFile Symbol Ann)
tf) Int
formattingWidth (ProjectPath
pp ProjectPath -> Getting Absolute ProjectPath Absolute -> Absolute
forall s a. s -> Getting a s a -> a
^. Getting Absolute ProjectPath Absolute
forall p b (f :: * -> *).
Functor f =>
(Absolute -> f Absolute)
-> ProjectPathG p b -> f (ProjectPathG p b)
PP.absPath_) Maybe (UnisonFile Symbol Ann)
mayParsedFile Maybe (TypecheckedUnisonFile Symbol Ann)
mayTypecheckedFile ((Range -> Range) -> Set Range -> Set Range
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map Range -> Range
lspToURange (Set Range -> Set Range) -> Maybe (Set Range) -> Maybe (Set Range)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (Set Range)
mayRangesToFormat)
        Lsp (Maybe [TextReplacement])
-> (Maybe [TextReplacement] -> Maybe [TextEdit])
-> Lsp (Maybe [TextEdit])
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (([TextReplacement] -> [TextEdit])
-> Maybe [TextReplacement] -> Maybe [TextEdit]
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([TextReplacement] -> [TextEdit])
 -> Maybe [TextReplacement] -> Maybe [TextEdit])
-> ((TextReplacement -> TextEdit)
    -> [TextReplacement] -> [TextEdit])
-> (TextReplacement -> TextEdit)
-> Maybe [TextReplacement]
-> Maybe [TextEdit]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TextReplacement -> TextEdit) -> [TextReplacement] -> [TextEdit]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap) TextReplacement -> TextEdit
uTextReplacementToLSP
  where
    uTextReplacementToLSP :: Formatting.TextReplacement -> TextEdit
    uTextReplacementToLSP :: TextReplacement -> TextEdit
uTextReplacementToLSP (Formatting.TextReplacement Text
newText Range
range) = Range -> Text -> TextEdit
TextEdit (Range -> Range
uToLspRange Range
range) Text
newText