Start swapping file URLs with JSON parsing
[lsp-test.git] / src / Language / Haskell / LSP / Test / Files.hs
index 52632eb91d7b9e81b1795dc0c1b435f65d7f19b3..0d79ebd658c2a3f03d80d073a41f8f6552f2385f 100644 (file)
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE OverloadedStrings #-}
 module Language.Haskell.LSP.Test.Files
-  ( loadSwappedFiles
+  ( swapFiles
   , FileMap
   , emptyFileMap
   )
 where
 
-import           Language.Haskell.LSP.Core
-import qualified Language.Haskell.LSP.Control  as Control
 import           Language.Haskell.LSP.Types        hiding ( error )
-import           Data.Default
-import           Control.Lens
+import           Language.Haskell.LSP.Test.Parsing
 import           Control.Monad
-import           Control.Concurrent
 import           Data.Aeson
 import qualified Data.ByteString.Lazy.Char8    as B
-import           Data.Map                      as Map
+import qualified Data.Text                     as T
+import qualified Data.Map                      as Map
+import           Data.Map ((!))
+import qualified Data.HashMap.Strict           as HashMap
+import qualified Data.Set                      as Set
 import           Data.Maybe
 import           System.Directory
 import           System.IO
+import           System.FilePath
 
-type FileMap = Map.Map FilePath FilePath
+type FileMap = Map.Map Uri Uri
 
 emptyFileMap :: FileMap
 emptyFileMap = Map.empty
 
-buildFiles
-  :: (HasParams a b, HasTextDocument b c, HasUri c Uri)
-  => [a]
-  -> FileMap
-  -> IO FileMap
-buildFiles ns oldMap = foldM createFile oldMap ns
+buildFileMap :: [Uri] -> FileMap -> IO FileMap
+buildFileMap uris oldMap = foldM createFile oldMap uris
  where
-  createFile map n = do
-    let fp = fromMaybe (error "Couldn't convert file path")
-                       (uriToFilePath $ n ^. params . textDocument . uri)
-    if Map.member fp map
+  createFile map uri =
+    if Map.member uri map
       then return map
       else do
-        tmpDir        <- getTemporaryDirectory
-        (tmpFp, tmpH) <- openTempFile tmpDir "lspTestDoc"
+        let fp = fromMaybe (error "Couldn't convert file path")
+                 (uriToFilePath uri)
+
+        -- Need to store in a directory inside tmp directory
+        -- otherwise ghc-mod ends up creating one for us
+        tmpDir <- (</> "lsp-test") <$> getTemporaryDirectory
+        createDirectoryIfMissing False tmpDir
+
+        (tmpFp, tmpH) <- openTempFile tmpDir (takeFileName fp)
+
         readFile fp >>= hPutStr tmpH
-        return $ Map.insert fp tmpFp map
-
-swapFile :: (HasUri a Uri) => FileMap -> a -> a
-swapFile m msg = fromMaybe msg $ do
-  let oldUri = msg ^. uri
-  oldFp <- uriToFilePath oldUri
-  newFp <- Map.lookup oldFp m
-  let newUri = filePathToUri newFp
-  return $ uri .~ newUri $ msg
-
-loadSwappedFiles :: FileMap -> Handle -> IO ([B.ByteString], FileMap)
-loadSwappedFiles map h = do
-  fileMapVar <- newMVar map
-  msgsVar    <- newMVar []
-  nullH      <- openFile "/dev/null" WriteMode
-  Control.runWithHandles h
-                         nullH
-                         (const $ Right (), const $ return Nothing)
-                         (handlers msgsVar fileMapVar)
-                         def
-                         Nothing
-                         Nothing
-  newMap <- readMVar fileMapVar
-  msgs   <- reverse <$> readMVar msgsVar
-  return (msgs, newMap)
-
-handlers :: MVar [B.ByteString] -> MVar FileMap -> Handlers
-handlers msgs fileMap = Handlers
-  {
-    -- Requests
-    hoverHandler                             = Just put
-  , completionHandler                        = Just put
-  , completionResolveHandler                 = Just put
-  , signatureHelpHandler                     = Just put
-  , definitionHandler                        = Just put
-  , referencesHandler                        = Just put
-  , documentHighlightHandler                 = Just put
-  , documentSymbolHandler                    = Just $ swapUri (params . textDocument)
-  , workspaceSymbolHandler                   = Just put
-  , codeActionHandler                        = Just put
-  , codeLensHandler                          = Just put
-  , codeLensResolveHandler                   = Just put
-  , documentFormattingHandler                = Just put
-  , documentRangeFormattingHandler           = Just put
-  , documentTypeFormattingHandler            = Just put
-  , renameHandler                            = Just $ swapUri (params . textDocument)
-  , documentLinkHandler                      = Just $ swapUri (params . textDocument)
-  , documentLinkResolveHandler               = Just put
-  , executeCommandHandler                    = Just put
-  , initializeRequestHandler                 = Just put
-    -- Notifications
-  , didChangeConfigurationParamsHandler      = Just put
-  , didOpenTextDocumentNotificationHandler   = Just $ swapUri (params . textDocument)
-  , didChangeTextDocumentNotificationHandler = Just $ swapUri (params . textDocument)
-  , didCloseTextDocumentNotificationHandler  = Just $ swapUri (params . textDocument)
-  , didSaveTextDocumentNotificationHandler   = Just $ swapUri (params . textDocument)
-  , willSaveWaitUntilTextDocHandler          = Just put
-  , didChangeWatchedFilesNotificationHandler = Just put
-  , initializedHandler                       = Just put
-  , willSaveTextDocumentNotificationHandler  = Just $ swapUri (params . textDocument)
-  , cancelNotificationHandler                = Just put
-  , exitNotificationHandler                  = Just put
-    -- Responses
-  , responseHandler                          = Just put
-  }
- where
-  swapUri f msg = do
-    modifyMVar_ fileMap (buildFiles [msg])
-    map <- readMVar fileMap
-    put $ swapFile map $ msg ^. f
+        tmpUri <- filePathToUri <$> canonicalizePath tmpFp
+        return $ Map.insert uri tmpUri map
+
+swapFiles :: FileMap -> Handle -> IO ([B.ByteString], FileMap)
+swapFiles fileMap h = do
+  msgs <- getAllMessages h
+
+  let oldUris = Set.unions $ map extractUris msgs
+
+  newMap <- buildFileMap (Set.elems oldUris) fileMap
+
+  let newMsgs = map (swapUris newMap) msgs
+
+  return (newMsgs, newMap)
+
+extractUris :: B.ByteString -> Set.Set Uri
+extractUris msgs =
+  case decode msgs :: Maybe Object of
+    Just obj -> HashMap.foldlWithKey' gather Set.empty obj
+    Nothing -> error "nooo"
+  where gather :: Set.Set Uri -> T.Text -> Value -> Set.Set Uri
+        gather uris "uri" (String s) = Set.insert (Uri s) uris
+        gather uris _ (Object o) = HashMap.foldlWithKey' gather uris o
+        gather uris _ _ = uris
+
+swapUris :: FileMap -> B.ByteString -> B.ByteString
+swapUris fileMap msg =
+  case decode msg :: Maybe Object of
+    Just obj -> encode $ HashMap.mapWithKey f obj
+    Nothing -> error "Couldn't decode message"
+
+  where f :: T.Text -> Value -> Value
+        f "uri" (String uri) = String $ swap uri
+        f "changes" (Object obj) = Object $
+          HashMap.foldlWithKey' (\acc k v -> HashMap.insert (swap k) v acc)
+                                HashMap.empty
+                                obj
+        f _ x = g x
+
+        g :: Value -> Value
+        g (Array arr) = Array $ fmap g arr
+        g (Object obj) = Object $ HashMap.mapWithKey f obj
+        g x = x
 
-  put :: ToJSON a => a -> IO ()
-  put msg = modifyMVar_ msgs (return . (encode msg :))
+        swap origUri = let (Uri newUri) = fileMap ! Uri origUri in newUri