(* $Id: lexical.ml,v 1.10 2001/09/17 13:09:32 garrigue Exp $ *)

open StdLabels
open Parser

type tags = [`none|`control|`define|`structure|`char|`infix|`label|`uident]

let colors : (tags * GDraw.color) list Lazy.t =
  lazy
    (List.map ~f:(fun (tag,col) -> tag, `COLOR (GDraw.color (`NAME col)))
       [ `control, "blue";
	 `define, "forestgreen";
	 `structure, "purple";
	 `char, "gray40";
	 `infix, "indianred4";
	 `label, "brown";
	 `uident, "midnightblue";
         `none, "black" ])

let tag ?(start=0) ?stop:pend (tw : GEdit.text) =
  let pend = Gaux.default tw#length ~opt:pend in
  let colors = Lazy.force colors in
  tw#freeze ();
  let position = tw#position
  and text = tw#get_chars ~start ~stop:pend in
  let replace ~start:pstart ~stop:pend ~tag =
    if pend > pstart then begin
      tw#delete_text ~start:(start+pstart) ~stop:(start+pend);
      tw#set_point (start+pstart);
      tw#insert ~foreground:(List.assoc tag colors)
	(String.sub text ~pos:pstart ~len:(pend-pstart));
    end
  and next_lf = ref (-1) in
  let colorize ~start:rstart ~stop:rend ~tag =
    let rstart = ref rstart in
    while !rstart < rend do
      if !next_lf < !rstart then begin
	try next_lf := String.index_from text !rstart '\n'
	with Not_found -> next_lf := pend-start
      end;
      replace ~start:!rstart ~stop:(min !next_lf rend) ~tag;
      rstart := !next_lf + 1
    done
  in
  let buffer = Lexing.from_string text
  and last = ref (EOF, 0, 0)
  and last_pos = ref 0 in
  try
    while true do
    let token = Lexer.token buffer
    and start = Lexing.lexeme_start buffer
    and stop = Lexing.lexeme_end buffer in
    let tag =
      match token with
        AMPERAMPER
      | AMPERSAND
      | BARBAR
      | DO | DONE
      | DOWNTO
      | ELSE
      | FOR
      | IF
      | LAZY
      | MATCH
      | OR
      | THEN
      | TO
      | TRY
      | WHEN
      | WHILE
      | WITH
          -> `control
      | AND
      | AS
      | BAR
      | CLASS
      | CONSTRAINT
      | EXCEPTION
      | EXTERNAL
      | FUN
      | FUNCTION
      | FUNCTOR
      | IN
      | INHERIT
      | INITIALIZER
      | LET
      | METHOD
      | MODULE
      | MUTABLE
      | NEW
      | OF
      | PRIVATE
      | REC
      | TYPE
      | VAL
      | VIRTUAL
          -> `define
      | BEGIN
      | END
      | INCLUDE
      | OBJECT
      | OPEN
      | SIG
      | STRUCT
          -> `structure
      | CHAR _
      | STRING _
          -> `char
      | BACKQUOTE
      | INFIXOP1 _
      | INFIXOP2 _
      | INFIXOP3 _
      | INFIXOP4 _
      | PREFIXOP _
      | SHARP
          -> `infix
      | LABEL _
      | OPTLABEL _
      | QUESTION
      | TILDE
          -> `label
      | UIDENT _ -> `uident
      | LIDENT _ ->
          begin match !last with
            (QUESTION | TILDE), _, _ -> `label
          | _ -> `none
          end
      | COLON ->
          begin match !last with
            LIDENT _, lstart, lstop when lstop = start ->
              colorize ~tag:`none ~start:!last_pos ~stop:lstart;
              colorize ~tag:`label ~start:lstart ~stop;
              last_pos := stop;
              `none
          | _ -> `none
          end
      | EOF -> raise End_of_file
      | _ -> `none
    in
    if tag <> `none then begin
      colorize ~tag:`none ~start:!last_pos ~stop:start;
      colorize ~tag ~start ~stop;
      last_pos := stop
    end;
    last := (token, start, stop)
    done
  with exn ->
    colorize ~tag:`none ~start:!last_pos ~stop:(pend-start);
    tw#thaw ();
    tw#set_position position;
    tw#set_point position;
    match exn with
      End_of_file | Lexer.Error _ -> ()
    | _ -> raise exn
