@@ -560,24 +560,74 @@ getCompletions
560560    ->  ClientCapabilities 
561561    ->  CompletionsConfig 
562562    ->  HM. HashMap  T. Text  (HashSet. HashSet  IdentInfo )
563-     ->  IO   [Scored  CompletionItem ]
564- getCompletions plId ideOpts CC  {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules}
565-                maybe_parsed (localBindings, bmapping) prefixInfo caps config moduleExportsMap =  do 
566-   let  VFS. PosPrefixInfo  { fullLine, prefixModule, prefixText } =  prefixInfo
567-       enteredQual =  if  T. null  prefixModule then  " "   else  prefixModule <>  " ." 
563+     ->  [Scored  CompletionItem ]
564+ getCompletions
565+     plId
566+     ideOpts
567+     CC  {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules}
568+     maybe_parsed
569+     (localBindings, bmapping)
570+     VFS. PosPrefixInfo  {fullLine, prefixModule, prefixText, cursorPos}
571+     caps
572+     config
573+     moduleExportsMap
574+     --  ------------------------------------------------------------------------
575+     --  IMPORT MODULENAME (NAM|)
576+     |  Just  (ImportListContext  moduleName) <-  maybeContext
577+     =  moduleImportListCompletions moduleName
578+ 
579+     |  Just  (ImportHidingContext  moduleName) <-  maybeContext
580+     =  moduleImportListCompletions moduleName
581+ 
582+     --  TODO: Is manual parsing ever needed or is context always present for module?
583+     --  If possible only keep the above.
584+     |  " import "   `T.isPrefixOf`  fullLine
585+     , Just  moduleName <-  getModuleName fullLine
586+     , " ("   `T.isInfixOf`  fullLine
587+     =  moduleImportListCompletions $  T. unpack moduleName
588+ 
589+     --  ------------------------------------------------------------------------
590+     --  IMPORT MODULENAM|
591+     |  Just  (ImportContext  _moduleName) <-  maybeContext
592+     =  filtImportCompls
593+ 
594+     --  TODO: Can we avoid this manual parsing?
595+     --  If possible only keep the above.
596+     |  " import "   `T.isPrefixOf`  fullLine
597+     =  filtImportCompls
598+ 
599+     --  ------------------------------------------------------------------------
600+     --  {-# LA| #-}
601+     --  we leave this condition here to avoid duplications and return empty list
602+     --  since HLS implements these completions (#haskell-language-server/pull/662)
603+     |  " {-# "   `T.isPrefixOf`  fullLine
604+     =  [] 
605+ 
606+     --  ------------------------------------------------------------------------
607+     |  otherwise  = 
608+         --  assumes that nubOrdBy is stable
609+         let  uniqueFiltCompls =  nubOrdBy (uniqueCompl `on`  snd  .  Fuzzy. original) filtCompls
610+             compls =  (fmap . fmap . fmap ) (mkCompl plId ideOpts) uniqueFiltCompls
611+         in  (fmap . fmap ) snd  $ 
612+           sortBy (compare  `on`  lexicographicOrdering) $ 
613+           mergeListsBy (flip  compare  `on`  score)
614+             [ (fmap . fmap ) (notQual,) filtModNameCompls
615+             , (fmap . fmap ) (notQual,) filtKeywordCompls
616+             , (fmap . fmap . fmap ) (toggleSnippets caps config) compls
617+             ]
618+     where 
619+       --  construct the qualified completion (do not confuse with qualified import)
620+       enteredQual  ::  T. Text
621+       enteredQual =  if  qual then  prefixModule <>  " ."   else  " " 
622+       fullPrefix  ::  T. Text
568623      fullPrefix  =  enteredQual <>  prefixText
569624
570625      --  Boolean labels to tag suggestions as qualified (or not)
571-       qual =  not (T. null  prefixModule)
626+       qual , notQual  ::  Bool 
627+       qual =  not  (T. null  prefixModule)
572628      notQual =  False 
573629
574-       {-  correct the position by moving 'foo :: Int -> String ->    '
575-                                                                     ^ 
576-           to                             'foo :: Int -> String ->    ' 
577-                                                               ^ 
578-       -}  
579-       pos =  VFS. cursorPos prefixInfo
580- 
630+       maxC  ::  Int 
581631      maxC =  maxCompletions config
582632
583633      filtModNameCompls  ::  [Scored  CompletionItem ]
@@ -587,15 +637,29 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu
587637          $  (if  T. null  enteredQual then  id  else  mapMaybe (T. stripPrefix enteredQual))
588638            allModNamesAsNS
589639
640+       --  ----------------------------------------
641+       --  Note: correct the cursorPos by moving
642+       -- 
643+       --    'foo :: Int -> String ->    '
644+       --                                ^ 
645+       --  to
646+       -- 
647+       --    'foo :: Int -> String ->    '
648+       --                         ^ 
649+       --  ----------------------------------------
650+ 
651+       --  If we have a parsed module, use it to determine which completion to show.
652+       maybeContext  ::  Maybe   Context 
590653      maybeContext =  case  maybe_parsed of 
591654            Nothing  ->  Nothing 
592655            Just  (pm, pmapping) -> 
593656              let  PositionMapping  pDelta =  pmapping
594-                   position' =  fromDelta pDelta pos 
657+                   position' =  fromDelta pDelta cursorPos 
595658                  lpos =  lowerRange position'
596659                  hpos =  upperRange position'
597660              in  getCContext lpos pm <|>  getCContext hpos pm
598661
662+       filtCompls  ::  [Scored  (Bool  , CompItem )]
599663      filtCompls =  Fuzzy. filter  chunkSize maxC prefixText ctxCompls (label .  snd )
600664        where 
601665          --  completions specific to the current context
@@ -608,10 +672,10 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu
608672          ctxCompls =  (fmap . fmap ) (\ comp ->  toggleAutoExtend config $  comp { isInfix =  infixCompls }) ctxCompls'
609673
610674          infixCompls  ::  Maybe   Backtick 
611-           infixCompls =  isUsedAsInfix fullLine prefixModule prefixText pos 
675+           infixCompls =  isUsedAsInfix fullLine prefixModule prefixText cursorPos 
612676
613677          PositionMapping  bDelta =  bmapping
614-           oldPos =  fromDelta bDelta $   VFS. cursorPos prefixInfo 
678+           oldPos =  fromDelta bDelta cursorPos
615679          startLoc =  lowerRange oldPos
616680          endLoc =  upperRange oldPos
617681          localCompls =  map  (uncurry  localBindsToCompItem) $  getFuzzyScope localBindings startLoc endLoc
@@ -629,6 +693,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu
629693            else  ((qual,) <$>  Map. findWithDefault []  prefixModule (getQualCompls qualCompls))
630694                 ++  ((notQual,) .  ($  Just  prefixModule) <$>  anyQualCompls)
631695
696+       filtListWith  ::  (T. Text  ->  CompletionItem ) ->  [T. Text ] ->  [Scored  CompletionItem ]
632697      filtListWith f list = 
633698        [ fmap  f label
634699        |  label <-  Fuzzy. simpleFilter chunkSize maxC fullPrefix list
@@ -643,64 +708,31 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu
643708        in  filterModuleExports moduleName $  map  T. pack funs
644709
645710      --  manually parse in case we don't have completion context ("import [qualified ]ModuleName")
711+       getModuleName  ::  T. Text  ->  Maybe   T. Text
646712      getModuleName line =  filter  (/=  " qualified"  ) (T. words  line) !?  1 
713+ 
714+       filtImportCompls  ::  [Scored  CompletionItem ]
647715      filtImportCompls =  filtListWith (mkImportCompl enteredQual) importableModules
716+ 
717+       filterModuleExports  ::  T. Text  ->  [T. Text ] ->  [Scored  CompletionItem ]
648718      filterModuleExports moduleName =  filtListWith $  mkModuleFunctionImport moduleName
719+ 
720+       filtKeywordCompls  ::  [Scored  CompletionItem ]
649721      filtKeywordCompls
650722          |  T. null  prefixModule =  filtListWith mkExtCompl (optKeywords ideOpts)
651723          |  otherwise  =  [] 
652724
653-   if 
654-     --  TODO: handle multiline imports
655-     |  Just  (ImportListContext  moduleName) <-  maybeContext
656-     ->  pure  $  moduleImportListCompletions moduleName
657- 
658-     |  Just  (ImportHidingContext  moduleName) <-  maybeContext
659-     ->  pure  $  moduleImportListCompletions moduleName
660- 
661-     --  TODO: Is manual parsing ever needed or is context always present for module?
662-     --  If possible only keep the above.
663-     |  " import "   `T.isPrefixOf`  fullLine
664-     , Just  moduleName <-  getModuleName fullLine
665-     , " ("   `T.isInfixOf`  fullLine
666-     ->  pure  $  moduleImportListCompletions $  T. unpack moduleName
667- 
668-     |  Just  (ImportContext  _moduleName) <-  maybeContext
669-     ->  return  filtImportCompls
670- 
671-     --  TODO: Can we avoid this manual parsing?
672-     --  If possible only keep the above.
673-     |  " import "   `T.isPrefixOf`  fullLine
674-     ->  return  filtImportCompls
675- 
676-     --  we leave this condition here to avoid duplications and return empty list
677-     --  since HLS implements these completions (#haskell-language-server/pull/662)
678-     |  " {-# "   `T.isPrefixOf`  fullLine
679-     ->  return  [] 
680- 
681-     |  otherwise  ->  do 
682-         --  assumes that nubOrdBy is stable
683-         let  uniqueFiltCompls =  nubOrdBy (uniqueCompl `on`  snd  .  Fuzzy. original) filtCompls
684-         let  compls =  (fmap . fmap . fmap ) (mkCompl plId ideOpts) uniqueFiltCompls
685-         return  $ 
686-           (fmap . fmap ) snd  $ 
687-           sortBy (compare  `on`  lexicographicOrdering) $ 
688-           mergeListsBy (flip  compare  `on`  score)
689-             [ (fmap . fmap ) (notQual,) filtModNameCompls
690-             , (fmap . fmap ) (notQual,) filtKeywordCompls
691-             , (fmap . fmap . fmap ) (toggleSnippets caps config) compls
692-             ]
693-     where 
694-         --  We use this ordering to alphabetically sort suggestions while respecting
695-         --  all the previously applied ordering sources. These are:
696-         --   1. Qualified suggestions go first
697-         --   2. Fuzzy score ranks next
698-         --   3. In-scope completions rank next
699-         --   4. label alphabetical ordering next
700-         --   4. detail alphabetical ordering (proxy for module)
701-         lexicographicOrdering Fuzzy. Scored {score, original} = 
725+       --  We use this ordering to alphabetically sort suggestions while respecting
726+       --  all the previously applied ordering sources. These are:
727+       --   1. Qualified suggestions go first
728+       --   2. Fuzzy score ranks next
729+       --   3. In-scope completions rank next
730+       --   4. label alphabetical ordering next
731+       --   4. detail alphabetical ordering (proxy for module)
732+       lexicographicOrdering  ::  Scored  (a , CompletionItem ) ->  (Down  a , Down  Int  , Down  Bool  , T. Text , Maybe   T. Text )
733+       lexicographicOrdering Fuzzy. Scored {score, original} = 
702734          case  original of 
703-              (isQual, CompletionItem {_label,_detail}) ->  do 
735+           (isQual, CompletionItem {_label,_detail}) ->  do 
704736              let  isLocal =  maybe  False   (" :"   `T.isPrefixOf` ) _detail
705737              (Down  isQual, Down  score, Down  isLocal, _label, _detail)
706738
0 commit comments