@@ -152,39 +152,102 @@ pub fn default_log_file() -> PathBuf {
152
152
cache_dir ( ) . join ( "helix.log" )
153
153
}
154
154
155
+ pub struct MergeStrategy {
156
+ pub array : MergeMode ,
157
+ pub table : MergeMode ,
158
+ }
159
+
160
+ pub enum MergeMode {
161
+ Never ,
162
+ Always ,
163
+ MaxDepth ( usize ) ,
164
+ }
165
+
166
+ impl MergeMode {
167
+ pub fn should_merge ( & self , depth : usize ) -> bool {
168
+ match self {
169
+ MergeMode :: Always => true ,
170
+ MergeMode :: MaxDepth ( max_depth) => depth < * max_depth,
171
+ MergeMode :: Never => false ,
172
+ }
173
+ }
174
+ }
175
+
155
176
/// Merge two TOML documents, merging values from `right` onto `left`
156
177
///
157
- /// `merge_depth` sets the nesting depth up to which values are merged instead
158
- /// of overridden.
178
+ /// `max_merge_depth` sets the nesting depth up to which values are merged
179
+ /// instead of overridden.
180
+ ///
181
+ /// When an array exists in both `left` and `right`, the merged array is formed
182
+ /// by concatenating `left`'s elements with `right`'s. But if any elements share
183
+ /// the same `name` field, they are merged recursively and included only once.
159
184
///
160
185
/// When a table exists in both `left` and `right`, the merged table consists of
161
186
/// all keys in `left`'s table unioned with all keys in `right` with the values
162
187
/// of `right` being merged recursively onto values of `left`.
163
188
///
189
+ /// Setting `max_merge_depth` is useful for TOML documents that use a
190
+ /// top-level array of values, where the top-level arrays should be merged
191
+ /// but nested arrays should act as overrides. For the `languages.toml`
192
+ /// config for example, this means that you can specify a sub-set of
193
+ /// languages in an overriding `languages.toml` but that nested arrays
194
+ /// like Language Server arguments are replaced instead of merged.
195
+ ///
164
196
/// `crate::merge_toml_values(a, b, 3)` combines, for example:
165
197
///
166
- /// b :
198
+ /// a :
167
199
/// ```toml
168
200
/// [[language]]
169
201
/// name = "toml"
202
+ /// scope = "source.toml"
170
203
/// language-server = { command = "taplo", args = ["lsp", "stdio"] }
171
204
/// ```
172
- /// a :
205
+ /// b :
173
206
/// ```toml
174
207
/// [[language]]
208
+ /// name = "toml"
175
209
/// language-server = { command = "/usr/bin/taplo" }
176
210
/// ```
177
211
///
178
212
/// into:
179
213
/// ```toml
180
214
/// [[language]]
181
215
/// name = "toml"
216
+ /// scope = "source.toml"
182
217
/// language-server = { command = "/usr/bin/taplo" }
183
218
/// ```
184
219
///
185
- /// thus it overrides the third depth-level of b with values of a if they exist,
220
+ /// thus it overrides the third depth-level of a with values of b if they exist,
186
221
/// but otherwise merges their values
187
- pub fn merge_toml_values ( left : toml:: Value , right : toml:: Value , merge_depth : usize ) -> toml:: Value {
222
+ pub fn merge_toml_values (
223
+ left : toml:: Value ,
224
+ right : toml:: Value ,
225
+ max_merge_depth : usize ,
226
+ ) -> toml:: Value {
227
+ merge_toml_values_with_strategy (
228
+ left,
229
+ right,
230
+ & MergeStrategy {
231
+ array : MergeMode :: MaxDepth ( max_merge_depth) ,
232
+ table : MergeMode :: MaxDepth ( max_merge_depth) ,
233
+ } ,
234
+ )
235
+ }
236
+
237
+ pub fn merge_toml_values_with_strategy (
238
+ left : toml:: Value ,
239
+ right : toml:: Value ,
240
+ strategy : & MergeStrategy ,
241
+ ) -> toml:: Value {
242
+ merge_toml_values_recursive ( left, right, strategy, 0 )
243
+ }
244
+
245
+ fn merge_toml_values_recursive (
246
+ left : toml:: Value ,
247
+ right : toml:: Value ,
248
+ strategy : & MergeStrategy ,
249
+ depth : usize ,
250
+ ) -> toml:: Value {
188
251
use toml:: Value ;
189
252
190
253
fn get_name ( v : & Value ) -> Option < & str > {
@@ -193,7 +256,7 @@ pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usi
193
256
194
257
match ( left, right) {
195
258
( Value :: Array ( mut left_items) , Value :: Array ( right_items) ) => {
196
- if merge_depth > 0 {
259
+ if strategy . array . should_merge ( depth ) {
197
260
left_items. reserve ( right_items. len ( ) ) ;
198
261
for rvalue in right_items {
199
262
let lvalue = get_name ( & rvalue)
@@ -202,7 +265,9 @@ pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usi
202
265
} )
203
266
. map ( |lpos| left_items. remove ( lpos) ) ;
204
267
let mvalue = match lvalue {
205
- Some ( lvalue) => merge_toml_values ( lvalue, rvalue, merge_depth - 1 ) ,
268
+ Some ( lvalue) => {
269
+ merge_toml_values_recursive ( lvalue, rvalue, strategy, depth + 1 )
270
+ }
206
271
None => rvalue,
207
272
} ;
208
273
left_items. push ( mvalue) ;
@@ -213,11 +278,12 @@ pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usi
213
278
}
214
279
}
215
280
( Value :: Table ( mut left_map) , Value :: Table ( right_map) ) => {
216
- if merge_depth > 0 {
281
+ if strategy . table . should_merge ( depth ) {
217
282
for ( rname, rvalue) in right_map {
218
283
match left_map. remove ( & rname) {
219
284
Some ( lvalue) => {
220
- let merged_value = merge_toml_values ( lvalue, rvalue, merge_depth - 1 ) ;
285
+ let merged_value =
286
+ merge_toml_values_recursive ( lvalue, rvalue, strategy, depth + 1 ) ;
221
287
left_map. insert ( rname, merged_value) ;
222
288
}
223
289
None => {
0 commit comments