diff --git a/src/lsp/cobol_indent/config.ml b/src/lsp/cobol_indent/config.ml index fd65ae0d0..9bca93e85 100644 --- a/src/lsp/cobol_indent/config.ml +++ b/src/lsp/cobol_indent/config.ml @@ -20,7 +20,7 @@ let verbose = false let fixed_format = { name = "fixed" ; - free = false ; (* whether there is an indicator or inline + format = SFFixed ; (* whether there is an indicator or inline comments *) skip_before = 6 ; (* skip 6 columns, 1..6 *) max_text_length = 65; (* skip columns 8..72 *) @@ -28,7 +28,7 @@ let fixed_format = { let free_format = { name = "free" ; - free = true ; + format = SFFree ; skip_before = 0 ; max_text_length = 65536; } diff --git a/src/lsp/cobol_indent/config.mli b/src/lsp/cobol_indent/config.mli index cf9f094a3..a2b5a6ad5 100644 --- a/src/lsp/cobol_indent/config.mli +++ b/src/lsp/cobol_indent/config.mli @@ -34,3 +34,10 @@ val load : val to_string : Types.config -> string val generate : ?config:Types.config -> ?only_comment:bool -> string -> unit + + +val free_format : Types.source_format +val fixed_format : Types.source_format +val variable_format : Types.source_format +val xcard_format : Types.source_format +val cobolx_format : Types.source_format diff --git a/src/lsp/cobol_indent/editor.ml b/src/lsp/cobol_indent/editor.ml index 57bfe0b73..cde30571b 100644 --- a/src/lsp/cobol_indent/editor.ml +++ b/src/lsp/cobol_indent/editor.ml @@ -11,8 +11,6 @@ (* *) (**************************************************************************) -open Ez_file.V1 - open Types let verbose = Engine.verbose @@ -22,8 +20,13 @@ let verbose = Engine.verbose (* ~char: the position within the line. For now, it does not change with insertions/deletions, but it should, in the future... *) -let apply_edits ~contents ~range ~config ~filename ~edits ~symbolic = +let apply_edits (type t) ~contents ~range ~config ~edits + (output : t output) = + let symbolic = match output with + | Output_contents -> false + | Output_edits -> true + in let len = String.length contents in let b = Buffer.create (if symbolic then 4 else 2 * len ) in let ops = ref [] in @@ -57,7 +60,7 @@ let apply_edits ~contents ~range ~config ~filename ~edits ~symbolic = if line = edit.lnum then skip_before ~pos ~char ~nbefore:( - (if config.source_format.free then 0 else 1) + + (if config.source_format.format = SFFree then 0 else 1) + config.source_format.skip_before ) edit ~line edits @@ -137,7 +140,7 @@ let apply_edits ~contents ~range ~config ~filename ~edits ~symbolic = if verbose then Printf.eprintf "iter_eol ~pos:%d ~line:%d ~addspaces:%d\n%!" pos line addspaces; - if config.source_format.free then + if config.source_format.format = SFFree then iter_edits ~pos ~char ~line edits else let textlen = @@ -225,13 +228,8 @@ let apply_edits ~contents ~range ~config ~filename ~edits ~symbolic = in iter_edits ~pos:0 ~char:0 ~line:1 edits; (* lnum starts at 1 ? *) - if not symbolic then begin - let contents = Buffer.contents b in - if filename = "-" then - Printf.printf "%s%!" contents - else begin - EzFile.write_file filename contents ; - Printf.eprintf "File %S indented\n%!" filename - end; - end; - List.rev !ops + match output with + | Output_contents -> + ( Buffer.contents b : t ) + | Output_edits -> + ( { edits ; operations = List.rev !ops } : t ) diff --git a/src/lsp/cobol_indent/editor.mli b/src/lsp/cobol_indent/editor.mli index 190b36b81..4da401a17 100644 --- a/src/lsp/cobol_indent/editor.mli +++ b/src/lsp/cobol_indent/editor.mli @@ -11,11 +11,9 @@ (* *) (**************************************************************************) + val apply_edits : contents:string -> range:Types.range -> config:Types.config -> - filename:string -> - edits:Types.indent_record list -> - symbolic:bool -> - Types.edit_space_operation list + edits:Types.indent_record list -> 't Types.output -> 't diff --git a/src/lsp/cobol_indent/main.ml b/src/lsp/cobol_indent/main.ml index b93aa3a9b..30cef0e6d 100644 --- a/src/lsp/cobol_indent/main.ml +++ b/src/lsp/cobol_indent/main.ml @@ -73,17 +73,12 @@ open Types closest line with a saved state. *) let indent ~source_format - ~config - ~dialect (* why ? we usually don't care about the dialect to indent *) ~filename ?(verbose=false) - ?output ?contents ?range - () + output = - ignore ( config ) ; - ignore ( dialect ) ; let contents = match contents with | None -> EzFile.read_file filename | Some contents -> contents @@ -138,12 +133,5 @@ let indent in let edits = iter [] edits in - let symbolic, filename = - match output with - | Some filename -> false, filename - | None -> true, "-" - in - let ops = Editor.apply_edits - ~contents ~edits ~range ~filename ~config ~symbolic - in - edits, ops + Editor.apply_edits + ~contents ~edits ~range ~config output diff --git a/src/lsp/cobol_indent/main.mli b/src/lsp/cobol_indent/main.mli index 78558055f..b1bbc8c84 100644 --- a/src/lsp/cobol_indent/main.mli +++ b/src/lsp/cobol_indent/main.mli @@ -12,13 +12,7 @@ (**************************************************************************) val indent : - source_format:Cobol_config.source_format_spec -> - config: Types.unparsed_config -> - dialect:'c -> + source_format:Cobol_config.Types.source_format_spec -> filename:string -> ?verbose:bool -> - ?output:string -> - ?contents:string -> - ?range:Types.range -> - unit -> - Types.indent_record list * Types.edit_space_operation list + ?contents:string -> ?range:Types.range -> 'c Types.output -> 'c diff --git a/src/lsp/cobol_indent/reformat.ml b/src/lsp/cobol_indent/reformat.ml new file mode 100644 index 000000000..3d4c46c79 --- /dev/null +++ b/src/lsp/cobol_indent/reformat.ml @@ -0,0 +1,330 @@ +(**************************************************************************) +(* *) +(* SuperBOL OSS Studio *) +(* *) +(* Copyright (c) 2022-2023 OCamlPro SAS *) +(* *) +(* All rights reserved. *) +(* This source code is licensed under the GNU Affero General Public *) +(* License version 3 found in the LICENSE.md file in the root directory *) +(* of this source tree. *) +(* *) +(**************************************************************************) + +open Types + +type indicator = + | COMMENT + | LINE + | CONTINUATION + +type state = + | INITIAL + | NORMAL + | LITERAL_OPENED of char + | LITERAL_CLOSED_ON_BORDER of char + +type context = { + mutable current_state : state ; + buffer : Buffer.t ; + mutable source_format : source_format ; + target_format : source_format ; +} + +exception Error of int * string +let error pos fmt = + Printf.ksprintf (fun s -> raise (Error (pos, s))) fmt + +let output context fmt = + Printf.bprintf context.buffer fmt + + +let output_newline context = + match context.current_state with + | INITIAL -> () + | _ -> output context "\n" + +module FIXED2FREE = struct + + let parse_text ?(cont=INITIAL) ~source_format text = + let len = String.length text in + let init_pos = ref 0 in + let rec iter_space pos0 pos = + if pos = len then + NORMAL, String.sub text !init_pos (pos0- !init_pos) + else + let c = text.[pos] in + match c with + | ' ' -> + iter_space pos0 (pos+1) + | '"' -> + iter_literal c (pos+1) + | _ -> + iter_non_space (pos+1) + + and iter_non_space pos = + if pos = len then + NORMAL, String.sub text !init_pos (pos- !init_pos) + else + let c = text.[pos] in + match c with + | ' ' -> + iter_space pos (pos+1) + | '"' -> + iter_literal c (pos+1) + | _ -> + iter_non_space (pos+1) + + and iter_literal delim pos = + if pos = len then + LITERAL_OPENED delim, String.sub text !init_pos (pos- !init_pos) + else + let c = text.[pos] in + if c = delim then + let pos=pos+1 in + if pos = source_format.max_text_length then begin + LITERAL_CLOSED_ON_BORDER delim, + String.sub text !init_pos (pos- !init_pos) + end else + iter_space pos pos + else + iter_literal delim (pos+1) + + and iter_cont pos = + if pos = len then + NORMAL, "" + else + let c = text.[pos] in + match c with + | ' ' -> iter_cont (pos+1) + | _ -> + init_pos := pos; + iter_non_space pos + + and iter_cont_literal double delim pos = + if pos = len then + let state = + if double then + LITERAL_CLOSED_ON_BORDER delim + else + LITERAL_OPENED delim + in + state, "" + else + let c = text.[pos] in + match c with + | ' ' -> iter_cont_literal double delim (pos+1) + | _ -> + if c = delim then + let pos = pos+1 in + if double then + if pos = len || text.[pos] <> delim then + failwith "expecting double literal delimiter for continuation" + else + let pos = pos+1 in + init_pos := pos; + iter_literal delim pos + else begin + init_pos := pos; + iter_literal delim pos + end + else + failwith "expecting literal delimiter for continuation" + in + match cont with + | INITIAL -> + iter_space 0 0 + | NORMAL -> + iter_cont 0 + | LITERAL_OPENED delim -> + iter_cont_literal false delim 0 + | LITERAL_CLOSED_ON_BORDER delim -> + iter_cont_literal true delim 0 + + let new_line context pos kind text = + let source_format = context.source_format in + match kind with + | COMMENT -> + output_newline context; + output context "*> %s\n" text ; + context.current_state <- INITIAL (* newline has been sent *) + | LINE -> + output_newline context; + let state, text = parse_text ~source_format text in + output context "%s" text; + context.current_state <- state + | CONTINUATION -> + match context.current_state with + | INITIAL -> + error pos "line cannot continue previous line" + | _ -> + let state, text = + try + parse_text ~cont:context.current_state ~source_format text + with + | Failure msg -> + error pos "%s" msg + in + output context "%s" text; + context.current_state <- state + +end + +let is_source_pragam context pos text = + match EzString.split_simplify ( String.uppercase_ascii text ) ' ' with + | ">>SOURCE" :: tokens + | ">>" :: "SOURCE" :: tokens -> + let tokens = + match tokens with + | "FORMAT" :: tokens + | tokens -> tokens + in + let tokens = + match tokens with + | "IS" :: tokens + | tokens -> tokens + in + context.source_format <- + begin + match tokens with + | [ "FIXED" ] -> Config.fixed_format + | [ "FREE" ] -> Config.free_format + | [ "COBOLX" ] -> Config.cobolx_format + | [ "VARIABLE" ] -> Config.variable_format + | [ "XCARD" ] -> Config.xcard_format + | _ -> + error pos "wrong >>SOURCE line" + end; + true + | "$SET" :: tokens + | "$" :: "SET" :: tokens + | ">>SET" :: tokens + | ">>" :: "SET" :: tokens + -> + begin + let set_format format = + context.source_format <- + begin + match format with + | "\"FIXED\"" -> Config.fixed_format + | "\"FREE\"" -> Config.free_format + | "\"COBOLX\"" -> Config.cobolx_format + | "\"VARIABLE\"" -> Config.variable_format + | "\"XCARD\"" -> Config.xcard_format + | _ -> + error pos "wrong >>SOURCE line" + end; + true + + in + match tokens with + | [ "SOURCEFORMAT" ; format ] -> + set_format format + | [ token ] -> + begin + match EzString.chop_prefix token ~prefix:"SOURCEFORMAT" with + | None -> false + | Some format -> + set_format format + end + | _ -> false + end + | _ -> false + +(* convert a file content from some fixed format to free format *) +let to_free ~source_format ~filename ~contents = + + let len = String.length contents in + let buffer = Buffer.create len in + + let target_format = Config.free_format in + + let context = { + buffer ; + current_state = INITIAL ; + source_format = Config.source_format source_format ; + target_format ; + } + in + + let new_line pos kind text = + match context.source_format.format, context.target_format.format with + | SFFixed, SFFree -> + FIXED2FREE.new_line context pos kind text + | SFFree, SFFree -> + output_newline context ; + context.current_state <- INITIAL ; + output context "%s\n" text + | _ -> assert false + in + + let new_line pos0 pos = + let len = pos - pos0 in + (* Printf.eprintf "new_line %d %d [%d]\n%!" pos0 pos len ; *) + if len > context.source_format.skip_before then + if context.source_format.format = SFFree then begin + (* This is more complicated, + as we must take care of inline comments... *) + let text = String.sub contents pos0 len in + if not ( is_source_pragam context pos0 text ) then + new_line pos0 LINE text + end + else + let indicator = contents.[pos0+context.source_format.skip_before] in + let text = + let text_len = len - context.source_format.skip_before - 1 in + let text_len = min context.source_format.max_text_length text_len in + String.sub contents (pos0+context.source_format.skip_before+1) text_len + in + match indicator with + | '*' | '/' -> + new_line pos0 COMMENT text + | ' ' -> + if not ( is_source_pragam context pos0 text ) then + new_line pos0 LINE text + | '-' -> + new_line pos0 CONTINUATION text + | 'd' | 'D' -> + new_line pos0 COMMENT (" >>Debug: " ^ text) + | _ -> + error pos "unknown indicator '%c'\n%!" indicator + else + new_line pos0 LINE "" + in + + let rec iter pos0 pos = + if pos = len then + new_line pos0 pos + else + let c = contents.[pos] in + if c = '\r' then begin + new_line pos0 pos; + let pos = pos+1 in + if pos = len then + () + else + if contents.[pos] = '\n' then + let pos = pos+1 in + iter pos pos + else + error pos "carriage-return without newline" + end else + if c = '\n' then begin + new_line pos0 pos; + let pos = pos+1 in + iter pos pos + end + else + let pos = pos+1 in + iter pos0 pos + in + iter 0 0; + + let contents = + Buffer.contents buffer + in + Main.indent + ~source_format: (SF target_format.format) + ~filename + ~contents + Output_contents diff --git a/src/lsp/cobol_indent/reformat.mli b/src/lsp/cobol_indent/reformat.mli new file mode 100644 index 000000000..82a6ba07e --- /dev/null +++ b/src/lsp/cobol_indent/reformat.mli @@ -0,0 +1,20 @@ +(**************************************************************************) +(* *) +(* SuperBOL OSS Studio *) +(* *) +(* Copyright (c) 2022-2023 OCamlPro SAS *) +(* *) +(* All rights reserved. *) +(* This source code is licensed under the GNU Affero General Public *) +(* License version 3 found in the LICENSE.md file in the root directory *) +(* of this source tree. *) +(* *) +(**************************************************************************) + +exception Error of int * string + +val to_free : + source_format:Cobol_config.source_format_spec -> + filename:string -> + contents:string -> + string diff --git a/src/lsp/cobol_indent/scanner.ml b/src/lsp/cobol_indent/scanner.ml index 4d2190ac8..66661adc1 100644 --- a/src/lsp/cobol_indent/scanner.ml +++ b/src/lsp/cobol_indent/scanner.ml @@ -96,7 +96,7 @@ let tokens_of_lines ~filename ~config ~contents lines = let edit = { bol = not dont_indent_line ; edit } in let revtokens = revtokens_of_line ~edit ~filename ~config s in let maybe_comment_entry = - not config.source_format.free && + config.source_format.format <> SFFree && match revtokens with | (INFORMATION _, _) :: _ -> true | (DOT, _) :: (PROGRAM_ID, _) :: _ -> true @@ -128,7 +128,7 @@ let tokenize ~config ~filename ~contents = let lines = ref [] in let max_text_len = - if source_format.free then + if source_format.format = SFFree then 65536 else source_format.max_text_length @@ -155,7 +155,7 @@ let tokenize ~config ~filename ~contents = c skip source_format.skip_before ; *) if c = '\t' - && not source_format.free + && source_format.format <> SFFree && skip = 6 && source_format.skip_before = 6 then iter_in_line false pos (pos+1) line @@ -172,7 +172,7 @@ let tokenize ~config ~filename ~contents = if c = '\n' then iter (pos+1) (line+1) else - if source_format.free then + if source_format.format = SFFree then iter_in_line false pos (pos+1) line else match c with diff --git a/src/lsp/cobol_indent/types.ml b/src/lsp/cobol_indent/types.ml index 078648ec6..e19d1bc6e 100644 --- a/src/lsp/cobol_indent/types.ml +++ b/src/lsp/cobol_indent/types.ml @@ -25,6 +25,15 @@ type edit_space_operation = { spaces : int ; (* positive for addition, negative for deletion *) } +type edits = { + edits : indent_record list ; + operations : edit_space_operation list ; +} + +type _ output = + | Output_edits : edits output + | Output_contents : string output + type range = { start_line : int ; end_line : int ; @@ -32,7 +41,7 @@ type range = { type source_format = { name : string ; - free : bool ; (* has column identifier *) + format : Cobol_config.Types.source_format ; skip_before : int ; (* fixed = 6 *) max_text_length : int ; (* fixed = 8..72 = 65 chars *) } diff --git a/src/lsp/cobol_lsp/lsp_request.ml b/src/lsp/cobol_lsp/lsp_request.ml index 6b3c6ed89..80c7cdbb6 100644 --- a/src/lsp/cobol_lsp/lsp_request.ml +++ b/src/lsp/cobol_lsp/lsp_request.ml @@ -396,33 +396,37 @@ let handle_range_formatting registry params = end_line = end_.line + 1 } in - let _edit_list, edit_ops = + let output = Cobol_indent.Main.indent - ~dialect:(Cobol_config.dialect project.config.cobol_config) ~source_format:project.config.source_format + (* + ~dialect:(Cobol_config.dialect project.config.cobol_config) ~config:project.config.indent_config +*) ~filename:(Lsp.Uri.to_path doc.uri) ~contents:(Lsp.Text_document.text textdoc) ~range:range_to_indent - () + Output_edits in - Some ( to_textedits edit_ops ) (* (List.map lsp_text_edit edit_list) *) + Some ( to_textedits output.operations ) let handle_formatting registry params = let DocumentFormattingParams.{ textDocument = doc; _ } = params in let Lsp_document.{ project; textdoc; _ } = Lsp_server.find_document doc registry in try - let _editList, edit_ops = + let output = Cobol_indent.Main.indent - ~dialect:(Cobol_config.dialect project.config.cobol_config) ~source_format:project.config.source_format + (* + ~dialect:(Cobol_config.dialect project.config.cobol_config) ~config:project.config.indent_config +*) ~filename:(Lsp.Uri.to_path doc.uri) ~contents:(Lsp.Text_document.text textdoc) - () + Output_edits in - Some ( to_textedits edit_ops ) (* List.map lsp_text_edit editList) *) + Some ( to_textedits output.operations ) with Failure msg -> Lsp_error.internal "Formatting error: %s" msg diff --git a/src/lsp/superbol_free_lib/command_indent_file.ml b/src/lsp/superbol_free_lib/command_indent_file.ml index afb787924..1b9bb1250 100644 --- a/src/lsp/superbol_free_lib/command_indent_file.ml +++ b/src/lsp/superbol_free_lib/command_indent_file.ml @@ -25,9 +25,9 @@ let action ~numeric ~intext ~inplace ?suffix ?range let module Config = (val config) in let f ?contents filename = - let project = Project.for_ ~filename in + (* let project = Project.for_ ~filename in *) - let output = + let output_file = match suffix with | Some ext -> Some ( filename ^ "." ^ ext ) @@ -38,21 +38,35 @@ let action ~numeric ~intext ~inplace ?suffix ?range None in - let edits, _ops = Cobol_indent.Main.indent - ~source_format - ~config:project.config.indent_config - ~dialect:Config.dialect - ~filename - ?output - ?range - ?contents - () - in - - match output with - | Some _ -> - () + match output_file with + | Some file -> + let new_contents = Cobol_indent.Main.indent + ~source_format + ~filename + ?range + ?contents + Output_contents + in + begin + match file with + | "-" -> + Printf.printf "%s\n%!" new_contents + | _ -> + EzFile.write_file file new_contents; + Printf.eprintf "File %S generated\n%!" file + end | None -> + let output = Cobol_indent.Main.indent + ~source_format + (* + ~config:project.config.indent_config + ~dialect:Config.dialect +*) + ~filename + ?range + ?contents + Output_edits + in if numeric then let source_format = Cobol_indent.Config.source_format source_format in @@ -72,7 +86,7 @@ let action ~numeric ~intext ~inplace ?suffix ?range iter_edit (line+1) edit edits end else begin let indent = - if intext || source_format.free then + if intext || source_format.format = SFFree then edit.offset_modif else source_format.skip_before + 1 + edit.offset_modif @@ -85,19 +99,19 @@ let action ~numeric ~intext ~inplace ?suffix ?range | None -> 1 | Some { start_line ; _ } -> start_line in - iter start_line edits + iter start_line output.edits in output_numeric stdout else - if edits = [] then + if output.edits = [] then Printf.eprintf "File %S: good indentation\n%!" filename else begin Printf.eprintf "File %S: %d lines to modify\n%!" filename - ( List.length edits ); + ( List.length output.edits ); List.iter (fun edit -> Printf.printf " Line %d: move from %d to %d\n%!" edit.lnum edit.offset_orig edit.offset_modif - ) edits; + ) output.edits; end; in diff --git a/src/lsp/superbol_free_lib/command_reformat.ml b/src/lsp/superbol_free_lib/command_reformat.ml new file mode 100644 index 000000000..09d1f92ca --- /dev/null +++ b/src/lsp/superbol_free_lib/command_reformat.ml @@ -0,0 +1,105 @@ +(**************************************************************************) +(* *) +(* SuperBOL OSS Studio *) +(* *) +(* Copyright (c) 2022-2023 OCamlPro SAS *) +(* *) +(* All rights reserved. *) +(* This source code is licensed under the GNU Affero General Public *) +(* License version 3 found in the LICENSE.md file in the root directory *) +(* of this source tree. *) +(* *) +(**************************************************************************) + +open Ez_file.V1 +open EzFile.OP + +open Ezcmd.V2 +open EZCMD.TYPES + +open Common_args + +let action ~inplace ?suffix + { preproc_options = { source_format; config; _ } ; _ } files = + let module Config = (val config) in + + let f ?contents filename = + let contents = match contents with + | Some contents -> contents + | None -> EzFile.read_file filename + in + let new_contents = + try + Cobol_indent.Reformat.to_free + ~source_format + ~filename + ~contents + with + | Cobol_indent.Reformat.Error (pos, msg) -> + Printf.eprintf "Error in %s at pos %d: %s\n%!" + filename pos msg; + exit 2 + in + + let output = + match suffix with + | Some ext -> + Some ( filename ^ "." ^ ext ) + | None -> + if inplace then + Some filename + else + None + in + + match output with + | Some filename -> + EzFile.write_file filename new_contents + | None -> + Printf.printf "%s%!" new_contents + in + match files with + | [] -> + let contents = FileChannel.read_file stdin in + f ~contents ( Sys.getcwd () // "stdin.cob" ) + | files -> + List.iter (fun filename -> + f filename + ) files + +let to_free_cmd = + let files = ref [] in + let inplace = ref false in + let suffix = ref None in + let common, common_args = Common_args.get () in + let args = common_args in + EZCMD.sub + "reformat to-free" + (fun () -> + let common = common () in + action + ~inplace:!inplace + ?suffix:!suffix + common + !files) + ~args:(args @ [ + [], Arg.Anons (fun list -> files := list), + EZCMD.info ~docv:"FILES" "Cobol files to indent" ; + + [ "inplace" ], Arg.Set inplace, + EZCMD.info "Modify files in place"; + + [ "suffix" ], Arg.String (fun s -> + suffix := Some s), + EZCMD.info ~docv:"EXT" + "Set an extension for the file being generated"; + + ] + ) + ~doc: "Reformat" + ~man:[ + `S "DESCRIPTION"; + `Blocks [ + `P "" + ]; + ] diff --git a/src/lsp/superbol_free_lib/command_reformat.mli b/src/lsp/superbol_free_lib/command_reformat.mli new file mode 100644 index 000000000..38df3d09f --- /dev/null +++ b/src/lsp/superbol_free_lib/command_reformat.mli @@ -0,0 +1,14 @@ +(**************************************************************************) +(* *) +(* SuperBOL OSS Studio *) +(* *) +(* Copyright (c) 2022-2023 OCamlPro SAS *) +(* *) +(* All rights reserved. *) +(* This source code is licensed under the GNU Affero General Public *) +(* License version 3 found in the LICENSE.md file in the root directory *) +(* of this source tree. *) +(* *) +(**************************************************************************) + +val to_free_cmd : Ezcmd.V2.EZCMD.TYPES.sub diff --git a/src/lsp/superbol_free_lib/main.ml b/src/lsp/superbol_free_lib/main.ml index f7609bf2f..fcfdddf8a 100644 --- a/src/lsp/superbol_free_lib/main.ml +++ b/src/lsp/superbol_free_lib/main.ml @@ -27,6 +27,7 @@ let public_subcommands = [ Command_check_syntax.cmd; Command_json_vscode.cmd; Command_snapshot.cmd; + Command_reformat.to_free_cmd; Command_ebcdic.ebcdic_translate_cmd ;