# HG changeset patch # User wenzelm # Date 1353869742 -3600 # Node ID ec0f2f8dbeb9cbd60fb0c13eaad1039cbaf4fb76 # Parent c26369c9eda677e64578dac994ffe6608ee07e91 tuned file name; diff -r c26369c9eda6 -r ec0f2f8dbeb9 src/Tools/jEdit/lib/Tools/jedit --- a/src/Tools/jEdit/lib/Tools/jedit Sun Nov 25 19:49:24 2012 +0100 +++ b/src/Tools/jEdit/lib/Tools/jedit Sun Nov 25 19:55:42 2012 +0100 @@ -19,12 +19,11 @@ "src/isabelle_encoding.scala" "src/isabelle_logic.scala" "src/isabelle_options.scala" - "src/isabelle_rendering.scala" "src/isabelle_sidekick.scala" "src/jedit_lib.scala" "src/jedit_main.scala" + "src/jedit_options.scala" "src/jedit_thy_load.scala" - "src/jedit_options.scala" "src/output_dockable.scala" "src/plugin.scala" "src/pretty_text_area.scala" @@ -32,6 +31,7 @@ "src/protocol_dockable.scala" "src/raw_output_dockable.scala" "src/readme_dockable.scala" + "src/rendering.scala" "src/rich_text_area.scala" "src/scala_console.scala" "src/sendback.scala" diff -r c26369c9eda6 -r ec0f2f8dbeb9 src/Tools/jEdit/src/isabelle_rendering.scala --- a/src/Tools/jEdit/src/isabelle_rendering.scala Sun Nov 25 19:49:24 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,488 +0,0 @@ -/* Title: Tools/jEdit/src/isabelle_rendering.scala - Author: Makarius - -Isabelle-specific implementation of quasi-abstract rendering and -markup interpretation. -*/ - -package isabelle.jedit - - -import isabelle._ - -import java.awt.Color -import javax.swing.Icon - -import org.gjt.sp.jedit.syntax.{Token => JEditToken} -import org.gjt.sp.jedit.GUIUtilities - -import scala.collection.immutable.SortedMap - - -object Rendering -{ - def apply(snapshot: Document.Snapshot, options: Options): Rendering = - new Rendering(snapshot, options) - - - /* message priorities */ - - private val writeln_pri = 1 - private val tracing_pri = 2 - private val warning_pri = 3 - private val legacy_pri = 4 - private val error_pri = 5 - - private val message_pri = Map( - Markup.WRITELN -> writeln_pri, Markup.WRITELN_MESSAGE -> writeln_pri, - Markup.TRACING -> tracing_pri, Markup.TRACING_MESSAGE -> tracing_pri, - Markup.WARNING -> warning_pri, Markup.WARNING_MESSAGE -> warning_pri, - Markup.ERROR -> error_pri, Markup.ERROR_MESSAGE -> error_pri) - - - /* icons */ - - private def load_icon(name: String): Icon = - { - val icon = GUIUtilities.loadIcon(name) - if (icon.getIconWidth < 0 || icon.getIconHeight < 0) error("Bad icon: " + name) - else icon - } - - private val gutter_icons = Map( - warning_pri -> load_icon("16x16/status/dialog-information.png"), - legacy_pri -> load_icon("16x16/status/dialog-warning.png"), - error_pri -> load_icon("16x16/status/dialog-error.png")) - - val tooltip_close_icon = load_icon("16x16/actions/document-close.png") - val tooltip_detach_icon = load_icon("16x16/actions/window-new.png") - - - /* token markup -- text styles */ - - private val command_style: Map[String, Byte] = - { - import JEditToken._ - Map[String, Byte]( - Keyword.THY_END -> KEYWORD2, - Keyword.THY_SCRIPT -> LABEL, - Keyword.PRF_SCRIPT -> LABEL, - Keyword.PRF_ASM -> KEYWORD3, - Keyword.PRF_ASM_GOAL -> KEYWORD3 - ).withDefaultValue(KEYWORD1) - } - - private val token_style: Map[Token.Kind.Value, Byte] = - { - import JEditToken._ - Map[Token.Kind.Value, Byte]( - Token.Kind.KEYWORD -> KEYWORD2, - Token.Kind.IDENT -> NULL, - Token.Kind.LONG_IDENT -> NULL, - Token.Kind.SYM_IDENT -> NULL, - Token.Kind.VAR -> NULL, - Token.Kind.TYPE_IDENT -> NULL, - Token.Kind.TYPE_VAR -> NULL, - Token.Kind.NAT -> NULL, - Token.Kind.FLOAT -> NULL, - Token.Kind.STRING -> LITERAL1, - Token.Kind.ALT_STRING -> LITERAL2, - Token.Kind.VERBATIM -> COMMENT3, - Token.Kind.SPACE -> NULL, - Token.Kind.COMMENT -> COMMENT1, - Token.Kind.ERROR -> INVALID - ).withDefaultValue(NULL) - } - - def token_markup(syntax: Outer_Syntax, token: Token): Byte = - if (token.is_command) command_style(syntax.keyword_kind(token.content).getOrElse("")) - else if (token.is_operator) JEditToken.OPERATOR - else token_style(token.kind) -} - - -class 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 tooltip_color = color_value("tooltip_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 writeln_message_color = color_value("writeln_message_color") - val tracing_message_color = color_value("tracing_message_color") - val warning_message_color = color_value("warning_message_color") - val error_message_color = color_value("error_message_color") - val bad_color = color_value("bad_color") - val intensify_color = color_value("intensify_color") - val quoted_color = color_value("quoted_color") - val highlight_color = color_value("highlight_color") - val hyperlink_color = color_value("hyperlink_color") - val sendback_color = color_value("sendback_color") - val sendback_active_color = color_value("sendback_active_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 */ - - val overview_limit = options.int("jedit_text_overview_limit") - - 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 + Markup.WARNING + Markup.ERROR), - { - case ((status, pri), Text.Info(_, XML.Elem(markup, _))) => - if (markup.name == Markup.WARNING || markup.name == Markup.ERROR) - (status, pri max Rendering.message_pri(markup.name)) - 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 == Rendering.warning_pri) Some(warning_color) - else if (pri == 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 highlight_include = - Set(Markup.SORT, Markup.TYP, Markup.TERM, Markup.PROP, Markup.ML_TYPING, Markup.TOKEN_RANGE, - Markup.ENTITY, Markup.PATH, Markup.SORTING, Markup.TYPING, Markup.FREE, Markup.SKOLEM, - Markup.BOUND, Markup.VAR, Markup.TFREE, Markup.TVAR, Markup.ML_SOURCE, Markup.DOCUMENT_SOURCE) - - def highlight(range: Text.Range): Option[Text.Info[Color]] = - { - snapshot.select_markup(range, Some(highlight_include), - { - case Text.Info(info_range, XML.Elem(Markup(name, _), _)) if highlight_include(name) => - Text.Info(snapshot.convert(info_range), highlight_color) - }) match { case Text.Info(_, info) #:: _ => Some(info) case _ => None } - } - - - private val hyperlink_include = Set(Markup.ENTITY, 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(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(Markup.ENTITY, props), _))) - if !props.exists( - { case (Markup.KIND, Markup.ML_OPEN) => true - case (Markup.KIND, Markup.ML_STRUCT) => true - case _ => false }) => - - props match { - case Position.Def_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.Def_Id_Offset(id, offset) => - 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 } - } - - - def sendback(range: Text.Range): Option[Text.Info[Option[Document.Exec_ID]]] = - snapshot.select_markup(range, Some(Set(Markup.SENDBACK)), - { - case Text.Info(info_range, XML.Elem(Markup(Markup.SENDBACK, props), _)) => - Text.Info(snapshot.convert(info_range), Position.Id.unapply(props)) - }) match { case Text.Info(_, info) #:: _ => Some(info) case _ => None } - - - def tooltip_message(range: Text.Range): XML.Body = - { - val msgs = - snapshot.cumulate_markup[SortedMap[Long, XML.Tree]](range, SortedMap.empty, - Some(Set(Markup.WRITELN, Markup.WARNING, Markup.ERROR, Markup.BAD)), - { - case (msgs, Text.Info(_, XML.Elem(Markup(name, props @ Markup.Serial(serial)), body))) - if name == Markup.WRITELN || - name == Markup.WARNING || - name == Markup.ERROR => - msgs + (serial -> XML.Elem(Markup(Markup.message(name), props), body)) - case (msgs, Text.Info(_, msg @ XML.Elem(Markup(Markup.BAD, _), body))) - if !body.isEmpty => msgs + (Document.new_id() -> msg) - }).toList.flatMap(_.info) - Pretty.separate(msgs.iterator.map(_._2).toList) - } - - - private val tooltips: Map[String, String] = - Map( - Markup.SORT -> "sort", - Markup.TYP -> "type", - Markup.TERM -> "term", - Markup.PROP -> "proposition", - Markup.TOKEN_RANGE -> "inner syntax token", - Markup.FREE -> "free variable", - Markup.SKOLEM -> "skolem variable", - Markup.BOUND -> "bound variable", - Markup.VAR -> "schematic variable", - Markup.TFREE -> "free type variable", - Markup.TVAR -> "schematic type variable", - Markup.ML_SOURCE -> "ML source", - Markup.DOCUMENT_SOURCE -> "document source") - - private val tooltip_elements = - Set(Markup.ENTITY, Markup.SORTING, Markup.TYPING, Markup.ML_TYPING, Markup.PATH) ++ - tooltips.keys - - private def pretty_typing(kind: String, body: XML.Body): XML.Tree = - Pretty.block(XML.Text(kind) :: Pretty.Break(1) :: body) - - def tooltip(range: Text.Range): XML.Body = - { - def add(prev: Text.Info[List[(Boolean, XML.Tree)]], r: Text.Range, p: (Boolean, XML.Tree)) = - 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, XML.Tree)])]]( - range, Text.Info(range, Nil), Some(tooltip_elements), - { - case (prev, Text.Info(r, XML.Elem(Markup.Entity(kind, name), _))) => - val kind1 = space_explode('_', kind).mkString(" ") - add(prev, r, (true, XML.Text(kind1 + " " + quote(name)))) - case (prev, Text.Info(r, XML.Elem(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, XML.Text("file " + quote(jedit_file)))) - case (prev, Text.Info(r, XML.Elem(Markup(name, _), body))) - if name == Markup.SORTING || name == Markup.TYPING => - add(prev, r, (true, pretty_typing("::", body))) - case (prev, Text.Info(r, XML.Elem(Markup(Markup.ML_TYPING, _), body))) => - add(prev, r, (false, pretty_typing("ML:", body))) - case (prev, Text.Info(r, XML.Elem(Markup(name, _), _))) - if tooltips.isDefinedAt(name) => add(prev, r, (true, XML.Text(tooltips(name)))) - }).toList.flatMap(_.info.info) - - val all_tips = - (tips.filter(_._1) ++ tips.filter(!_._1).lastOption.toList).map(_._2) - Library.separate(Pretty.FBreak, all_tips.toList) - } - - - def gutter_message(range: Text.Range): Option[Icon] = - { - val results = - snapshot.cumulate_markup[Int](range, 0, Some(Set(Markup.WARNING, Markup.ERROR)), - { - case (pri, Text.Info(_, XML.Elem(Markup(Markup.WARNING, _), body))) => - body match { - case List(XML.Elem(Markup(Markup.LEGACY, _), _)) => - pri max Rendering.legacy_pri - case _ => pri max Rendering.warning_pri - } - case (pri, Text.Info(_, XML.Elem(Markup(Markup.ERROR, _), _))) => - pri max Rendering.error_pri - }) - val pri = (0 /: results) { case (p1, Text.Info(_, p2)) => p1 max p2 } - Rendering.gutter_icons.get(pri) - } - - - private val squiggly_colors = Map( - Rendering.writeln_pri -> writeln_color, - Rendering.warning_pri -> warning_color, - 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(Markup.WRITELN, Markup.WARNING, Markup.ERROR)), - { - case (pri, Text.Info(_, XML.Elem(Markup(name, _), _))) - if name == Markup.WRITELN || - name == Markup.WARNING || - name == Markup.ERROR => pri max Rendering.message_pri(name) - }) - for { - Text.Info(r, pri) <- results - color <- squiggly_colors.get(pri) - } yield Text.Info(r, color) - } - - - private val messages_include = - Set(Markup.WRITELN_MESSAGE, Markup.TRACING_MESSAGE, Markup.WARNING_MESSAGE, Markup.ERROR_MESSAGE) - - private val message_colors = Map( - Rendering.writeln_pri -> writeln_message_color, - Rendering.tracing_pri -> tracing_message_color, - Rendering.warning_pri -> warning_message_color, - Rendering.error_pri -> error_message_color) - - def line_background(range: Text.Range): Option[(Color, Boolean)] = - { - val results = - snapshot.cumulate_markup[Int](range, 0, Some(messages_include), - { - case (pri, Text.Info(_, XML.Elem(Markup(name, _), _))) - if name == Markup.WRITELN_MESSAGE || - name == Markup.TRACING_MESSAGE || - name == Markup.WARNING_MESSAGE || - name == Markup.ERROR_MESSAGE => pri max Rendering.message_pri(name) - }) - val pri = (0 /: results) { case (p1, Text.Info(_, p2)) => p1 max p2 } - - val is_separator = pri > 0 && - snapshot.cumulate_markup[Boolean](range, false, Some(Set(Markup.SEPARATOR)), - { - case (_, Text.Info(_, XML.Elem(Markup(Markup.SEPARATOR, _), _))) => true - }).exists(_.info) - - message_colors.get(pri).map((_, is_separator)) - } - - - private val background1_include = - Protocol.command_status_markup + Markup.WRITELN_MESSAGE + Markup.TRACING_MESSAGE + - Markup.WARNING_MESSAGE + Markup.ERROR_MESSAGE + Markup.BAD + Markup.INTENSIFY + - Markup.SENDBACK - - 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(background1_include), - { - 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(Markup.BAD, _), _))) => - (None, Some(bad_color)) - case (_, Text.Info(_, XML.Elem(Markup(Markup.INTENSIFY, _), _))) => - (None, Some(intensify_color)) - case (_, Text.Info(_, XML.Elem(Markup(Markup.SENDBACK, _), _))) => - (None, Some(sendback_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(Markup.TOKEN_RANGE)), - { - case Text.Info(_, XML.Elem(Markup(Markup.TOKEN_RANGE, _), _)) => light_color - }) - - - def foreground(range: Text.Range): Stream[Text.Info[Color]] = - snapshot.select_markup(range, Some(Set(Markup.STRING, Markup.ALTSTRING, Markup.VERBATIM)), - { - case Text.Info(_, XML.Elem(Markup(Markup.STRING, _), _)) => quoted_color - case Text.Info(_, XML.Elem(Markup(Markup.ALTSTRING, _), _)) => quoted_color - case Text.Info(_, XML.Elem(Markup(Markup.VERBATIM, _), _)) => quoted_color - }) - - - private val text_colors: Map[String, Color] = Map( - Markup.KEYWORD1 -> keyword1_color, - Markup.KEYWORD2 -> keyword2_color, - Markup.STRING -> Color.BLACK, - Markup.ALTSTRING -> Color.BLACK, - Markup.VERBATIM -> Color.BLACK, - Markup.LITERAL -> keyword1_color, - Markup.DELIMITER -> Color.BLACK, - Markup.TFREE -> tfree_color, - Markup.TVAR -> tvar_color, - Markup.FREE -> free_color, - Markup.SKOLEM -> skolem_color, - Markup.BOUND -> bound_color, - Markup.VAR -> var_color, - Markup.INNER_STRING -> inner_quoted_color, - Markup.INNER_COMMENT -> inner_comment_color, - Markup.DYNAMIC_FACT -> dynamic_color, - Markup.ML_KEYWORD -> keyword1_color, - Markup.ML_DELIMITER -> Color.BLACK, - Markup.ML_NUMERAL -> inner_numeral_color, - Markup.ML_CHAR -> inner_quoted_color, - Markup.ML_STRING -> inner_quoted_color, - Markup.ML_COMMENT -> inner_comment_color, - 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]] = - { - if (color == Token_Markup.hidden_color) Stream(Text.Info(range, color)) - else - 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 c26369c9eda6 -r ec0f2f8dbeb9 src/Tools/jEdit/src/rendering.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Tools/jEdit/src/rendering.scala Sun Nov 25 19:55:42 2012 +0100 @@ -0,0 +1,488 @@ +/* Title: Tools/jEdit/src/rendering.scala + Author: Makarius + +Isabelle-specific implementation of quasi-abstract rendering and +markup interpretation. +*/ + +package isabelle.jedit + + +import isabelle._ + +import java.awt.Color +import javax.swing.Icon + +import org.gjt.sp.jedit.syntax.{Token => JEditToken} +import org.gjt.sp.jedit.GUIUtilities + +import scala.collection.immutable.SortedMap + + +object Rendering +{ + def apply(snapshot: Document.Snapshot, options: Options): Rendering = + new Rendering(snapshot, options) + + + /* message priorities */ + + private val writeln_pri = 1 + private val tracing_pri = 2 + private val warning_pri = 3 + private val legacy_pri = 4 + private val error_pri = 5 + + private val message_pri = Map( + Markup.WRITELN -> writeln_pri, Markup.WRITELN_MESSAGE -> writeln_pri, + Markup.TRACING -> tracing_pri, Markup.TRACING_MESSAGE -> tracing_pri, + Markup.WARNING -> warning_pri, Markup.WARNING_MESSAGE -> warning_pri, + Markup.ERROR -> error_pri, Markup.ERROR_MESSAGE -> error_pri) + + + /* icons */ + + private def load_icon(name: String): Icon = + { + val icon = GUIUtilities.loadIcon(name) + if (icon.getIconWidth < 0 || icon.getIconHeight < 0) error("Bad icon: " + name) + else icon + } + + private val gutter_icons = Map( + warning_pri -> load_icon("16x16/status/dialog-information.png"), + legacy_pri -> load_icon("16x16/status/dialog-warning.png"), + error_pri -> load_icon("16x16/status/dialog-error.png")) + + val tooltip_close_icon = load_icon("16x16/actions/document-close.png") + val tooltip_detach_icon = load_icon("16x16/actions/window-new.png") + + + /* token markup -- text styles */ + + private val command_style: Map[String, Byte] = + { + import JEditToken._ + Map[String, Byte]( + Keyword.THY_END -> KEYWORD2, + Keyword.THY_SCRIPT -> LABEL, + Keyword.PRF_SCRIPT -> LABEL, + Keyword.PRF_ASM -> KEYWORD3, + Keyword.PRF_ASM_GOAL -> KEYWORD3 + ).withDefaultValue(KEYWORD1) + } + + private val token_style: Map[Token.Kind.Value, Byte] = + { + import JEditToken._ + Map[Token.Kind.Value, Byte]( + Token.Kind.KEYWORD -> KEYWORD2, + Token.Kind.IDENT -> NULL, + Token.Kind.LONG_IDENT -> NULL, + Token.Kind.SYM_IDENT -> NULL, + Token.Kind.VAR -> NULL, + Token.Kind.TYPE_IDENT -> NULL, + Token.Kind.TYPE_VAR -> NULL, + Token.Kind.NAT -> NULL, + Token.Kind.FLOAT -> NULL, + Token.Kind.STRING -> LITERAL1, + Token.Kind.ALT_STRING -> LITERAL2, + Token.Kind.VERBATIM -> COMMENT3, + Token.Kind.SPACE -> NULL, + Token.Kind.COMMENT -> COMMENT1, + Token.Kind.ERROR -> INVALID + ).withDefaultValue(NULL) + } + + def token_markup(syntax: Outer_Syntax, token: Token): Byte = + if (token.is_command) command_style(syntax.keyword_kind(token.content).getOrElse("")) + else if (token.is_operator) JEditToken.OPERATOR + else token_style(token.kind) +} + + +class 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 tooltip_color = color_value("tooltip_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 writeln_message_color = color_value("writeln_message_color") + val tracing_message_color = color_value("tracing_message_color") + val warning_message_color = color_value("warning_message_color") + val error_message_color = color_value("error_message_color") + val bad_color = color_value("bad_color") + val intensify_color = color_value("intensify_color") + val quoted_color = color_value("quoted_color") + val highlight_color = color_value("highlight_color") + val hyperlink_color = color_value("hyperlink_color") + val sendback_color = color_value("sendback_color") + val sendback_active_color = color_value("sendback_active_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 */ + + val overview_limit = options.int("jedit_text_overview_limit") + + 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 + Markup.WARNING + Markup.ERROR), + { + case ((status, pri), Text.Info(_, XML.Elem(markup, _))) => + if (markup.name == Markup.WARNING || markup.name == Markup.ERROR) + (status, pri max Rendering.message_pri(markup.name)) + 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 == Rendering.warning_pri) Some(warning_color) + else if (pri == 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 highlight_include = + Set(Markup.SORT, Markup.TYP, Markup.TERM, Markup.PROP, Markup.ML_TYPING, Markup.TOKEN_RANGE, + Markup.ENTITY, Markup.PATH, Markup.SORTING, Markup.TYPING, Markup.FREE, Markup.SKOLEM, + Markup.BOUND, Markup.VAR, Markup.TFREE, Markup.TVAR, Markup.ML_SOURCE, Markup.DOCUMENT_SOURCE) + + def highlight(range: Text.Range): Option[Text.Info[Color]] = + { + snapshot.select_markup(range, Some(highlight_include), + { + case Text.Info(info_range, XML.Elem(Markup(name, _), _)) if highlight_include(name) => + Text.Info(snapshot.convert(info_range), highlight_color) + }) match { case Text.Info(_, info) #:: _ => Some(info) case _ => None } + } + + + private val hyperlink_include = Set(Markup.ENTITY, 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(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(Markup.ENTITY, props), _))) + if !props.exists( + { case (Markup.KIND, Markup.ML_OPEN) => true + case (Markup.KIND, Markup.ML_STRUCT) => true + case _ => false }) => + + props match { + case Position.Def_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.Def_Id_Offset(id, offset) => + 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 } + } + + + def sendback(range: Text.Range): Option[Text.Info[Option[Document.Exec_ID]]] = + snapshot.select_markup(range, Some(Set(Markup.SENDBACK)), + { + case Text.Info(info_range, XML.Elem(Markup(Markup.SENDBACK, props), _)) => + Text.Info(snapshot.convert(info_range), Position.Id.unapply(props)) + }) match { case Text.Info(_, info) #:: _ => Some(info) case _ => None } + + + def tooltip_message(range: Text.Range): XML.Body = + { + val msgs = + snapshot.cumulate_markup[SortedMap[Long, XML.Tree]](range, SortedMap.empty, + Some(Set(Markup.WRITELN, Markup.WARNING, Markup.ERROR, Markup.BAD)), + { + case (msgs, Text.Info(_, XML.Elem(Markup(name, props @ Markup.Serial(serial)), body))) + if name == Markup.WRITELN || + name == Markup.WARNING || + name == Markup.ERROR => + msgs + (serial -> XML.Elem(Markup(Markup.message(name), props), body)) + case (msgs, Text.Info(_, msg @ XML.Elem(Markup(Markup.BAD, _), body))) + if !body.isEmpty => msgs + (Document.new_id() -> msg) + }).toList.flatMap(_.info) + Pretty.separate(msgs.iterator.map(_._2).toList) + } + + + private val tooltips: Map[String, String] = + Map( + Markup.SORT -> "sort", + Markup.TYP -> "type", + Markup.TERM -> "term", + Markup.PROP -> "proposition", + Markup.TOKEN_RANGE -> "inner syntax token", + Markup.FREE -> "free variable", + Markup.SKOLEM -> "skolem variable", + Markup.BOUND -> "bound variable", + Markup.VAR -> "schematic variable", + Markup.TFREE -> "free type variable", + Markup.TVAR -> "schematic type variable", + Markup.ML_SOURCE -> "ML source", + Markup.DOCUMENT_SOURCE -> "document source") + + private val tooltip_elements = + Set(Markup.ENTITY, Markup.SORTING, Markup.TYPING, Markup.ML_TYPING, Markup.PATH) ++ + tooltips.keys + + private def pretty_typing(kind: String, body: XML.Body): XML.Tree = + Pretty.block(XML.Text(kind) :: Pretty.Break(1) :: body) + + def tooltip(range: Text.Range): XML.Body = + { + def add(prev: Text.Info[List[(Boolean, XML.Tree)]], r: Text.Range, p: (Boolean, XML.Tree)) = + 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, XML.Tree)])]]( + range, Text.Info(range, Nil), Some(tooltip_elements), + { + case (prev, Text.Info(r, XML.Elem(Markup.Entity(kind, name), _))) => + val kind1 = space_explode('_', kind).mkString(" ") + add(prev, r, (true, XML.Text(kind1 + " " + quote(name)))) + case (prev, Text.Info(r, XML.Elem(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, XML.Text("file " + quote(jedit_file)))) + case (prev, Text.Info(r, XML.Elem(Markup(name, _), body))) + if name == Markup.SORTING || name == Markup.TYPING => + add(prev, r, (true, pretty_typing("::", body))) + case (prev, Text.Info(r, XML.Elem(Markup(Markup.ML_TYPING, _), body))) => + add(prev, r, (false, pretty_typing("ML:", body))) + case (prev, Text.Info(r, XML.Elem(Markup(name, _), _))) + if tooltips.isDefinedAt(name) => add(prev, r, (true, XML.Text(tooltips(name)))) + }).toList.flatMap(_.info.info) + + val all_tips = + (tips.filter(_._1) ++ tips.filter(!_._1).lastOption.toList).map(_._2) + Library.separate(Pretty.FBreak, all_tips.toList) + } + + + def gutter_message(range: Text.Range): Option[Icon] = + { + val results = + snapshot.cumulate_markup[Int](range, 0, Some(Set(Markup.WARNING, Markup.ERROR)), + { + case (pri, Text.Info(_, XML.Elem(Markup(Markup.WARNING, _), body))) => + body match { + case List(XML.Elem(Markup(Markup.LEGACY, _), _)) => + pri max Rendering.legacy_pri + case _ => pri max Rendering.warning_pri + } + case (pri, Text.Info(_, XML.Elem(Markup(Markup.ERROR, _), _))) => + pri max Rendering.error_pri + }) + val pri = (0 /: results) { case (p1, Text.Info(_, p2)) => p1 max p2 } + Rendering.gutter_icons.get(pri) + } + + + private val squiggly_colors = Map( + Rendering.writeln_pri -> writeln_color, + Rendering.warning_pri -> warning_color, + 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(Markup.WRITELN, Markup.WARNING, Markup.ERROR)), + { + case (pri, Text.Info(_, XML.Elem(Markup(name, _), _))) + if name == Markup.WRITELN || + name == Markup.WARNING || + name == Markup.ERROR => pri max Rendering.message_pri(name) + }) + for { + Text.Info(r, pri) <- results + color <- squiggly_colors.get(pri) + } yield Text.Info(r, color) + } + + + private val messages_include = + Set(Markup.WRITELN_MESSAGE, Markup.TRACING_MESSAGE, Markup.WARNING_MESSAGE, Markup.ERROR_MESSAGE) + + private val message_colors = Map( + Rendering.writeln_pri -> writeln_message_color, + Rendering.tracing_pri -> tracing_message_color, + Rendering.warning_pri -> warning_message_color, + Rendering.error_pri -> error_message_color) + + def line_background(range: Text.Range): Option[(Color, Boolean)] = + { + val results = + snapshot.cumulate_markup[Int](range, 0, Some(messages_include), + { + case (pri, Text.Info(_, XML.Elem(Markup(name, _), _))) + if name == Markup.WRITELN_MESSAGE || + name == Markup.TRACING_MESSAGE || + name == Markup.WARNING_MESSAGE || + name == Markup.ERROR_MESSAGE => pri max Rendering.message_pri(name) + }) + val pri = (0 /: results) { case (p1, Text.Info(_, p2)) => p1 max p2 } + + val is_separator = pri > 0 && + snapshot.cumulate_markup[Boolean](range, false, Some(Set(Markup.SEPARATOR)), + { + case (_, Text.Info(_, XML.Elem(Markup(Markup.SEPARATOR, _), _))) => true + }).exists(_.info) + + message_colors.get(pri).map((_, is_separator)) + } + + + private val background1_include = + Protocol.command_status_markup + Markup.WRITELN_MESSAGE + Markup.TRACING_MESSAGE + + Markup.WARNING_MESSAGE + Markup.ERROR_MESSAGE + Markup.BAD + Markup.INTENSIFY + + Markup.SENDBACK + + 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(background1_include), + { + 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(Markup.BAD, _), _))) => + (None, Some(bad_color)) + case (_, Text.Info(_, XML.Elem(Markup(Markup.INTENSIFY, _), _))) => + (None, Some(intensify_color)) + case (_, Text.Info(_, XML.Elem(Markup(Markup.SENDBACK, _), _))) => + (None, Some(sendback_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(Markup.TOKEN_RANGE)), + { + case Text.Info(_, XML.Elem(Markup(Markup.TOKEN_RANGE, _), _)) => light_color + }) + + + def foreground(range: Text.Range): Stream[Text.Info[Color]] = + snapshot.select_markup(range, Some(Set(Markup.STRING, Markup.ALTSTRING, Markup.VERBATIM)), + { + case Text.Info(_, XML.Elem(Markup(Markup.STRING, _), _)) => quoted_color + case Text.Info(_, XML.Elem(Markup(Markup.ALTSTRING, _), _)) => quoted_color + case Text.Info(_, XML.Elem(Markup(Markup.VERBATIM, _), _)) => quoted_color + }) + + + private val text_colors: Map[String, Color] = Map( + Markup.KEYWORD1 -> keyword1_color, + Markup.KEYWORD2 -> keyword2_color, + Markup.STRING -> Color.BLACK, + Markup.ALTSTRING -> Color.BLACK, + Markup.VERBATIM -> Color.BLACK, + Markup.LITERAL -> keyword1_color, + Markup.DELIMITER -> Color.BLACK, + Markup.TFREE -> tfree_color, + Markup.TVAR -> tvar_color, + Markup.FREE -> free_color, + Markup.SKOLEM -> skolem_color, + Markup.BOUND -> bound_color, + Markup.VAR -> var_color, + Markup.INNER_STRING -> inner_quoted_color, + Markup.INNER_COMMENT -> inner_comment_color, + Markup.DYNAMIC_FACT -> dynamic_color, + Markup.ML_KEYWORD -> keyword1_color, + Markup.ML_DELIMITER -> Color.BLACK, + Markup.ML_NUMERAL -> inner_numeral_color, + Markup.ML_CHAR -> inner_quoted_color, + Markup.ML_STRING -> inner_quoted_color, + Markup.ML_COMMENT -> inner_comment_color, + 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]] = + { + if (color == Token_Markup.hidden_color) Stream(Text.Info(range, color)) + else + snapshot.cumulate_markup(range, color, Some(text_color_elements), + { + case (_, Text.Info(_, XML.Elem(Markup(m, _), _))) + if text_colors.isDefinedAt(m) => text_colors(m) + }) + } +}