@@ -258,6 +258,24 @@ let recover (type a) : a Ast_passes.Ast0.t -> _ -> a = function
258258 | Module_type -> failwith " no recovery for module_type"
259259 | Expression -> failwith " no recovery for expression"
260260
261+ let strconst_mapper locs =
262+ let constant self c =
263+ match c with
264+ | Parsetree. Pconst_string (_, {Location. loc_start; loc_end; _}, Some _)
265+ ->
266+ locs := (loc_start.Lexing. pos_cnum, loc_end.Lexing. pos_cnum) :: ! locs ;
267+ c
268+ | _ -> Ast_mapper. default_mapper.constant self c
269+ in
270+ {Ast_mapper. default_mapper with constant}
271+
272+ let collect_strlocs (type a ) (fgN : a Ast_passes.Ast_final.t ) (ast : a ) :
273+ (int * int ) list =
274+ let locs = ref [] in
275+ let _ = Ast_passes.Ast_final. map fgN (strconst_mapper locs) ast in
276+ let compare (c1 , _ ) (c2 , _ ) = Stdlib. compare c1 c2 in
277+ List. sort ~compare ! locs
278+
261279let format (type a b ) (fg0 : a Ast_passes.Ast0.t )
262280 (fgN : b Ast_passes.Ast_final.t ) ?output_file ~input_name ~prev_source
263281 ~parsed conf opts =
@@ -307,7 +325,8 @@ let format (type a b) (fg0 : a Ast_passes.Ast0.t)
307325 if opts.Conf. margin_check then
308326 check_margin conf ~fmted
309327 ~filename: (Option. value output_file ~default: input_name) ;
310- Ok fmted )
328+ let strlocs = collect_strlocs fgN t.ast in
329+ Ok (strlocs, fmted) )
311330 else
312331 let exn_args () =
313332 [(" output file" , dump_formatted ~suffix: " .invalid-ast" fmted)]
@@ -412,29 +431,34 @@ let parse_result ?(f = Ast_passes.Ast0.Parse.ast) fragment conf ~source
412431 | exception exn -> Error (Error. Invalid_source {exn ; input_name})
413432 | parsed -> Ok parsed
414433
415- let normalize_eol ~line_endings s =
434+ let normalize_eol ~strlocs ~ line_endings s =
416435 let buf = Buffer. create (String. length s) in
417- let rec loop seen_cr i =
418- if i = String. length s then (
419- if seen_cr then Buffer. add_char buf '\r' ;
420- Buffer. contents buf )
436+ let add_cr n = Buffer. add_string buf (String. init n ~f: (fun _ -> '\r' )) in
437+ let rec normalize_segment ~seen_cr i stop =
438+ if i = stop then add_cr seen_cr
421439 else
422- match (s.[i], line_endings) with
423- | '\r' , _ ->
424- if seen_cr then Buffer. add_char buf '\r' ;
425- loop true (i + 1 )
426- | '\n' , `Crlf ->
427- Buffer. add_string buf " \r\n " ;
428- loop false (i + 1 )
429- | '\n' , `Lf ->
430- Buffer. add_char buf '\n' ;
431- loop false (i + 1 )
432- | c , _ ->
433- if seen_cr then Buffer. add_char buf '\r' ;
440+ match s.[i] with
441+ | '\r' -> normalize_segment ~seen_cr: (seen_cr + 1 ) (i + 1 ) stop
442+ | '\n' ->
443+ Buffer. add_string buf
444+ (match line_endings with `Crlf -> " \r\n " | `Lf -> " \n " ) ;
445+ normalize_segment ~seen_cr: 0 (i + 1 ) stop
446+ | c ->
447+ add_cr seen_cr ;
434448 Buffer. add_char buf c ;
435- loop false (i + 1 )
449+ normalize_segment ~seen_cr: 0 (i + 1 ) stop
450+ in
451+ let rec loop locs i =
452+ match locs with
453+ | [] ->
454+ normalize_segment ~seen_cr: 0 i (String. length s) ;
455+ Buffer. contents buf
456+ | (start , stop ) :: xs ->
457+ normalize_segment ~seen_cr: 0 i start ;
458+ Buffer. add_substring buf s ~pos: start ~len: (stop - start) ;
459+ loop xs stop
436460 in
437- loop false 0
461+ loop strlocs 0
438462
439463let parse_and_format (type a b ) (fg0 : a Ast_passes.Ast0.t )
440464 (fgN : b Ast_passes.Ast_final.t ) ?output_file ~input_name ~source conf
@@ -445,8 +469,8 @@ let parse_and_format (type a b) (fg0 : a Ast_passes.Ast0.t)
445469 let parsed = {parsed with ast= Ast_passes. run fg0 fgN parsed.ast} in
446470 format fg0 fgN ?output_file ~input_name ~prev_source: source ~parsed conf
447471 opts
448- >> = fun formatted ->
449- Ok (normalize_eol ~line_endings: conf.Conf. line_endings formatted)
472+ >> = fun ( strlocs , formatted ) ->
473+ Ok (normalize_eol ~strlocs ~ line_endings: conf.Conf. line_endings formatted)
450474
451475let parse_and_format = function
452476 | Syntax. Structure -> parse_and_format Structure Structure
@@ -483,7 +507,7 @@ let numeric (type a b) (fg0 : a list Ast_passes.Ast0.t)
483507 let parsed = {parsed with ast= Ast_passes. run fg0 fgN parsed.ast} in
484508 let {ast= parsed_ast; source= parsed_src; _} = parsed in
485509 match format fg0 fgN ~input_name ~prev_source: src ~parsed conf opts with
486- | Ok fmted_src -> (
510+ | Ok ( _ , fmted_src ) -> (
487511 match parse_result fg0 ~source: fmted_src conf ~input_name with
488512 | Ok {ast = fmted_ast ; source = fmted_src ; _} ->
489513 let fmted_ast = Ast_passes. run fg0 fgN fmted_ast in
0 commit comments