11module Ide.Plugin.Cabal.Completion.CabalFields
2- ( findStanzaForColumn ,
3- findFieldSection ,
4- findTextWord ,
5- findFieldLine ,
6- getOptionalSectionName ,
7- getAnnotation ,
8- getFieldName ,
9- onelineSectionArgs ,
10- getFieldEndPosition ,
11- getSectionArgEndPosition ,
12- getNameEndPosition ,
13- getFieldLineEndPosition ,
14- getFieldLSPRange
15- ) where
2+ ( findStanzaForColumn
3+ , getModulesNames
4+ , getFieldLSPRange
5+ , findFieldSection
6+ , findTextWord
7+ , findFieldLine
8+ , getOptionalSectionName
9+ , getAnnotation
10+ , getFieldName
11+ , onelineSectionArgs
12+ , getFieldEndPosition
13+ , getSectionArgEndPosition
14+ , getNameEndPosition
15+ , getFieldLineEndPosition
16+ )
17+ where
1618
1719import qualified Data.ByteString as BS
1820import Data.List (find )
21+ import Data.List.Extra (groupSort )
1922import Data.List.NonEmpty (NonEmpty )
2023import qualified Data.List.NonEmpty as NE
2124import qualified Data.Text as T
2225import qualified Data.Text.Encoding as T
26+ import Data.Tuple (swap )
2327import qualified Distribution.Fields as Syntax
2428import qualified Distribution.Parsec.Position as Syntax
2529import Ide.Plugin.Cabal.Completion.Types
@@ -138,6 +142,9 @@ getFieldName :: Syntax.Field ann -> FieldName
138142getFieldName (Syntax. Field (Syntax. Name _ fn) _) = T. decodeUtf8 fn
139143getFieldName (Syntax. Section (Syntax. Name _ fn) _ _) = T. decodeUtf8 fn
140144
145+ getFieldLineName :: Syntax. FieldLine ann -> FieldName
146+ getFieldLineName (Syntax. FieldLine _ fn) = T. decodeUtf8 fn
147+
141148-- | Returns the name of a section if it has a name.
142149--
143150-- This assumes that the given section args belong to named stanza
@@ -148,6 +155,107 @@ getOptionalSectionName (x:xs) = case x of
148155 Syntax. SecArgName _ name -> Just (T. decodeUtf8 name)
149156 _ -> getOptionalSectionName xs
150157
158+ type BuildTargetName = T. Text
159+ type ModuleName = T. Text
160+
161+ -- | Given a cabal AST returns pairs of all respective target names
162+ -- and the module name bound to them. If a target is a main library gives
163+ -- @Nothing@, otherwise @Just target-name@
164+ --
165+ -- Examples of input cabal files and the outputs:
166+ --
167+ -- * Target is a main library module:
168+ --
169+ -- > library
170+ -- > exposed-modules:
171+ -- > MyLib
172+ --
173+ -- * @getModulesNames@ output:
174+ --
175+ -- > [([Nothing], "MyLib")]
176+ --
177+ -- * Same module names in different targets:
178+ --
179+ -- > test-suite first-target
180+ -- > other-modules:
181+ -- > Config
182+ -- > test-suite second-target
183+ -- > other-modules:
184+ -- > Config
185+ --
186+ -- * @getModulesNames@ output:
187+ --
188+ -- > [([Just "first-target", Just "second-target"], "Config")]
189+ getModulesNames :: [Syntax. Field any ] -> [([Maybe BuildTargetName ], ModuleName )]
190+ getModulesNames fields = map swap $ groupSort rawModuleTargetPairs
191+ where
192+ rawModuleTargetPairs = concatMap getSectionModuleNames sections
193+ sections = getSectionsWithModules fields
194+
195+ getSectionModuleNames :: Syntax. Field any -> [(ModuleName , Maybe BuildTargetName )]
196+ getSectionModuleNames (Syntax. Section _ secArgs fields) = map (, getArgsName secArgs) $ concatMap getFieldModuleNames fields
197+ getSectionModuleNames _ = []
198+
199+ getArgsName [Syntax. SecArgName _ name] = Just $ T. decodeUtf8 name
200+ getArgsName _ = Nothing -- Can be only a main library, that has no name
201+ -- since it's impossible to have multiple names for a build target
202+
203+ getFieldModuleNames field@ (Syntax. Field _ modules) = if getFieldName field == T. pack " exposed-modules" ||
204+ getFieldName field == T. pack " other-modules"
205+ then map getFieldLineName modules
206+ else []
207+ getFieldModuleNames _ = []
208+
209+ -- | Trims a given cabal AST leaving only targets and their
210+ -- @exposed-modules@ and @other-modules@ sections.
211+ --
212+ -- For example:
213+ --
214+ -- * Given a cabal file like this:
215+ --
216+ -- > library
217+ -- > import: extra
218+ -- > hs-source-dirs: source/directory
219+ -- > ...
220+ -- > exposed-modules:
221+ -- > Important.Exposed.Module
222+ -- > other-modules:
223+ -- > Important.Other.Module
224+ -- >
225+ -- > test-suite tests
226+ -- > type: type
227+ -- > build-tool-depends: tool
228+ -- > other-modules:
229+ -- > Important.Other.Module
230+ --
231+ -- * @getSectionsWithModules@ gives output:
232+ --
233+ -- > library
234+ -- > exposed-modules:
235+ -- > Important.Exposed.Module
236+ -- > other-modules:
237+ -- > Important.Other.Module
238+ -- > test-suite tests
239+ -- > other-modules:
240+ -- > Important.Other.Module
241+ getSectionsWithModules :: [Syntax. Field any ] -> [Syntax. Field any ]
242+ getSectionsWithModules fields = concatMap go fields
243+ where
244+ go :: Syntax. Field any -> [Syntax. Field any ]
245+ go (Syntax. Field _ _) = []
246+ go section@ (Syntax. Section _ _ fields) = concatMap onlySectionsWithModules (section: fields)
247+
248+ onlySectionsWithModules :: Syntax. Field any -> [Syntax. Field any ]
249+ onlySectionsWithModules (Syntax. Field _ _) = []
250+ onlySectionsWithModules (Syntax. Section name secArgs fields)
251+ | (not . null ) newFields = [Syntax. Section name secArgs newFields]
252+ | otherwise = []
253+ where newFields = filter subfieldHasModule fields
254+
255+ subfieldHasModule :: Syntax. Field any -> Bool
256+ subfieldHasModule field@ (Syntax. Field _ _) = getFieldName field == T. pack " exposed-modules" ||
257+ getFieldName field == T. pack " other-modules"
258+ subfieldHasModule (Syntax. Section _ _ _) = False
151259
152260-- | Makes a single text line out of multiple
153261-- @SectionArg@s. Allows to display conditions,
@@ -165,7 +273,6 @@ onelineSectionArgs sectionArgs = joinedName
165273 getName (Syntax. SecArgStr _ quotedString) = T. decodeUtf8 quotedString
166274 getName (Syntax. SecArgOther _ string) = T. decodeUtf8 string
167275
168-
169276-- | Returns the end position of a provided field
170277getFieldEndPosition :: Syntax. Field Syntax. Position -> Syntax. Position
171278getFieldEndPosition (Syntax. Field name [] ) = getNameEndPosition name
0 commit comments