# HG changeset patch # User wenzelm # Date 1283949981 -7200 # Node ID 7f4fb691e4b6b222b3904cb5b287d0c2c04bcbba # Parent 1d5e81f5f083b5e91a8682cdfab8a097c55881fe# Parent cce0c10ed943acff30c9d8a11cf8060c5bbd89bd merged diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/General/markup.ML --- a/src/Pure/General/markup.ML Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/General/markup.ML Wed Sep 08 14:46:21 2010 +0200 @@ -58,6 +58,7 @@ val literalN: string val literal: T val inner_stringN: string val inner_string: T val inner_commentN: string val inner_comment: T + val token_rangeN: string val token_range: T val sortN: string val sort: T val typN: string val typ: T val termN: string val term: T @@ -119,6 +120,7 @@ val promptN: string val prompt: T val readyN: string val ready: T val reportN: string val report: T + val badN: string val bad: T val no_output: output * output val default_output: T -> output * output val add_mode: string -> (T -> output * output) -> unit @@ -239,6 +241,8 @@ val (inner_stringN, inner_string) = markup_elem "inner_string"; val (inner_commentN, inner_comment) = markup_elem "inner_comment"; +val (token_rangeN, token_range) = markup_elem "token_range"; + val (sortN, sort) = markup_elem "sort"; val (typN, typ) = markup_elem "typ"; val (termN, term) = markup_elem "term"; @@ -345,6 +349,8 @@ val (reportN, report) = markup_elem "report"; +val (badN, bad) = markup_elem "bad"; + (** print mode operations **) diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/General/markup.scala --- a/src/Pure/General/markup.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/General/markup.scala Wed Sep 08 14:46:21 2010 +0200 @@ -136,6 +136,8 @@ val LITERAL = "literal" val INNER_COMMENT = "inner_comment" + val TOKEN_RANGE = "token_range" + val SORT = "sort" val TYP = "typ" val TERM = "term" @@ -247,6 +249,8 @@ val SIGNAL = "signal" val EXIT = "exit" + val BAD = "bad" + val Ready = Markup("ready", Nil) diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/Isar/proof_context.ML --- a/src/Pure/Isar/proof_context.ML Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/Isar/proof_context.ML Wed Sep 08 14:46:21 2010 +0200 @@ -736,18 +736,22 @@ local +fun parse_failed ctxt pos msg kind = + (Context_Position.report ctxt Markup.bad pos; + cat_error msg ("Failed to parse " ^ kind)); + fun parse_sort ctxt text = let val (syms, pos) = Syntax.parse_token ctxt Markup.sort text; val S = Syntax.standard_parse_sort ctxt (syn_of ctxt) (syms, pos) - handle ERROR msg => cat_error msg "Failed to parse sort"; + handle ERROR msg => parse_failed ctxt pos msg "sort"; in Type.minimize_sort (tsig_of ctxt) S end; fun parse_typ ctxt text = let val (syms, pos) = Syntax.parse_token ctxt Markup.typ text; val T = Syntax.standard_parse_typ ctxt (syn_of ctxt) (get_sort ctxt) (syms, pos) - handle ERROR msg => cat_error msg "Failed to parse type"; + handle ERROR msg => parse_failed ctxt pos msg "type"; in T end; fun parse_term T ctxt text = @@ -764,7 +768,7 @@ val t = Syntax.standard_parse_term check get_sort map_const map_free ctxt (Type.is_logtype (tsig_of ctxt)) (syn_of ctxt) T' (syms, pos) - handle ERROR msg => cat_error msg ("Failed to parse " ^ kind); + handle ERROR msg => parse_failed ctxt pos msg kind; in t end; diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/PIDE/command.scala --- a/src/Pure/PIDE/command.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/PIDE/command.scala Wed Sep 08 14:46:21 2010 +0200 @@ -48,11 +48,11 @@ case XML.Elem(Markup(Markup.REPORT, _), msgs) => (this /: msgs)((state, msg) => msg match { - case XML.Elem(Markup(name, atts @ Position.Id_Range(id, range)), args) - if id == command.id => + case XML.Elem(Markup(name, atts @ Position.Id_Range(id, raw_range)), args) + if id == command.id && command.range.contains(command.decode(raw_range)) => + val range = command.decode(raw_range) val props = Position.purge(atts) - val info = - Text.Info[Any](command.decode(range), XML.Elem(Markup(name, props), args)) + val info = Text.Info[Any](range, XML.Elem(Markup(name, props), args)) state.add_markup(info) case _ => System.err.println("Ignored report message: " + msg); state }) @@ -60,8 +60,8 @@ atts match { case Markup.Serial(i) => val result = XML.Elem(Markup(name, Position.purge(atts)), body) - (add_result(i, result) /: Isar_Document.reported_positions(command.id, message))( - (st, range) => st.add_markup(Text.Info(command.decode(range), result))) + (add_result(i, result) /: Isar_Document.reported_positions(command, message))( + (st, range) => st.add_markup(Text.Info(range, result))) case _ => System.err.println("Ignored message without serial number: " + message); this } } diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/PIDE/document.scala --- a/src/Pure/PIDE/document.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/PIDE/document.scala Wed Sep 08 14:46:21 2010 +0200 @@ -169,11 +169,11 @@ def lookup_command(id: Command_ID): Option[Command] def state(command: Command): Command.State def convert(i: Text.Offset): Text.Offset - def convert(range: Text.Range): Text.Range = range.map(convert(_)) + def convert(range: Text.Range): Text.Range def revert(i: Text.Offset): Text.Offset - def revert(range: Text.Range): Text.Range = range.map(revert(_)) - def select_markup[A](range: Text.Range) - (result: PartialFunction[Text.Info[Any], A])(default: A): Iterator[Text.Info[A]] + def revert(range: Text.Range): Text.Range + def select_markup[A](range: Text.Range)(result: Markup_Tree.Select[A]) + : Stream[Text.Info[Option[A]]] } object State @@ -304,18 +304,24 @@ def convert(offset: Text.Offset) = (offset /: edits)((i, edit) => edit.convert(i)) def revert(offset: Text.Offset) = (offset /: reverse_edits)((i, edit) => edit.revert(i)) - def select_markup[A](range: Text.Range) - (result: PartialFunction[Text.Info[Any], A])(default: A): Iterator[Text.Info[A]] = + def convert(range: Text.Range): Text.Range = + if (edits.isEmpty) range else range.map(convert(_)) + + def revert(range: Text.Range): Text.Range = + if (edits.isEmpty) range else range.map(revert(_)) + + def select_markup[A](range: Text.Range)(result: Markup_Tree.Select[A]) + : Stream[Text.Info[Option[A]]] = { val former_range = revert(range) for { - (command, command_start) <- node.command_range(former_range) + (command, command_start) <- node.command_range(former_range).toStream Text.Info(r0, x) <- state(command).markup. select((former_range - command_start).restrict(command.range)) { case Text.Info(r0, info) if result.isDefinedAt(Text.Info(convert(r0 + command_start), info)) => result(Text.Info(convert(r0 + command_start), info)) - } { default } + } val r = convert(r0 + command_start) if !r.is_singularity } yield Text.Info(r, x) diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/PIDE/isar_document.scala --- a/src/Pure/PIDE/isar_document.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/PIDE/isar_document.scala Wed Sep 08 14:46:21 2010 +0200 @@ -61,19 +61,27 @@ private val include_pos = Set(Markup.BINDING, Markup.ENTITY, Markup.REPORT, Markup.POSITION) private val exclude_pos = Set(Markup.LOCATION) - def reported_positions(command_id: Document.Command_ID, message: XML.Elem): Set[Text.Range] = + private def is_state(msg: XML.Tree): Boolean = + msg match { + case XML.Elem(Markup(Markup.WRITELN, _), List(XML.Elem(Markup(Markup.STATE, _), _))) => true + case _ => false + } + + def reported_positions(command: Command, message: XML.Elem): Set[Text.Range] = { def reported(set: Set[Text.Range], tree: XML.Tree): Set[Text.Range] = tree match { - case XML.Elem(Markup(name, Position.Id_Range(id, range)), body) - if include_pos(name) && id == command_id => - body.foldLeft(set + range)(reported) + case XML.Elem(Markup(name, Position.Id_Range(id, raw_range)), body) + if include_pos(name) && id == command.id => + val range = command.decode(raw_range).restrict(command.range) + body.foldLeft(if (range.is_singularity) set else set + range)(reported) case XML.Elem(Markup(name, _), body) if !exclude_pos(name) => body.foldLeft(set)(reported) case _ => set } val set = reported(Set.empty, message) - if (set.isEmpty) set ++ Position.Range.unapply(message.markup.properties) + if (set.isEmpty && !is_state(message)) + set ++ Position.Range.unapply(message.markup.properties).map(command.decode(_)) else set } } diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/PIDE/markup_tree.scala --- a/src/Pure/PIDE/markup_tree.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/PIDE/markup_tree.scala Wed Sep 08 14:46:21 2010 +0200 @@ -43,6 +43,8 @@ } val empty = new Markup_Tree(Branches.empty) + + type Select[A] = PartialFunction[Text.Info[Any], A] } @@ -89,11 +91,13 @@ private def overlapping(range: Text.Range): Stream[(Text.Range, Branches.Entry)] = Branches.overlapping(range, branches).toStream - def select[A](root_range: Text.Range) - (result: PartialFunction[Text.Info[Any], A])(default: A): Iterator[Text.Info[A]] = + def select[A](root_range: Text.Range)(result: Markup_Tree.Select[A]) + : Stream[Text.Info[Option[A]]] = { - def stream(last: Text.Offset, stack: List[(Text.Info[A], Stream[(Text.Range, Branches.Entry)])]) - : Stream[Text.Info[A]] = + def stream( + last: Text.Offset, + stack: List[(Text.Info[Option[A]], Stream[(Text.Range, Branches.Entry)])]) + : Stream[Text.Info[Option[A]]] = { stack match { case (parent, (range, (info, tree)) #:: more) :: rest => @@ -102,7 +106,7 @@ val start = subrange.start if (result.isDefinedAt(info)) { - val next = Text.Info(subrange, result(info)) + val next = Text.Info[Option[A]](subrange, Some(result(info))) val nexts = stream(start, (next, subtree) :: (parent, more) :: rest) if (last < start) parent.restrict(Text.Range(last, start)) #:: nexts else nexts @@ -117,12 +121,11 @@ case Nil => val stop = root_range.stop - if (last < stop) Stream(Text.Info(Text.Range(last, stop), default)) + if (last < stop) Stream(Text.Info(Text.Range(last, stop), None)) else Stream.empty } } - stream(root_range.start, List((Text.Info(root_range, default), overlapping(root_range)))) - .iterator + stream(root_range.start, List((Text.Info(root_range, None), overlapping(root_range)))) } def swing_tree(parent: DefaultMutableTreeNode) diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/Syntax/lexicon.ML --- a/src/Pure/Syntax/lexicon.ML Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/Syntax/lexicon.ML Wed Sep 08 14:46:21 2010 +0200 @@ -66,6 +66,7 @@ val terminals: string list val is_terminal: string -> bool val report_token: Proof.context -> token -> unit + val report_token_range: Proof.context -> token -> unit val matching_tokens: token * token -> bool val valued_token: token -> bool val predef_term: string -> token option @@ -188,6 +189,11 @@ fun report_token ctxt (Token (kind, _, (pos, _))) = Context_Position.report ctxt (token_kind_markup kind) pos; +fun report_token_range ctxt tok = + if is_proper tok + then Context_Position.report ctxt Markup.token_range (pos_of_token tok) + else (); + (* matching_tokens *) diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Pure/Syntax/syntax.ML --- a/src/Pure/Syntax/syntax.ML Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Pure/Syntax/syntax.ML Wed Sep 08 14:46:21 2010 +0200 @@ -709,7 +709,8 @@ val _ = List.app (Lexicon.report_token ctxt) toks; val root' = if root <> "prop" andalso is_logtype root then Syn_Ext.logic else root; - val pts = Parser.parse ctxt gram root' (filter Lexicon.is_proper toks); + val pts = Parser.parse ctxt gram root' (filter Lexicon.is_proper toks) + handle ERROR msg => (List.app (Lexicon.report_token_range ctxt) toks; error msg); val len = length pts; val limit = Config.get ctxt ambiguity_limit; diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Tools/jEdit/dist-template/properties/jedit.props --- a/src/Tools/jEdit/dist-template/properties/jedit.props Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Tools/jEdit/dist-template/properties/jedit.props Wed Sep 08 14:46:21 2010 +0200 @@ -177,6 +177,7 @@ end.shortcut= fallbackEncodings=UTF-8 ISO-8859-15 US-ASCII firstTime=false +foldPainter=Circle home.shortcut= insert-newline-indent.shortcut= insert-newline.shortcut=ENTER @@ -203,6 +204,7 @@ view.fontsize=18 view.fracFontMetrics=false view.gutter.fontsize=12 +view.gutter.selectionAreaWidth=18 view.height=787 view.middleMousePaste=true view.showToolbar=false diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Tools/jEdit/src/jedit/document_model.scala --- a/src/Tools/jEdit/src/jedit/document_model.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Tools/jEdit/src/jedit/document_model.scala Wed Sep 08 14:46:21 2010 +0200 @@ -69,87 +69,6 @@ case _ => false } } - - - /* mapping to jEdit token types */ - // TODO: as properties or CSS style sheet - - val command_style: Map[String, Byte] = - { - import Token._ - 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) - } - - val token_style: Map[String, Byte] = - { - import Token._ - Map[String, Byte]( - // logical entities - Markup.TCLASS -> NULL, - Markup.TYCON -> NULL, - Markup.FIXED_DECL -> FUNCTION, - Markup.FIXED -> NULL, - Markup.CONST_DECL -> FUNCTION, - Markup.CONST -> NULL, - Markup.FACT_DECL -> FUNCTION, - Markup.FACT -> NULL, - Markup.DYNAMIC_FACT -> LABEL, - Markup.LOCAL_FACT_DECL -> FUNCTION, - Markup.LOCAL_FACT -> NULL, - // inner syntax - Markup.TFREE -> NULL, - Markup.FREE -> NULL, - Markup.TVAR -> NULL, - Markup.SKOLEM -> NULL, - Markup.BOUND -> NULL, - Markup.VAR -> NULL, - Markup.NUM -> DIGIT, - Markup.FLOAT -> DIGIT, - Markup.XNUM -> DIGIT, - Markup.XSTR -> LITERAL4, - Markup.LITERAL -> OPERATOR, - Markup.INNER_COMMENT -> COMMENT1, - Markup.SORT -> NULL, - Markup.TYP -> NULL, - Markup.TERM -> NULL, - Markup.PROP -> NULL, - Markup.ATTRIBUTE -> NULL, - Markup.METHOD -> NULL, - // ML syntax - Markup.ML_KEYWORD -> KEYWORD1, - Markup.ML_DELIMITER -> OPERATOR, - Markup.ML_IDENT -> NULL, - Markup.ML_TVAR -> NULL, - Markup.ML_NUMERAL -> DIGIT, - Markup.ML_CHAR -> LITERAL1, - Markup.ML_STRING -> LITERAL1, - Markup.ML_COMMENT -> COMMENT1, - Markup.ML_MALFORMED -> INVALID, - // embedded source text - Markup.ML_SOURCE -> COMMENT3, - Markup.DOC_SOURCE -> COMMENT3, - Markup.ANTIQ -> COMMENT4, - Markup.ML_ANTIQ -> COMMENT4, - Markup.DOC_ANTIQ -> COMMENT4, - // outer syntax - Markup.KEYWORD -> KEYWORD2, - Markup.OPERATOR -> OPERATOR, - Markup.COMMAND -> KEYWORD1, - Markup.IDENT -> NULL, - Markup.VERBATIM -> COMMENT3, - Markup.COMMENT -> COMMENT1, - Markup.CONTROL -> COMMENT3, - Markup.MALFORMED -> INVALID, - Markup.STRING -> LITERAL3, - Markup.ALTSTRING -> LITERAL1 - ).withDefaultValue(NULL) - } } @@ -272,27 +191,18 @@ handler.handleToken(line_segment, style, offset, length, context) val syntax = session.current_syntax() - val token_markup: PartialFunction[Text.Info[Any], Byte] = - { - case Text.Info(_, XML.Elem(Markup(Markup.COMMAND, List((Markup.NAME, name))), _)) - if syntax.keyword_kind(name).isDefined => - Document_Model.Token_Markup.command_style(syntax.keyword_kind(name).get) - - case Text.Info(_, XML.Elem(Markup(name, _), _)) - if Document_Model.Token_Markup.token_style(name) != Token.NULL => - Document_Model.Token_Markup.token_style(name) - } + val tokens = snapshot.select_markup(Text.Range(start, stop))(Isabelle_Markup.tokens(syntax)) var last = start - for (token <- snapshot.select_markup(Text.Range(start, stop))(token_markup)(Token.NULL)) { + for (token <- tokens.iterator) { val Text.Range(token_start, token_stop) = token.range if (last < token_start) handle_token(Token.COMMENT1, last - start, token_start - last) - handle_token(token.info, token_start - start, token_stop - token_start) + handle_token(token.info getOrElse Token.NULL, + token_start - start, token_stop - token_start) last = token_stop } - if (last < stop) - handle_token(Token.COMMENT1, last - start, stop - last) + if (last < stop) handle_token(Token.COMMENT1, last - start, stop - last) handle_token(Token.END, line_segment.count, 0) handler.setLineContext(context) diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Tools/jEdit/src/jedit/document_view.scala --- a/src/Tools/jEdit/src/jedit/document_view.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Tools/jEdit/src/jedit/document_view.scala Wed Sep 08 14:46:21 2010 +0200 @@ -13,42 +13,19 @@ import scala.actors.Actor._ import java.awt.event.{MouseAdapter, MouseMotionAdapter, MouseEvent, FocusAdapter, FocusEvent} -import java.awt.{BorderLayout, Graphics, Dimension, Color, Graphics2D} +import java.awt.{BorderLayout, Graphics, Color, Dimension, Graphics2D} import javax.swing.{JPanel, ToolTipManager} import javax.swing.event.{CaretListener, CaretEvent} -import org.gjt.sp.jedit.OperatingSystem +import org.gjt.sp.jedit.{jEdit, OperatingSystem} import org.gjt.sp.jedit.gui.RolloverButton +import org.gjt.sp.jedit.options.GutterOptionPane import org.gjt.sp.jedit.textarea.{JEditTextArea, TextArea, TextAreaExtension, TextAreaPainter} import org.gjt.sp.jedit.syntax.SyntaxStyle object Document_View { - /* physical rendering */ - - def status_color(snapshot: Document.Snapshot, command: Command): Color = - { - val state = snapshot.state(command) - if (snapshot.is_outdated) new Color(240, 240, 240) - else - Isar_Document.command_status(state.status) match { - case Isar_Document.Forked(i) if i > 0 => new Color(255, 228, 225) - case Isar_Document.Finished => new Color(234, 248, 255) - case Isar_Document.Failed => new Color(255, 193, 193) - case Isar_Document.Unprocessed => new Color(255, 228, 225) - case _ => Color.red - } - } - - val message_markup: PartialFunction[Text.Info[Any], Color] = - { - case Text.Info(_, XML.Elem(Markup(Markup.WRITELN, _), _)) => new Color(220, 220, 220) - case Text.Info(_, XML.Elem(Markup(Markup.WARNING, _), _)) => new Color(255, 165, 0) - case Text.Info(_, XML.Elem(Markup(Markup.ERROR, _), _)) => new Color(255, 106, 106) - } - - /* document view of text area */ private val key = new Object @@ -171,19 +148,17 @@ /* subexpression highlighting */ - private def subexp_range(snapshot: Document.Snapshot, x: Int, y: Int): Option[Text.Range] = + private def subexp_range(snapshot: Document.Snapshot, x: Int, y: Int) + : Option[(Text.Range, Color)] = { - val subexp_markup: PartialFunction[Text.Info[Any], Option[Text.Range]] = - { - case Text.Info(range, XML.Elem(Markup(Markup.ML_TYPING, _), _)) => - Some(snapshot.convert(range)) + val offset = text_area.xyToOffset(x, y) + snapshot.select_markup(Text.Range(offset, offset + 1))(Isabelle_Markup.subexp) match { + case Text.Info(_, Some((range, color))) #:: _ => Some((snapshot.convert(range), color)) + case _ => None } - val offset = text_area.xyToOffset(x, y) - val markup = snapshot.select_markup(Text.Range(offset, offset + 1))(subexp_markup)(None) - if (markup.hasNext) markup.next.info else None } - private var highlight_range: Option[Text.Range] = None + private var highlight_range: Option[(Text.Range, Color)] = None private val focus_listener = new FocusAdapter { override def focusLost(e: FocusEvent) { highlight_range = None } @@ -195,10 +170,10 @@ if (!model.buffer.isLoaded) highlight_range = None else Isabelle.swing_buffer_lock(model.buffer) { - highlight_range.map(invalidate_line_range(_)) + highlight_range map { case (range, _) => invalidate_line_range(range) } highlight_range = if (control) subexp_range(model.snapshot(), e.getX(), e.getY()) else None - highlight_range.map(invalidate_line_range(_)) + highlight_range map { case (range, _) => invalidate_line_range(range) } } } } @@ -217,53 +192,70 @@ val saved_color = gfx.getColor val ascent = text_area.getPainter.getFontMetrics.getAscent - try { - for (i <- 0 until physical_lines.length) { - if (physical_lines(i) != -1) { - val line_range = proper_line_range(start(i), end(i)) + for (i <- 0 until physical_lines.length) { + if (physical_lines(i) != -1) { + val line_range = proper_line_range(start(i), end(i)) - // background color - val cmds = snapshot.node.command_range(snapshot.revert(line_range)) - for { - (command, command_start) <- cmds if !command.is_ignored - val range = line_range.restrict(snapshot.convert(command.range + command_start)) - r <- Isabelle.gfx_range(text_area, range) - } { - gfx.setColor(Document_View.status_color(snapshot, command)) - gfx.fillRect(r.x, y + i * line_height, r.length, line_height) - } + // background color: status + val cmds = snapshot.node.command_range(snapshot.revert(line_range)) + for { + (command, command_start) <- cmds if !command.is_ignored + val range = line_range.restrict(snapshot.convert(command.range + command_start)) + r <- Isabelle.gfx_range(text_area, range) + color <- Isabelle_Markup.status_color(snapshot, command) + } { + gfx.setColor(color) + gfx.fillRect(r.x, y + i * line_height, r.length, line_height) + } + + // background color: markup + for { + Text.Info(range, Some(color)) <- + snapshot.select_markup(line_range)(Isabelle_Markup.background).iterator + r <- Isabelle.gfx_range(text_area, range) + } { + gfx.setColor(color) + gfx.fillRect(r.x, y + i * line_height, r.length, line_height) + } - // subexpression highlighting -- potentially from other snapshot - if (highlight_range.isDefined) { - if (line_range.overlaps(highlight_range.get)) { - Isabelle.gfx_range(text_area, line_range.restrict(highlight_range.get)) match { - case None => - case Some(r) => - gfx.setColor(Color.black) - gfx.drawRect(r.x, y + i * line_height, r.length, line_height - 1) - } + // sub-expression highlighting -- potentially from other snapshot + highlight_range match { + case Some((range, color)) if line_range.overlaps(range) => + Isabelle.gfx_range(text_area, line_range.restrict(range)) match { + case None => + case Some(r) => + gfx.setColor(color) + gfx.drawRect(r.x, y + i * line_height, r.length, line_height - 1) } - } + case _ => + } - // squiggly underline - for { - Text.Info(range, color) <- - snapshot.select_markup(line_range)(Document_View.message_markup)(null) - if color != null - r <- Isabelle.gfx_range(text_area, range) - } { - gfx.setColor(color) - val x0 = (r.x / 2) * 2 - val y0 = r.y + ascent + 1 - for (x1 <- Range(x0, x0 + r.length, 2)) { - val y1 = if (x1 % 4 < 2) y0 else y0 + 1 - gfx.drawLine(x1, y1, x1 + 1, y1) - } + // boxed text + for { + Text.Info(range, Some(color)) <- + snapshot.select_markup(line_range)(Isabelle_Markup.box).iterator + r <- Isabelle.gfx_range(text_area, range) + } { + gfx.setColor(color) + gfx.drawRect(r.x + 1, y + i * line_height + 1, r.length - 2, line_height - 3) + } + + // squiggly underline + for { + Text.Info(range, Some(color)) <- + snapshot.select_markup(line_range)(Isabelle_Markup.message).iterator + r <- Isabelle.gfx_range(text_area, range) + } { + gfx.setColor(color) + val x0 = (r.x / 2) * 2 + val y0 = r.y + ascent + 1 + for (x1 <- Range(x0, x0 + r.length, 2)) { + val y1 = if (x1 % 4 < 2) y0 else y0 + 1 + gfx.drawLine(x1, y1, x1 + 1, y1) } } } } - finally { gfx.setColor(saved_color) } } } @@ -272,12 +264,52 @@ Isabelle.swing_buffer_lock(model.buffer) { val snapshot = model.snapshot() val offset = text_area.xyToOffset(x, y) - val markup = - snapshot.select_markup(Text.Range(offset, offset + 1)) { - case Text.Info(range, XML.Elem(Markup(Markup.ML_TYPING, _), body)) => - Isabelle.tooltip(Pretty.string_of(List(Pretty.block(body)), margin = 40)) - } { null } - if (markup.hasNext) markup.next.info else null + snapshot.select_markup(Text.Range(offset, offset + 1))(Isabelle_Markup.tooltip) match + { + case Text.Info(_, Some(text)) #:: _ => Isabelle.tooltip(text) + case _ => null + } + } + } + } + + + /* gutter_extension */ + + private val gutter_extension = new TextAreaExtension + { + override def paintScreenLineRange(gfx: Graphics2D, + first_line: Int, last_line: Int, physical_lines: Array[Int], + start: Array[Int], end: Array[Int], y: Int, line_height: Int) + { + val gutter = text_area.getGutter + val width = GutterOptionPane.getSelectionAreaWidth + val border_width = jEdit.getIntegerProperty("view.gutter.borderWidth", 3) + val FOLD_MARKER_SIZE = 12 + + if (gutter.isSelectionAreaEnabled && !gutter.isExpanded && width >= 12 && line_height >= 12) { + Isabelle.swing_buffer_lock(model.buffer) { + val snapshot = model.snapshot() + for (i <- 0 until physical_lines.length) { + if (physical_lines(i) != -1) { + val line_range = proper_line_range(start(i), end(i)) + + // gutter icons + val icons = + (for (Text.Info(_, Some(icon)) <- + snapshot.select_markup(line_range)(Isabelle_Markup.gutter_message).iterator) + yield icon).toList.sortWith(_ >= _) + icons match { + case icon :: _ => + val icn = icon.icon + val x0 = (FOLD_MARKER_SIZE + width - border_width - icn.getIconWidth) max 10 + val y0 = y + i * line_height + (((line_height - icn.getIconHeight) / 2) max 0) + icn.paintIcon(gutter, gfx, x0, y0) + case Nil => + } + } + } + } } } } @@ -328,13 +360,6 @@ super.removeNotify } - override def getToolTipText(event: MouseEvent): String = - { - val line = y_to_line(event.getY()) - if (line >= 0 && line < text_area.getLineCount) "TODO:
Tooltip" - else "" - } - override def paintComponent(gfx: Graphics) { super.paintComponent(gfx) @@ -342,18 +367,18 @@ val buffer = model.buffer Isabelle.buffer_lock(buffer) { val snapshot = model.snapshot() - val saved_color = gfx.getColor // FIXME needed!? - try { - for ((command, start) <- snapshot.node.command_starts if !command.is_ignored) { - val line1 = buffer.getLineOfOffset(snapshot.convert(start)) - val line2 = buffer.getLineOfOffset(snapshot.convert(start + command.length)) + 1 - val y = line_to_y(line1) - val height = HEIGHT * (line2 - line1) - gfx.setColor(Document_View.status_color(snapshot, command)) - gfx.fillRect(0, y, getWidth - 1, height) - } + for { + (command, start) <- snapshot.node.command_starts + if !command.is_ignored + val line1 = buffer.getLineOfOffset(snapshot.convert(start)) + val line2 = buffer.getLineOfOffset(snapshot.convert(start + command.length)) + 1 + val y = line_to_y(line1) + val height = HEIGHT * (line2 - line1) + color <- Isabelle_Markup.overview_color(snapshot, command) + } { + gfx.setColor(color) + gfx.fillRect(0, y, getWidth - 1, height) } - finally { gfx.setColor(saved_color) } } } @@ -371,6 +396,7 @@ { text_area.getPainter. addExtension(TextAreaPainter.LINE_BACKGROUND_LAYER + 1, text_area_extension) + text_area.getGutter.addExtension(gutter_extension) text_area.addFocusListener(focus_listener) text_area.getPainter.addMouseMotionListener(mouse_motion_listener) text_area.addCaretListener(caret_listener) @@ -385,6 +411,7 @@ text_area.getPainter.removeMouseMotionListener(mouse_motion_listener) text_area.removeCaretListener(caret_listener) text_area.removeLeftOfScrollBar(overview) + text_area.getGutter.removeExtension(gutter_extension) text_area.getPainter.removeExtension(text_area_extension) } } \ No newline at end of file diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Tools/jEdit/src/jedit/isabelle_hyperlinks.scala --- a/src/Tools/jEdit/src/jedit/isabelle_hyperlinks.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Tools/jEdit/src/jedit/isabelle_hyperlinks.scala Wed Sep 08 14:46:21 2010 +0200 @@ -72,9 +72,11 @@ case _ => null } } - } { null } - if (markup.hasNext) markup.next.info else null - + } + markup match { + case Text.Info(_, Some(link)) #:: _ => link + case _ => null + } case None => null } } diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Tools/jEdit/src/jedit/isabelle_markup.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Tools/jEdit/src/jedit/isabelle_markup.scala Wed Sep 08 14:46:21 2010 +0200 @@ -0,0 +1,198 @@ +/* Title: Tools/jEdit/src/jedit/isabelle_markup.scala + Author: Makarius + +Isabelle specific physical rendering and markup selection. +*/ + +package isabelle.jedit + + +import isabelle._ + +import java.awt.Color + +import org.gjt.sp.jedit.GUIUtilities +import org.gjt.sp.jedit.syntax.Token + + +object Isabelle_Markup +{ + /* physical rendering */ + + val outdated_color = new Color(240, 240, 240) + val unfinished_color = new Color(255, 228, 225) + + val regular_color = new Color(192, 192, 192) + val warning_color = new Color(255, 168, 0) + val error_color = new Color(255, 80, 80) + val bad_color = new Color(255, 204, 153, 100) + + class Icon(val priority: Int, val icon: javax.swing.Icon) + { + def >= (that: Icon): Boolean = this.priority >= that.priority + } + val warning_icon = new Icon(1, GUIUtilities.loadIcon("16x16/status/dialog-warning.png")) + val error_icon = new Icon(2, GUIUtilities.loadIcon("16x16/status/dialog-error.png")) + + + /* command status */ + + def status_color(snapshot: Document.Snapshot, command: Command): Option[Color] = + { + val state = snapshot.state(command) + if (snapshot.is_outdated) Some(outdated_color) + else + Isar_Document.command_status(state.status) match { + case Isar_Document.Forked(i) if i > 0 => Some(unfinished_color) + case Isar_Document.Unprocessed => Some(unfinished_color) + case _ => None + } + } + + def overview_color(snapshot: Document.Snapshot, command: Command): Option[Color] = + { + val state = snapshot.state(command) + if (snapshot.is_outdated) None + else + Isar_Document.command_status(state.status) match { + case Isar_Document.Forked(i) if i > 0 => Some(unfinished_color) + case Isar_Document.Unprocessed => Some(unfinished_color) + case Isar_Document.Failed => Some(error_color) + case _ => None + } + } + + + /* markup selectors */ + + private val subexp_include = + Set(Markup.SORT, Markup.TYP, Markup.TERM, Markup.PROP, Markup.ML_TYPING) + + val subexp: Markup_Tree.Select[(Text.Range, Color)] = + { + case Text.Info(range, XML.Elem(Markup(name, _), _)) if subexp_include(name) => + (range, Color.black) + } + + val message: Markup_Tree.Select[Color] = + { + case Text.Info(_, XML.Elem(Markup(Markup.WRITELN, _), _)) => regular_color + case Text.Info(_, XML.Elem(Markup(Markup.WARNING, _), _)) => warning_color + case Text.Info(_, XML.Elem(Markup(Markup.ERROR, _), _)) => error_color + } + + val gutter_message: Markup_Tree.Select[Icon] = + { + case Text.Info(_, XML.Elem(Markup(Markup.WARNING, _), _)) => warning_icon + case Text.Info(_, XML.Elem(Markup(Markup.ERROR, _), _)) => error_icon + } + + val background: Markup_Tree.Select[Color] = + { + case Text.Info(_, XML.Elem(Markup(Markup.BAD, _), _)) => bad_color + } + + val box: Markup_Tree.Select[Color] = + { + case Text.Info(_, XML.Elem(Markup(Markup.TOKEN_RANGE, _), _)) => regular_color + } + + val tooltip: Markup_Tree.Select[String] = + { + case Text.Info(_, XML.Elem(Markup(Markup.ML_TYPING, _), body)) => + Pretty.string_of(List(Pretty.block(body)), margin = 40) + case Text.Info(_, XML.Elem(Markup(Markup.SORT, _), _)) => "sort" + case Text.Info(_, XML.Elem(Markup(Markup.TYP, _), _)) => "type" + case Text.Info(_, XML.Elem(Markup(Markup.TERM, _), _)) => "term" + case Text.Info(_, XML.Elem(Markup(Markup.PROP, _), _)) => "proposition" + } + + + /* token markup -- text styles */ + + private val command_style: Map[String, Byte] = + { + import Token._ + 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[String, Byte] = + { + import Token._ + Map[String, Byte]( + // logical entities + Markup.TCLASS -> NULL, + Markup.TYCON -> NULL, + Markup.FIXED_DECL -> FUNCTION, + Markup.FIXED -> NULL, + Markup.CONST_DECL -> FUNCTION, + Markup.CONST -> NULL, + Markup.FACT_DECL -> FUNCTION, + Markup.FACT -> NULL, + Markup.DYNAMIC_FACT -> LABEL, + Markup.LOCAL_FACT_DECL -> FUNCTION, + Markup.LOCAL_FACT -> NULL, + // inner syntax + Markup.TFREE -> NULL, + Markup.FREE -> NULL, + Markup.TVAR -> NULL, + Markup.SKOLEM -> NULL, + Markup.BOUND -> NULL, + Markup.VAR -> NULL, + Markup.NUM -> DIGIT, + Markup.FLOAT -> DIGIT, + Markup.XNUM -> DIGIT, + Markup.XSTR -> LITERAL4, + Markup.LITERAL -> OPERATOR, + Markup.INNER_COMMENT -> COMMENT1, + Markup.SORT -> NULL, + Markup.TYP -> NULL, + Markup.TERM -> NULL, + Markup.PROP -> NULL, + Markup.ATTRIBUTE -> NULL, + Markup.METHOD -> NULL, + // ML syntax + Markup.ML_KEYWORD -> KEYWORD1, + Markup.ML_DELIMITER -> OPERATOR, + Markup.ML_IDENT -> NULL, + Markup.ML_TVAR -> NULL, + Markup.ML_NUMERAL -> DIGIT, + Markup.ML_CHAR -> LITERAL1, + Markup.ML_STRING -> LITERAL1, + Markup.ML_COMMENT -> COMMENT1, + Markup.ML_MALFORMED -> INVALID, + // embedded source text + Markup.ML_SOURCE -> COMMENT3, + Markup.DOC_SOURCE -> COMMENT3, + Markup.ANTIQ -> COMMENT4, + Markup.ML_ANTIQ -> COMMENT4, + Markup.DOC_ANTIQ -> COMMENT4, + // outer syntax + Markup.KEYWORD -> KEYWORD2, + Markup.OPERATOR -> OPERATOR, + Markup.COMMAND -> KEYWORD1, + Markup.IDENT -> NULL, + Markup.VERBATIM -> COMMENT3, + Markup.COMMENT -> COMMENT1, + Markup.CONTROL -> COMMENT3, + Markup.MALFORMED -> INVALID, + Markup.STRING -> LITERAL3, + Markup.ALTSTRING -> LITERAL1 + ).withDefaultValue(NULL) + } + + def tokens(syntax: Outer_Syntax): Markup_Tree.Select[Byte] = + { + case Text.Info(_, XML.Elem(Markup(Markup.COMMAND, List((Markup.NAME, name))), _)) + if syntax.keyword_kind(name).isDefined => command_style(syntax.keyword_kind(name).get) + + case Text.Info(_, XML.Elem(Markup(name, _), _)) + if token_style(name) != Token.NULL => token_style(name) + } +} diff -r 1d5e81f5f083 -r 7f4fb691e4b6 src/Tools/jEdit/src/jedit/plugin.scala --- a/src/Tools/jEdit/src/jedit/plugin.scala Wed Sep 08 13:30:41 2010 +0100 +++ b/src/Tools/jEdit/src/jedit/plugin.scala Wed Sep 08 14:46:21 2010 +0200 @@ -286,10 +286,9 @@ Isabelle.setup_tooltips() } - override def stop() + override def stop() // FIXME fragile { Isabelle.session.stop() // FIXME dialog!? Isabelle.session = null - Isabelle.system = null } }