Skip to content

Commit 56b0eee

Browse files
author
Ishmum Jawad Khan
committed
[wip] add pragma after she-bang
1 parent 691d821 commit 56b0eee

File tree

3 files changed

+61
-10
lines changed

3 files changed

+61
-10
lines changed

plugins/default/src/Ide/Plugin/Pragmas.hs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ module Ide.Plugin.Pragmas
1111
) where
1212

1313
import Control.Lens hiding (List)
14+
import Control.Applicative ((<|>))
1415
import qualified Data.HashMap.Strict as H
1516
import qualified Data.Text as T
17+
import Data.Maybe (listToMaybe, fromMaybe)
18+
import Data.List (findIndex)
1619
import Development.IDE as D
1720
import Ide.Types
1821
import Language.Haskell.LSP.Types
@@ -40,16 +43,17 @@ descriptor plId = (defaultPluginDescriptor plId)
4043
-- Pragma is added to the first line of the Uri.
4144
-- It is assumed that the pragma name is a valid pragma,
4245
-- thus, not validated.
43-
mkPragmaEdit :: Uri -> T.Text -> WorkspaceEdit
44-
mkPragmaEdit uri pragmaName = res where
46+
mkPragmaEdit :: Uri -> T.Text -> Maybe ParsedModule -> Maybe WorkspaceEdit
47+
mkPragmaEdit uri pragmaName mpm = res mpm where
4548
pos = J.Position 0 0
46-
textEdits = J.List
47-
[J.TextEdit (J.Range pos pos)
49+
textEdits pm = J.List
50+
[J.TextEdit (endOfModuleHeader pm Nothing)
4851
("{-# LANGUAGE " <> pragmaName <> " #-}\n")
4952
]
50-
res = J.WorkspaceEdit
51-
(Just $ H.singleton uri textEdits)
53+
res (Just pm) = Just $ J.WorkspaceEdit
54+
(Just . H.singleton uri $ textEdits pm)
5255
Nothing
56+
res Nothing = Nothing
5357

5458
-- ---------------------------------------------------------------------
5559
-- | Offer to add a missing Language Pragma to the top of a file.
@@ -61,14 +65,14 @@ codeActionProvider _ state _plId docId _ (J.CodeActionContext (J.List diags) _mo
6165
let dflags = ms_hspp_opts . pm_mod_summary <$> pm
6266
-- Get all potential Pragmas for all diagnostics.
6367
pragmas = nubOrd $ concatMap (\d -> genPragma dflags (d ^. J.message)) diags
64-
cmds <- mapM mkCodeAction pragmas
68+
cmds <- mapM (mkCodeAction pm) pragmas
6569
return $ Right $ List cmds
6670
where
67-
mkCodeAction pragmaName = do
71+
mkCodeAction pm pragmaName = do
6872
let
69-
codeAction = J.CACodeAction $ J.CodeAction title (Just J.CodeActionQuickFix) (Just (J.List [])) (Just edit) Nothing
73+
codeAction = J.CACodeAction $ J.CodeAction title (Just J.CodeActionQuickFix) (Just (J.List [])) (Just =<< edit) Nothing
7074
title = "Add \"" <> pragmaName <> "\""
71-
edit = mkPragmaEdit (docId ^. J.uri) pragmaName
75+
edit = mkPragmaEdit (docId ^. J.uri) pragmaName pm
7276
return codeAction
7377

7478
genPragma mDynflags target =
@@ -164,3 +168,17 @@ completion lspFuncs _ide complParams = do
164168
_xdata = Nothing
165169
}
166170
_ -> return $ Completions $ List []
171+
172+
-- | Find the first non-blank line before the first of (module name / imports / declarations).
173+
-- Useful for inserting pragmas.
174+
endOfModuleHeader :: ParsedModule -> Maybe T.Text -> Range
175+
endOfModuleHeader pm contents =
176+
let mod = unLoc $ pm_parsed_source pm
177+
modNameLoc = getLoc <$> hsmodName mod
178+
firstImportLoc = getLoc <$> listToMaybe (hsmodImports mod)
179+
firstDeclLoc = getLoc <$> listToMaybe (hsmodDecls mod)
180+
line = fromMaybe 0 $ firstNonBlankBefore . _line . _start =<< srcSpanToRange =<<
181+
modNameLoc <|> firstImportLoc <|> firstDeclLoc
182+
firstNonBlankBefore n = (n -) . fromMaybe 0 . findIndex (not . T.null) . reverse . take n . T.lines <$> contents
183+
loc = Position line 0
184+
in Range loc loc

test/functional/FunctionalCodeAction.hs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,31 @@ missingPragmaTests = testGroup "missing pragma warning code actions" [
547547
, "f Record{a, b} = a"
548548
]
549549

550+
liftIO $ T.lines contents @?= expected
551+
, testCase "After Shebang" $ do
552+
runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do
553+
doc <- openDoc "AfterShebang.hs" "haskell"
554+
555+
_ <- waitForDiagnosticsFrom doc
556+
cas <- map fromAction <$> getAllCodeActions doc
557+
558+
liftIO $ "Add \"TypeApplications\"" `elem` map (^. L.title) cas @? "Contains TypeApplications code action"
559+
560+
executeCodeAction $ head cas
561+
562+
contents <- documentContents doc
563+
564+
let expected =
565+
[ "#! /usr/bin/env nix-shell"
566+
, "#! nix-shell --pure -i runghc -p \"haskellPackages.ghcWithPackages (hp: with hp; [ turtle ])\""
567+
, "{-# LANGUAGE TypeApplications #-}"
568+
, "{-# LANGUAGE ScopedTypeVariables #-}"
569+
, "module TypeApplications where"
570+
, ""
571+
, "foo :: forall a. a -> a"
572+
, "foo = id @a"
573+
]
574+
550575
liftIO $ T.lines contents @?= expected
551576
]
552577

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#! /usr/bin/env nix-shell
2+
#! nix-shell --pure -i runghc -p "haskellPackages.ghcWithPackages (hp: with hp; [ turtle ])"
3+
4+
{-# LANGUAGE ScopedTypeVariables #-}
5+
module AfterShebang where
6+
7+
foo :: forall a. a -> a
8+
foo = id @a

0 commit comments

Comments
 (0)