openPosopenArgsmoduleState=Machine.StatemoduleStack=Machine.StackmoduleSexp=Sexplib.Sexp(* No open Sexplib, since Parser corrides with Sexplib.Parser *)typeregion=(int*int)option(* [None] means the whole file *)(** [l] is in the interested area or not? *)letcheck_line:int->region->[>`Before|`Inside|`Over]=funl->function|None->`Inside|Some(start,end_)->ifl<startthen`Beforeelseifl<=end_then`Insideelse`OvermodulePrinter:sigvalprint_debug:region->int->string->unitvalprint_string:region->int->string->unitend=structletprint_debugtlnums=matchcheck_linelnumtwith|`Before|`Over->()|`Inside->print_strings(** Print a string [s] for the line number [lnum]. If the printing text exceeds the interested region, It stops printing. Currently it cannot print a string correctly if it has different number of lines from the original. *)letprint_stringtlnums=letadd_linetlnums=matchcheck_linelnumtwith|`Before->()|`Over->raiseExit|`Inside->print_stringsinletget_lines=tryletpos=String.indexs'\n'inString.subs0(pos+1),Some(String.subs(pos+1)(String.lengths-pos-1))with|Not_found->s,Noneinletreciterlnums=letline,rest=get_linesinadd_linetlnumline;matchrestwith|Somes->iter(lnum+1)s|None->()intryiterlnumswithExit->()endopenTokenstrletindent_filepath=letprint_string=Printer.print_stringlinesinletprint_debug=Printer.print_debuglinesinletstr=ifpath=""thenTokenstr.of_channelstdinelseTokenstr.of_pathpathinletflush_remaining_spaceinfo=print_string(Region.lnum(fstinfo.space))(sndinfo.space)inletreclooplast_orig_regionstatestr=matchTokenstr.destrstrwith|None->state|Some(({token=Parser.EOF}asinfo),_)->flush_remaining_spaceinfo;state|Some(({token=t;region=orig_region;space=(_space_between_region,space_between);substr}asinfo),str)->matchcheck_line(Region.lnumorig_region)lineswith|`Over->(* The token is outside of our interest. Print the remaining things and go out *)flush_remaining_spaceinfo;state|(`Before|`Insideasline_status)->(* Format.eprintf "<%s %d>@." *)letlast_line=Region.lnumlast_orig_regioninletcurrent_line=Region.lnumorig_regioninletat_new_line=last_line<>current_linein(* Is this token at a new line? *)(* update the original indent if [at_new_line] *)letstate=ifat_new_linethen{statewithState.orig_indent=Region.columnsorig_region}elsestatein(* Where the cursor move *)let_cursor_info=matchcursorwith|None->None|Somelines_cols->matchRegion.contain_lines_colslast_orig_regionlines_cols,Region.contain_lines_colsorig_regionlines_colswith|(`In|`Right),_->None(* far past *)|`Left,`Right->None(* far future *)|`Left,`Left->Some`In_space_between(* cursor in the space_between *)|`Left,`In->Some`In_the_token(* cursor on the token *)|_->assertfalse(* It must be more tolerant, but for now... *)inletfix_indent=matchline_statuswith|`Inside->true|`Before->false|`Over->assertfalseinletpre,post=Machine.update_statestate~at_new_line~fix_indentstrorig_regiontin(* printing *)ifnotat_new_linethenprint_stringcurrent_linespace_betweenelsebegin(* the line 1 has no previous new line char *)letspaces=tryString.subspace_between0(String.rindexspace_between'\n'+1)with_->""inletindent_string=String.make(State.indentpre)' 'in(* CR jfuruse: can be a bug. something from space_between_region *)print_stringlast_linespaces;ifdebugthenbeginprint_stringcurrent_lineindent_string;ifpre==postthenprint_debugcurrent_line(Printf.sprintf"-- %s\n"(Sexp.to_string_mach(Stack.sexp_of_tpre.State.bases)))elseprint_debugcurrent_line(Printf.sprintf"-- %s // %s\n"(Sexp.to_string_mach(Stack.sexp_of_tpre.State.bases))(Sexp.to_string_mach(Stack.sexp_of_tpost.State.bases)))end;print_stringcurrent_lineindent_string;end;print_stringcurrent_linesubstr;(* Now move to the next token *)(* CR jfuruse: last_token thing seems strange. The state machine should be able to access previous tokens freely. (But with risk of memory leak) *)letlast_token=matchtwithParser.COMMENT_->state.State.last_token|_->Sometinletpost={postwithState.last_token=last_token;last_indent=ifat_new_linethenState.indentpreelsepost.State.last_indent}inlooporig_regionpoststrinletfinal_state=loopRegion.zeroState.initstrinifshowstatethenState.printfinal_state;Tokenstr.closestrlet_=List.iterindent_filepaths