1
1
module 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
16
18
17
19
import qualified Data.ByteString as BS
18
20
import Data.List (find )
21
+ import Data.List.Extra (groupSort )
19
22
import Data.List.NonEmpty (NonEmpty )
20
23
import qualified Data.List.NonEmpty as NE
21
24
import qualified Data.Text as T
22
25
import qualified Data.Text.Encoding as T
26
+ import Data.Tuple (swap )
23
27
import qualified Distribution.Fields as Syntax
24
28
import qualified Distribution.Parsec.Position as Syntax
25
29
import Ide.Plugin.Cabal.Completion.Types
@@ -138,6 +142,9 @@ getFieldName :: Syntax.Field ann -> FieldName
138
142
getFieldName (Syntax. Field (Syntax. Name _ fn) _) = T. decodeUtf8 fn
139
143
getFieldName (Syntax. Section (Syntax. Name _ fn) _ _) = T. decodeUtf8 fn
140
144
145
+ getFieldLineName :: Syntax. FieldLine ann -> FieldName
146
+ getFieldLineName (Syntax. FieldLine _ fn) = T. decodeUtf8 fn
147
+
141
148
-- | Returns the name of a section if it has a name.
142
149
--
143
150
-- This assumes that the given section args belong to named stanza
@@ -148,6 +155,107 @@ getOptionalSectionName (x:xs) = case x of
148
155
Syntax. SecArgName _ name -> Just (T. decodeUtf8 name)
149
156
_ -> getOptionalSectionName xs
150
157
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
151
259
152
260
-- | Makes a single text line out of multiple
153
261
-- @SectionArg@s. Allows to display conditions,
@@ -165,7 +273,6 @@ onelineSectionArgs sectionArgs = joinedName
165
273
getName (Syntax. SecArgStr _ quotedString) = T. decodeUtf8 quotedString
166
274
getName (Syntax. SecArgOther _ string) = T. decodeUtf8 string
167
275
168
-
169
276
-- | Returns the end position of a provided field
170
277
getFieldEndPosition :: Syntax. Field Syntax. Position -> Syntax. Position
171
278
getFieldEndPosition (Syntax. Field name [] ) = getNameEndPosition name
0 commit comments