Source

erlint / src / formatter.ml

Full commit
open Token

module Po = Program_options

module R = Return

type error = { line_number : int
	     ; msg : string
	     }

type warning = error


type function_info = { name : string
		     ; spec : Token.t list
		     }

type parser_state =
  | Top_level
  | Function of function_info

type state = { program_options : Program_options.t
	     ; imputed_line_length : int
	     (* The number of indentations, not the number of spaces *)
	     ; indentation_level : int
	     (* This is the current line we aer reading, starting at 0 *)
	     ; src_line_number : int
	     (* This is current line number we are writing, starting at 0 *)
	     ; dst_line_number : int
	     ; parser_state : parser_state
	     ; buffer : Buffer.t
	     ; warnings : warning list
	     }


let default_state program_options buffer =
  { program_options = program_options
  ; imputed_line_length = 0
  ; indentation_level = 0
  ; src_line_number = 0
  ; dst_line_number = 0
  ; parser_state = Top_level
  ; buffer = buffer
  ; warnings = []
  }

let incr ?(step = 1) num = num + step

let incr_src_line state =
  { state with src_line_number = incr state.src_line_number }

let incr_dst_line state =
  Buffer.add_char state.buffer '\n';
  { state with
    dst_line_number = incr state.dst_line_number;
    imputed_line_length = 0
  }

let fail state msg =
  R.Failure { line_number = state.src_line_number
	    ; msg = msg
	    }

let add_warning state msg =
  { state with
    warnings = { line_number = state.dst_line_number
	       ; msg = msg
	       }::state.warnings
  }

let is_line_too_long state line =
  (String.length line
   + state.indentation_level * state.program_options.Po.indent_spaces
   > state.program_options.Po.max_line_length)

let succeed state =
  R.Success (Buffer.contents state.buffer, state.warnings)

let rec format_top_level state = function
  | Newline::xs ->
    format_top_level (incr_dst_line (incr_src_line state)) xs
  | (Comment text)::xs -> begin
    Buffer.add_string state.buffer ("%" ^ text);
    if is_line_too_long state text then
      format_top_level
	(add_warning state "Comment too long, please fix")
	xs
    else
      format_top_level state xs
  end
  | (Keyword "-")::(Atom "module")::(Keyword "("
  | [] -> succeed state
  | _::xs -> format_top_level state xs

let format_code program_options code =
  (*
   * Initiate the buffer at the number of tokens * 10, just
   * a reasonable round number
   *)
  let buffer = Buffer.create (List.length code * 10) in
  let state = default_state program_options buffer in
  format_top_level state code

let format program_options out_chan code =
  match format_code program_options code with
    | R.Success (formatted_code, warnings) -> begin
      output_string out_chan formatted_code;
      R.Success warnings
    end
    | R.Failure errors ->
      R.Failure errors