Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contextToCompleter :: Context -> Completer
contextToCompleter (TopLevel, None) =
snippetCompleter
<> ( constantCompleter $
Map.keys (cabalVersionKeyword <> cabalKeywords) ++ Map.keys stanzaKeywordMap
Map.keys cabalKeywords ++ Map.keys stanzaKeywordMap
)
-- if we are in a keyword context in the top level,
-- we look up that keyword in the top level context and can complete its possible values
Expand All @@ -55,6 +55,11 @@ contextToCompleter (Stanza s _, KeyWord kw) =
Just m -> case Map.lookup kw m of
Nothing -> errorNoopCompleter (LogUnknownKeyWordInContextError kw)
Just l -> l
contextToCompleter (FirstLine, None) = constantCompleter ["cabal-version:"]
contextToCompleter (FirstLine, KeyWord kw) =
case Map.lookup kw (cabalVersionKeyword <> cabalKeywords) of
Nothing -> errorNoopCompleter (LogUnknownKeyWordInContextError kw)
Just l -> l

-- | Takes prefix info about the previously written text
-- and a rope (representing a file), returns the corresponding context.
Expand Down Expand Up @@ -135,10 +140,13 @@ findCursorContext ::
Context
findCursorContext cursor parentHistory prefixText fields =
case findFieldSection cursor fields of
Nothing -> (snd $ NE.head parentHistory, None)
Nothing
| cursorRow == 1 -> (FirstLine, None)
| otherwise -> (snd $ NE.head parentHistory, None)
-- We found the most likely section. Now, are we starting a new section or are we completing an existing one?
Just field@(Syntax.Field _ _) -> classifyFieldContext parentHistory cursor field
Just section@(Syntax.Section _ args sectionFields)
| cursorRow == 1 -> (FirstLine, None)
| inSameLineAsSectionName section -> (stanzaCtx, None) -- TODO: test whether keyword in same line is parsed correctly
| getFieldName section `elem` conditionalKeywords -> findCursorContext cursor parentHistory prefixText sectionFields -- Ignore if conditionals, they are not real sections
| otherwise ->
Expand All @@ -150,6 +158,8 @@ findCursorContext cursor parentHistory prefixText fields =
stanzaCtx = snd $ NE.head parentHistory
conditionalKeywords = ["if", "elif", "else"]

cursorRow = Syntax.positionRow cursor

-- | Finds the cursor's context, where the cursor is already found to be in a specific field
--
-- Due to the way the field context is recognised for incomplete cabal files,
Expand All @@ -159,11 +169,11 @@ classifyFieldContext :: NonEmpty (Int, StanzaContext) -> Syntax.Position -> Synt
classifyFieldContext ctx cursor field
-- the cursor is not indented enough to be within the field
-- but still indented enough to be within the stanza
| cursorColumn <= fieldColumn && minIndent <= cursorColumn = (stanzaCtx, None)
| cursorColumn <= fieldColumn && minIndent <= cursorColumn = (firstLineOrStanzaCtx, None)
-- the cursor is not in the current stanza's context as it is not indented enough
| cursorColumn < minIndent = findStanzaForColumn cursorColumn ctx
| cursorIsInFieldName = (stanzaCtx, None)
| cursorIsBeforeFieldName = (stanzaCtx, None)
| cursorIsInFieldName = (firstLineOrStanzaCtx, None)
| cursorIsBeforeFieldName = (firstLineOrStanzaCtx, None)
| otherwise = (stanzaCtx, KeyWord (getFieldName field <> ":"))
where
(minIndent, stanzaCtx) = NE.head ctx
Expand All @@ -179,3 +189,5 @@ classifyFieldContext ctx cursor field

cursorColumn = Syntax.positionCol cursor
fieldColumn = Syntax.positionCol (getAnnotation field)

firstLineOrStanzaCtx = if Syntax.positionRow cursor == 1 then FirstLine else stanzaCtx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ type Context = (StanzaContext, FieldContext)
-- | Context inside a cabal file.
-- Used to decide which keywords to suggest.
data StanzaContext
= -- | Top level context in a cabal file such as 'author'
= -- | First line in a cabal file. Only cabal-version is valid here
FirstLine
| -- | Top level context in a cabal file such as 'author'
TopLevel
| -- | Nested context in a cabal file, such as 'library'.
--
Expand All @@ -88,6 +90,7 @@ data StanzaContext
deriving (Eq, Show, Read)

instance Pretty StanzaContext where
pretty FirstLine = "FirstLine"
pretty TopLevel = "TopLevel"
pretty (Stanza t ms) = "Stanza" <+> pretty t <+> (maybe mempty pretty ms)

Expand Down
25 changes: 25 additions & 0 deletions plugins/hls-cabal-plugin/test/Completer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ basicCompleterTests =
let complTexts = getTextEditTexts compls
liftIO $ assertBool "suggests f2" $ "f2.hs" `elem` complTexts
liftIO $ assertBool "does not suggest" $ "Content.hs" `notElem` complTexts
, runCabalTestCaseSession "only suggest cabal-version at start of file" "" $ do
doc <- openDoc "empty.cabal" "cabal"
compls <- getCompletions doc (Position 0 0)
let complTexts = getTextEditTexts compls
liftIO $ complTexts @?= ["cabal-version:"]
, runCabalTestCaseSession "only suggest cabal-version at start of file with other fields" "" $ do
doc <- openDoc "cabal-version-missing.cabal" "cabal"
compls <- getCompletions doc (Position 0 0)
let complTexts = getTextEditTexts compls
liftIO $ complTexts @?= ["cabal-version:"]
, runCabalTestCaseSession "only suggest cabal-version at start of file with some input" "" $ do
doc <- openDoc "start-cabal-version.cabal" "cabal"
compls <- getCompletions doc (Position 0 2)
let complTexts = getTextEditTexts compls
liftIO $ complTexts @?= ["cabal-version:"]
, runCabalTestCaseSession "suggest field value completion in first line for fields other than cabal-version" "" $ do
doc <- openDoc "cabal-version-not-in-first-line.cabal" "cabal"
compls <- getCompletions doc (Position 0 8)
let complTexts = getTextEditTexts compls
liftIO $ complTexts @?= []
, runCabalTestCaseSession "Don't suggest cabal-version at lines after the first line" "" $ do
doc <- openDoc "cabal-version-missing.cabal" "cabal"
compls <- getCompletions doc (Position 2 0)
let complTexts = getTextEditTexts compls
liftIO . assertBool "cabal-version is not suggested after first line" . not $ "cabal-version:" `elem` complTexts
, parameterisedCursorTestM "extensions completion" libraryStanzaData
[ \_ actual -> assertBool "suggests FieldSelectors" $ "FieldSelectors" `elem` actual
, \_ actual -> assertBool "suggests OverloadedStrings" $ "OverloadedStrings" `elem` actual
Expand Down
10 changes: 5 additions & 5 deletions plugins/hls-cabal-plugin/test/Context.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Ide.Plugin.Cabal.Completion.Completer.Paths
import Ide.Plugin.Cabal.Completion.Completions
import Ide.Plugin.Cabal.Completion.Types (Context,
FieldContext (KeyWord, None),
StanzaContext (Stanza, TopLevel))
StanzaContext (FirstLine, Stanza, TopLevel))
import qualified Ide.Plugin.Cabal.Parse as Parse
import Test.Hls
import Utils as T
Expand Down Expand Up @@ -65,7 +65,7 @@ getContextTests =
-- for a completely empty file, the context needs to
-- be top level without a specified keyword
ctx <- callGetContext (Position 0 0) "" ""
ctx @?= (TopLevel, None)
ctx @?= (FirstLine, None)
, testCase "Cabal version keyword - no value, no space after :" $ do
-- on a file, where the keyword is already written
-- the context should still be toplevel but the keyword should be recognized
Expand All @@ -76,7 +76,7 @@ getContextTests =
-- but the cursor is in the middle of the keyword,
-- we are not in a keyword context
ctx <- callGetContext (Position 0 5) "cabal" "cabal-version:\n"
ctx @?= (TopLevel, None)
ctx @?= (FirstLine, None)
, testCase "Cabal version keyword - no value, many spaces" $ do
-- on a file, where the "cabal-version:" keyword is already written
-- the context should still be top level but the keyword should be recognized
Expand All @@ -87,7 +87,7 @@ getContextTests =
-- has not been written completely, the keyword context
-- should still be None
ctx <- callGetContext (Position 0 5) "cabal" "cabal"
ctx @?= (TopLevel, None)
ctx @?= (FirstLine, None)
, testCase "Cabal version keyword - value partly written" $ do
-- in the first line of the file, if the keyword
-- has not been written completely, the keyword context
Expand Down Expand Up @@ -182,7 +182,7 @@ getContextTests =
ctx <- callGetContext (Position 8 14) "" multiLineOptsData
ctx @?= (Stanza "library" Nothing, KeyWord "build-depends:")
, parameterisedCursorTest "Contexts in large testfile" multiPositionTestData
[ (TopLevel, None)
[ (FirstLine, None)
, (TopLevel, KeyWord "cabal-version:")
, (TopLevel, None)
, (TopLevel, KeyWord "description:")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

name: test-hls
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lisence:
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ca
Loading