(* Éditeur de texte, en utilisant la structure de zipper pour une liste *)

(* Programme 73 page 308 *)
module L = struct

  type 'a zipper = { left: 'a list; right: 'a list; }

  let of_list l =
    { left = []; right = l }

  let move_right z = match z.right with
    | [] -> invalid_arg "move_right"
    | x :: r -> { left = x :: z.left; right = r }

  let move_left z = match z.left with
    | [] -> invalid_arg "move_left"
    | x :: l -> { left = l; right = x :: z.right }

  let insert z x =
    { z with left = x :: z.left }

  let delete_left z =  match z.left with
    | [] -> invalid_arg "delete_left"
    | _ :: l -> { z with left = l }

  let delete_right z =  match z.right with
    | [] -> invalid_arg "delete_right"
    | _ :: r -> { z with right = r }

  let to_start z =
    { left = []; right = List.rev_append z.left z.right }

  let to_end z =
    { left = List.rev_append z.right z.left; right = [] }

end

module TextEditor : sig

  type line
  type text

  val empty: text
  val insert_char: text -> char -> text
  val return: text -> text
  val backspace: text -> text
  val delete: text -> text
  val up: text -> text
  val left: text -> text

end = struct

  open L

  (* une ligne = une zipper pour une liste de caractères *)
  type line = char zipper

  (* la ligne courante + un zipper pour représenter les lignes avant et après *)
  type text = line zipper * line

  let empty = of_list [], of_list []

  let insert_char (ctx, l) c =
    (ctx, insert l c)

  let return (ctx, line) =
    { left = { left = line.left; right = [] } :: ctx.left;
      right = ctx.right },
    { left = []; right = line.right }

  let backspace ((ctx, line) as text) =
    try ctx, delete_left line
    with Invalid_argument _ -> match ctx.left with
      | [] -> (* début du texte *) text
      | prev :: left ->
          { ctx with left = left }, { (to_end prev) with right = line.right }

  let delete ((ctx, line) as text) =
    try ctx, delete_right line
    with Invalid_argument _ -> match ctx.right with
      | [] -> (* fin du texte *) text
      | next :: right ->
          { ctx with right = right }, { (to_start next) with left = line.left }

  let up ((ctx, line) as text) = match ctx.left with
    | [] -> (* première ligne *) text
    | prev :: left -> { left = left; right = line :: ctx.right }, prev

  (* laissé en exercice: down *)

  let left ((ctx, line) as text) =
    try ctx, move_left line
    with Invalid_argument _ -> match ctx.left with
      | [] -> (* début du texte *) text
      | prev :: left -> { left = left; right = line :: ctx.right}, to_end prev

  (* laissé en exercice: right *)

end


This document was generated using caml2html