@@ -10,9 +10,9 @@ package com.nexthink.utils.parsing.combinator.completion
10
10
import org .json4s
11
11
12
12
import scala .util .parsing .input .{NoPosition , Position }
13
- import org .json4s ._
14
13
import org .json4s .JsonDSL ._
15
14
import org .json4s .native .JsonMethods ._
15
+ import scala .collection .immutable
16
16
17
17
/** Collection of data types allowing definition of structured parser completions.
18
18
* A `Completions` instance can contain multiple `CompletionSet`s instances. A `CompletionSet` provides a set of
@@ -73,45 +73,52 @@ trait CompletionTypes {
73
73
* @param tag set tag
74
74
* @param completions set of unique completion entries
75
75
*/
76
- case class CompletionSet (tag : CompletionTag , completions : Set [Completion ]) {
77
- def label : String = tag.label
78
- def score : Int = tag.score
79
- def description : Option [String ] = tag.description
80
- def meta : Option [String ] = tag.meta
81
- def completionStrings : Seq [String ] =
82
- completions.toSeq.sorted.map(_.value.toString)
76
+ case class CompletionSet (tag : CompletionTag , completions : immutable.HashMap [Elems , Completion ]) {
77
+ def label : String = tag.label
78
+ def score : Int = tag.score
79
+ def description : Option [String ] = tag.description
80
+ def meta : Option [String ] = tag.meta
81
+ def entries : Iterable [Completion ] = completions.values
82
+ def sortedEntries : Seq [Completion ] = entries.toSeq.sorted
83
+ def stringEntries : Seq [String ] = sortedEntries.map(_.value.toString)
83
84
84
85
private [CompletionTypes ] def serializeJson =
85
- (" tag" -> tag.serializeJson) ~ (" completions" -> completions .map(_.serializeJson).toList)
86
+ (" tag" -> tag.serializeJson) ~ (" completions" -> entries .map(_.serializeJson).toList)
86
87
87
88
override def toString : String = pretty(render(serializeJson))
88
89
def toJson : String = compact(render(serializeJson))
89
90
}
90
91
91
92
case object CompletionSet {
93
+ def apply (tag : CompletionTag , completions : Seq [(Elems , Completion )]): CompletionSet =
94
+ CompletionSet (tag, immutable.HashMap (completions : _* ))
95
+
92
96
def apply (tag : String , el : Elem ): CompletionSet =
93
- CompletionSet (CompletionTag (tag), Set ( Completion (el)))
97
+ CompletionSet (CompletionTag (tag), Seq ( Seq (el) -> Completion (el)))
94
98
95
99
def apply (tag : String , elems : Elems ): CompletionSet =
96
- CompletionSet (CompletionTag (tag), Set ( Completion (elems)))
100
+ CompletionSet (CompletionTag (tag), Seq (elems -> Completion (elems)))
97
101
98
102
def apply (tag : String , completion : Completion ): CompletionSet =
99
- CompletionSet (CompletionTag (tag), Set (completion))
103
+ CompletionSet (CompletionTag (tag), Seq (completion.value -> completion))
104
+
105
+ def apply (tag : CompletionTag , completions : Iterable [Completion ]): CompletionSet =
106
+ CompletionSet (tag, completions.map(c => c.value -> c).toSeq)
100
107
101
108
def apply (tag : String , completions : Iterable [Completion ]): CompletionSet =
102
- CompletionSet (CompletionTag (tag), completions.toSet )
109
+ CompletionSet (CompletionTag (tag), completions.map(c => c.value -> c).toSeq )
103
110
104
111
def apply (completions : Iterable [Completion ]): CompletionSet =
105
- CompletionSet (CompletionTag .Default , completions.toSet )
112
+ CompletionSet (CompletionTag .Default , completions.map(c => c.value -> c).toSeq )
106
113
107
114
def apply (completions : Completion * ): CompletionSet =
108
- CompletionSet (CompletionTag .Default , completions.toSet )
115
+ CompletionSet (CompletionTag .Default , completions.map(c => c.value -> c) )
109
116
110
117
def apply (el : Elem ): CompletionSet =
111
- CompletionSet (CompletionTag .Default , Set ( Completion (el)))
118
+ CompletionSet (CompletionTag .Default , Seq ( Seq (el) -> Completion (el)))
112
119
113
120
def apply (completions : Traversable [Elems ]): CompletionSet =
114
- CompletionSet (CompletionTag .Default , completions.map(Completion (_ )).toSet )
121
+ CompletionSet (CompletionTag .Default , completions.map(c => c -> Completion (c )).toSeq )
115
122
}
116
123
117
124
type Elems = Seq [Elem ]
@@ -142,46 +149,45 @@ trait CompletionTypes {
142
149
* @param position position in the input where completion entries apply
143
150
* @param sets completion entries, grouped per tag
144
151
*/
145
- case class Completions (position : Position , sets : Map [String , CompletionSet ]) {
152
+ case class Completions (position : Position , sets : immutable. HashMap [String , CompletionSet ]) {
146
153
def isEmpty : Boolean = sets.isEmpty
147
154
def nonEmpty : Boolean = ! isEmpty
148
155
def setWithTag (tag : String ): Option [CompletionSet ] = sets.get(tag)
149
156
def allSets : Iterable [CompletionSet ] = sets.values
150
- def allCompletions : Iterable [Completion ] = allSets.flatMap(_.completions )
157
+ def allCompletions : Iterable [Completion ] = allSets.flatMap(_.sortedEntries )
151
158
def defaultSet : Option [CompletionSet ] = sets.get(" " )
152
159
153
160
private def serializeJson = (" position" -> ((" line" -> position.line) ~ (" column" -> position.column))) ~ (" sets" -> allSets.map(_.serializeJson))
154
161
155
162
override def toString : String = pretty(render(serializeJson))
156
163
def toJson : String = compact(render(serializeJson))
157
164
158
- private def unionSets (left : CompletionSet , right : CompletionSet ): CompletionSet = {
159
- def offsetCompletions (set : CompletionSet ) = {
160
- val isOffsetRequired =
161
- set.completions.map(_.score).exists(_ < set.score)
162
- if (isOffsetRequired)
163
- set.completions.map(c => Completion (c.value, set.score + c.score, c.meta))
164
- else set.completions
165
- }
166
- CompletionSet (
167
- CompletionTag (left.tag.label, left.score.min(right.score), left.description, left.meta.orElse(right.meta)),
168
- offsetCompletions(left) ++ offsetCompletions(right)
165
+ private def mergeMetaData (left : Option [String ], right : Option [String ]) = (left, right) match {
166
+ case (Some (l), Some (r)) =>
167
+ (parseOpt(l), parseOpt(r)) match {
168
+ case (Some (lJson), Some (rJson)) => Some (compact(render(lJson merge rJson)))
169
+ case _ => Some (l + r)
170
+ }
171
+ case (Some (l), None ) => Some (l)
172
+ case (None , Some (r)) => Some (r)
173
+ case (None , None ) => None
174
+ }
175
+
176
+ private def mergeCompletion (left : Completion , right : Completion ): Completion = {
177
+ assert(left.value == right.value, " Attempt to merge different completion entries" )
178
+ Completion (
179
+ left.value,
180
+ left.score.max(right.score),
181
+ mergeMetaData(left.meta, right.meta)
169
182
)
170
183
}
171
184
172
- private def mergeCompletions (other : Completions ) = {
173
- val overlappingSetTags = sets.keySet.intersect(other.sets.keySet)
174
- val unions =
175
- overlappingSetTags.map(name => (sets(name), other.sets(name))).map {
176
- case (left, right) => unionSets(left, right)
177
- }
178
- val leftExclusive = sets.keySet.diff(overlappingSetTags).map(sets(_))
179
- val rightExclusive =
180
- other.sets.keySet.diff(overlappingSetTags).map(other.sets(_))
181
- Completions (position,
182
- (unions ++ leftExclusive ++ rightExclusive)
183
- .map(s => s.tag.label -> s)
184
- .toMap)
185
+ private def mergeSets (left : CompletionSet , right : CompletionSet ): CompletionSet = {
186
+ assert(left.label == right.label, " Attempt to merge sets with different completion tags" )
187
+ CompletionSet (
188
+ CompletionTag (left.tag.label, left.score.max(right.score), left.description.orElse(right.description), mergeMetaData(left.meta, right.meta)),
189
+ left.completions.merged(right.completions)((l, r) => (l._1, mergeCompletion(l._2, r._2)))
190
+ )
185
191
}
186
192
187
193
def | (other : Completions ): Completions = {
@@ -190,7 +196,7 @@ trait CompletionTypes {
190
196
case _ =>
191
197
other.position match {
192
198
case otherPos if otherPos < position => this
193
- case otherPos if otherPos == position => mergeCompletions( other)
199
+ case otherPos if otherPos == position => Completions (position, sets.merged( other.sets)((l, r) => (l._1, mergeSets(l._2, r._2))) )
194
200
case _ => other
195
201
}
196
202
}
@@ -200,17 +206,17 @@ trait CompletionTypes {
200
206
sets.values.toSeq
201
207
.sortBy(_.score)
202
208
.reverse
203
- .flatMap(_.completionStrings )
209
+ .flatMap(_.stringEntries )
204
210
.toList
205
211
206
212
def takeTop (count : Int ): Completions = {
207
213
val allEntries = allSets
208
- .flatMap(s => s.completions.map((_, s.tag)))
214
+ .flatMap(s => s.completions.values. map((_, s.tag)))
209
215
.toList
210
216
val sortedEntries =
211
217
allEntries
212
218
.sortBy {
213
- case (Completion (_, score, meta ), CompletionTag (_, tagScore, _, _)) =>
219
+ case (Completion (_, score, _ ), CompletionTag (_, tagScore, _, _)) =>
214
220
(tagScore, score)
215
221
}
216
222
.reverse
@@ -219,27 +225,31 @@ trait CompletionTypes {
219
225
.groupBy { case (_, tag) => tag }
220
226
.map {
221
227
case (groupTag, completions) =>
222
- CompletionSet (groupTag, completions.map(_ ._1).toSet )
228
+ CompletionSet (groupTag, completions.map(c => c ._1))
223
229
}
224
- copy(sets = regroupedSets.map(s => ( s.tag.label, s)).toMap )
230
+ Completions (position, regroupedSets.map(s => s.tag.label -> s).toSeq )
225
231
}
226
232
227
233
def setsScoredWithMaxCompletion (): Completions = {
228
- Completions (position, sets.mapValues(s => CompletionSet (s.tag.copy(score = s.completions.map(_.score).max), s.completions)))
234
+ Completions (position, sets.mapValues(s => CompletionSet (s.tag.copy(score = s.completions.values. map(_.score).max), s.completions)).toSeq )
229
235
}
230
236
}
231
237
232
238
case object Completions {
239
+ def apply (position : Position , completionSets : Seq [(String , CompletionSet )]): Completions =
240
+ Completions (position, immutable.HashMap (completionSets : _* ))
233
241
def apply (position : Position , completionSet : CompletionSet ): Completions =
234
- Completions (position, Map (completionSet.tag.label -> completionSet))
242
+ Completions (position, Seq (completionSet.tag.label -> completionSet))
235
243
def apply (position : Position , completions : Traversable [Elems ]): Completions =
236
244
Completions (position, CompletionSet (completions))
237
245
def apply (completionSet : CompletionSet ): Completions =
238
246
Completions (NoPosition , completionSet)
247
+ def apply (position : Position , completionSets : Iterable [CompletionSet ]): Completions =
248
+ Completions (position, completionSets.map(s => s.tag.label -> s).toSeq)
239
249
def apply (completionSets : Iterable [CompletionSet ]): Completions =
240
- Completions (NoPosition , completionSets.map(s => s.tag.label -> s).toMap )
250
+ Completions (NoPosition , completionSets.map(s => s.tag.label -> s).toSeq )
241
251
242
- val empty = Completions (NoPosition , Map [String , CompletionSet ]())
252
+ val empty = Completions (NoPosition , immutable. HashMap [String , CompletionSet ]())
243
253
}
244
254
245
255
}
0 commit comments