
/******************************************************************************
* MODULE     : edit_spell.gen.cc
* DESCRIPTION: spell checker based on ispell
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include <analyze.gen.h>

#module code_edit_spell
#import analyze

/******************************************************************************
* Internationalization
******************************************************************************/

static tree
translate (tree t, string (*rout) (string)) {
  if (is_atomic (t)) return rout (t->label);
  else {
    int i, n= N(t);
    tree u (t, n);
    for (i=0; i<n; i++)
      u[i]= translate (t[i], rout);
    return u;
  }
}

void
edit_replace_rep::spell_write (string s) {
  if ((search_lan == "czech") || (search_lan == "hungarian") ||
      (search_lan == "polish"))
  {
    string transl= cork_to_il2 (s);
    connection_write ("ispell", "default", transl);
  }
  else if ((search_lan == "russian") || (search_lan == "ukrainian")) {
    string transl= koi8_to_iso (s);
    connection_write ("ispell", "default", transl);
  }
  else if (search_lan == "spanish") {
    string transl= spanish_to_ispanish (s);
    connection_write ("ispell", "default", transl);
  }
  else connection_write ("ispell", "default", s);
}

tree
edit_replace_rep::spell_read () {
  tree t= connection_read ("ispell", "default", 10000);
  if ((search_lan == "czech") || (search_lan == "hungarian") ||
      (search_lan == "polish"))
    return translate (t, il2_to_cork);
  else if ((search_lan == "russian") || (search_lan == "ukrainian"))
    return translate (t, iso_to_koi8);
  else if (search_lan == "spanish")
    return translate (t, ispanish_to_spanish);
  else return t;
}

/******************************************************************************
* Find next word to be spelled
******************************************************************************/

path
edit_replace_rep::test_spellable (path p) {
  tree st= subtree (et, path_up (p));
  if (is_compound (st)) return p;
  string s= st->label;
  int    b= last_item (p);
  int    e= b;
  if ((e > 0) && ((is_iso_alpha (s[e-1])) || (is_digit (s[e-1])))) return p;
  while ((e < N(s)) && (is_iso_alpha (s[e]))) e++;
  if ((e < N(s)) && (is_digit (s[e]))) return p;
  if (e == b) return p;
  spell_s= s (b, e);
  return path_add (p, e - b);
}

static tree
parse_ispell (string s) {
  bool flag= TRUE;
  int i, j;
  tree t (TUPLE);
  for (i=0, j=0; j<N(s); j++)
    if (s[j]==':') flag= FALSE;
    else if (((s[j]==' ') && (flag || (j==i) || (s[j-1]==':'))) ||
	     (s[j]==','))
      {
	if (j>i) t << s (i, j);
	i= j+1;
      }
  t << s (i, j);
  return t;
}

static string
message_ispell (tree t) {
  int i;
  string s= "a: accept, r: replace, i: insert";
  for (i=1; i<N(t); i++) {
    s << ", " << as_string (i) << ": " << t[i]->label;
    if (i==9) return s << ", ...";
  }
  return s;
}

void
edit_replace_rep::spell_next () {
  while (TRUE) {
    if (path_inf (spell_end_p, search_at))
      search_at= path ();
    if (nil (search_at)) {
      spell_end ();
      return;
    }
    search_end= test_spellable (search_at);
    if (search_end != search_at) {
      spell_write ("^" * spell_s);
      spell_t= spell_read ();
      if (arity (spell_t) == 0) {
	spell_end ();
	set_message ("Error: ispell does not respond", "spelling text");
	return;
      }
      while ((N(spell_t) > 0) && (spell_t[0] == ""))
	spell_t= spell_t (1, N(spell_t));
      if (N(spell_t) == 0) spell_t << "*";
      spell_t= parse_ispell (tree_to_verbatim (spell_t[0]));
      if ((spell_t[0] != "+") && (spell_t[0] != "*") && (spell_t[0] != "-")) {
	string mode= as_string (get_env_value (MODE, search_at));
	string lan = as_string (get_env_value (LANGUAGE (mode), search_at));
	if ((search_mode == mode) && (search_lan == lan)) {
	  if (((spell_t[0]=="&") || (spell_t[0]=="?")) && (N(spell_t)>=4)) {
	    tree t (TUPLE, spell_t[2]);
	    t << A (spell_t (4, N (spell_t)));
	    spell_t= t;
	  }
	  else spell_t= tree (TUPLE, "0");
	  set_selection (search_at, search_end);
	  notify_change (THE_SELECTION);
	  go_to (copy (search_end));
	  set_message (message_ispell (spell_t), "spelling error");
	  return;
	}
      }
    }
    step_horizontal (forward);
  }
}

/******************************************************************************
* Main spell checking commands
******************************************************************************/

void
edit_replace_rep::spell_start () {
  /********** get paths ***********/
  search_at   = start (et, path ());
  spell_end_p = end (et, path ());
  path spell_p= copy (tp);
  if (selection_active_normal ()) {
    get_selection (search_at, spell_end_p);
    spell_p= copy (search_at);
  }

  /********** determine dictionary to use ***********/
  search_mode = copy (as_string (get_env_value (MODE, spell_p)));
  search_lan  =
    copy (as_string (get_env_value (LANGUAGE (search_mode), spell_p)));
  string lan= search_lan;
  if (search_lan == "british") lan= "british";
  else if (search_lan == "dutch") lan= "nederlands";
  else if (search_lan == "french") lan= "francais";
  else if (search_lan == "german") lan= "deutsch";
  else if (search_lan == "portuguese") lan= "portugues";
  else if (search_lan == "spanish") {
    lan= "espanol";
    if (!file_exists ("/usr/lib/ispell", "espanol.hash"))
      if (file_exists ("/usr/lib/aspell", "spanish"))
	lan= "spanish";
  }
  else if (search_lan == "swedish") lan= "svenska";
  connection_declare ("ispell", "ispell -a -d " * lan);

  /********** connect ***********/
  if (connection_start ("ispell", "default") != "ok") {
    connection_stop ("ispell", "default");
    if (connection_start ("ispell", "default") != "ok") {
      set_message ("ispell is already busy", "correct text");
      return;
    }
  }
  the_input_mode()= INPUT_SPELL;
  forward         = TRUE;
  nr_replaced     = 0;
  spell_dicmod    = FALSE;

  /********** check banner ***********/
  tree t= spell_read ();
  if ((arity (t) == 0) || is_compound (t[0])) {
    spell_end ();
    set_message ("Error: ispell does not respond", "spelling text");
    return;
  }
  if ((N (t[0]->label) == 0) || (t[0]->label[0] != '@')) {
    spell_end ();
    set_message ("Error: no dictionary for#" * search_lan, "correct text");
    return;
  }

  /********** start spell checker ***********/
  spell_next ();
  if (nil (search_at))
    set_message ("no spelling errors found in text", "correct text");
}

void
edit_replace_rep::spell_end () {
  if (spell_dicmod) {
    connection_write ("ispell", "default", "#");
    tree t= spell_read ();
    set_message ("personal dictionary has been modified", "correct text");
  }
  else if (nr_replaced == 1)
    set_message ("one spelling error has been corrected", "correct text");
  else if (nr_replaced > 1)
    set_message (as_string (nr_replaced) *
		 "#spelling errors have been corrected", "correct text");
  else set_message ("spell checking complete", "correct text");
  cerr << '\a';
  connection_stop ("ispell", "default");
  set_input_normal ();
}

void
edit_replace_rep::spell_replace (string by) {
  go_to (copy (search_at));
  cut (search_at, search_end);
  insert_tree (copy (by));
  nr_replaced++;
  spell_next ();
}

bool
edit_replace_rep::spell_keypress (string s) {
  if ((s == "C-c") || (s == "C-g") || (s == "escape"))
    spell_end ();
  else if ((s == "a") || (s == "A")) {
    spell_write ("@" * spell_s);
    tree t= spell_read ();
    step_horizontal (forward);
    spell_next ();
  }
  else if ((s == "r") || (s == "R")) {
    SERVER (exec (string ("(interactive '(\"Replace by:\") 'spell-replace)")));
  }
  else if ((s == "i") || (s == "I")) {
    spell_write ("*" * spell_s);
    tree t= spell_read ();
    spell_dicmod= TRUE;
    step_horizontal (forward);
    spell_next ();
  }
  else if ((N(s)==1) && (is_digit (s[0])) && (s != "0")) {
    int i= as_int (s);
    int r= as_int (spell_t[0]);
    if (i <= r) {
      go_to (copy (search_end));
      cut (search_at, search_end);
      insert_tree (copy (spell_t[i]));
      search_at= copy (tp);
      nr_replaced++;
      spell_next ();
    }
    else if (i < N (spell_t)) {
      spell_write ("@" * spell_s);
      tree t= spell_read ();
      step_horizontal (forward);
      spell_next ();
    }
    else set_message (message_ispell (spell_t), "spelling error");
  }
  else set_message (message_ispell (spell_t), "spelling error");
  return TRUE;
}

#endmodule // code_edit_spell
