@@ -55,6 +55,7 @@ use crate::{
55
55
} ;
56
56
57
57
use crate :: job:: { self , Jobs } ;
58
+ use futures_util:: future:: join_all;
58
59
use std:: {
59
60
collections:: { HashMap , HashSet } ,
60
61
fmt,
@@ -399,7 +400,7 @@ impl MappableCommand {
399
400
paste_primary_clipboard_before, "Paste primary clipboard before selections" ,
400
401
indent, "Indent selection" ,
401
402
unindent, "Unindent selection" ,
402
- format_selections, "Format selection " ,
403
+ format_selections, "Format selections " ,
403
404
join_selections, "Join lines inside selection" ,
404
405
join_selections_space, "Join lines inside selection and select spaces" ,
405
406
keep_selections, "Keep selections matching regex" ,
@@ -4233,25 +4234,10 @@ fn format_selections(cx: &mut Context) {
4233
4234
let ( view, doc) = current ! ( cx. editor) ;
4234
4235
let view_id = view. id ;
4235
4236
4236
- // via lsp if available
4237
- // TODO: else via tree-sitter indentation calculations
4237
+ // TODO: via lsp if available else via tree-sitter indentation calculations
4238
4238
4239
- if doc. selection ( view_id) . len ( ) != 1 {
4240
- cx. editor
4241
- . set_error ( "format_selections only supports a single selection for now" ) ;
4242
- return ;
4243
- }
4244
-
4245
- // TODO extra LanguageServerFeature::FormatSelections?
4246
- // maybe such that LanguageServerFeature::Format contains it as well
4247
4239
let Some ( language_server) = doc
4248
- . language_servers_with_feature ( LanguageServerFeature :: Format )
4249
- . find ( |ls| {
4250
- matches ! (
4251
- ls. capabilities( ) . document_range_formatting_provider,
4252
- Some ( lsp:: OneOf :: Left ( true ) | lsp:: OneOf :: Right ( _) )
4253
- )
4254
- } )
4240
+ . language_servers_with_feature ( LanguageServerFeature :: FormatSelection ) . next ( )
4255
4241
else {
4256
4242
cx. editor
4257
4243
. set_error ( "No configured language server supports range formatting" ) ;
@@ -4262,27 +4248,38 @@ fn format_selections(cx: &mut Context) {
4262
4248
let ranges: Vec < lsp:: Range > = doc
4263
4249
. selection ( view_id)
4264
4250
. iter ( )
4251
+ // request and process range formatting in reverse order from last selection
4252
+ // to the first selection to reduce the chances of collisions.
4253
+ . rev ( )
4265
4254
. map ( |range| range_to_lsp_range ( doc. text ( ) , * range, offset_encoding) )
4266
4255
. collect ( ) ;
4267
4256
4268
- // TODO: handle fails
4269
- // TODO: concurrent map over all ranges
4270
-
4271
- let range = ranges[ 0 ] ;
4272
-
4273
- let future = language_server
4274
- . text_document_range_formatting (
4257
+ let futures = ranges. into_iter ( ) . filter_map ( |range| {
4258
+ language_server. text_document_range_formatting (
4275
4259
doc. identifier ( ) ,
4276
4260
range,
4277
4261
lsp:: FormattingOptions :: default ( ) ,
4278
4262
None ,
4279
4263
)
4280
- . unwrap ( ) ;
4264
+ } ) ;
4281
4265
4282
- let edits = tokio:: task:: block_in_place ( || helix_lsp:: block_on ( future) ) . unwrap_or_default ( ) ;
4266
+ let results = helix_lsp:: block_on ( join_all ( futures) ) ;
4267
+
4268
+ let all_edits = results
4269
+ . into_iter ( )
4270
+ . filter_map ( |result| {
4271
+ match result {
4272
+ // TODO: handle colliding edits (edits outside the range) and edits that result into collision.
4273
+ // See: https://github.com/helix-editor/helix/issues/3209#issuecomment-1197463913
4274
+ Ok ( edits) => Some ( edits) ,
4275
+ Err ( _) => None ,
4276
+ }
4277
+ } )
4278
+ . flatten ( )
4279
+ . collect ( ) ;
4283
4280
4284
4281
let transaction =
4285
- helix_lsp:: util:: generate_transaction_from_edits ( doc. text ( ) , edits , offset_encoding) ;
4282
+ helix_lsp:: util:: generate_transaction_from_edits ( doc. text ( ) , all_edits , offset_encoding) ;
4286
4283
4287
4284
doc. apply ( & transaction, view_id) ;
4288
4285
}
0 commit comments