support for persistent user dictionaries;
authorwenzelm
Mon, 14 Apr 2014 10:55:56 +0200
changeset 56570 282f3b06fa86
parent 56569 1f9d706968c2
child 56571 f4635657d66f
support for persistent user dictionaries;
src/Tools/jEdit/src/spell_checker.scala
--- a/src/Tools/jEdit/src/spell_checker.scala	Mon Apr 14 09:28:42 2014 +0200
+++ b/src/Tools/jEdit/src/spell_checker.scala	Mon Apr 14 10:55:56 2014 +0200
@@ -15,6 +15,7 @@
 import scala.collection.mutable
 import scala.swing.ComboBox
 import scala.annotation.tailrec
+import scala.collection.immutable.{SortedSet, SortedMap}
 
 
 object Spell_Checker
@@ -55,19 +56,57 @@
   }
 
 
-  /* dictionary consisting of word list */
+  /* dictionary -- include/exclude word list */
+
+  private val USER_DICTIONARIES = Path.explode("$ISABELLE_HOME_USER/dictionaries")
 
   class Dictionary private [Spell_Checker](path: Path)
   {
     val lang = path.split_ext._1.base.implode
     override def toString: String = lang
 
-    def load_words: List[String] =
-      path.split_ext._2 match {
-        case "gz" => split_lines(File.read_gzip(path))
-        case "" => split_lines(File.read(path))
-        case ext => error("Bad file extension for dictionary " + path)
+    private val user_path = USER_DICTIONARIES + Path.basic(lang)
+
+    def load(): (List[String], List[String], List[String]) =
+    {
+      val main = split_lines(File.read_gzip(path))
+
+      val user_entries =
+        if (user_path.is_file)
+          for {
+            raw_line <- split_lines(File.read(user_path))
+            line = raw_line.trim
+            if line != "" && !line.startsWith("#")
+            entry =
+              Library.try_unprefix("-", line) match {
+                case Some(s) => (s, false)
+                case None => (line, true)
+              }
+          } yield entry
+        else Nil
+      val user = SortedMap.empty[String, Boolean] ++ user_entries
+      val include = user.toList.filter(_._2).map(_._1)
+      val exclude = user.toList.filterNot(_._2).map(_._1)
+
+      (main, include, exclude)
+    }
+
+    def save(include: List[String], exclude: List[String])
+    {
+      if (!include.isEmpty || !exclude.isEmpty || user_path.is_file) {
+        val header = """# Spell-checker dictionary
+#
+#   * each line contains at most one word
+#   * extra blanks are ignored
+#   * lines starting with "#" are ignored
+#   * lines starting with "-" indicate excluded words
+#   * later entries take precedence
+#
+"""
+        Isabelle_System.mkdirs(USER_DICTIONARIES)
+        File.write(user_path, header + cat_lines(include ::: exclude.map("-" + _)))
       }
+    }
   }
 
 
@@ -117,7 +156,11 @@
 {
   override def toString: String = dictionary.toString
 
-  private val dict =
+  private var dict = new Object
+  private var include_set = SortedSet.empty[String]
+  private var exclude_set = SortedSet.empty[String]
+
+  private def make_dict()
   {
     val factory_class = Class.forName("com.inet.jortho.DictionaryFactory")
     val factory_cons = factory_class.getConstructor()
@@ -126,18 +169,46 @@
 
     val add = factory_class.getDeclaredMethod("add", classOf[String])
     add.setAccessible(true)
-    dictionary.load_words.foreach(add.invoke(factory, _))
+
+    val (main, include, exclude) = dictionary.load()
+    include_set = SortedSet.empty[String] ++ include
+    exclude_set = SortedSet.empty[String] ++ exclude
+
+    for {
+      word <- main.iterator ++ include.iterator
+      if !exclude_set.contains(word)
+    } add.invoke(factory, word)
 
     val create = factory_class.getDeclaredMethod("create")
     create.setAccessible(true)
-    create.invoke(factory)
+    dict = create.invoke(factory)
+  }
+  make_dict()
+
+  def save()
+  {
+    dictionary.save(include_set.toList, exclude_set.toList)
   }
 
-  def add(word: String)
+  def include(word: String)
   {
+    include_set += word
+    exclude_set -= word
+
     val m = dict.getClass.getDeclaredMethod("add", classOf[String])
     m.setAccessible(true)
     m.invoke(dict, word)
+
+    save()
+  }
+
+  def exclude(word: String)
+  {
+    include_set -= word
+    exclude_set += word
+
+    save()
+    make_dict()
   }
 
   def contains(word: String): Boolean =