# HG changeset patch # User wenzelm # Date 1347623536 -7200 # Node ID 6e0c0ffb6ec7b75fdc88b2c9223127d933a93a01 # Parent 7d1af0a6e7974f5d0f8fafcdfa49924aafb64090 more static handling of rendering options; diff -r 7d1af0a6e797 -r 6e0c0ffb6ec7 src/Tools/jEdit/src/document_view.scala --- a/src/Tools/jEdit/src/document_view.scala Fri Sep 14 12:46:33 2012 +0200 +++ b/src/Tools/jEdit/src/document_view.scala Fri Sep 14 13:52:16 2012 +0200 @@ -232,25 +232,26 @@ else Isabelle.buffer_lock(model.buffer) { val snapshot = model.snapshot() + val rendering = Isabelle_Rendering(snapshot, Isabelle.options.value) if (control) init_popup(snapshot, x, y) def update_range[A]( - rendering: (Document.Snapshot, Text.Range) => Option[Text.Info[A]], + rendering: Text.Range => Option[Text.Info[A]], info: Option[Text.Info[A]]): Option[Text.Info[A]] = { for (Text.Info(range, _) <- info) invalidate_range(range) val new_info = if (control) { val offset = text_area.xyToOffset(x, y) - rendering(snapshot, Text.Range(offset, offset + 1)) + rendering(Text.Range(offset, offset + 1)) } else None for (Text.Info(range, _) <- info) invalidate_range(range) new_info } - _highlight_range = update_range(Isabelle_Rendering.subexp, _highlight_range) - _hyperlink_range = update_range(Isabelle_Rendering.hyperlink, _hyperlink_range) + _highlight_range = update_range(rendering.subexp, _highlight_range) + _hyperlink_range = update_range(rendering.hyperlink, _hyperlink_range) } } } @@ -265,12 +266,12 @@ override def getToolTipText(x: Int, y: Int): String = { robust_body(null: String) { - val snapshot = model.snapshot() + val rendering = Isabelle_Rendering(model.snapshot(), Isabelle.options.value) val offset = text_area.xyToOffset(x, y) val range = Text.Range(offset, offset + 1) val tip = - if (control) Isabelle_Rendering.tooltip(snapshot, range) - else Isabelle_Rendering.tooltip_message(snapshot, range) + if (control) rendering.tooltip(range) + else rendering.tooltip_message(range) tip.map(Isabelle.tooltip(_)) getOrElse null } } @@ -292,13 +293,14 @@ if (gutter.isSelectionAreaEnabled && !gutter.isExpanded && width >= 12 && line_height >= 12) { Isabelle.buffer_lock(model.buffer) { - val snapshot = model.snapshot() + val rendering = Isabelle_Rendering(model.snapshot(), Isabelle.options.value) + for (i <- 0 until physical_lines.length) { if (physical_lines(i) != -1) { val line_range = proper_line_range(start(i), end(i)) // gutter icons - Isabelle_Rendering.gutter_message(snapshot, line_range) match { + rendering.gutter_message(line_range) match { case Some(icon) => val x0 = (FOLD_MARKER_SIZE + width - border_width - icon.getIconWidth) max 10 val y0 = y + i * line_height + (((line_height - icon.getIconHeight) / 2) max 0) diff -r 7d1af0a6e797 -r 6e0c0ffb6ec7 src/Tools/jEdit/src/isabelle_rendering.scala --- a/src/Tools/jEdit/src/isabelle_rendering.scala Fri Sep 14 12:46:33 2012 +0200 +++ b/src/Tools/jEdit/src/isabelle_rendering.scala Fri Sep 14 13:52:16 2012 +0200 @@ -20,326 +20,22 @@ object Isabelle_Rendering { - /* physical rendering */ + def apply(snapshot: Document.Snapshot, options: Options): Isabelle_Rendering = + new Isabelle_Rendering(snapshot, options) - def color_value(s: String): Color = Color_Value(Isabelle.options.value.string(s)) + + /* physical rendering */ private val writeln_pri = 1 private val warning_pri = 2 private val legacy_pri = 3 private val error_pri = 4 - - /* command overview */ - - def overview_color(snapshot: Document.Snapshot, range: Text.Range): Option[Color] = - { - if (snapshot.is_outdated) None - else { - val results = - snapshot.cumulate_markup[(Protocol.Status, Int)]( - range, (Protocol.Status.init, 0), - Some(Protocol.command_status_markup + Isabelle_Markup.WARNING + Isabelle_Markup.ERROR), - { - case ((status, pri), Text.Info(_, XML.Elem(markup, _))) => - if (markup.name == Isabelle_Markup.WARNING) (status, pri max warning_pri) - else if (markup.name == Isabelle_Markup.ERROR) (status, pri max error_pri) - else (Protocol.command_status(status, markup), pri) - }) - if (results.isEmpty) None - else { - val (status, pri) = - ((Protocol.Status.init, 0) /: results) { - case ((s1, p1), Text.Info(_, (s2, p2))) => (s1 + s2, p1 max p2) } - - if (pri == warning_pri) Some(color_value("warning_color")) - else if (pri == error_pri) Some(color_value("error_color")) - else if (status.is_unprocessed) Some(color_value("unprocessed_color")) - else if (status.is_running) Some(color_value("running_color")) - else if (status.is_failed) Some(color_value("error_color")) - else None - } - } - } - - - /* markup selectors */ - - private val subexp_include = - Set(Isabelle_Markup.SORT, Isabelle_Markup.TYP, Isabelle_Markup.TERM, Isabelle_Markup.PROP, - Isabelle_Markup.ML_TYPING, Isabelle_Markup.TOKEN_RANGE, Isabelle_Markup.ENTITY, - Isabelle_Markup.PATH, Isabelle_Markup.TYPING, Isabelle_Markup.FREE, Isabelle_Markup.SKOLEM, - Isabelle_Markup.BOUND, Isabelle_Markup.VAR, Isabelle_Markup.TFREE, Isabelle_Markup.TVAR, - Isabelle_Markup.ML_SOURCE, Isabelle_Markup.DOCUMENT_SOURCE) - - def subexp(snapshot: Document.Snapshot, range: Text.Range): Option[Text.Info[Color]] = - { - snapshot.select_markup(range, Some(subexp_include), - { - case Text.Info(info_range, XML.Elem(Markup(name, _), _)) if subexp_include(name) => - Text.Info(snapshot.convert(info_range), color_value("subexp_color")) - }) match { case Text.Info(_, info) #:: _ => Some(info) case _ => None } - } - - - private val hyperlink_include = Set(Isabelle_Markup.ENTITY, Isabelle_Markup.PATH) - - def hyperlink(snapshot: Document.Snapshot, range: Text.Range): Option[Text.Info[Hyperlink]] = - { - snapshot.cumulate_markup[List[Text.Info[Hyperlink]]](range, Nil, Some(hyperlink_include), - { - case (links, Text.Info(info_range, XML.Elem(Isabelle_Markup.Path(name), _))) - if Path.is_ok(name) => - val jedit_file = Isabelle.thy_load.append(snapshot.node_name.dir, Path.explode(name)) - Text.Info(snapshot.convert(info_range), Hyperlink(jedit_file, 0, 0)) :: links - - case (links, Text.Info(info_range, XML.Elem(Markup(Isabelle_Markup.ENTITY, props), _))) - if (props.find( - { case (Markup.KIND, Isabelle_Markup.ML_OPEN) => true - case (Markup.KIND, Isabelle_Markup.ML_STRUCT) => true - case _ => false }).isEmpty) => - - props match { - case Position.Line_File(line, name) if Path.is_ok(name) => - Isabelle_System.source_file(Path.explode(name)) match { - case Some(path) => - val jedit_file = Isabelle_System.platform_path(path) - Text.Info(snapshot.convert(info_range), Hyperlink(jedit_file, line, 0)) :: links - case None => links - } - - case Position.Id_Offset(id, offset) if !snapshot.is_outdated => - snapshot.state.find_command(snapshot.version, id) match { - case Some((node, command)) => - val sources = - node.commands.iterator.takeWhile(_ != command).map(_.source) ++ - Iterator.single(command.source(Text.Range(0, command.decode(offset)))) - val (line, column) = ((1, 1) /: sources)(Symbol.advance_line_column) - Text.Info(snapshot.convert(info_range), - Hyperlink(command.node_name.node, line, column)) :: links - case None => links - } - - case _ => links - } - }) match { case Text.Info(_, info :: _) #:: _ => Some(info) case _ => None } - } - - - private def tooltip_text(msg: XML.Tree): String = - Pretty.string_of(List(msg), margin = Isabelle.options.int("jedit_tooltip_margin")) - - def tooltip_message(snapshot: Document.Snapshot, range: Text.Range): Option[String] = - { - val msgs = - snapshot.cumulate_markup[SortedMap[Long, String]](range, SortedMap.empty, - Some(Set(Isabelle_Markup.WRITELN, Isabelle_Markup.WARNING, Isabelle_Markup.ERROR, - Isabelle_Markup.BAD)), - { - case (msgs, Text.Info(_, msg @ - XML.Elem(Markup(markup, Isabelle_Markup.Serial(serial)), _))) - if markup == Isabelle_Markup.WRITELN || - markup == Isabelle_Markup.WARNING || - markup == Isabelle_Markup.ERROR => - msgs + (serial -> tooltip_text(msg)) - case (msgs, Text.Info(_, msg @ XML.Elem(Markup(Isabelle_Markup.BAD, _), body))) - if !body.isEmpty => msgs + (Document.new_id() -> tooltip_text(msg)) - }).toList.flatMap(_.info) - if (msgs.isEmpty) None else Some(cat_lines(msgs.iterator.map(_._2))) - } - - - private val tooltips: Map[String, String] = - Map( - Isabelle_Markup.SORT -> "sort", - Isabelle_Markup.TYP -> "type", - Isabelle_Markup.TERM -> "term", - Isabelle_Markup.PROP -> "proposition", - Isabelle_Markup.TOKEN_RANGE -> "inner syntax token", - Isabelle_Markup.FREE -> "free variable", - Isabelle_Markup.SKOLEM -> "skolem variable", - Isabelle_Markup.BOUND -> "bound variable", - Isabelle_Markup.VAR -> "schematic variable", - Isabelle_Markup.TFREE -> "free type variable", - Isabelle_Markup.TVAR -> "schematic type variable", - Isabelle_Markup.ML_SOURCE -> "ML source", - Isabelle_Markup.DOCUMENT_SOURCE -> "document source") - - private val tooltip_elements = - Set(Isabelle_Markup.ENTITY, Isabelle_Markup.TYPING, Isabelle_Markup.ML_TYPING, - Isabelle_Markup.PATH) ++ tooltips.keys - - private def string_of_typing(kind: String, body: XML.Body): String = - Pretty.string_of(List(Pretty.block(XML.Text(kind) :: Pretty.Break(1) :: body)), - margin = Isabelle.options.int("jedit_tooltip_margin")) - - def tooltip(snapshot: Document.Snapshot, range: Text.Range): Option[String] = - { - def add(prev: Text.Info[List[(Boolean, String)]], r: Text.Range, p: (Boolean, String)) = - if (prev.range == r) Text.Info(r, p :: prev.info) else Text.Info(r, List(p)) - - val tips = - snapshot.cumulate_markup[Text.Info[(List[(Boolean, String)])]]( - range, Text.Info(range, Nil), Some(tooltip_elements), - { - case (prev, Text.Info(r, XML.Elem(Isabelle_Markup.Entity(kind, name), _))) => - val kind1 = space_explode('_', kind).mkString(" ") - add(prev, r, (true, kind1 + " " + quote(name))) - case (prev, Text.Info(r, XML.Elem(Isabelle_Markup.Path(name), _))) - if Path.is_ok(name) => - val jedit_file = Isabelle.thy_load.append(snapshot.node_name.dir, Path.explode(name)) - add(prev, r, (true, "file " + quote(jedit_file))) - case (prev, Text.Info(r, XML.Elem(Markup(Isabelle_Markup.TYPING, _), body))) => - add(prev, r, (true, string_of_typing("::", body))) - case (prev, Text.Info(r, XML.Elem(Markup(Isabelle_Markup.ML_TYPING, _), body))) => - add(prev, r, (false, string_of_typing("ML:", body))) - case (prev, Text.Info(r, XML.Elem(Markup(name, _), _))) - if tooltips.isDefinedAt(name) => add(prev, r, (true, tooltips(name))) - }).toList.flatMap(_.info.info) - - val all_tips = - (tips.filter(_._1) ++ tips.filter(!_._1).lastOption.toList).map(_._2) - if (all_tips.isEmpty) None else Some(cat_lines(all_tips)) - } - - private val gutter_icons = Map( warning_pri -> Isabelle.load_icon("16x16/status/dialog-information.png"), legacy_pri -> Isabelle.load_icon("16x16/status/dialog-warning.png"), error_pri -> Isabelle.load_icon("16x16/status/dialog-error.png")) - def gutter_message(snapshot: Document.Snapshot, range: Text.Range): Option[Icon] = - { - val results = - snapshot.cumulate_markup[Int](range, 0, - Some(Set(Isabelle_Markup.WARNING, Isabelle_Markup.ERROR)), - { - case (pri, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.WARNING, _), body))) => - body match { - case List(XML.Elem(Markup(Isabelle_Markup.LEGACY, _), _)) => pri max legacy_pri - case _ => pri max warning_pri - } - case (pri, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.ERROR, _), _))) => - pri max error_pri - }) - val pri = (0 /: results) { case (p1, Text.Info(_, p2)) => p1 max p2 } - gutter_icons.get(pri) - } - - - def squiggly_underline(snapshot: Document.Snapshot, range: Text.Range): Stream[Text.Info[Color]] = - { - val squiggly_colors = Map( - writeln_pri -> color_value("writeln_color"), - warning_pri -> color_value("warning_color"), - error_pri -> color_value("error_color")) - - val results = - snapshot.cumulate_markup[Int](range, 0, - Some(Set(Isabelle_Markup.WRITELN, Isabelle_Markup.WARNING, Isabelle_Markup.ERROR)), - { - case (pri, Text.Info(_, XML.Elem(Markup(name, _), _))) => - name match { - case Isabelle_Markup.WRITELN => pri max writeln_pri - case Isabelle_Markup.WARNING => pri max warning_pri - case Isabelle_Markup.ERROR => pri max error_pri - case _ => pri - } - }) - for { - Text.Info(r, pri) <- results - color <- squiggly_colors.get(pri) - } yield Text.Info(r, color) - } - - - def background1(snapshot: Document.Snapshot, range: Text.Range): Stream[Text.Info[Color]] = - { - if (snapshot.is_outdated) Stream(Text.Info(range, color_value("outdated_color"))) - else - for { - Text.Info(r, result) <- - snapshot.cumulate_markup[(Option[Protocol.Status], Option[Color])]( - range, (Some(Protocol.Status.init), None), - Some(Protocol.command_status_markup + Isabelle_Markup.BAD + Isabelle_Markup.HILITE), - { - case (((Some(status), color), Text.Info(_, XML.Elem(markup, _)))) - if (Protocol.command_status_markup(markup.name)) => - (Some(Protocol.command_status(status, markup)), color) - case (_, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.BAD, _), _))) => - (None, Some(color_value("bad_color"))) - case (_, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.HILITE, _), _))) => - (None, Some(color_value("hilite_color"))) - }) - color <- - (result match { - case (Some(status), opt_color) => - if (status.is_unprocessed) Some(color_value("unprocessed1_color")) - else if (status.is_running) Some(color_value("running1_color")) - else opt_color - case (_, opt_color) => opt_color - }) - } yield Text.Info(r, color) - } - - - def background2(snapshot: Document.Snapshot, range: Text.Range): Stream[Text.Info[Color]] = - snapshot.select_markup(range, - Some(Set(Isabelle_Markup.TOKEN_RANGE)), - { - case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.TOKEN_RANGE, _), _)) => - color_value("light_color") - }) - - - def foreground(snapshot: Document.Snapshot, range: Text.Range): Stream[Text.Info[Color]] = - snapshot.select_markup(range, - Some(Set(Isabelle_Markup.STRING, Isabelle_Markup.ALTSTRING, Isabelle_Markup.VERBATIM)), - { - case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.STRING, _), _)) => - color_value("quoted_color") - case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.ALTSTRING, _), _)) => - color_value("quoted_color") - case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.VERBATIM, _), _)) => - color_value("quoted_color") - }) - - - def text_color(snapshot: Document.Snapshot, range: Text.Range, color: Color) - : Stream[Text.Info[Color]] = - { - val text_colors: Map[String, Color] = Map( - Isabelle_Markup.STRING -> Color.BLACK, - Isabelle_Markup.ALTSTRING -> Color.BLACK, - Isabelle_Markup.VERBATIM -> Color.BLACK, - Isabelle_Markup.LITERAL -> color_value("keyword1_color"), - Isabelle_Markup.DELIMITER -> Color.BLACK, - Isabelle_Markup.TFREE -> color_value("tfree_color"), - Isabelle_Markup.TVAR -> color_value("tvar_color"), - Isabelle_Markup.FREE -> color_value("free_color"), - Isabelle_Markup.SKOLEM -> color_value("skolem_color"), - Isabelle_Markup.BOUND -> color_value("bound_color"), - Isabelle_Markup.VAR -> color_value("var_color"), - Isabelle_Markup.INNER_STRING -> color_value("inner_quoted_color"), - Isabelle_Markup.INNER_COMMENT -> color_value("inner_comment_color"), - Isabelle_Markup.DYNAMIC_FACT -> color_value("dynamic_color"), - Isabelle_Markup.ML_KEYWORD -> color_value("keyword1_color"), - Isabelle_Markup.ML_DELIMITER -> Color.BLACK, - Isabelle_Markup.ML_NUMERAL -> color_value("inner_numeral_color"), - Isabelle_Markup.ML_CHAR -> color_value("inner_quoted_color"), - Isabelle_Markup.ML_STRING -> color_value("inner_quoted_color"), - Isabelle_Markup.ML_COMMENT -> color_value("inner_comment_color"), - Isabelle_Markup.ANTIQ -> color_value("antiquotation_color")) - - val text_color_elements = Set.empty[String] ++ text_colors.keys - - snapshot.cumulate_markup(range, color, Some(text_color_elements), - { - case (_, Text.Info(_, XML.Elem(Markup(m, _), _))) - if text_colors.isDefinedAt(m) => text_colors(m) - }) - } - /* token markup -- text styles */ @@ -382,3 +78,346 @@ else if (token.is_operator) JEditToken.OPERATOR else token_style(token.kind) } + + +class Isabelle_Rendering private(val snapshot: Document.Snapshot, val options: Options) +{ + /* colors */ + + def color_value(s: String): Color = Color_Value(options.string(s)) + + val outdated_color = color_value("outdated_color") + val unprocessed_color = color_value("unprocessed_color") + val unprocessed1_color = color_value("unprocessed1_color") + val running_color = color_value("running_color") + val running1_color = color_value("running1_color") + val light_color = color_value("light_color") + val writeln_color = color_value("writeln_color") + val warning_color = color_value("warning_color") + val error_color = color_value("error_color") + val error1_color = color_value("error1_color") + val bad_color = color_value("bad_color") + val hilite_color = color_value("hilite_color") + val quoted_color = color_value("quoted_color") + val subexp_color = color_value("subexp_color") + val hyperlink_color = color_value("hyperlink_color") + val keyword1_color = color_value("keyword1_color") + val keyword2_color = color_value("keyword2_color") + + val tfree_color = color_value("tfree_color") + val tvar_color = color_value("tvar_color") + val free_color = color_value("free_color") + val skolem_color = color_value("skolem_color") + val bound_color = color_value("bound_color") + val var_color = color_value("var_color") + val inner_numeral_color = color_value("inner_numeral_color") + val inner_quoted_color = color_value("inner_quoted_color") + val inner_comment_color = color_value("inner_comment_color") + val antiquotation_color = color_value("antiquotation_color") + val dynamic_color = color_value("dynamic_color") + + + /* command overview */ + + def overview_color(range: Text.Range): Option[Color] = + { + if (snapshot.is_outdated) None + else { + val results = + snapshot.cumulate_markup[(Protocol.Status, Int)]( + range, (Protocol.Status.init, 0), + Some(Protocol.command_status_markup + Isabelle_Markup.WARNING + Isabelle_Markup.ERROR), + { + case ((status, pri), Text.Info(_, XML.Elem(markup, _))) => + if (markup.name == Isabelle_Markup.WARNING) + (status, pri max Isabelle_Rendering.warning_pri) + else if (markup.name == Isabelle_Markup.ERROR) + (status, pri max Isabelle_Rendering.error_pri) + else (Protocol.command_status(status, markup), pri) + }) + if (results.isEmpty) None + else { + val (status, pri) = + ((Protocol.Status.init, 0) /: results) { + case ((s1, p1), Text.Info(_, (s2, p2))) => (s1 + s2, p1 max p2) } + + if (pri == Isabelle_Rendering.warning_pri) Some(warning_color) + else if (pri == Isabelle_Rendering.error_pri) Some(error_color) + else if (status.is_unprocessed) Some(unprocessed_color) + else if (status.is_running) Some(running_color) + else if (status.is_failed) Some(error_color) + else None + } + } + } + + + /* markup selectors */ + + private val subexp_include = + Set(Isabelle_Markup.SORT, Isabelle_Markup.TYP, Isabelle_Markup.TERM, Isabelle_Markup.PROP, + Isabelle_Markup.ML_TYPING, Isabelle_Markup.TOKEN_RANGE, Isabelle_Markup.ENTITY, + Isabelle_Markup.PATH, Isabelle_Markup.TYPING, Isabelle_Markup.FREE, Isabelle_Markup.SKOLEM, + Isabelle_Markup.BOUND, Isabelle_Markup.VAR, Isabelle_Markup.TFREE, Isabelle_Markup.TVAR, + Isabelle_Markup.ML_SOURCE, Isabelle_Markup.DOCUMENT_SOURCE) + + def subexp(range: Text.Range): Option[Text.Info[Color]] = + { + snapshot.select_markup(range, Some(subexp_include), + { + case Text.Info(info_range, XML.Elem(Markup(name, _), _)) if subexp_include(name) => + Text.Info(snapshot.convert(info_range), subexp_color) + }) match { case Text.Info(_, info) #:: _ => Some(info) case _ => None } + } + + + private val hyperlink_include = Set(Isabelle_Markup.ENTITY, Isabelle_Markup.PATH) + + def hyperlink(range: Text.Range): Option[Text.Info[Hyperlink]] = + { + snapshot.cumulate_markup[List[Text.Info[Hyperlink]]](range, Nil, Some(hyperlink_include), + { + case (links, Text.Info(info_range, XML.Elem(Isabelle_Markup.Path(name), _))) + if Path.is_ok(name) => + val jedit_file = Isabelle.thy_load.append(snapshot.node_name.dir, Path.explode(name)) + Text.Info(snapshot.convert(info_range), Hyperlink(jedit_file, 0, 0)) :: links + + case (links, Text.Info(info_range, XML.Elem(Markup(Isabelle_Markup.ENTITY, props), _))) + if (props.find( + { case (Markup.KIND, Isabelle_Markup.ML_OPEN) => true + case (Markup.KIND, Isabelle_Markup.ML_STRUCT) => true + case _ => false }).isEmpty) => + + props match { + case Position.Line_File(line, name) if Path.is_ok(name) => + Isabelle_System.source_file(Path.explode(name)) match { + case Some(path) => + val jedit_file = Isabelle_System.platform_path(path) + Text.Info(snapshot.convert(info_range), Hyperlink(jedit_file, line, 0)) :: links + case None => links + } + + case Position.Id_Offset(id, offset) if !snapshot.is_outdated => + snapshot.state.find_command(snapshot.version, id) match { + case Some((node, command)) => + val sources = + node.commands.iterator.takeWhile(_ != command).map(_.source) ++ + Iterator.single(command.source(Text.Range(0, command.decode(offset)))) + val (line, column) = ((1, 1) /: sources)(Symbol.advance_line_column) + Text.Info(snapshot.convert(info_range), + Hyperlink(command.node_name.node, line, column)) :: links + case None => links + } + + case _ => links + } + }) match { case Text.Info(_, info :: _) #:: _ => Some(info) case _ => None } + } + + + private def tooltip_text(msg: XML.Tree): String = + Pretty.string_of(List(msg), margin = options.int("jedit_tooltip_margin")) + + def tooltip_message(range: Text.Range): Option[String] = + { + val msgs = + snapshot.cumulate_markup[SortedMap[Long, String]](range, SortedMap.empty, + Some(Set(Isabelle_Markup.WRITELN, Isabelle_Markup.WARNING, Isabelle_Markup.ERROR, + Isabelle_Markup.BAD)), + { + case (msgs, Text.Info(_, msg @ + XML.Elem(Markup(markup, Isabelle_Markup.Serial(serial)), _))) + if markup == Isabelle_Markup.WRITELN || + markup == Isabelle_Markup.WARNING || + markup == Isabelle_Markup.ERROR => + msgs + (serial -> tooltip_text(msg)) + case (msgs, Text.Info(_, msg @ XML.Elem(Markup(Isabelle_Markup.BAD, _), body))) + if !body.isEmpty => msgs + (Document.new_id() -> tooltip_text(msg)) + }).toList.flatMap(_.info) + if (msgs.isEmpty) None else Some(cat_lines(msgs.iterator.map(_._2))) + } + + + private val tooltips: Map[String, String] = + Map( + Isabelle_Markup.SORT -> "sort", + Isabelle_Markup.TYP -> "type", + Isabelle_Markup.TERM -> "term", + Isabelle_Markup.PROP -> "proposition", + Isabelle_Markup.TOKEN_RANGE -> "inner syntax token", + Isabelle_Markup.FREE -> "free variable", + Isabelle_Markup.SKOLEM -> "skolem variable", + Isabelle_Markup.BOUND -> "bound variable", + Isabelle_Markup.VAR -> "schematic variable", + Isabelle_Markup.TFREE -> "free type variable", + Isabelle_Markup.TVAR -> "schematic type variable", + Isabelle_Markup.ML_SOURCE -> "ML source", + Isabelle_Markup.DOCUMENT_SOURCE -> "document source") + + private val tooltip_elements = + Set(Isabelle_Markup.ENTITY, Isabelle_Markup.TYPING, Isabelle_Markup.ML_TYPING, + Isabelle_Markup.PATH) ++ tooltips.keys + + private def string_of_typing(kind: String, body: XML.Body): String = + Pretty.string_of(List(Pretty.block(XML.Text(kind) :: Pretty.Break(1) :: body)), + margin = options.int("jedit_tooltip_margin")) + + def tooltip(range: Text.Range): Option[String] = + { + def add(prev: Text.Info[List[(Boolean, String)]], r: Text.Range, p: (Boolean, String)) = + if (prev.range == r) Text.Info(r, p :: prev.info) else Text.Info(r, List(p)) + + val tips = + snapshot.cumulate_markup[Text.Info[(List[(Boolean, String)])]]( + range, Text.Info(range, Nil), Some(tooltip_elements), + { + case (prev, Text.Info(r, XML.Elem(Isabelle_Markup.Entity(kind, name), _))) => + val kind1 = space_explode('_', kind).mkString(" ") + add(prev, r, (true, kind1 + " " + quote(name))) + case (prev, Text.Info(r, XML.Elem(Isabelle_Markup.Path(name), _))) + if Path.is_ok(name) => + val jedit_file = Isabelle.thy_load.append(snapshot.node_name.dir, Path.explode(name)) + add(prev, r, (true, "file " + quote(jedit_file))) + case (prev, Text.Info(r, XML.Elem(Markup(Isabelle_Markup.TYPING, _), body))) => + add(prev, r, (true, string_of_typing("::", body))) + case (prev, Text.Info(r, XML.Elem(Markup(Isabelle_Markup.ML_TYPING, _), body))) => + add(prev, r, (false, string_of_typing("ML:", body))) + case (prev, Text.Info(r, XML.Elem(Markup(name, _), _))) + if tooltips.isDefinedAt(name) => add(prev, r, (true, tooltips(name))) + }).toList.flatMap(_.info.info) + + val all_tips = + (tips.filter(_._1) ++ tips.filter(!_._1).lastOption.toList).map(_._2) + if (all_tips.isEmpty) None else Some(cat_lines(all_tips)) + } + + + def gutter_message(range: Text.Range): Option[Icon] = + { + val results = + snapshot.cumulate_markup[Int](range, 0, + Some(Set(Isabelle_Markup.WARNING, Isabelle_Markup.ERROR)), + { + case (pri, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.WARNING, _), body))) => + body match { + case List(XML.Elem(Markup(Isabelle_Markup.LEGACY, _), _)) => + pri max Isabelle_Rendering.legacy_pri + case _ => pri max Isabelle_Rendering.warning_pri + } + case (pri, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.ERROR, _), _))) => + pri max Isabelle_Rendering.error_pri + }) + val pri = (0 /: results) { case (p1, Text.Info(_, p2)) => p1 max p2 } + Isabelle_Rendering.gutter_icons.get(pri) + } + + + private val squiggly_colors = Map( + Isabelle_Rendering.writeln_pri -> writeln_color, + Isabelle_Rendering.warning_pri -> warning_color, + Isabelle_Rendering.error_pri -> error_color) + + def squiggly_underline(range: Text.Range): Stream[Text.Info[Color]] = + { + val results = + snapshot.cumulate_markup[Int](range, 0, + Some(Set(Isabelle_Markup.WRITELN, Isabelle_Markup.WARNING, Isabelle_Markup.ERROR)), + { + case (pri, Text.Info(_, XML.Elem(Markup(name, _), _))) => + name match { + case Isabelle_Markup.WRITELN => pri max Isabelle_Rendering.writeln_pri + case Isabelle_Markup.WARNING => pri max Isabelle_Rendering.warning_pri + case Isabelle_Markup.ERROR => pri max Isabelle_Rendering.error_pri + case _ => pri + } + }) + for { + Text.Info(r, pri) <- results + color <- squiggly_colors.get(pri) + } yield Text.Info(r, color) + } + + + def background1(range: Text.Range): Stream[Text.Info[Color]] = + { + if (snapshot.is_outdated) Stream(Text.Info(range, outdated_color)) + else + for { + Text.Info(r, result) <- + snapshot.cumulate_markup[(Option[Protocol.Status], Option[Color])]( + range, (Some(Protocol.Status.init), None), + Some(Protocol.command_status_markup + Isabelle_Markup.BAD + Isabelle_Markup.HILITE), + { + case (((Some(status), color), Text.Info(_, XML.Elem(markup, _)))) + if (Protocol.command_status_markup(markup.name)) => + (Some(Protocol.command_status(status, markup)), color) + case (_, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.BAD, _), _))) => + (None, Some(bad_color)) + case (_, Text.Info(_, XML.Elem(Markup(Isabelle_Markup.HILITE, _), _))) => + (None, Some(hilite_color)) + }) + color <- + (result match { + case (Some(status), opt_color) => + if (status.is_unprocessed) Some(unprocessed1_color) + else if (status.is_running) Some(running1_color) + else opt_color + case (_, opt_color) => opt_color + }) + } yield Text.Info(r, color) + } + + + def background2(range: Text.Range): Stream[Text.Info[Color]] = + snapshot.select_markup(range, + Some(Set(Isabelle_Markup.TOKEN_RANGE)), + { + case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.TOKEN_RANGE, _), _)) => light_color + }) + + + def foreground(range: Text.Range): Stream[Text.Info[Color]] = + snapshot.select_markup(range, + Some(Set(Isabelle_Markup.STRING, Isabelle_Markup.ALTSTRING, Isabelle_Markup.VERBATIM)), + { + case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.STRING, _), _)) => quoted_color + case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.ALTSTRING, _), _)) => quoted_color + case Text.Info(_, XML.Elem(Markup(Isabelle_Markup.VERBATIM, _), _)) => quoted_color + }) + + + private val text_colors: Map[String, Color] = Map( + Isabelle_Markup.STRING -> Color.BLACK, + Isabelle_Markup.ALTSTRING -> Color.BLACK, + Isabelle_Markup.VERBATIM -> Color.BLACK, + Isabelle_Markup.LITERAL -> keyword1_color, + Isabelle_Markup.DELIMITER -> Color.BLACK, + Isabelle_Markup.TFREE -> tfree_color, + Isabelle_Markup.TVAR -> tvar_color, + Isabelle_Markup.FREE -> free_color, + Isabelle_Markup.SKOLEM -> skolem_color, + Isabelle_Markup.BOUND -> bound_color, + Isabelle_Markup.VAR -> var_color, + Isabelle_Markup.INNER_STRING -> inner_quoted_color, + Isabelle_Markup.INNER_COMMENT -> inner_comment_color, + Isabelle_Markup.DYNAMIC_FACT -> dynamic_color, + Isabelle_Markup.ML_KEYWORD -> keyword1_color, + Isabelle_Markup.ML_DELIMITER -> Color.BLACK, + Isabelle_Markup.ML_NUMERAL -> inner_numeral_color, + Isabelle_Markup.ML_CHAR -> inner_quoted_color, + Isabelle_Markup.ML_STRING -> inner_quoted_color, + Isabelle_Markup.ML_COMMENT -> inner_comment_color, + Isabelle_Markup.ANTIQ -> antiquotation_color) + + private val text_color_elements = Set.empty[String] ++ text_colors.keys + + def text_color(range: Text.Range, color: Color) + : Stream[Text.Info[Color]] = + { + snapshot.cumulate_markup(range, color, Some(text_color_elements), + { + case (_, Text.Info(_, XML.Elem(Markup(m, _), _))) + if text_colors.isDefinedAt(m) => text_colors(m) + }) + } +} diff -r 7d1af0a6e797 -r 6e0c0ffb6ec7 src/Tools/jEdit/src/jedit_options.scala --- a/src/Tools/jEdit/src/jedit_options.scala Fri Sep 14 12:46:33 2012 +0200 +++ b/src/Tools/jEdit/src/jedit_options.scala Fri Sep 14 13:52:16 2012 +0200 @@ -9,6 +9,7 @@ import isabelle._ +import java.awt.Color import javax.swing.{InputVerifier, JComponent, UIManager} import javax.swing.text.JTextComponent @@ -26,6 +27,8 @@ class JEdit_Options extends Options_Variable { + def color_value(s: String): Color = Color_Value(string(s)) + def make_color_component(opt: Options.Opt): Option_Component = { Swing_Thread.require() diff -r 7d1af0a6e797 -r 6e0c0ffb6ec7 src/Tools/jEdit/src/session_dockable.scala --- a/src/Tools/jEdit/src/session_dockable.scala Fri Sep 14 12:46:33 2012 +0200 +++ b/src/Tools/jEdit/src/session_dockable.scala Fri Sep 14 13:52:16 2012 +0200 @@ -89,10 +89,10 @@ var end = size.width - insets.right for { (n, color) <- List( - (st.unprocessed, Isabelle_Rendering.color_value("unprocessed1_color")), - (st.running, Isabelle_Rendering.color_value("running_color")), - (st.warned, Isabelle_Rendering.color_value("warning_color")), - (st.failed, Isabelle_Rendering.color_value("error_color"))) } + (st.unprocessed, Isabelle.options.color_value("unprocessed1_color")), + (st.running, Isabelle.options.color_value("running_color")), + (st.warned, Isabelle.options.color_value("warning_color")), + (st.failed, Isabelle.options.color_value("error_color"))) } { gfx.setColor(color) val v = (n * w / st.total) max (if (n > 0) 2 else 0) diff -r 7d1af0a6e797 -r 6e0c0ffb6ec7 src/Tools/jEdit/src/text_area_painter.scala --- a/src/Tools/jEdit/src/text_area_painter.scala Fri Sep 14 12:46:33 2012 +0200 +++ b/src/Tools/jEdit/src/text_area_painter.scala Fri Sep 14 13:52:16 2012 +0200 @@ -75,7 +75,7 @@ /* common painter state */ - @volatile private var painter_snapshot: Document.Snapshot = null + @volatile private var painter_rendering: Isabelle_Rendering = null @volatile private var painter_clip: Shape = null private val set_state = new TextAreaExtension @@ -84,7 +84,7 @@ first_line: Int, last_line: Int, physical_lines: Array[Int], start: Array[Int], end: Array[Int], y: Int, line_height: Int) { - painter_snapshot = model.snapshot() + painter_rendering = Isabelle_Rendering(model.snapshot(), Isabelle.options.value) painter_clip = gfx.getClip } } @@ -95,14 +95,14 @@ first_line: Int, last_line: Int, physical_lines: Array[Int], start: Array[Int], end: Array[Int], y: Int, line_height: Int) { - painter_snapshot = null + painter_rendering = null painter_clip = null } } - private def robust_snapshot(body: Document.Snapshot => Unit) + private def robust_rendering(body: Isabelle_Rendering => Unit) { - doc_view.robust_body(()) { body(painter_snapshot) } + doc_view.robust_body(()) { body(painter_rendering) } } @@ -114,7 +114,7 @@ first_line: Int, last_line: Int, physical_lines: Array[Int], start: Array[Int], end: Array[Int], y: Int, line_height: Int) { - robust_snapshot { snapshot => + robust_rendering { rendering => val ascent = text_area.getPainter.getFontMetrics.getAscent for (i <- 0 until physical_lines.length) { @@ -123,7 +123,7 @@ // background color (1) for { - Text.Info(range, color) <- Isabelle_Rendering.background1(snapshot, line_range) + Text.Info(range, color) <- rendering.background1(line_range) r <- gfx_range(range) } { gfx.setColor(color) @@ -132,7 +132,7 @@ // background color (2) for { - Text.Info(range, color) <- Isabelle_Rendering.background2(snapshot, line_range) + Text.Info(range, color) <- rendering.background2(line_range) r <- gfx_range(range) } { gfx.setColor(color) @@ -141,7 +141,7 @@ // squiggly underline for { - Text.Info(range, color) <- Isabelle_Rendering.squiggly_underline(snapshot, line_range) + Text.Info(range, color) <- rendering.squiggly_underline(line_range) r <- gfx_range(range) } { gfx.setColor(color) @@ -161,7 +161,7 @@ /* text */ - private def paint_chunk_list(snapshot: Document.Snapshot, + private def paint_chunk_list(rendering: Isabelle_Rendering, gfx: Graphics2D, line_start: Text.Offset, head: Chunk, x: Float, y: Float): Float = { val clip_rect = gfx.getClipBounds @@ -190,7 +190,7 @@ val markup = for { - r1 <- Isabelle_Rendering.text_color(snapshot, chunk_range, chunk_color) + r1 <- rendering.text_color(chunk_range, chunk_color) r2 <- r1.try_restrict(chunk_range) } yield r2 @@ -246,7 +246,7 @@ first_line: Int, last_line: Int, physical_lines: Array[Int], start: Array[Int], end: Array[Int], y: Int, line_height: Int) { - robust_snapshot { snapshot => + robust_rendering { rendering => val clip = gfx.getClip val x0 = text_area.getHorizontalOffset val fm = text_area.getPainter.getFontMetrics @@ -260,7 +260,7 @@ if (chunks != null) { val line_start = text_area.getBuffer.getLineStartOffset(line) gfx.clipRect(x0, y + line_height * i, Integer.MAX_VALUE, line_height) - val w = paint_chunk_list(snapshot, gfx, line_start, chunks, x0, y0).toInt + val w = paint_chunk_list(rendering, gfx, line_start, chunks, x0, y0).toInt gfx.clipRect(x0 + w.toInt, 0, Integer.MAX_VALUE, Integer.MAX_VALUE) orig_text_painter.paintValidLine(gfx, screen_line, line, start(i), end(i), y + line_height * i) @@ -282,14 +282,14 @@ first_line: Int, last_line: Int, physical_lines: Array[Int], start: Array[Int], end: Array[Int], y: Int, line_height: Int) { - robust_snapshot { snapshot => + robust_rendering { rendering => for (i <- 0 until physical_lines.length) { if (physical_lines(i) != -1) { val line_range = doc_view.proper_line_range(start(i), end(i)) // foreground color for { - Text.Info(range, color) <- Isabelle_Rendering.foreground(snapshot, line_range) + Text.Info(range, color) <- rendering.foreground(line_range) r <- gfx_range(range) } { gfx.setColor(color) @@ -312,7 +312,7 @@ Text.Info(range, _) <- info.try_restrict(line_range) r <- gfx_range(range) } { - gfx.setColor(Isabelle_Rendering.color_value("hyperlink_color")) + gfx.setColor(rendering.hyperlink_color) gfx.drawRect(r.x, y + i * line_height, r.length - 1, line_height - 1) } } @@ -329,7 +329,7 @@ override def paintValidLine(gfx: Graphics2D, screen_line: Int, physical_line: Int, start: Int, end: Int, y: Int) { - robust_snapshot { _ => + robust_rendering { _ => if (before) gfx.clipRect(0, 0, 0, 0) else gfx.setClip(painter_clip) } @@ -346,7 +346,7 @@ override def paintValidLine(gfx: Graphics2D, screen_line: Int, physical_line: Int, start: Int, end: Int, y: Int) { - robust_snapshot { _ => + robust_rendering { _ => if (text_area.isCaretVisible) { val caret = text_area.getCaretPosition if (start <= caret && caret == end - 1) { diff -r 7d1af0a6e797 -r 6e0c0ffb6ec7 src/Tools/jEdit/src/text_overview.scala --- a/src/Tools/jEdit/src/text_overview.scala Fri Sep 14 12:46:33 2012 +0200 +++ b/src/Tools/jEdit/src/text_overview.scala Fri Sep 14 13:52:16 2012 +0200 @@ -54,6 +54,7 @@ private var cached_colors: List[(Color, Int, Int)] = Nil private var last_snapshot = Document.State.init.snapshot() + private var last_options = Isabelle.options.value private var last_line_count = 0 private var last_char_count = 0 private var last_L = 0 @@ -69,7 +70,7 @@ val snapshot = doc_view.model.snapshot() if (snapshot.is_outdated) { - gfx.setColor(Isabelle_Rendering.color_value("outdated_color")) + gfx.setColor(Isabelle.options.color_value("outdated_color")) gfx.asInstanceOf[Graphics2D].fill(gfx.getClipBounds) } else { @@ -82,9 +83,14 @@ val L = lines() val H = getHeight() + val options = Isabelle.options.value + if (!(line_count == last_line_count && char_count == last_char_count && - L == last_L && H == last_H && (snapshot eq_markup last_snapshot))) + L == last_L && H == last_H && (snapshot eq_markup last_snapshot) && + (options eq last_options))) { + val rendering = Isabelle_Rendering(snapshot, options) + @tailrec def loop(l: Int, h: Int, p: Int, q: Int, colors: List[(Color, Int, Int)]) : List[(Color, Int, Int)] = { @@ -102,7 +108,7 @@ val range = Text.Range(start, end) val colors1 = - (Isabelle_Rendering.overview_color(snapshot, range), colors) match { + (rendering.overview_color(range), colors) match { case (Some(color), (old_color, old_h, old_h1) :: rest) if color == old_color && old_h1 == h => (color, old_h, h1) :: rest case (Some(color), _) => (color, h, h1) :: colors @@ -115,6 +121,7 @@ cached_colors = loop(0, 0, 0, 0, Nil) last_snapshot = snapshot + last_options = options last_line_count = line_count last_char_count = char_count last_L = L