Skip to content

Tactics again (hoping CI will pick up this PR) #397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 71 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
c7cc993
Add refinery
isovector Sep 6, 2020
c81f2eb
[WIP] Add Skeleton for Tactic Plugin
TOTBWF Sep 6, 2020
a31a9f9
Local bindings
isovector Sep 6, 2020
120cb04
Merge branch 'tactics' of github.com:isovector/haskell-language-serve…
TOTBWF Sep 6, 2020
b79a23a
more cases for bindings
isovector Sep 6, 2020
2b7040d
[WIP] Add more to the code action provider
TOTBWF Sep 6, 2020
3d9f4d2
Merge branch 'tactics' of github.com:isovector/haskell-language-serve…
TOTBWF Sep 6, 2020
945d38a
is it a hole?
isovector Sep 6, 2020
ae533eb
Beginning of tactics machinery
isovector Sep 6, 2020
ca24aa5
tactics machinery
isovector Sep 7, 2020
7128ed0
split out tactics machinery; finish porting tactics
isovector Sep 7, 2020
b82ea25
Haddock for tactics machinery
isovector Sep 7, 2020
5e6454b
Use a map for hypothesis
isovector Sep 7, 2020
e434f6c
Better types on LocalBindings
isovector Sep 7, 2020
979dc53
render the result of running a tactic
isovector Sep 7, 2020
03f04f6
Hypothesis from bindings
isovector Sep 7, 2020
5aacafc
Sort types
isovector Sep 7, 2020
c7bb991
mostSpecificSpan
isovector Sep 7, 2020
0078219
Render
isovector Sep 7, 2020
6c1ea41
slightly better span
isovector Sep 7, 2020
edae736
better sorting for specific spans
isovector Sep 7, 2020
5bb8c66
Actually add the tactic plugin :)
TOTBWF Sep 7, 2020
a78b440
[WIP] Do stuff
TOTBWF Sep 7, 2020
95bca32
Merge branch 'tactics' of github.com:isovector/haskell-language-serve…
TOTBWF Sep 7, 2020
65e59fc
Fix size
isovector Sep 7, 2020
6070284
Merge branch 'tactics' of github.com:isovector/haskell-language-serve…
TOTBWF Sep 7, 2020
ccb799d
[WIP] It does the thing!!
TOTBWF Sep 7, 2020
425e9bc
Parenthesize if necessary
isovector Sep 7, 2020
385ee7e
Multiple tactic actions
TOTBWF Sep 7, 2020
b0ac8fe
Merge branch 'tactics' of github.com:isovector/haskell-language-serve…
TOTBWF Sep 7, 2020
1fcc5d8
[WIP] Home on the 'Range'
TOTBWF Sep 7, 2020
9b567e6
destruct and homo
isovector Sep 7, 2020
4de1ab2
fix naming and parens
isovector Sep 7, 2020
ca6ef7a
Cleanup Plugin Tactic
isovector Sep 7, 2020
73854c7
context dependent destruct and homo
isovector Sep 8, 2020
26df332
Generalized interface
isovector Sep 8, 2020
7475f3f
More composable
isovector Sep 8, 2020
42d16be
Remove TacticVariety
isovector Sep 8, 2020
83ef214
Haddock
isovector Sep 8, 2020
405fec2
Describe spooky monoidal behavior
isovector Sep 8, 2020
964354e
Only look at actual holes
isovector Sep 8, 2020
e1617bb
Auto if possible
isovector Sep 8, 2020
44528b0
debugging
isovector Sep 8, 2020
690e50f
Merge pull request #1 from isovector/general-interface
isovector Sep 8, 2020
9115e10
Maybe grafting works now
isovector Sep 8, 2020
f94eb4b
Merge branch 'printdiff' into tactics
isovector Sep 8, 2020
9d28b43
Transformation works; tree doesnt
isovector Sep 8, 2020
87f23ee
Remove debugging
isovector Sep 9, 2020
64a30b3
Proper indentation and parenthesizing
isovector Sep 9, 2020
2f49670
Less fancy parenthesizing
isovector Sep 9, 2020
56e1bed
Don't crash if we can't lookup things
isovector Sep 9, 2020
9e10c92
Holes must start with an underscore
isovector Sep 9, 2020
8aa8983
Haddock pass
isovector Sep 9, 2020
d698b88
Module restructuring
isovector Sep 9, 2020
a5ee18a
Fix the cabal file
isovector Sep 9, 2020
bf2e93d
Intros, and disable some of the unpolished tactics
isovector Sep 9, 2020
e0ecbb5
Disable autoIfPossible
isovector Sep 9, 2020
4967997
Fix stack.yaml
isovector Sep 9, 2020
670bcdc
Respond to simple PR comments.
isovector Sep 11, 2020
9b05441
Get a proper dflags
isovector Sep 11, 2020
67b94af
WIP on a better bindings interface
isovector Sep 11, 2020
a45fde6
Simplify dflags lookup and expose titles
isovector Sep 11, 2020
75cb381
Tactic tests
isovector Sep 11, 2020
a41fa2d
Add a few more tests
isovector Sep 11, 2020
30d214a
Cleanup imports
isovector Sep 11, 2020
bb14844
Haddock the tests
isovector Sep 11, 2020
4aa28e6
Merge branch 'master' into tactics
isovector Sep 11, 2020
716b8ef
Move tactic plugin
isovector Sep 11, 2020
d452bf3
Almost there!
isovector Sep 11, 2020
f072881
Get the tests running again
isovector Sep 12, 2020
54cd86a
Empty commit for CI
isovector Sep 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion exe/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Ide.Plugin.ImportLens as ImportLens
import Ide.Plugin.Ormolu as Ormolu
import Ide.Plugin.StylishHaskell as StylishHaskell
import Ide.Plugin.Retrie as Retrie
import Ide.Plugin.Tactic as Tactic
#if AGPL
import Ide.Plugin.Brittany as Brittany
#endif
Expand Down Expand Up @@ -48,6 +49,7 @@ idePlugins includeExamples = pluginDescToIdePlugins allPlugins
, Ormolu.descriptor "ormolu"
, StylishHaskell.descriptor "stylish-haskell"
, Retrie.descriptor "retrie"
, Tactic.descriptor "tactic"
#if AGPL
, Brittany.descriptor "brittany"
#endif
Expand All @@ -65,7 +67,7 @@ main :: IO ()
main = do
args <- getArguments "haskell-language-server"

let withExamples =
let withExamples =
case args of
LspMode (LspArguments{..}) -> argsExamplePlugin
_ -> False
Expand Down
28 changes: 27 additions & 1 deletion haskell-language-server.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ executable haskell-language-server
main-is: Main.hs
hs-source-dirs: exe plugins/default/src
other-modules:
Ide.LocalBindings
Ide.Plugin.Eval
Ide.Plugin.Example
Ide.Plugin.Example2
Expand All @@ -92,6 +93,10 @@ executable haskell-language-server
Ide.Plugin.Pragmas
Ide.Plugin.Retrie
Ide.Plugin.StylishHaskell
Ide.Plugin.Tactic
Ide.Plugin.Tactic.Machinery
Ide.Plugin.Tactic.Tactics
Ide.TreeTransform

ghc-options:
-threaded -Wall -Wno-name-shadowing -Wredundant-constraints
Expand All @@ -114,18 +119,23 @@ executable haskell-language-server
, fourmolu ^>=0.1
, ghc
, ghc-boot-th
, ghc-exactprint
, ghc-source-gen
, ghcide >=0.1
, hashable
, haskell-language-server
, haskell-lsp ^>=0.22
, hls-plugin-api
, lens
, mtl
, ormolu ^>=0.1.2
, refinery
, regex-tdfa
, retrie >=0.1.1.0
, safe-exceptions
, shake >=0.17.5
, stylish-haskell ^>=0.11
, syb
, temporary
, time
, transformers
Expand Down Expand Up @@ -212,13 +222,23 @@ test-suite func-test
, bytestring
, data-default
, lens
, ghc
, ghc-source-gen
, ghc-exactprint
, ghcide
, mtl
, refinery
, regex-tdfa
, retrie
, shake
, syb
, tasty
, tasty-ant-xml >=1.1.6
, tasty-expected-failure
, tasty-golden
, tasty-rerun

hs-source-dirs: test/functional
hs-source-dirs: test/functional plugins/default/src
main-is: Main.hs
other-modules:
Command
Expand All @@ -237,7 +257,13 @@ test-suite func-test
Reference
Rename
Symbol
Tactic
TypeDefinition
Ide.LocalBindings
Ide.Plugin.Tactic
Ide.Plugin.Tactic.Machinery
Ide.Plugin.Tactic.Tactics
Ide.TreeTransform

ghc-options:
-Wall -Wno-name-shadowing -threaded -rtsopts -with-rtsopts=-N
Expand Down
214 changes: 214 additions & 0 deletions plugins/default/src/Ide/LocalBindings.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Ide.LocalBindings
( Bindings (..)
, bindings
, mostSpecificSpan
, holify
) where

import Bag
import Control.Lens
import Control.Monad
import Data.Data.Lens
import Data.Function
import Data.Generics
import Data.List
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
import Data.Ord
import Data.Set (Set)
import qualified Data.Set as S
import Development.IDE.GHC.Compat (TypecheckedModule (..), GhcTc, NoExt (..), RefMap, identType)
import HsBinds
import HsExpr
import Id
import OccName
import SrcLoc


------------------------------------------------------------------------------
-- | WIP function for getting 'bindings' from HIE, rather than stupidly
-- traversing the entire AST.
_bindigsHIE :: RefMap -> SrcSpan -> Set Id
_bindigsHIE _ (UnhelpfulSpan _) = mempty
_bindigsHIE refmap (RealSrcSpan span) = S.fromList $ do
(ident, refs) <- M.toList refmap
Right _name <- pure ident
(ref_span, ident_details) <- refs
Just _ty <- pure $ identType ident_details
guard $ ref_span `containsSpan` span
mempty



------------------------------------------------------------------------------
-- | The available bindings at every point in a Haskell tree.
data Bindings = Bindings
{ bGlobalBinds :: Set Id
, bLocalBinds :: Map SrcSpan (Set Id)
} deriving (Eq, Ord)

instance Semigroup Bindings where
Bindings g1 l1 <> Bindings g2 l2 = Bindings (g1 <> g2) (l1 <> l2)

instance Monoid Bindings where
mempty = Bindings mempty mempty


------------------------------------------------------------------------------
-- | Determine what bindings are in scope at every point in a program.
--
-- **WARNING:** This doesn't find bindings inside of TH splices or arrow syntax
-- --- and possibly other obscure pieces of the AST.
bindings :: TypecheckedModule -> Bindings
bindings = uncurry Bindings . bindsBindings mempty . tm_typechecked_source


------------------------------------------------------------------------------
-- | Helper function for implementing 'binding'.
--
-- **WARNING:** This doesn't yet work over TH splices or arrow syntax --- and
-- possibly other obscure pieces of the AST.
dataBindings :: Data a => S.Set Id -> a -> M.Map SrcSpan (S.Set Id)
dataBindings in_scope = foldMapOf biplate $ cool collect
where
cool
:: (HsExpr GhcTc -> M.Map SrcSpan (S.Set Id))
-> LHsExpr GhcTc -> M.Map SrcSpan (S.Set Id)
cool f (L src expr) = M.union (f expr) (M.singleton src in_scope)

collect :: HsExpr GhcTc -> M.Map SrcSpan (S.Set Id)
collect (HsLam _ matches) = matchGroupBindings in_scope matches
collect (HsLamCase _ matches) = matchGroupBindings in_scope matches
collect (HsCase _ scrutinee matches) =
M.union (dataBindings in_scope scrutinee) $ matchGroupBindings in_scope matches
collect (HsLet _ (L _ binds) expr) =
let (new, res) = localBindsBindings in_scope binds
in_scope' = S.union new in_scope
in M.union (dataBindings in_scope' expr) res
collect (HsVar _ _) = mempty
collect (HsUnboundVar _ _) = mempty
collect (HsConLikeOut _ _) = mempty
collect (HsRecFld _ _) = mempty
collect (HsOverLabel _ _ _) = mempty
collect (HsIPVar _ _) = mempty
collect (HsOverLit _ _) = mempty
collect (HsLit _ _) = mempty
collect (HsApp _ a b) = M.union (dataBindings in_scope a) (dataBindings in_scope b)
collect (HsAppType _ _ a) = dataBindings in_scope a
collect (OpApp _ a b c) =
mconcat
[ dataBindings in_scope a
, dataBindings in_scope b
, dataBindings in_scope c
]
collect (NegApp _ a _) = dataBindings in_scope a
collect (HsPar _ a) = dataBindings in_scope a
collect (SectionL _ a b) =
mconcat
[ dataBindings in_scope a
, dataBindings in_scope b
]
collect (SectionR _ a b) =
mconcat
[ dataBindings in_scope a
, dataBindings in_scope b
]
collect (ExplicitTuple _ a _) = dataBindings in_scope a
collect (ExplicitSum _ _ _ a) = dataBindings in_scope a
collect (HsIf _ _ a b c) =
mconcat
[ dataBindings in_scope a
, dataBindings in_scope b
, dataBindings in_scope c
]
collect (HsMultiIf _ a) = dataBindings in_scope a
collect (HsDo _ _ a) = dataBindings in_scope a
collect (ExplicitList _ _ a) = dataBindings in_scope a
collect (RecordCon _ _ a) = dataBindings in_scope a
collect (RecordUpd _ _ a) = dataBindings in_scope a
collect (ExprWithTySig _ _ a) = dataBindings in_scope a
collect (ArithSeq _ _ a) = dataBindings in_scope a
collect (HsSCC _ _ _ a) = dataBindings in_scope a
collect (HsBracket _ a) = dataBindings in_scope a
collect (HsStatic _ a) = dataBindings in_scope a
-- TODO(sandy): This doesn't do arrow syntax
collect _ = mempty


------------------------------------------------------------------------------
-- | Map the binds from a match group into over their containing spans.
matchGroupBindings :: S.Set Id -> MatchGroup GhcTc (LHsExpr GhcTc) -> M.Map SrcSpan (S.Set Id)
matchGroupBindings _ (XMatchGroup _) = M.empty
matchGroupBindings in_scope (MG _ (L _ alts) _) = M.fromList $ do
L _ (Match _ _ pats body) <- alts
let bound = S.filter isId $ everything S.union (mkQ S.empty S.singleton) pats
M.toList $ dataBindings (S.union bound in_scope) body


------------------------------------------------------------------------------
-- | Map the binds from a local binds into over their containing spans.
localBindsBindings :: S.Set Id -> HsLocalBindsLR GhcTc GhcTc -> (S.Set Id, M.Map SrcSpan (S.Set Id))
localBindsBindings in_scope (HsValBinds _ (ValBinds _ binds _sigs)) = bindsBindings in_scope binds
localBindsBindings in_scope (HsValBinds _ (XValBindsLR (NValBinds groups _sigs))) =
flip foldMap groups $ bindsBindings in_scope . snd
localBindsBindings _ _ = (mempty, mempty)


------------------------------------------------------------------------------
-- | Map the binds from a hsbindlr into over their containing spans.
bindsBindings :: S.Set Id -> Bag (LHsBindLR GhcTc GhcTc) -> (S.Set Id, M.Map SrcSpan (S.Set Id))
bindsBindings in_scope binds =
flip foldMap (fmap unLoc $ bagToList binds) $ \case
FunBind _ (L _ name) matches _ _ ->
(S.singleton name, matchGroupBindings (S.insert name in_scope) matches)
PatBind _ pat rhs _ ->
let bound = S.filter isId $ everything S.union (mkQ S.empty S.singleton) pat
in (bound, dataBindings (S.union bound in_scope) rhs)
AbsBinds _ _ _ _ _ binds' _ -> bindsBindings in_scope binds'
VarBind _ name c _ -> (S.singleton name, dataBindings in_scope c)
PatSynBind _ _ -> mempty
XHsBindsLR _ -> mempty


------------------------------------------------------------------------------
-- | How many lines and columns does a SrcSpan span?
srcSpanSize :: SrcSpan -> (Int, Int)
srcSpanSize (UnhelpfulSpan _) = maxBound
srcSpanSize (RealSrcSpan span) =
( srcSpanEndLine span - srcSpanStartLine span
, srcSpanEndCol span - srcSpanStartCol span
)


------------------------------------------------------------------------------
-- | Given a SrcSpan, find the smallest LHsExpr that entirely contains that
-- span. Useful for determining what node in the tree your cursor is hovering over.
mostSpecificSpan :: (Data a, Typeable pass) => SrcSpan -> a -> Maybe (LHsExpr pass)
mostSpecificSpan span z
= listToMaybe
$ sortBy (comparing srcSpanSize `on` getLoc)
$ everything (<>) (mkQ mempty $ \case
l@(L span' _) | span `isSubspanOf` span' -> [l]
_ -> [])
$ z

------------------------------------------------------------------------------
-- | Convert an HsVar back into an HsUnboundVar if it isn't actually in scope.
-- TODO(sandy): this will throw away the type >:(
holify :: Bindings -> LHsExpr GhcTc -> LHsExpr GhcTc
holify (Bindings _ local) v@(L span (HsVar _ (L _ var))) =
let occ = occName var
in case M.lookup span local of
Nothing -> v
Just binds ->
-- Make sure the binding is not in scope and that it begins with an
-- underscore
case not (S.member var binds) && take 1 (occNameString occ) == "_" of
True -> L span $ HsUnboundVar NoExt $ TrueExprHole occ
False -> v
holify _ v = v

Loading