diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ee5cdb7..f559357e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,6 +40,7 @@ jobs: - run: opam exec -- dune build @install - run: opam install . --deps-only -t - run: opam exec -- dune runtest + - run: opam exec -- dune build @fmt - run: opam config report - run: opam install . - run: opam exec ocp-indent --help diff --git a/.gitignore b/.gitignore index 08215feb..4ccd786b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ ocp-build.root* _obuild config.log config.status -ocp-indent src/indentVersion.ml *~ version.ocp @@ -18,4 +17,4 @@ src/buildable_targets.list **/.fe.sexp _build .merlin -*.install \ No newline at end of file +*.install diff --git a/CHANGELOG.md b/CHANGELOG.md index c7575e37..0e8bc1bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ + Fix "Missing ‘lexical-binding’ cookie" warning in Emacs (#329, @nojb) + Fix a bug where ocp-indent translate LF to CRLF when run on Windows instead of preserving the input's newlines. (#334, @nojb) ++ Add `ocp-indent-gen-rules` to support `dune fmt` like workflow with ocp-indent + (#333, @NathanReb) ## 1.8.1 * tiny API change to help with the detection of top-level phrase boundaries diff --git a/README.md b/README.md index a2a7c888..3d8212d8 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,44 @@ line like: will enable you to have the indentation after `in` setup to 2 locally on this file. +## Autoformat files with ocp-indent in dune + +`dune fmt` or `dune build @fmt` can be used to format dune and OCaml files +with `ocamlformat`. This can prove a convenient workflow for new projects so we +made it available to `ocp-indent` users as well. + +First you need to disable the default formatting rules for OCaml source files +by adding the following to your `dune-project`: +```dune +(formatting (enabled_for dune)) +``` + +`dune fmt` won't try to format your OCaml files with `ocamlformat` from there. + +The `ocp-indent` formatting rules need to be enabled on a per-directory basis by +adding the following dune rules to the `dune` file: + +```dune +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules -o %{target})))) +``` + +`ocp-indent-gen-rules` will generate promotion based formatting rules for each +`.ml` and `.mli` files in this folder. You can simply use the same +`dune build @fmt`/`dune promote` workflow as with ocamlformat. + ## How does it compare to tuareg ? diff --git a/doc/dune b/doc/dune index 15053872..f769fde9 100644 --- a/doc/dune +++ b/doc/dune @@ -1,10 +1,10 @@ (rule (targets ocp-indent.1) (action - (with-stdout-to %{targets} - (run %{bin:ocp-indent} --help=groff))) -) + (with-stdout-to + %{targets} + (run %{bin:ocp-indent} --help=groff)))) + (install (section man) - (files ocp-indent.1) -) + (files ocp-indent.1)) diff --git a/dune-project b/dune-project index 7cf0066e..cd135659 100644 --- a/dune-project +++ b/dune-project @@ -2,3 +2,5 @@ (name ocp-indent) (version 1.8.1) + +(formatting (enabled_for dune)) diff --git a/src/ocp-indent-dynlink/dune b/src/ocp-indent-dynlink/dune index c654e1cf..5b56b233 100644 --- a/src/ocp-indent-dynlink/dune +++ b/src/ocp-indent-dynlink/dune @@ -2,5 +2,19 @@ (name ocp_indent_dynlink) (public_name ocp-indent.dynlink) (wrapped false) - (libraries findlib dynlink ocp-indent.lexer ocp-indent.utils) -) + (libraries findlib dynlink ocp-indent.lexer ocp-indent.utils)) + +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules -o %{target})))) diff --git a/src/ocp-indent-gen-rules/dune b/src/ocp-indent-gen-rules/dune new file mode 100644 index 00000000..cdb3fe41 --- /dev/null +++ b/src/ocp-indent-gen-rules/dune @@ -0,0 +1,24 @@ +(rule + (with-stdout-to + version.ml + (echo "let v = \"%{version:ocp-indent}\""))) + +(executable + (name main) + (public_name ocp-indent-gen-rules) + (libraries cmdliner)) + +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules -o %{target})))) diff --git a/src/ocp-indent-gen-rules/main.ml b/src/ocp-indent-gen-rules/main.ml new file mode 100644 index 00000000..85b66706 --- /dev/null +++ b/src/ocp-indent-gen-rules/main.ml @@ -0,0 +1,82 @@ +let exe_name = "ocp-indent-gen-rules" + +let named f = Cmdliner.Term.(app (const f)) + +let ignore = + let open Cmdliner.Arg in + let doc = "Comma separated list of files that should not be indented" in + named + (fun x -> `Ignore x) + (value & opt (list string) [] & info ~doc ["ignore"]) + +let alias = + let open Cmdliner.Arg in + let doc = "The alias for the ocp-indent rules, default is fmt" in + named + (fun x -> `Alias x) + (value & opt string "fmt" & info ~doc ["alias"]) + +let static = + let open Cmdliner.Arg in + let doc = + "Generate rules for a regular include stanza rather than a dynamic_include" + in + named + (fun x -> `Static x) + (value & flag & info ~doc ["static"]) + +let output = + let open Cmdliner.Arg in + let docv = "OUTFILE" in + let doc = "Writes rules to $(docv). When absent, writes rules to stdout." in + named + (fun x -> `Output x) + (value & opt (some string) None & info ~doc ~docv ["o"]) + +let print_rules ~ignore ~alias ~static oc file = + let source_file = if static then file else "../" ^ file in + if List.mem file ignore then + () + else + match Filename.extension file with + | ".ml" | ".mli" -> + let formatted_ext = "fmtd" in + let pf fmt = Printf.fprintf oc (fmt ^^ "\n") in + pf "(rule"; + pf " (target %s.%s)" file formatted_ext; + pf " (action (run ocp-indent %%{dep:%s} -o %%{target})))" source_file; + pf ""; + pf "(rule"; + pf " (alias %s)" alias; + pf " (action (diff %s %s.%s)))" source_file file formatted_ext; + pf "" + | _ -> () + +let with_output ~f output = + match output with + | None -> f stdout + | Some file -> + let oc = open_out file in + f oc; + close_out oc + +let run (`Ignore ignore) (`Alias alias) (`Static static) + (`Output output) = + let src_dir = if static then Sys.getcwd () else ".." in + let files = Sys.readdir src_dir in + with_output output + ~f:(fun oc -> + Array.sort String.compare files; + Array.iter (print_rules ~ignore ~alias ~static oc) files) + +let term = + let open Cmdliner.Term in + const run $ ignore $ alias $ static $ output + +let info = + let open Cmdliner in + Cmd.info exe_name ~version:Version.v ~exits:Cmd.Exit.defaults + ~doc:"Generate ocp-indent dune rules" + +let () = + exit (Cmdliner.Cmd.eval (Cmdliner.Cmd.v info term)) diff --git a/src/ocp-indent-lexer/approx_tokens.ml b/src/ocp-indent-lexer/approx_tokens.ml index c0e55d1c..d5b47f99 100644 --- a/src/ocp-indent-lexer/approx_tokens.ml +++ b/src/ocp-indent-lexer/approx_tokens.ml @@ -40,13 +40,13 @@ type token = | COLONEQUAL | COLONGREATER | COMMA - (* Start of comment from code *) + (* Start of comment from code *) | COMMENT - (* Start of inline code section within comment: "{[" *) + (* Start of inline code section within comment: "{[" *) | OCAMLDOC_CODE - (* Start of verbatim section within comment: "{v" *) + (* Start of verbatim section within comment: "{v" *) | OCAMLDOC_VERB - (* Continuation of comment after a closed ocamldoc code or verb section *) + (* Continuation of comment after a closed ocamldoc code or verb section *) | COMMENTCONT | CONSTRAINT | DO @@ -170,13 +170,13 @@ let to_string = function | COLONEQUAL -> "COLONEQUAL" | COLONGREATER -> "COLONGREATER" | COMMA -> "COMMA" - (* Start of comment from code *) + (* Start of comment from code *) | COMMENT -> "COMMENT" - (* Start of inline code section within comment: "{[" *) + (* Start of inline code section within comment: "{[" *) | OCAMLDOC_CODE -> "OCAMLDOC_CODE" - (* Start of verbatim section within comment: "{v" *) + (* Start of verbatim section within comment: "{v" *) | OCAMLDOC_VERB -> "OCAMLDOC_VERB" - (* Continuation of comment after a closed ocamldoc code or verb section *) + (* Continuation of comment after a closed ocamldoc code or verb section *) | COMMENTCONT -> "COMMENTCONT" | CONSTRAINT -> "CONSTRAINT" | DO -> "DO" diff --git a/src/ocp-indent-lexer/dune b/src/ocp-indent-lexer/dune index 8fdd38cf..494ef2a0 100644 --- a/src/ocp-indent-lexer/dune +++ b/src/ocp-indent-lexer/dune @@ -3,5 +3,19 @@ (library (name ocp_indent_lexer) (public_name ocp-indent.lexer) - (wrapped false) -) + (wrapped false)) + +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules --ignore approx_lexer.ml -o %{target})))) diff --git a/src/ocp-indent-lib/dune b/src/ocp-indent-lib/dune index 97ebc216..9cb96c97 100644 --- a/src/ocp-indent-lib/dune +++ b/src/ocp-indent-lib/dune @@ -3,5 +3,19 @@ (wrapped false) (public_name ocp-indent.lib) (libraries ocp-indent.utils) - (flags :standard -w -9 -warn-error -57) -) + (flags :standard -w -9 -warn-error -57)) + +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules -o %{target})))) diff --git a/src/ocp-indent-lib/indentBlock.ml b/src/ocp-indent-lib/indentBlock.ml index 02b090a5..fa9e7197 100644 --- a/src/ocp-indent-lib/indentBlock.ml +++ b/src/ocp-indent-lib/indentBlock.ml @@ -258,7 +258,7 @@ let shift t n = let to_string t = Path.to_string t.path - (* Printf.sprintf "%s\n%d %b" (Path.to_string t.path) t.toff *) +(* Printf.sprintf "%s\n%d %b" (Path.to_string t.path) t.toff *) let empty = { path = []; @@ -410,10 +410,10 @@ let reset_line_indent config current_line path = | _ -> p, acc, 0 in List.fold_left (fun p t -> - {t with indent = t.line_indent - + limit_overindent (t.indent - t.line_indent) - + extra} - ::p) + {t with indent = t.line_indent + + limit_overindent (t.indent - t.line_indent) + + extra} + ::p) p acc in aux [] path @@ -468,9 +468,9 @@ let op_prio_align_indent config = when is_monadop s -> prio_flatop,L,0 | INFIXOP0 s -> - (match String.sub s 0 (min 2 (String.length s)) with - | "|!" | "|>" -> prio_flatop,T,0 - | _ -> 60,align,indent) + (match String.sub s 0 (min 2 (String.length s)) with + | "|!" | "|>" -> prio_flatop,T,0 + | _ -> 60,align,indent) | EQUAL | LESS | GREATER -> 60,align,0 | INFIXOP1 _ -> 70,align,indent | LBRACKETAT -> prio_lbracketat,align,indent @@ -507,14 +507,14 @@ let handle_dotted block tok = let make_attr_id name = function | ({ kind = (KExtendedItem ([],_) | KExtendedExpr ([],_)); indent; pad; } :: _ as path) -> - let indent = - if starts_line then indent + pad - else indent + pad + String.length (Lazy.force tok.between) - 1 in - let column = - if starts_line then indent else block.toff + tok.offset in - { kind = (KAttrId ([name], false)); indent; - line_indent = indent; column; line = current_line; - pad = 0 } :: path + let indent = + if starts_line then indent + pad + else indent + pad + String.length (Lazy.force tok.between) - 1 in + let column = + if starts_line then indent else block.toff + tok.offset in + { kind = (KAttrId ([name], false)); indent; + line_indent = indent; column; line = current_line; + pad = 0 } :: path | ({ kind = KAttrId (names, _)} as node) :: path -> { node with kind = KAttrId (name :: names, false); } :: path | _ -> assert false in @@ -664,8 +664,8 @@ let rec update_path config block stream tok = as p) -> (* ignore align_params for functor application *) extend (KExpr prio_apply) L (reset_line_indent config line p) | Some({kind=KExpr _; line} - :: {kind=KArrow (KMatch|KTry) | KTry | KMatch; - line=arrow_line}::_ as p) + :: {kind=KArrow (KMatch|KTry) | KTry | KMatch; + line=arrow_line}::_ as p) when config.i_align_params = Auto && line = arrow_line -> (* Special case: switch to token-aligned (see test js-args) *) @@ -751,12 +751,12 @@ let rec update_path config block stream tok = let make_infix tok path = let op_prio, align, indent = op_prio_align_indent config tok.token in let in_delim_block () = - match unwind_while (fun kind -> prio kind >= op_prio) path with - | Some ({ kind = KExpr _; line } :: - { kind = (KBrace|KParen|KBracket|KBracketBar); line = bline } :: - _) -> - line = bline - | _ -> false in + match unwind_while (fun kind -> prio kind >= op_prio) path with + | Some ({ kind = KExpr _; line } :: + { kind = (KBrace|KParen|KBracket|KBracketBar); line = bline } :: + _) -> + line = bline + | _ -> false in (* special cases *) let indent = (* don't back-indent operators when alone on their line @@ -912,10 +912,10 @@ let rec update_path config block stream tok = | {kind=KParen|KBegin}::{kind=KExpr prio; line; indent}::_ when prio = prio_apply && line = current_line -> indent, reset_padding block.path - | {kind=KInclude; line; indent; pad}::_ - when line < current_line -> - indent + pad, block.path - | _ -> Path.indent block.path, reset_padding block.path + | {kind=KInclude; line; indent; pad}::_ + when line < current_line -> + indent + pad, block.path + | _ -> Path.indent block.path, reset_padding block.path in Path.maptop (fun n -> {n with indent}) (append k L path) @@ -923,10 +923,10 @@ let rec update_path config block stream tok = | WHEN -> append KWhen L ~pad:(config.i_base + if starts_line then 0 else 2) (unwind (function - | KWith(KTry|KMatch) | KBar(KTry|KMatch) | KFun | KExtendedExpr _ -> - true - | _ -> false) - block.path) + | KWith(KTry|KMatch) | KBar(KTry|KMatch) | KFun | KExtendedExpr _ -> + true + | _ -> false) + block.path) | OPEN -> if last_token block = Some LET then append KOpen L block.path @@ -949,11 +949,11 @@ let rec update_path config block stream tok = append KLet L (unwind_top p) | _ -> append KLetIn L (fold_expr block.path)) - (* - or if after a specific token *) - (* if close_top_let block.last then *) - (* append KLet L config.i_base (unwind_top block.path) *) - (* else *) - (* append KLetIn L config.i_base (fold_expr block.path) *) + (* - or if after a specific token *) + (* if close_top_let block.last then *) + (* append KLet L config.i_base (unwind_top block.path) *) + (* else *) + (* append KLetIn L config.i_base (fold_expr block.path) *) | CLASS -> append KLet L (unwind_top block.path) @@ -1033,10 +1033,10 @@ let rec update_path config block stream tok = | Some ({token = TYPE|MODULE as tm}, _) -> let path = unwind (function - | KModule | KOpen | KInclude | KParen - | KBegin | KColon | KBody KModule -> - true - | _ -> false) + | KModule | KOpen | KInclude | KParen + | KBegin | KColon | KBody KModule -> + true + | _ -> false) block.path in let kind = @@ -1058,7 +1058,7 @@ let rec update_path config block stream tok = (match next with | Some (next, _) when Region.start_line next.region - = Region.end_line tok.region -> + = Region.end_line tok.region -> Path.maptop (fun n -> {n with indent=n.column}) (append (KWith KBrace) L ~pad:next.offset path) | _ -> @@ -1208,7 +1208,7 @@ let rec update_path config block stream tok = append (KArrow KFun) L path | {kind=KBar m}::{kind=KWith _; line}::_ when line = current_line -> (* Special case: don't respect match_clause when 'with X ->' is on - a single line *) + a single line *) let pad = if next_offset tok stream <> None then config.i_base else match next_token stream with @@ -1225,17 +1225,17 @@ let rec update_path config block stream tok = | {kind=KArrow(KMatch|KTry)} :: p -> (* might happen if doing 'when match' for example *) (match - unwind (function - | KParen | KBegin | KBracket | KBrace | KBracketBar - | KWith(KMatch|KTry) - | KFun - | KBody(KType|KExternal) | KColon - | KStruct | KSig | KObject -> true - | _ -> false) - p - with - | {kind=KWith(_)}::p -> find_parent p - | _ -> make_infix tok block.path) + unwind (function + | KParen | KBegin | KBracket | KBrace | KBracketBar + | KWith(KMatch|KTry) + | KFun + | KBody(KType|KExternal) | KColon + | KStruct | KSig | KObject -> true + | _ -> false) + p + with + | {kind=KWith(_)}::p -> find_parent p + | _ -> make_infix tok block.path) | _ -> make_infix tok block.path in find_parent block.path @@ -1296,12 +1296,12 @@ let rec update_path config block stream tok = | COLONEQUAL | INFIXOP2 "+=" -> (match - unwind_while (function KExpr _ | KType -> true | _ -> false) block.path - with - | Some ({kind=KType}::_ as p) -> (* type t := t' *) - replace (KBody KType) L p - | _ -> - make_infix tok block.path) + unwind_while (function KExpr _ | KType -> true | _ -> false) block.path + with + | Some ({kind=KType}::_ as p) -> (* type t := t' *) + replace (KBody KType) L p + | _ -> + make_infix tok block.path) | COLON -> let path = unwind (function @@ -1321,7 +1321,7 @@ let rec update_path config block stream tok = path | None -> make_infix tok block.path) | {kind = KModule|KLet|KLetIn - | KAnd(KModule|KLet|KLetIn)} :: _ -> + | KAnd(KModule|KLet|KLetIn)} :: _ -> append KColon L path | {kind = KExternal} :: _ as path -> append KColon L ~pad:(if starts_line then 0 else config.i_base) path @@ -1356,7 +1356,7 @@ let rec update_path config block stream tok = | UIDENT ("INCLUDE"|"IFDEF"|"THEN"|"ELSE"|"ENDIF" |"TEST"|"TEST_UNIT"|"TEST_MODULE" |"BENCH"|"BENCH_FUN"|"BENCH_MODULE"|"BENCH_INDEXED" - as s) + as s) when starts_line -> if String.sub s 0 4 = "TEST" @@ -1373,25 +1373,25 @@ let rec update_path config block stream tok = (match block.path with | {kind = KArrow KMatch} :: _ -> append expr_atom L block.path | _ -> - let last_expr = - unwind_while (function KExpr _ -> true | _ -> false) block.path - in - match last_expr with - | Some ({kind=KExpr _} :: {kind=KType} :: ({kind=KColon} :: _ as p)) -> - (* let f: type t. t -> t = ... *) - p - | Some ({kind=KExpr 200} :: - ({kind=KColon} :: {kind=KLet|KLetIn} :: _ as p))-> - (* method m : 'x 'y . ... = (KLet is actually "method") *) - (* let m : 'x 'y . ... = (in) *) - (match last_token block with - | Some (UIDENT _) -> make_infix tok block.path - | _ -> p) - | Some ({kind=KExpr i} :: ({kind=KBrace|KWith KBrace} as h :: p)) - when (i = prio_max || i = prio_dot) && next_offset tok stream = None -> - (* special case: distributive { Module. field; field } *) - { h with pad = config.i_base } :: p - | _ -> make_infix tok block.path) + let last_expr = + unwind_while (function KExpr _ -> true | _ -> false) block.path + in + match last_expr with + | Some ({kind=KExpr _} :: {kind=KType} :: ({kind=KColon} :: _ as p)) -> + (* let f: type t. t -> t = ... *) + p + | Some ({kind=KExpr 200} :: + ({kind=KColon} :: {kind=KLet|KLetIn} :: _ as p))-> + (* method m : 'x 'y . ... = (KLet is actually "method") *) + (* let m : 'x 'y . ... = (in) *) + (match last_token block with + | Some (UIDENT _) -> make_infix tok block.path + | _ -> p) + | Some ({kind=KExpr i} :: ({kind=KBrace|KWith KBrace} as h :: p)) + when (i = prio_max || i = prio_dot) && next_offset tok stream = None -> + (* special case: distributive { Module. field; field } *) + { h with pad = config.i_base } :: p + | _ -> make_infix tok block.path) | AMPERAMPER | BARBAR -> (* back-indented when after if or when and not alone *) @@ -1437,27 +1437,27 @@ let rec update_path config block stream tok = | LABEL _ | OPTLABEL _ -> (match - unwind_while (function - | KExpr _ | KLet | KLetIn | KFun | KAnd(KLet|KLetIn) -> true - | _ -> false) - block.path - with - | Some ( (* (opt)labels in types *) - {kind = KExpr 32 (* prio_arrow *)} :: - ({kind = KBody(KVal|KType|KExternal) | KColon} :: _) | - ({kind = KBody(KVal|KType|KExternal) | KColon} :: _) - ) -> - (* this is for the case [?foo:], parsed as OPTLABEL, but make sure we - are consistent with [foo:] or [? foo:], which are parsed as 2 or 3 - tokens *) - extend (KExpr prio_colon) - (if config.i_align_params = Never then L else T) - (append expr_atom L block.path) - | Some ({kind=KExpr _}::_) | None -> - (* considered as infix, but forcing function application *) - make_infix tok (fold_expr block.path) - | _ -> (* in function definition *) - atom block.path) + unwind_while (function + | KExpr _ | KLet | KLetIn | KFun | KAnd(KLet|KLetIn) -> true + | _ -> false) + block.path + with + | Some ( (* (opt)labels in types *) + {kind = KExpr 32 (* prio_arrow *)} :: + ({kind = KBody(KVal|KType|KExternal) | KColon} :: _) | + ({kind = KBody(KVal|KType|KExternal) | KColon} :: _) + ) -> + (* this is for the case [?foo:], parsed as OPTLABEL, but make sure we + are consistent with [foo:] or [? foo:], which are parsed as 2 or 3 + tokens *) + extend (KExpr prio_colon) + (if config.i_align_params = Never then L else T) + (append expr_atom L block.path) + | Some ({kind=KExpr _}::_) | None -> + (* considered as infix, but forcing function application *) + make_infix tok (fold_expr block.path) + | _ -> (* in function definition *) + atom block.path) | UIDENT _ -> (match block.path with @@ -1553,59 +1553,59 @@ let rec update_path config block stream tok = (append (KComment (tok, col)) L ~pad block.path) else (match block.path with - | {kind=KExpr i}::_ when i = prio_max -> - let blocklevel () = - let p = unwind_top block.path in - let col = Path.indent p + Path.pad p in - append (KComment (tok, col)) (A col) ~pad block.path - in - (* if we are directly after a case in a sum-type, use that for - alignment *) - let align_bar = - if tok.newlines > 1 || not (is_inside_type block0.path) - then None - else - let find_bar = - unwind_while - (function KBar _ | KExpr _ -> true | _ -> false) - block0.path - in match find_bar with - | Some ({kind=KBar _; column}::_) -> Some column - | _ -> None - in - (* after a closed expr: look-ahead *) - (match next_token_full stream, align_bar with - | None, None -> blocklevel () - | Some ((* full block-closing tokens + newline *) - {token = SEMISEMI | DONE | END - | GREATERRBRACE | GREATERRBRACKET | RBRACE - | RBRACKET | RPAREN } - , _), _ - when tok.newlines > 1 -> - blocklevel () - | Some ((* semi block-closing tokens *) - {token = SEMISEMI | DONE | END - | GREATERRBRACE | GREATERRBRACKET | RBRACE - | RBRACKET | RPAREN - | THEN | ELSE | IN | EQUAL } - , _), None - when tok.newlines <= 1 -> (* indent as above *) - let col = (Path.top block0.path).line_indent in - append (KComment (tok, col)) (A col) ~pad block.path - | _, Some indent -> - append (KComment (tok,indent)) (A indent) ~pad block.path - | next, None -> (* recursive call to indent like next line *) - let path = match next with - | Some ({token = EOF }, _) | None -> [] - | Some (next,stream) -> - update_path config block stream - { next with newlines = tok.newlines } - in - let col = Path.indent path in - append (KComment (tok,col)) (A col) ~pad block.path) - | _ -> - let col = Path.indent block.path + Path.pad block.path in - append (KComment (tok,col)) (A col) ~pad block.path) + | {kind=KExpr i}::_ when i = prio_max -> + let blocklevel () = + let p = unwind_top block.path in + let col = Path.indent p + Path.pad p in + append (KComment (tok, col)) (A col) ~pad block.path + in + (* if we are directly after a case in a sum-type, use that for + alignment *) + let align_bar = + if tok.newlines > 1 || not (is_inside_type block0.path) + then None + else + let find_bar = + unwind_while + (function KBar _ | KExpr _ -> true | _ -> false) + block0.path + in match find_bar with + | Some ({kind=KBar _; column}::_) -> Some column + | _ -> None + in + (* after a closed expr: look-ahead *) + (match next_token_full stream, align_bar with + | None, None -> blocklevel () + | Some ((* full block-closing tokens + newline *) + {token = SEMISEMI | DONE | END + | GREATERRBRACE | GREATERRBRACKET | RBRACE + | RBRACKET | RPAREN } + , _), _ + when tok.newlines > 1 -> + blocklevel () + | Some ((* semi block-closing tokens *) + {token = SEMISEMI | DONE | END + | GREATERRBRACE | GREATERRBRACKET | RBRACE + | RBRACKET | RPAREN + | THEN | ELSE | IN | EQUAL } + , _), None + when tok.newlines <= 1 -> (* indent as above *) + let col = (Path.top block0.path).line_indent in + append (KComment (tok, col)) (A col) ~pad block.path + | _, Some indent -> + append (KComment (tok,indent)) (A indent) ~pad block.path + | next, None -> (* recursive call to indent like next line *) + let path = match next with + | Some ({token = EOF }, _) | None -> [] + | Some (next,stream) -> + update_path config block stream + { next with newlines = tok.newlines } + in + let col = Path.indent path in + append (KComment (tok,col)) (A col) ~pad block.path) + | _ -> + let col = Path.indent block.path + Path.pad block.path in + append (KComment (tok,col)) (A col) ~pad block.path) |DOTDOT -> (match block.path with diff --git a/src/ocp-indent-lib/indentBlock.mli b/src/ocp-indent-lib/indentBlock.mli index c11d006e..398d9021 100644 --- a/src/ocp-indent-lib/indentBlock.mli +++ b/src/ocp-indent-lib/indentBlock.mli @@ -63,8 +63,8 @@ val guess_indent: int -> t -> int val is_clean: t -> bool (** True only when the block is at the root of the file (the stack is empty, the - block isn't included in any syntactical construct), and for top-level - constructs. Implies is_clean *) + block isn't included in any syntactical construct), and for top-level + constructs. Implies is_clean *) val is_at_top: t -> bool (** True for any block that is alone on its stack *) diff --git a/src/ocp-indent-lib/indentConfig.ml b/src/ocp-indent-lib/indentConfig.ml index 027a88b7..a1d3e7fe 100644 --- a/src/ocp-indent-lib/indentConfig.ml +++ b/src/ocp-indent-lib/indentConfig.ml @@ -83,7 +83,7 @@ let intoption_of_string = function | n -> try Some (int_of_string n) with Failure _ -> - failwith "intoption_of_string" + failwith "intoption_of_string" let string_of_intoption = function | Some n -> string_of_int n @@ -168,16 +168,16 @@ let set ?(extra=fun _ -> None) t var_name value = let update_from_string ?extra indent s = List.fold_left (fun indent s -> match Util.string_split '=' s with - | [] | [""] -> indent - | [var;value] -> set ?extra indent (String.trim var) (String.trim value) - | [preset] -> - (try List.assoc (String.trim preset) presets with - Not_found -> - let e = Printf.sprintf "unknown preset %S" preset in - raise (Invalid_argument e)) - | _ -> - let e = Printf.sprintf "wrong \"param=value\" pair in %S" s in - raise (Invalid_argument e)) + | [] | [""] -> indent + | [var;value] -> set ?extra indent (String.trim var) (String.trim value) + | [preset] -> + (try List.assoc (String.trim preset) presets with + Not_found -> + let e = Printf.sprintf "unknown preset %S" preset in + raise (Invalid_argument e)) + | _ -> + let e = Printf.sprintf "wrong \"param=value\" pair in %S" s in + raise (Invalid_argument e)) indent (Util.string_split_chars ",\n" s) @@ -206,130 +206,130 @@ let man = `P "Syntax: $(b,[PRESET,]VAR=VALUE[,VAR=VALUE...])" ] @ - `I (option_name "base" "INT" (string_of_int default.i_base), - "Indentation used when none of the following options applies.") - :: pre " let foo =\n\ - \ $(b,..)bar" + `I (option_name "base" "INT" (string_of_int default.i_base), + "Indentation used when none of the following options applies.") + :: pre " let foo =\n\ + \ $(b,..)bar" @ - `I (option_name "type" "INT" (string_of_int default.i_type), - "Indentation for type definitions.") - :: pre " type t =\n\ - \ $(b,..)int" + `I (option_name "type" "INT" (string_of_int default.i_type), + "Indentation for type definitions.") + :: pre " type t =\n\ + \ $(b,..)int" @ - `I (option_name "in" "INT" (string_of_int default.i_in), - "Indentation after `let ... in', unless followed by another `let'.") - :: pre " let foo = () in\n\ - \ $(b,..)bar" + `I (option_name "in" "INT" (string_of_int default.i_in), + "Indentation after `let ... in', unless followed by another `let'.") + :: pre " let foo = () in\n\ + \ $(b,..)bar" @ - `I (option_name "with" "INT" (string_of_int default.i_with), - "Indentation after `match ... with', `try ... with' or `function'.") - :: pre " match foo with\n\ - \ $(b,..)| _ -> bar" + `I (option_name "with" "INT" (string_of_int default.i_with), + "Indentation after `match ... with', `try ... with' or `function'.") + :: pre " match foo with\n\ + \ $(b,..)| _ -> bar" @ - `I (option_name "match_clause" "INT" (string_of_int default.i_match_clause), - "Indentation for clauses inside a pattern-match (after arrows).") - :: pre " match foo with\n\ - \ | _ ->\n\ - \ $(b,..)bar" + `I (option_name "match_clause" "INT" (string_of_int default.i_match_clause), + "Indentation for clauses inside a pattern-match (after arrows).") + :: pre " match foo with\n\ + \ | _ ->\n\ + \ $(b,..)bar" @ - `I (option_name "ppx_stritem_ext" "INT" (string_of_int default.i_ppx_stritem_ext), - "Indentation for items inside a [%%id ... ] extension node).") - :: pre " [%% id.id\n\ - \ $(b,..)let x = 3\ - \ ]" + `I (option_name "ppx_stritem_ext" "INT" (string_of_int default.i_ppx_stritem_ext), + "Indentation for items inside a [%%id ... ] extension node).") + :: pre " [%% id.id\n\ + \ $(b,..)let x = 3\ + \ ]" @ - `I (option_name "max_indent" "" - (string_of_intoption default.i_max_indent), - "When nesting expressions on the same line, their indentations are \ - stacked in some cases so that they remain correct if you close them \ - one per line. However, this can lead to large indentations in complex \ - code, so this parameter sets a maximum indentation. Note that it \ - only affects indentation after function arrows and opening parens at \ - the ends of lines.") - :: pre " let f = g (h (i (fun x ->\n\ - \ $(b,....)x)\n\ - \ )\n\ - \ )" + `I (option_name "max_indent" "" + (string_of_intoption default.i_max_indent), + "When nesting expressions on the same line, their indentations are \ + stacked in some cases so that they remain correct if you close them \ + one per line. However, this can lead to large indentations in complex \ + code, so this parameter sets a maximum indentation. Note that it \ + only affects indentation after function arrows and opening parens at \ + the ends of lines.") + :: pre " let f = g (h (i (fun x ->\n\ + \ $(b,....)x)\n\ + \ )\n\ + \ )" @ - `I (option_name "strict_with" "" - (string_of_threechoices default.i_strict_with), - "If `never', match bars are indented, superseding `with', \ - whenever `match with' doesn't start its line.\n\ - If `auto', there are exceptions for constructs like \ - `begin match with'.\n\ - If `always', `with' is always strictly respected, and additionally \ - applies to variant types definition, for consistency.") - :: pre " Example with `strict_with=$(b,never),with=0':\n\ - \ begin match foo with\n\ - \ $(b,..)| _ -> bar\n\ - \ end" + `I (option_name "strict_with" "" + (string_of_threechoices default.i_strict_with), + "If `never', match bars are indented, superseding `with', \ + whenever `match with' doesn't start its line.\n\ + If `auto', there are exceptions for constructs like \ + `begin match with'.\n\ + If `always', `with' is always strictly respected, and additionally \ + applies to variant types definition, for consistency.") + :: pre " Example with `strict_with=$(b,never),with=0':\n\ + \ begin match foo with\n\ + \ $(b,..)| _ -> bar\n\ + \ end" @ - `I (option_name "strict_else" "" - (string_of_threechoices default.i_strict_else), - "If `always', indent after the `else' keyword normally, like after \ - `then'.\n\ - If `auto', indent after `else' unless in a few \ - \"unclosable\" cases (`let .... in', `match', etc.).\n\ - If `never', the `else' keyword won't indent when followed \ - by a newline.") - :: pre " Example with `strict_else=$(b,auto)':\n\ - \ if cond then\n\ - \ foo\n\ - \ else\n\ - \ $(b,let) x = bar in\n\ - \ baz" + `I (option_name "strict_else" "" + (string_of_threechoices default.i_strict_else), + "If `always', indent after the `else' keyword normally, like after \ + `then'.\n\ + If `auto', indent after `else' unless in a few \ + \"unclosable\" cases (`let .... in', `match', etc.).\n\ + If `never', the `else' keyword won't indent when followed \ + by a newline.") + :: pre " Example with `strict_else=$(b,auto)':\n\ + \ if cond then\n\ + \ foo\n\ + \ else\n\ + \ $(b,let) x = bar in\n\ + \ baz" @ - `I (option_name "strict_comments" "BOOL" - (string_of_bool default.i_strict_comments), - "In-comment indentation is normally preserved, as long as it respects \ - the left margin or the comments starts with a newline. Setting this \ - to `true' forces alignment within comments. Lines starting with `*' \ - are always aligned") - :: [] + `I (option_name "strict_comments" "BOOL" + (string_of_bool default.i_strict_comments), + "In-comment indentation is normally preserved, as long as it respects \ + the left margin or the comments starts with a newline. Setting this \ + to `true' forces alignment within comments. Lines starting with `*' \ + are always aligned") + :: [] @ - `I (option_name "align_ops" "BOOL" - (string_of_bool default.i_align_ops), - "Toggles preference of column-alignment over line indentation for most \ - of the common operators and after mid-line opening parentheses.") - :: pre " Example with `align_ops=$(b,true)':\n\ - \ let f x = x\n\ - \ + y\n\ - \ \n\ - \ Example with `align_ops=$(b,false)':\n\ - \ let f x = x\n\ - \ + y" + `I (option_name "align_ops" "BOOL" + (string_of_bool default.i_align_ops), + "Toggles preference of column-alignment over line indentation for most \ + of the common operators and after mid-line opening parentheses.") + :: pre " Example with `align_ops=$(b,true)':\n\ + \ let f x = x\n\ + \ + y\n\ + \ \n\ + \ Example with `align_ops=$(b,false)':\n\ + \ let f x = x\n\ + \ + y" @ - `I (option_name "align_params" "" - (string_of_threechoices default.i_align_params), - "If `never', function parameters are indented one level from the \ - line of the function. \ - If `always', they are aligned from the column of the function. \ - if `auto', alignment is chosen over indentation in a few cases, e.g. \ - after match arrows") - :: pre " Example with `align_params=$(b,never)':\n\ - \ match foo with\n\ - \ | _ -> some_fun\n\ - \ $(b,..)parameter\n\ - \ \n\ - \ Example with `align_params=$(b,always)' or `$(b,auto)':\n\ - \ match foo with\n\ - \ | _ -> some_fun\n\ - \ $(b,..)parameter" + `I (option_name "align_params" "" + (string_of_threechoices default.i_align_params), + "If `never', function parameters are indented one level from the \ + line of the function. \ + If `always', they are aligned from the column of the function. \ + if `auto', alignment is chosen over indentation in a few cases, e.g. \ + after match arrows") + :: pre " Example with `align_params=$(b,never)':\n\ + \ match foo with\n\ + \ | _ -> some_fun\n\ + \ $(b,..)parameter\n\ + \ \n\ + \ Example with `align_params=$(b,always)' or `$(b,auto)':\n\ + \ match foo with\n\ + \ | _ -> some_fun\n\ + \ $(b,..)parameter" @ - `I (option_name "match_tail_cascade" "BOOL" - (string_of_bool default.i_match_tail_cascade), - "If `true', the default indentation is suppressed for matches \ - directly following a match arrow. This can avoid shifting to the \ - right when matches eliminate specific cases in succession, which \ - Gabriel Scherer coined as \"cascading style\", as opposed to the \ - usual \"nesting style\". This is similar to what happens with \ - `strict_else=$(b,auto).") - :: pre " Example with `match_tail_cascade=$(b,true)':\n\ - \ match foo with\n\ - \ | Error err -> fail_foo_error err\n\ - \ | Ok y ->\n\ - \ match bar y with\n\ - \ | Error err ->" + `I (option_name "match_tail_cascade" "BOOL" + (string_of_bool default.i_match_tail_cascade), + "If `true', the default indentation is suppressed for matches \ + directly following a match arrow. This can avoid shifting to the \ + right when matches eliminate specific cases in succession, which \ + Gabriel Scherer coined as \"cascading style\", as opposed to the \ + usual \"nesting style\". This is similar to what happens with \ + `strict_else=$(b,auto).") + :: pre " Example with `match_tail_cascade=$(b,true)':\n\ + \ match foo with\n\ + \ | Error err -> fail_foo_error err\n\ + \ | Ok y ->\n\ + \ match bar y with\n\ + \ | Error err ->" @ [ `P "Available presets are `normal', the default, `apprentice' which may \ make some aspects of the syntax more obvious for beginners, and \ @@ -344,10 +344,10 @@ let save t file = output_char oc '\n'; true with Sys_error _ -> - Printf.eprintf - "ocp-indent warning: could not open %S for writing configuration.\n%!" - file; - false + Printf.eprintf + "ocp-indent warning: could not open %S for writing configuration.\n%!" + file; + false let syntax_ext syntax_list_ref dynlink_list_ref = function | "syntax" -> @@ -356,7 +356,7 @@ let syntax_ext syntax_list_ref dynlink_list_ref = function List.iter (fun syn -> (* if List.mem syn (IndentExt.available ()) then *) - syntax_list_ref := syn :: !syntax_list_ref + syntax_list_ref := syn :: !syntax_list_ref (* else *) (* let e = Printf.sprintf "unknown syntax extension %S" syn in *) (* raise (Invalid_argument e) *) @@ -387,7 +387,7 @@ let load ?(indent=default) file = let n = try String.index s '#' with Not_found -> String.length s in Buffer.add_substring b s 0 n; Buffer.add_char b '\n' - done; assert false + done; assert false with End_of_file -> close_in ic; Buffer.contents b in let exts = ref [] in diff --git a/src/ocp-indent-lib/indentPrinter.ml b/src/ocp-indent-lib/indentPrinter.ml index 3bfaa35b..080b579f 100644 --- a/src/ocp-indent-lib/indentPrinter.ml +++ b/src/ocp-indent-lib/indentPrinter.ml @@ -137,36 +137,36 @@ let print_token output block tok usr = match pad with | None -> orig_line_indent, false | Some pad -> match tok.token with - | STRING _ -> - if ends_with_escape last then - if is_prefix "\"" text || is_prefix "\\ " text - then start_column, item_cont - else start_column + pad, item_cont - else orig_line_indent, item_cont - | COMMENT | COMMENTCONT -> - let is_item = - is_prefix "- " text && not (is_prefix "- :" text) - in - let n = - if is_prefix "*" text then 1 else - if not is_item && item_cont then pad + 2 - else pad - in - let item_cont = is_item || item_cont && text <> "" in - let n = - if output.config.IndentConfig.i_strict_comments || is_item - then n else max orig_offset n - in - let n = if next_lines = [] && text = "*)" then 0 else n in - start_column + n, item_cont - | QUOTATION opening -> - if is_prefix "{" opening then orig_line_indent, item_cont - else - (start_column + - if next_lines = [] && text = ">>" then 0 - else max orig_offset pad), - item_cont - | _ -> start_column + max orig_offset pad, item_cont + | STRING _ -> + if ends_with_escape last then + if is_prefix "\"" text || is_prefix "\\ " text + then start_column, item_cont + else start_column + pad, item_cont + else orig_line_indent, item_cont + | COMMENT | COMMENTCONT -> + let is_item = + is_prefix "- " text && not (is_prefix "- :" text) + in + let n = + if is_prefix "*" text then 1 else + if not is_item && item_cont then pad + 2 + else pad + in + let item_cont = is_item || item_cont && text <> "" in + let n = + if output.config.IndentConfig.i_strict_comments || is_item + then n else max orig_offset n + in + let n = if next_lines = [] && text = "*)" then 0 else n in + start_column + n, item_cont + | QUOTATION opening -> + if is_prefix "{" opening then orig_line_indent, item_cont + else + (start_column + + if next_lines = [] && text = ">>" then 0 + else max orig_offset pad), + item_cont + | _ -> start_column + max orig_offset pad, item_cont in usr |> print_indent output line "" ~kind:(Fixed indent_value) block diff --git a/src/ocp-indent-utils/dune b/src/ocp-indent-utils/dune index 4f233ebd..89db32db 100644 --- a/src/ocp-indent-utils/dune +++ b/src/ocp-indent-utils/dune @@ -2,5 +2,19 @@ (name ocp_indent_utils) (public_name ocp-indent.utils) (wrapped false) - (libraries ocp-indent.lexer) -) + (libraries ocp-indent.lexer)) + +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules -o %{target})))) diff --git a/src/ocp-indent-utils/nstream.ml b/src/ocp-indent-utils/nstream.ml index 8a7bea20..457d927f 100644 --- a/src/ocp-indent-utils/nstream.ml +++ b/src/ocp-indent-utils/nstream.ml @@ -56,23 +56,23 @@ let of_string ?(start_pos=Position.zero) ?(start_offset=0) string = | EOL | SPACES -> loop last | token -> - let pos_last = Region.snd last - and pos_start = lexbuf.lex_start_p - and pos_end = lexbuf.lex_curr_p - in - let region = Region.create pos_start pos_end in - let offset = Region.start_column region - Region.start_column last - in - let spaces = pos_start.pos_cnum - pos_last.pos_cnum in - let len = pos_end.pos_cnum - pos_start.pos_cnum in - let newlines = pos_start.pos_lnum - pos_last.pos_lnum in - let between = lazy (String.sub string pos_last.pos_cnum spaces) in - let substr = lazy (String.sub string pos_start.pos_cnum len) in - Cons ({ region; token; newlines; between; substr; offset }, - lazy (match token with - | EOF -> Null - | _ -> loop region) - ) + let pos_last = Region.snd last + and pos_start = lexbuf.lex_start_p + and pos_end = lexbuf.lex_curr_p + in + let region = Region.create pos_start pos_end in + let offset = Region.start_column region - Region.start_column last + in + let spaces = pos_start.pos_cnum - pos_last.pos_cnum in + let len = pos_end.pos_cnum - pos_start.pos_cnum in + let newlines = pos_start.pos_lnum - pos_last.pos_lnum in + let between = lazy (String.sub string pos_last.pos_cnum spaces) in + let substr = lazy (String.sub string pos_start.pos_cnum len) in + Cons ({ region; token; newlines; between; substr; offset }, + lazy (match token with + | EOF -> Null + | _ -> loop region) + ) in let init_region = let pos_above = @@ -102,27 +102,27 @@ let of_channel ?(start_pos=Position.zero) ic = | EOL | SPACES -> loop last | token -> - let pos_last = Region.snd last - and pos_start = lexbuf.lex_start_p - and pos_end = lexbuf.lex_curr_p - in - let spaces = pos_start.pos_cnum - pos_last.pos_cnum in - let len = pos_end.pos_cnum - pos_start.pos_cnum in - let newlines = pos_start.pos_lnum - pos_last.pos_lnum in - let between = let s = Buffer.sub buf 0 spaces in lazy s in - let substr = let s = Buffer.sub buf spaces len in lazy s - in - let total = pos_end.pos_cnum - pos_last.pos_cnum in - let more = Buffer.sub buf total (Buffer.length buf - total) in - Buffer.clear buf; - Buffer.add_string buf more; - let region = Region.create pos_start pos_end in - let offset = Region.start_column region - Region.start_column last in - Cons ({ region; token; newlines; between; substr; offset }, - lazy (match token with - | EOF -> Null - | _ -> loop region) - ) + let pos_last = Region.snd last + and pos_start = lexbuf.lex_start_p + and pos_end = lexbuf.lex_curr_p + in + let spaces = pos_start.pos_cnum - pos_last.pos_cnum in + let len = pos_end.pos_cnum - pos_start.pos_cnum in + let newlines = pos_start.pos_lnum - pos_last.pos_lnum in + let between = let s = Buffer.sub buf 0 spaces in lazy s in + let substr = let s = Buffer.sub buf spaces len in lazy s + in + let total = pos_end.pos_cnum - pos_last.pos_cnum in + let more = Buffer.sub buf total (Buffer.length buf - total) in + Buffer.clear buf; + Buffer.add_string buf more; + let region = Region.create pos_start pos_end in + let offset = Region.start_column region - Region.start_column last in + Cons ({ region; token; newlines; between; substr; offset }, + lazy (match token with + | EOF -> Null + | _ -> loop region) + ) in let init_region = let pos_above = diff --git a/src/ocp-indent-utils/util.ml b/src/ocp-indent-utils/util.ml index d9ac74c9..578dcc59 100644 --- a/src/ocp-indent-utils/util.ml +++ b/src/ocp-indent-utils/util.ml @@ -46,7 +46,7 @@ let string_split_chars chars str = if i > pos then String.sub str pos (i - pos) :: split (succ i) else split (succ i) with Not_found | Invalid_argument _ -> - [ String.sub str pos (len - pos) ] + [ String.sub str pos (len - pos) ] in split 0 diff --git a/src/ocp-indent/dune b/src/ocp-indent/dune index 2c7578ed..5324e55e 100644 --- a/src/ocp-indent/dune +++ b/src/ocp-indent/dune @@ -1,14 +1,28 @@ (rule (targets indentVersion.ml) (action - (with-stdout-to %{targets} - (echo "let version = \"%{version:ocp-indent}\""))) -) + (with-stdout-to + %{targets} + (echo "let version = \"%{version:ocp-indent}\"")))) (executable (name indentMain) (public_name ocp-indent) (modules indentVersion indentArgs indentMain) (libraries cmdliner ocp-indent.lexer ocp-indent.lib ocp-indent.dynlink unix) - (flags :standard -w -9) -) + (flags :standard -w -9)) + +;; Auto indent files with `dune build @fmt` + +(subdir + run + (dynamic_include ../rules/dune.ocp-indent)) + +(subdir + rules + (rule + (deps + (glob_files ../*.{ml,mli})) + (target dune.ocp-indent) + (action + (run ocp-indent-gen-rules -o %{target})))) diff --git a/src/ocp-indent/indentArgs.ml b/src/ocp-indent/indentArgs.ml index 7b9b8de9..27f71c49 100644 --- a/src/ocp-indent/indentArgs.ml +++ b/src/ocp-indent/indentArgs.ml @@ -39,11 +39,11 @@ let options = let doc = "Configure the indentation parameters. See section \ $(b,CONFIGURATION) for more information." in let config_converter = Arg.conv' - ((fun str -> try (* just check syntax *) - ignore (IndentConfig.update_from_string IndentConfig.default str); - Ok str - with Invalid_argument s -> Error s), - (Format.pp_print_string)) + ((fun str -> try (* just check syntax *) + ignore (IndentConfig.update_from_string IndentConfig.default str); + Ok str + with Invalid_argument s -> Error s), + (Format.pp_print_string)) in Arg.(value & opt_all config_converter [] & info ["c";"config"] ~docv:"CONFIG" ~doc) @@ -67,24 +67,24 @@ let options = Lines start at 1." in let range_converter = Arg.conv' - ((fun str -> - try match Util.string_split '-' str with - | [s] -> - let li = int_of_string s in Ok(Some li, Some li) - | [s1;s2] -> - let f = function "" -> None - | s -> Some (int_of_string s) - in - Ok (f s1, f s2) - | _ -> failwith "range_converter" - with Failure _ -> Error "invalid range specification."), - (fun fmt -> function - | Some n1, Some n2 when n1 = n2 -> Format.pp_print_int fmt n1 - | o1, o2 -> - let f fmt = function None -> () - | Some n -> Format.pp_print_int fmt n - in - Format.fprintf fmt "%a-%a" f o1 f o2)) + ((fun str -> + try match Util.string_split '-' str with + | [s] -> + let li = int_of_string s in Ok(Some li, Some li) + | [s1;s2] -> + let f = function "" -> None + | s -> Some (int_of_string s) + in + Ok (f s1, f s2) + | _ -> failwith "range_converter" + with Failure _ -> Error "invalid range specification."), + (fun fmt -> function + | Some n1, Some n2 when n1 = n2 -> Format.pp_print_int fmt n1 + | o1, o2 -> + let f fmt = function None -> () + | Some n -> Format.pp_print_int fmt n + in + Format.fprintf fmt "%a-%a" f o1 f o2)) in Arg.(value & opt range_converter (None,None) & info ["l";"lines"] ~docv:"RANGE" ~doc) @@ -156,36 +156,36 @@ let options = | `Mod s -> s) dlink)); exit 0) else `Ok ( - { - file_out; numeric; indent_config; debug; inplace; - indent_empty = indent_empty || - (match lines with - | Some fst, Some lst when fst = lst -> true - | _ -> false); - in_lines = (match lines with - | None, None -> fun _ -> true - | Some first, Some last -> fun l -> first <= l && l <= last - | Some first, None -> fun l -> first <= l - | None, Some last -> fun l -> l <= last); - indent_printer = (fun oc -> - if numeric then - IndentPrinter.Numeric (fun n () -> - output_string oc (string_of_int n); - output_string oc "\n") - else - IndentPrinter.Print - (if debug then - (fun s () -> output_string oc s; - try let _ = String.index s '\n' in flush stdout - with Not_found -> ()) - else - (fun s () -> output_string oc s))); - syntax_exts; - dynlink = (List.map (fun s -> `Mod s) load_mods) @ - (List.map (fun s -> `Pkg s) load_pkgs) - }, - files - ) + { + file_out; numeric; indent_config; debug; inplace; + indent_empty = indent_empty || + (match lines with + | Some fst, Some lst when fst = lst -> true + | _ -> false); + in_lines = (match lines with + | None, None -> fun _ -> true + | Some first, Some last -> fun l -> first <= l && l <= last + | Some first, None -> fun l -> first <= l + | None, Some last -> fun l -> l <= last); + indent_printer = (fun oc -> + if numeric then + IndentPrinter.Numeric (fun n () -> + output_string oc (string_of_int n); + output_string oc "\n") + else + IndentPrinter.Print + (if debug then + (fun s () -> output_string oc s; + try let _ = String.index s '\n' in flush stdout + with Not_found -> ()) + else + (fun s () -> output_string oc s))); + syntax_exts; + dynlink = (List.map (fun s -> `Mod s) load_mods) @ + (List.map (fun s -> `Pkg s) load_pkgs) + }, + files + ) in let t = Term.(const build_t @@ -216,25 +216,25 @@ let info = `\\$HOME/.ocp/ocp-indent.conf', or the environment variable \ \\$OCP_INDENT_CONFIG." ] @ - IndentConfig.man - @ [ - `S "BUGS"; - `P "Bugs are tracked on github at \ - $(i,https://github.com/OCamlPro/ocp-indent/issues). The $(i,tests) \ - directory of the source distribution is a good snapshot of the current \ - status, and can be checked online at \ - $(i,http://htmlpreview.github.io/?\ - https://github.com/OCamlPro/ocp-indent/blob/master/tests/failing.html)"; - `S "SEE ALSO"; - `P "ocaml(1), ocp-index(1)"; - `S "AUTHORS"; - `P "Louis Gesbert and Thomas Gazagnaire from OCamlPro, from an original \ - prototype by Jun Furuse."; - `S "LICENSE"; - `P "Copyright (C) 2013 OCamlPro."; - `P "ocp-indent is free software, released under the terms of the GNU General \ - Public License version 3, the text of which can be found in the file \ - `LICENSE' distributed with the sources." - ] + IndentConfig.man + @ [ + `S "BUGS"; + `P "Bugs are tracked on github at \ + $(i,https://github.com/OCamlPro/ocp-indent/issues). The $(i,tests) \ + directory of the source distribution is a good snapshot of the current \ + status, and can be checked online at \ + $(i,http://htmlpreview.github.io/?\ + https://github.com/OCamlPro/ocp-indent/blob/master/tests/failing.html)"; + `S "SEE ALSO"; + `P "ocaml(1), ocp-index(1)"; + `S "AUTHORS"; + `P "Louis Gesbert and Thomas Gazagnaire from OCamlPro, from an original \ + prototype by Jun Furuse."; + `S "LICENSE"; + `P "Copyright (C) 2013 OCamlPro."; + `P "ocp-indent is free software, released under the terms of the GNU General \ + Public License version 3, the text of which can be found in the file \ + `LICENSE' distributed with the sources." + ] in Cmd.info "ocp-indent" ~version:IndentVersion.version ~doc ~man diff --git a/src/ocp-indent/indentMain.ml b/src/ocp-indent/indentMain.ml index 404d355a..322e8544 100644 --- a/src/ocp-indent/indentMain.ml +++ b/src/ocp-indent/indentMain.ml @@ -89,7 +89,7 @@ let indent_file args = function | Some src, Some dst -> Sys.rename src dst | _, _ -> () with e -> - close_in ic; raise e + close_in ic; raise e let main = Cmdliner.Cmd.v Args.info diff --git a/tests/failing/dune b/tests/failing/dune index 2a531567..9be0114e 100644 --- a/tests/failing/dune +++ b/tests/failing/dune @@ -1,4 +1,5 @@ (cram - (enabled_if (= %{os_type} "Unix")) + (enabled_if + (= %{os_type} "Unix")) (deps (package ocp-indent))) diff --git a/tests/inplace/dune b/tests/inplace/dune index 2a531567..9be0114e 100644 --- a/tests/inplace/dune +++ b/tests/inplace/dune @@ -1,4 +1,5 @@ (cram - (enabled_if (= %{os_type} "Unix")) + (enabled_if + (= %{os_type} "Unix")) (deps (package ocp-indent))) diff --git a/tests/ocp-indent-gen-rules/dune b/tests/ocp-indent-gen-rules/dune new file mode 100644 index 00000000..ddd4a8c5 --- /dev/null +++ b/tests/ocp-indent-gen-rules/dune @@ -0,0 +1,3 @@ +(cram + (deps + (package ocp-indent))) diff --git a/tests/ocp-indent-gen-rules/run.t b/tests/ocp-indent-gen-rules/run.t new file mode 100644 index 00000000..c28d1f89 --- /dev/null +++ b/tests/ocp-indent-gen-rules/run.t @@ -0,0 +1,109 @@ +ocp-indent-gen-rules is a tool that's meant to generate dune formatting +rules for ocp-indent in your project. + +---------------- Base -------------------- + +It generates two rules per file, one rule to generate the formatted version +of the source file and another one for promotion: + + $ touch test.ml + $ ocp-indent-gen-rules --static + (rule + (target test.ml.fmtd) + (action (run ocp-indent %{dep:test.ml} -o %{target}))) + + (rule + (alias fmt) + (action (diff test.ml test.ml.fmtd))) + + +---------------- Alias -------------------- + +By default it attaches those rule to the @fmt alias, this can be overwritten using +the --alias option: + + $ ocp-indent-gen-rules --static --alias indent + (rule + (target test.ml.fmtd) + (action (run ocp-indent %{dep:test.ml} -o %{target}))) + + (rule + (alias indent) + (action (diff test.ml test.ml.fmtd))) + + + +---------------- Output -------------------- + +It can be given the -o option to write to a file instead of the standard output: + + $ ocp-indent-gen-rules --static -o ./out + $ cat out + (rule + (target test.ml.fmtd) + (action (run ocp-indent %{dep:test.ml} -o %{target}))) + + (rule + (alias fmt) + (action (diff test.ml test.ml.fmtd))) + + + +---------------- Ignore -------------------- + +Sometimes, source files happen to be generated and in those cases, we don't want +to be formatting them. One can manually pass a list of files to ignore: + + $ touch do_not_format.ml + $ ocp-indent-gen-rules --static + (rule + (target do_not_format.ml.fmtd) + (action (run ocp-indent %{dep:do_not_format.ml} -o %{target}))) + + (rule + (alias fmt) + (action (diff do_not_format.ml do_not_format.ml.fmtd))) + + (rule + (target test.ml.fmtd) + (action (run ocp-indent %{dep:test.ml} -o %{target}))) + + (rule + (alias fmt) + (action (diff test.ml test.ml.fmtd))) + + +We see that we now produce one more set of rules for the new do_not_format.ml. If we +use the flag though: + + $ ocp-indent-gen-rules --static --ignore do_not_format.ml + (rule + (target test.ml.fmtd) + (action (run ocp-indent %{dep:test.ml} -o %{target}))) + + (rule + (alias fmt) + (action (diff test.ml test.ml.fmtd))) + + $ rm do_not_format.ml + +---------------- Dynamic -------------------- + +To keep the test simples we used the --static mode which is meant to be used +with a regular `(include ...)` dune stanza. +By default it is made to work with the more practical `(dynamic_include ...)` +stanza, which required the tool to be run from a subfolder. Rules write the .fmtd +file there but read the source file from the parent directory: + + $ mkdir rules + $ cd rules + $ ocp-indent-gen-rules + (rule + (target test.ml.fmtd) + (action (run ocp-indent %{dep:../test.ml} -o %{target}))) + + (rule + (alias fmt) + (action (diff ../test.ml test.ml.fmtd))) + + diff --git a/tools/dune b/tools/dune index 1c54cd32..d9ae020e 100644 --- a/tools/dune +++ b/tools/dune @@ -2,5 +2,4 @@ (section share_root) (files (ocp-indent.el as emacs/site-lisp/ocp-indent.el) - (ocp-indent.vim as ocp-indent/vim/indent/ocaml.vim)) -) + (ocp-indent.vim as ocp-indent/vim/indent/ocaml.vim)))