From 180c6928354b811ca9e6a5bdc26cdcdbc94e6b5a Mon Sep 17 00:00:00 2001
From: Nick Suchecki <nicksuchecki@gmail.com>
Date: Sat, 26 Feb 2022 23:13:08 -0500
Subject: [PATCH 1/2] Fix defining new function code action

Use `GetParsedModuleWithComments` rather than `GetParsedModule` as the
default `ToCodeAction` instance. The insertion code needs knowledge of
comments in order to properly insert the function definition.

Also swaps out the old default `error "Not Implemented"` definition with
a hole.
---
 .../src/Development/IDE/Plugin/CodeAction.hs  |  5 +-
 .../Development/IDE/Plugin/CodeAction/Args.hs |  2 +-
 ghcide/test/exe/Main.hs                       | 88 ++++++++++++++++---
 3 files changed, 78 insertions(+), 17 deletions(-)

diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs
index 37a4c7ecbc..7d10ff105b 100644
--- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs
+++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs
@@ -64,6 +64,8 @@ import qualified GHC.LanguageExtensions                            as Lang
 import           Ide.PluginUtils                                   (subRange)
 import           Ide.Types
 import qualified Language.LSP.Server                               as LSP
+
+
 import           Language.LSP.Types                                (CodeAction (..),
                                                                     CodeActionContext (CodeActionContext, _diagnostics),
                                                                     CodeActionKind (CodeActionQuickFix, CodeActionUnknown),
@@ -711,7 +713,7 @@ newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ
       , _start `isInsideSrcSpan` l]
     , nextLineP <- Position{ _line = _line lastLineP + 1, _character = 0}
     = [ ("Define " <> sig
-        , [TextEdit (Range nextLineP nextLineP) (T.unlines ["", sig, name <> " = error \"not implemented\""])]
+        , [TextEdit (Range nextLineP nextLineP) (T.unlines ["", sig, name <> " = _"])]
         )]
     | otherwise = []
   where
@@ -719,7 +721,6 @@ newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ
     sig = name <> colon <> T.dropWhileEnd isSpace typ
     ParsedModule{pm_parsed_source = L _ HsModule{hsmodDecls}} = parsedModule
 
-
 suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)]
 suggestFillTypeWildcard Diagnostic{_range=_range,..}
 -- Foo.hs:3:8: error:
diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs
index 32f5b34aa9..85f100ca66 100644
--- a/ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs
+++ b/ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs
@@ -65,7 +65,7 @@ runGhcideCodeAction state (CodeActionParams _ _ (TextDocumentIdentifier uri) _ra
           pure $ localExports <> pkgExports
         _ -> pure mempty
   caaIdeOptions <- onceIO $ runAction "GhcideCodeActions.getIdeOptions" state getIdeOptions
-  caaParsedModule <- onceIO $ runRule GetParsedModule
+  caaParsedModule <- onceIO $ runRule GetParsedModuleWithComments
   caaContents <-
     onceIO $
       runRule GetFileContents >>= \case
diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs
index 286c5e98d0..f82cc9509f 100644
--- a/ghcide/test/exe/Main.hs
+++ b/ghcide/test/exe/Main.hs
@@ -55,12 +55,12 @@ import           Development.IDE.Test                     (Cursor,
                                                            flushMessages,
                                                            getInterfaceFilesDir,
                                                            getStoredKeys,
+                                                           isReferenceReady,
+                                                           referenceReady,
                                                            standardizeQuotes,
                                                            waitForAction,
                                                            waitForGC,
-                                                           waitForTypecheck,
-                                                           isReferenceReady,
-                                                           referenceReady)
+                                                           waitForTypecheck)
 import           Development.IDE.Test.Runfiles
 import qualified Development.IDE.Types.Diagnostics        as Diagnostics
 import           Development.IDE.Types.Location
@@ -97,6 +97,7 @@ import           Test.QuickCheck
 import           Control.Concurrent.Async
 import           Control.Lens                             (to, (^.))
 import           Control.Monad.Extra                      (whenJust)
+import           Data.Function                            ((&))
 import           Data.IORef
 import           Data.IORef.Extra                         (atomicModifyIORef_)
 import           Data.String                              (IsString (fromString))
@@ -107,6 +108,18 @@ import qualified Development.IDE.Plugin.HLS.GhcIde        as Ghcide
 import           Development.IDE.Plugin.Test              (TestRequest (BlockSeconds),
                                                            WaitForIdeRuleResult (..),
                                                            blockCommandId)
+import           Development.IDE.Types.Logger             (Logger (Logger),
+                                                           LoggingColumn (DataColumn, PriorityColumn),
+                                                           Pretty (pretty),
+                                                           Priority (Debug),
+                                                           Recorder (Recorder, logger_),
+                                                           WithPriority (WithPriority, priority),
+                                                           cfilter,
+                                                           cmapWithPrio,
+                                                           makeDefaultStderrRecorder)
+import qualified FuzzySearch
+import           GHC.Stack                                (emptyCallStack)
+import qualified HieDbRetry
 import           Ide.PluginUtils                          (pluginDescToIdePlugins)
 import           Ide.Types
 import qualified Language.LSP.Types                       as LSP
@@ -120,19 +133,14 @@ import           Test.Tasty.Ingredients.Rerun
 import           Test.Tasty.QuickCheck
 import           Text.Printf                              (printf)
 import           Text.Regex.TDFA                          ((=~))
-import qualified HieDbRetry
-import Development.IDE.Types.Logger (WithPriority(WithPriority, priority), Priority (Debug), cmapWithPrio, Recorder (Recorder, logger_), makeDefaultStderrRecorder, cfilter, LoggingColumn (PriorityColumn, DataColumn), Logger (Logger), Pretty (pretty))
-import Data.Function ((&))
-import GHC.Stack (emptyCallStack)
-import qualified FuzzySearch
 
-data Log 
-  = LogGhcIde Ghcide.Log 
+data Log
+  = LogGhcIde Ghcide.Log
   | LogIDEMain IDE.Log
 
 instance Pretty Log where
   pretty = \case
-    LogGhcIde log -> pretty log
+    LogGhcIde log  -> pretty log
     LogIDEMain log -> pretty log
 
 -- | Wait for the next progress begin step
@@ -2411,7 +2419,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
       liftIO $ contentAfterAction @?= T.unlines (txtB ++
         [ ""
         , "select :: [Bool] -> Bool"
-        , "select = error \"not implemented\""
+        , "select = _"
         ]
         ++ txtB')
   , testSession "define a hole" $ do
@@ -2438,9 +2446,61 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
         ,"foo False = False"
         , ""
         , "select :: [Bool] -> Bool"
-        , "select = error \"not implemented\""
+        , "select = _"
         ]
         ++ txtB')
+  , testSession "insert new function definition - Haddock comments" $ do
+    let start =  ["foo :: Int -> Bool"
+                 , "foo x = select (x + 1)"
+                 , ""
+                 , "-- | This is a haddock comment"
+                 , "haddock :: Int -> Int"
+                 , "haddock = undefined"
+                 ]
+    let expected =  ["foo :: Int -> Bool"
+             , "foo x = select (x + 1)"
+             , ""
+             , "select :: Int -> Bool"
+             , "select = _"
+             , ""
+             , "-- | This is a haddock comment"
+             , "haddock :: Int -> Int"
+             , "haddock = undefined"]
+    docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start)
+    _ <- waitForDiagnostics
+    InR action@CodeAction { _title = actionTitle } : _
+                <- sortOn (\(InR CodeAction{_title=x}) -> x) <$>
+                    getCodeActions docB (R 1 0 0 50)
+    liftIO $ actionTitle @?= "Define select :: Int -> Bool"
+    executeCodeAction action
+    contentAfterAction <- documentContents docB
+    liftIO $ contentAfterAction @?= T.unlines expected
+  , testSession "insert new function definition - normal comments" $ do
+    let start =  ["foo :: Int -> Bool"
+                 , "foo x = select (x + 1)"
+                 , ""
+                 , "-- This is a normal comment"
+                 , "normal :: Int -> Int"
+                 , "normal = undefined"
+                 ]
+    let expected =  ["foo :: Int -> Bool"
+             , "foo x = select (x + 1)"
+             , ""
+             , "select :: Int -> Bool"
+             , "select = _"
+             , ""
+             , "-- This is a normal comment"
+             , "normal :: Int -> Int"
+             , "normal = undefined"]
+    docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start)
+    _ <- waitForDiagnostics
+    InR action@CodeAction { _title = actionTitle } : _
+                <- sortOn (\(InR CodeAction{_title=x}) -> x) <$>
+                    getCodeActions docB (R 1 0 0 50)
+    liftIO $ actionTitle @?= "Define select :: Int -> Bool"
+    executeCodeAction action
+    contentAfterAction <- documentContents docB
+    liftIO $ contentAfterAction @?= T.unlines expected
   ]
 
 
@@ -5613,7 +5673,7 @@ bootTests = testGroup "boot"
             hoverResponseOrReadyMessage <- skipManyTill anyMessage ((Left <$> parseHoverResponse) <|> (Right <$> parseReadyMessage))
             _ <- skipManyTill anyMessage $
               case hoverResponseOrReadyMessage of
-                Left _ -> void parseReadyMessage
+                Left _  -> void parseReadyMessage
                 Right _ -> void parseHoverResponse
             closeDoc cDoc
         cdoc <- createDoc cPath "haskell" cSource

From 7393e48a638bbc2890d40c4a94382d721fc4995d Mon Sep 17 00:00:00 2001
From: Nick Suchecki <nicksuchecki@gmail.com>
Date: Sun, 27 Feb 2022 11:34:10 -0500
Subject: [PATCH 2/2] Revert whitespace

---
 ghcide/src/Development/IDE/Plugin/CodeAction.hs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs
index 7d10ff105b..c3339d04de 100644
--- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs
+++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs
@@ -64,8 +64,6 @@ import qualified GHC.LanguageExtensions                            as Lang
 import           Ide.PluginUtils                                   (subRange)
 import           Ide.Types
 import qualified Language.LSP.Server                               as LSP
-
-
 import           Language.LSP.Types                                (CodeAction (..),
                                                                     CodeActionContext (CodeActionContext, _diagnostics),
                                                                     CodeActionKind (CodeActionQuickFix, CodeActionUnknown),