src/Pure/General/completion.scala
author wenzelm
Tue, 22 Jul 2014 11:46:34 +0200
changeset 58944 0f708666eb7c
parent 58931 e0e4ac981cf1
child 59180 85ec71012df8
permissions -rw-r--r--
no keyword completion within word context -- especially avoid its odd visual rendering;
wenzelm@57015
     1
/*  Title:      Pure/General/completion.scala
wenzelm@31775
     2
    Author:     Makarius
wenzelm@31775
     3
wenzelm@57029
     4
Semantic completion within the formal context (reported names).
wenzelm@57016
     5
Syntactic completion of keywords and symbols, with abbreviations
wenzelm@57334
     6
(based on language context).
wenzelm@57334
     7
*/
wenzelm@31775
     8
wenzelm@31775
     9
package isabelle
wenzelm@31775
    10
wenzelm@56960
    11
wenzelm@54474
    12
import scala.collection.immutable.SortedMap
wenzelm@31775
    13
import scala.util.parsing.combinator.RegexParsers
wenzelm@58931
    14
import scala.util.matching.Regex
wenzelm@54474
    15
import scala.math.Ordering
wenzelm@31775
    16
wenzelm@31775
    17
wenzelm@47498
    18
object Completion
wenzelm@31775
    19
{
wenzelm@57036
    20
  /** completion result **/
wenzelm@54412
    21
wenzelm@54433
    22
  sealed case class Item(
wenzelm@57034
    23
    range: Text.Range,
wenzelm@54433
    24
    original: String,
wenzelm@57008
    25
    name: String,
wenzelm@57320
    26
    description: List[String],
wenzelm@54433
    27
    replacement: String,
wenzelm@57008
    28
    move: Int,
wenzelm@54433
    29
    immediate: Boolean)
wenzelm@54412
    30
wenzelm@57256
    31
  object Result
wenzelm@57256
    32
  {
wenzelm@57256
    33
    def empty(range: Text.Range): Result = Result(range, "", false, Nil)
wenzelm@57517
    34
    def merge(history: History, result1: Option[Result], result2: Option[Result]): Option[Result] =
wenzelm@57517
    35
      (result1, result2) match {
wenzelm@57517
    36
        case (_, None) => result1
wenzelm@57517
    37
        case (None, _) => result2
wenzelm@57517
    38
        case (Some(res1), Some(res2)) =>
wenzelm@57517
    39
          if (res1.range != res2.range || res1.original != res2.original) result1
wenzelm@57517
    40
          else {
wenzelm@57517
    41
            val items = (res1.items ::: res2.items).sorted(history.ordering)
wenzelm@57517
    42
            Some(Result(res1.range, res1.original, false, items))
wenzelm@57517
    43
          }
wenzelm@57517
    44
      }
wenzelm@57256
    45
  }
wenzelm@57256
    46
wenzelm@57035
    47
  sealed case class Result(
wenzelm@57035
    48
    range: Text.Range,
wenzelm@57035
    49
    original: String,
wenzelm@57035
    50
    unique: Boolean,
wenzelm@57035
    51
    items: List[Item])
wenzelm@54462
    52
wenzelm@54412
    53
wenzelm@47498
    54
wenzelm@54474
    55
  /** persistent history **/
wenzelm@54474
    56
wenzelm@54474
    57
  private val COMPLETION_HISTORY = Path.explode("$ISABELLE_HOME_USER/etc/completion_history")
wenzelm@54474
    58
wenzelm@54474
    59
  object History
wenzelm@54474
    60
  {
wenzelm@54474
    61
    val empty: History = new History()
wenzelm@54474
    62
wenzelm@54474
    63
    def load(): History =
wenzelm@54474
    64
    {
wenzelm@54474
    65
      def ignore_error(msg: String): Unit =
wenzelm@58124
    66
        Output.warning("Ignoring bad content of file " + COMPLETION_HISTORY +
wenzelm@54474
    67
          (if (msg == "") "" else "\n" + msg))
wenzelm@54474
    68
wenzelm@54474
    69
      val content =
wenzelm@54474
    70
        if (COMPLETION_HISTORY.is_file) {
wenzelm@54474
    71
          try {
wenzelm@54474
    72
            import XML.Decode._
wenzelm@54474
    73
            list(pair(Symbol.decode_string, int))(
wenzelm@54474
    74
              YXML.parse_body(File.read(COMPLETION_HISTORY)))
wenzelm@54474
    75
          }
wenzelm@54474
    76
          catch {
wenzelm@54474
    77
            case ERROR(msg) => ignore_error(msg); Nil
wenzelm@54474
    78
            case _: XML.Error => ignore_error(""); Nil
wenzelm@54474
    79
          }
wenzelm@54474
    80
        }
wenzelm@54474
    81
        else Nil
wenzelm@54474
    82
      (empty /: content)(_ + _)
wenzelm@54474
    83
    }
wenzelm@54474
    84
  }
wenzelm@54474
    85
wenzelm@54474
    86
  final class History private(rep: SortedMap[String, Int] = SortedMap.empty)
wenzelm@54474
    87
  {
wenzelm@54474
    88
    override def toString: String = rep.mkString("Completion.History(", ",", ")")
wenzelm@54474
    89
wenzelm@58220
    90
    def frequency(name: String): Int =
wenzelm@58220
    91
      default_frequency(Symbol.encode(name)) getOrElse
wenzelm@58220
    92
      rep.getOrElse(name, 0)
wenzelm@54474
    93
wenzelm@54474
    94
    def + (entry: (String, Int)): History =
wenzelm@54474
    95
    {
wenzelm@54474
    96
      val (name, freq) = entry
wenzelm@57906
    97
      if (name == "") this
wenzelm@57906
    98
      else new History(rep + (name -> (frequency(name) + freq)))
wenzelm@54474
    99
    }
wenzelm@54474
   100
wenzelm@54474
   101
    def ordering: Ordering[Item] =
wenzelm@54474
   102
      new Ordering[Item] {
wenzelm@54474
   103
        def compare(item1: Item, item2: Item): Int =
wenzelm@57504
   104
          frequency(item2.name) compare frequency(item1.name)
wenzelm@57504
   105
      }
wenzelm@57504
   106
wenzelm@54474
   107
    def save()
wenzelm@54474
   108
    {
wenzelm@54474
   109
      Isabelle_System.mkdirs(COMPLETION_HISTORY.dir)
wenzelm@54474
   110
      File.write_backup(COMPLETION_HISTORY,
wenzelm@54474
   111
        {
wenzelm@54474
   112
          import XML.Encode._
wenzelm@54474
   113
          YXML.string_of_body(list(pair(Symbol.encode_string, int))(rep.toList))
wenzelm@54474
   114
        })
wenzelm@54474
   115
    }
wenzelm@54474
   116
  }
wenzelm@54474
   117
wenzelm@54474
   118
  class History_Variable
wenzelm@54474
   119
  {
wenzelm@54474
   120
    private var history = History.empty
wenzelm@54474
   121
    def value: History = synchronized { history }
wenzelm@54474
   122
wenzelm@54474
   123
    def load()
wenzelm@54474
   124
    {
wenzelm@54474
   125
      val h = History.load()
wenzelm@54474
   126
      synchronized { history = h }
wenzelm@54474
   127
    }
wenzelm@54474
   128
wenzelm@54474
   129
    def update(item: Item, freq: Int = 1): Unit = synchronized {
wenzelm@57008
   130
      history = history + (item.name -> freq)
wenzelm@54474
   131
    }
wenzelm@54474
   132
  }
wenzelm@54474
   133
wenzelm@54474
   134
wenzelm@57036
   135
wenzelm@57036
   136
  /** semantic completion **/
wenzelm@57036
   137
wenzelm@57515
   138
  object Semantic
wenzelm@57036
   139
  {
wenzelm@57036
   140
    object Info
wenzelm@57036
   141
    {
wenzelm@57515
   142
      def unapply(info: Text.Markup): Option[Text.Info[Semantic]] =
wenzelm@57036
   143
        info.info match {
wenzelm@57036
   144
          case XML.Elem(Markup(Markup.COMPLETION, _), body) =>
wenzelm@57036
   145
            try {
wenzelm@57036
   146
              val (total, names) =
wenzelm@57036
   147
              {
wenzelm@57036
   148
                import XML.Decode._
wenzelm@57319
   149
                pair(int, list(pair(string, pair(string, string))))(body)
wenzelm@57036
   150
              }
wenzelm@57515
   151
              Some(Text.Info(info.range, Names(total, names)))
wenzelm@57036
   152
            }
wenzelm@57036
   153
            catch { case _: XML.Error => None }
wenzelm@57256
   154
          case XML.Elem(Markup(Markup.NO_COMPLETION, _), _) =>
wenzelm@57515
   155
            Some(Text.Info(info.range, No_Completion))
wenzelm@57036
   156
          case _ => None
wenzelm@57036
   157
        }
wenzelm@57036
   158
    }
wenzelm@57036
   159
  }
wenzelm@57036
   160
wenzelm@57515
   161
  sealed abstract class Semantic
wenzelm@57517
   162
  case object No_Completion extends Semantic
wenzelm@57517
   163
  case class Names(total: Int, names: List[(String, (String, String))]) extends Semantic
wenzelm@57036
   164
  {
wenzelm@57036
   165
    def complete(
wenzelm@57515
   166
      range: Text.Range,
wenzelm@57515
   167
      history: Completion.History,
wenzelm@57515
   168
      do_decode: Boolean,
wenzelm@57036
   169
      original: String): Option[Completion.Result] =
wenzelm@57036
   170
    {
wenzelm@57319
   171
      def decode(s: String): String = if (do_decode) Symbol.decode(s) else s
wenzelm@57036
   172
      val items =
wenzelm@57036
   173
        for {
wenzelm@57319
   174
          (xname, (kind, name)) <- names
wenzelm@57319
   175
          xname1 = decode(xname)
wenzelm@57036
   176
          if xname1 != original
wenzelm@57319
   177
          (full_name, descr_name) =
wenzelm@57319
   178
            if (kind == "") (name, quote(decode(name)))
wenzelm@57942
   179
            else
wenzelm@58142
   180
             (Long_Name.qualify(kind, name),
wenzelm@57942
   181
              Word.implode(Word.explode('_', kind)) + " " + quote(decode(name)))
wenzelm@57320
   182
          description = List(xname1, "(" + descr_name + ")")
wenzelm@57319
   183
        } yield Item(range, original, full_name, description, xname1, 0, true)
wenzelm@57036
   184
wenzelm@57036
   185
      if (items.isEmpty) None
wenzelm@57036
   186
      else Some(Result(range, original, names.length == 1, items.sorted(history.ordering)))
wenzelm@57036
   187
    }
wenzelm@57036
   188
  }
wenzelm@57036
   189
wenzelm@57036
   190
wenzelm@57036
   191
wenzelm@57036
   192
  /** syntactic completion **/
wenzelm@57036
   193
wenzelm@57036
   194
  /* language context */
wenzelm@57036
   195
wenzelm@57091
   196
  object Language_Context
wenzelm@57036
   197
  {
wenzelm@57091
   198
    val outer = Language_Context("", true, false)
wenzelm@57091
   199
    val inner = Language_Context(Markup.Language.UNKNOWN, true, false)
wenzelm@57091
   200
    val ML_outer = Language_Context(Markup.Language.ML, false, true)
wenzelm@57091
   201
    val ML_inner = Language_Context(Markup.Language.ML, true, false)
wenzelm@57620
   202
    val SML_outer = Language_Context(Markup.Language.SML, false, false)
wenzelm@57036
   203
  }
wenzelm@57036
   204
wenzelm@57091
   205
  sealed case class Language_Context(language: String, symbols: Boolean, antiquotes: Boolean)
wenzelm@57036
   206
  {
wenzelm@57036
   207
    def is_outer: Boolean = language == ""
wenzelm@57036
   208
  }
wenzelm@57036
   209
wenzelm@57036
   210
wenzelm@57036
   211
  /* init */
wenzelm@57036
   212
wenzelm@57036
   213
  val empty: Completion = new Completion()
wenzelm@57036
   214
  def init(): Completion = empty.add_symbols()
wenzelm@57036
   215
wenzelm@57036
   216
wenzelm@57036
   217
  /* word parsers */
wenzelm@31775
   218
wenzelm@56957
   219
  private object Word_Parsers extends RegexParsers
wenzelm@31775
   220
  {
wenzelm@31775
   221
    override val whiteSpace = "".r
wenzelm@31775
   222
wenzelm@58931
   223
    private val symbol_regex: Regex = """\\<\^?[A-Za-z0-9_']+>""".r
wenzelm@58931
   224
    def is_symbol(s: CharSequence): Boolean = symbol_regex.pattern.matcher(s).matches
wenzelm@58931
   225
wenzelm@56957
   226
    private def reverse_symbol: Parser[String] = """>[A-Za-z0-9_']+\^?<\\""".r
wenzelm@56957
   227
    private def reverse_symb: Parser[String] = """[A-Za-z0-9_']{2,}\^?<\\""".r
wenzelm@56957
   228
    private def escape: Parser[String] = """[a-zA-Z0-9_']+\\""".r
wenzelm@56957
   229
wenzelm@57381
   230
    private val word_regex = "[a-zA-Z0-9_'.]+".r
wenzelm@56957
   231
    private def word: Parser[String] = word_regex
wenzelm@57381
   232
    private def word3: Parser[String] = "[a-zA-Z0-9_'.]{3,}".r
wenzelm@57338
   233
    private def underscores: Parser[String] = "_*".r
wenzelm@56957
   234
wenzelm@57155
   235
    def is_word(s: CharSequence): Boolean = word_regex.pattern.matcher(s).matches
wenzelm@57384
   236
    def is_word_char(c: Char): Boolean = Symbol.is_ascii_letdig(c) || c == '.'
wenzelm@31775
   237
wenzelm@57155
   238
    def read_symbol(in: CharSequence): Option[String] =
wenzelm@57155
   239
    {
wenzelm@57155
   240
      val reverse_in = new Library.Reverse(in)
wenzelm@57155
   241
      parse(reverse_symbol ^^ (_.reverse), reverse_in) match {
wenzelm@57155
   242
        case Success(result, _) => Some(result)
wenzelm@57155
   243
        case _ => None
wenzelm@57155
   244
      }
wenzelm@57155
   245
    }
wenzelm@57155
   246
wenzelm@57338
   247
    def read_word(explicit: Boolean, in: CharSequence): Option[(String, String)] =
wenzelm@31775
   248
    {
wenzelm@54459
   249
      val parse_word = if (explicit) word else word3
wenzelm@37112
   250
      val reverse_in = new Library.Reverse(in)
wenzelm@57338
   251
      val parser =
wenzelm@57338
   252
        (reverse_symbol | reverse_symb | escape) ^^ (x => (x.reverse, "")) |
wenzelm@57563
   253
        underscores ~ parse_word ~ opt("?") ^^
wenzelm@57563
   254
        { case x ~ y ~ z => (z.getOrElse("") + y.reverse, x) }
wenzelm@57338
   255
      parse(parser, reverse_in) match {
wenzelm@31775
   256
        case Success(result, _) => Some(result)
wenzelm@31775
   257
        case _ => None
wenzelm@31775
   258
      }
wenzelm@31775
   259
    }
wenzelm@31775
   260
  }
wenzelm@57008
   261
wenzelm@57008
   262
wenzelm@57008
   263
  /* abbreviations */
wenzelm@57008
   264
wenzelm@58003
   265
  private val caret_indicator = '\u0007'
wenzelm@57008
   266
  private val antiquote = "@{"
wenzelm@58220
   267
wenzelm@57008
   268
  private val default_abbrs =
wenzelm@58220
   269
    List("@{" -> "@{\u0007}",
wenzelm@58220
   270
      "`" -> "\\<close>",
wenzelm@58220
   271
      "`" -> "\\<open>",
wenzelm@58220
   272
      "`" -> "\\<open>\u0007\\<close>")
wenzelm@58220
   273
wenzelm@58220
   274
  private def default_frequency(name: String): Option[Int] =
wenzelm@58220
   275
    default_abbrs.iterator.map(_._2).zipWithIndex.find(_._1 == name).map(_._2)
wenzelm@31775
   276
}
wenzelm@31775
   277
wenzelm@47583
   278
final class Completion private(
wenzelm@57326
   279
  keywords: Map[String, Boolean] = Map.empty,
wenzelm@47499
   280
  words_lex: Scan.Lexicon = Scan.Lexicon.empty,
wenzelm@54455
   281
  words_map: Multi_Map[String, String] = Multi_Map.empty,
wenzelm@47499
   282
  abbrevs_lex: Scan.Lexicon = Scan.Lexicon.empty,
wenzelm@54455
   283
  abbrevs_map: Multi_Map[String, (String, String)] = Multi_Map.empty)
wenzelm@31775
   284
{
wenzelm@57325
   285
  /* keywords */
wenzelm@31775
   286
wenzelm@57328
   287
  private def is_symbol(name: String): Boolean = Symbol.names.isDefinedAt(name)
wenzelm@57328
   288
  private def is_keyword(name: String): Boolean = !is_symbol(name) && keywords.isDefinedAt(name)
wenzelm@57343
   289
  private def is_keyword_template(name: String, template: Boolean): Boolean =
wenzelm@57343
   290
    is_keyword(name) && keywords(name) == template
wenzelm@57326
   291
wenzelm@57326
   292
  def + (keyword: String, template: String): Completion =
wenzelm@47498
   293
    new Completion(
wenzelm@57326
   294
      keywords + (keyword -> (keyword != template)),
wenzelm@47498
   295
      words_lex + keyword,
wenzelm@57326
   296
      words_map + (keyword -> template),
wenzelm@47498
   297
      abbrevs_lex,
wenzelm@47498
   298
      abbrevs_map)
wenzelm@31775
   299
wenzelm@40779
   300
  def + (keyword: String): Completion = this + (keyword, keyword)
wenzelm@40779
   301
wenzelm@57325
   302
wenzelm@57325
   303
  /* symbols with abbreviations */
wenzelm@57325
   304
wenzelm@47498
   305
  private def add_symbols(): Completion =
wenzelm@31775
   306
  {
wenzelm@31775
   307
    val words =
wenzelm@56957
   308
      (for ((x, _) <- Symbol.names.toList) yield (x, x)) :::
wenzelm@56957
   309
      (for ((x, y) <- Symbol.names.toList) yield ("\\" + y, x)) :::
wenzelm@56957
   310
      (for ((x, y) <- Symbol.abbrevs.toList if Completion.Word_Parsers.is_word(y)) yield (y, x))
wenzelm@31775
   311
wenzelm@57008
   312
    val symbol_abbrs =
wenzelm@57008
   313
      (for ((x, y) <- Symbol.abbrevs.iterator if !Completion.Word_Parsers.is_word(y))
wenzelm@57008
   314
        yield (y, x)).toList
wenzelm@57008
   315
wenzelm@31775
   316
    val abbrs =
wenzelm@57933
   317
      for ((a, b) <- symbol_abbrs ::: Completion.default_abbrs)
wenzelm@57008
   318
        yield (a.reverse, (a, b))
wenzelm@31775
   319
wenzelm@47498
   320
    new Completion(
wenzelm@56957
   321
      keywords,
wenzelm@47498
   322
      words_lex ++ words.map(_._1),
wenzelm@47498
   323
      words_map ++ words,
wenzelm@47498
   324
      abbrevs_lex ++ abbrs.map(_._1),
wenzelm@47498
   325
      abbrevs_map ++ abbrs)
wenzelm@31775
   326
  }
wenzelm@31775
   327
wenzelm@31775
   328
wenzelm@31775
   329
  /* complete */
wenzelm@31775
   330
wenzelm@54474
   331
  def complete(
wenzelm@54474
   332
    history: Completion.History,
wenzelm@57319
   333
    do_decode: Boolean,
wenzelm@54474
   334
    explicit: Boolean,
wenzelm@57155
   335
    start: Text.Offset,
wenzelm@56957
   336
    text: CharSequence,
wenzelm@57155
   337
    caret: Int,
wenzelm@57091
   338
    language_context: Completion.Language_Context): Option[Completion.Result] =
wenzelm@31775
   339
  {
wenzelm@57319
   340
    def decode(s: String): String = if (do_decode) Symbol.decode(s) else s
wenzelm@57155
   341
    val length = text.length
wenzelm@57155
   342
wenzelm@56957
   343
    val abbrevs_result =
wenzelm@57155
   344
    {
wenzelm@57155
   345
      val reverse_in = new Library.Reverse(text.subSequence(0, caret))
wenzelm@57155
   346
      Scan.Parsers.parse(Scan.Parsers.literal(abbrevs_lex), reverse_in) match {
wenzelm@57008
   347
        case Scan.Parsers.Success(reverse_a, _) =>
wenzelm@57008
   348
          val abbrevs = abbrevs_map.get_list(reverse_a)
wenzelm@57008
   349
          abbrevs match {
wenzelm@57008
   350
            case Nil => None
wenzelm@57008
   351
            case (a, _) :: _ =>
wenzelm@57008
   352
              val ok =
wenzelm@57091
   353
                if (a == Completion.antiquote) language_context.antiquotes
wenzelm@58931
   354
                else
wenzelm@58931
   355
                  language_context.symbols ||
wenzelm@58931
   356
                  Completion.default_abbrs.exists(_._1 == a) ||
wenzelm@58931
   357
                  Completion.Word_Parsers.is_symbol(a)
wenzelm@58930
   358
              if (ok) Some((a, abbrevs))
wenzelm@57155
   359
              else None
wenzelm@57008
   360
          }
wenzelm@57008
   361
        case _ => None
wenzelm@57008
   362
      }
wenzelm@57155
   363
    }
wenzelm@56957
   364
wenzelm@56957
   365
    val words_result =
wenzelm@57324
   366
      if (abbrevs_result.isDefined) None
wenzelm@57324
   367
      else {
wenzelm@58944
   368
        val word_context =
wenzelm@58944
   369
          caret < length && Completion.Word_Parsers.is_word_char(text.charAt(caret))
wenzelm@57338
   370
        val result =
wenzelm@58930
   371
          Completion.Word_Parsers.read_symbol(text.subSequence(0, caret)) match {
wenzelm@57338
   372
            case Some(symbol) => Some((symbol, ""))
wenzelm@58930
   373
            case None => Completion.Word_Parsers.read_word(explicit, text.subSequence(0, caret))
wenzelm@57334
   374
          }
wenzelm@57338
   375
        result.map(
wenzelm@57334
   376
          {
wenzelm@57338
   377
            case (word, underscores) =>
wenzelm@57338
   378
              val complete_words = words_lex.completions(word)
wenzelm@57338
   379
              val full_word = word + underscores
wenzelm@57338
   380
              val completions =
wenzelm@57343
   381
                if (complete_words.contains(full_word) && is_keyword_template(full_word, false)) Nil
wenzelm@57338
   382
                else
wenzelm@57338
   383
                  for {
wenzelm@57338
   384
                    complete_word <- complete_words
wenzelm@57338
   385
                    ok =
wenzelm@58944
   386
                      if (is_keyword(complete_word)) !word_context && language_context.is_outer
wenzelm@57338
   387
                      else language_context.symbols
wenzelm@57338
   388
                    if ok
wenzelm@57338
   389
                    completion <- words_map.get_list(complete_word)
wenzelm@57338
   390
                  } yield (complete_word, completion)
wenzelm@58930
   391
              ((full_word, completions))
wenzelm@57334
   392
          })
wenzelm@54412
   393
      }
wenzelm@56957
   394
wenzelm@57324
   395
    (abbrevs_result orElse words_result) match {
wenzelm@58930
   396
      case Some((original, completions)) if !completions.isEmpty =>
wenzelm@58930
   397
        val range = Text.Range(- original.length, 0) + caret + start
wenzelm@57327
   398
        val immediate =
wenzelm@57327
   399
          explicit ||
wenzelm@57327
   400
            (!Completion.Word_Parsers.is_word(original) &&
wenzelm@57327
   401
              Character.codePointCount(original, 0, original.length) > 1)
wenzelm@57335
   402
        val unique = completions.length == 1
wenzelm@57327
   403
wenzelm@57327
   404
        val items =
wenzelm@57327
   405
          for {
wenzelm@57335
   406
            (complete_word, name0) <- completions
wenzelm@57335
   407
            name1 = decode(name0)
wenzelm@57327
   408
            if name1 != original
wenzelm@57327
   409
            (s1, s2) =
wenzelm@57327
   410
              space_explode(Completion.caret_indicator, name1) match {
wenzelm@57327
   411
                case List(s1, s2) => (s1, s2)
wenzelm@57327
   412
                case _ => (name1, "")
wenzelm@57327
   413
              }
wenzelm@57327
   414
            move = - s2.length
wenzelm@57327
   415
            description =
wenzelm@57327
   416
              if (is_symbol(name0)) {
wenzelm@57327
   417
                if (name0 == name1) List(name0)
wenzelm@57327
   418
                else List(name1, "(symbol " + quote(name0) + ")")
wenzelm@57327
   419
              }
wenzelm@57343
   420
              else if (is_keyword_template(complete_word, true))
wenzelm@57335
   421
                List(name1, "(template " + quote(complete_word) + ")")
wenzelm@57335
   422
              else if (move != 0) List(name1, "(template)")
wenzelm@57335
   423
              else if (is_keyword(complete_word)) List(name1, "(keyword)")
wenzelm@57327
   424
              else List(name1)
wenzelm@57327
   425
          }
wenzelm@57327
   426
          yield Completion.Item(range, original, name1, description, s1 + s2, move, immediate)
wenzelm@57327
   427
wenzelm@57327
   428
        if (items.isEmpty) None
wenzelm@57516
   429
        else
wenzelm@57516
   430
          Some(Completion.Result(range, original, unique,
wenzelm@57516
   431
            items.sortBy(_.name).sorted(history.ordering)))
wenzelm@57327
   432
wenzelm@57334
   433
      case _ => None
wenzelm@31777
   434
    }
wenzelm@31775
   435
  }
wenzelm@31775
   436
}