wenzelm@43414: /* Title: Tools/jEdit/src/token_markup.scala wenzelm@43414: Author: Makarius wenzelm@43414: wenzelm@43414: Outer syntax token markup. wenzelm@43414: */ wenzelm@43414: wenzelm@43414: package isabelle.jedit wenzelm@43414: wenzelm@43414: wenzelm@43414: import isabelle._ wenzelm@43414: wenzelm@43482: import java.awt.{Font, Color} wenzelm@43502: import java.awt.font.{TextAttribute, TransformAttribute, FontRenderContext, LineMetrics} wenzelm@43491: import java.awt.geom.AffineTransform wenzelm@43482: wenzelm@43482: import org.gjt.sp.util.SyntaxUtilities wenzelm@43452: import org.gjt.sp.jedit.Mode wenzelm@43452: import org.gjt.sp.jedit.syntax.{Token => JEditToken, TokenMarker, TokenHandler, wenzelm@43482: ParserRuleSet, ModeProvider, XModeHandler, SyntaxStyle} wenzelm@43452: wenzelm@43414: import javax.swing.text.Segment wenzelm@43414: wenzelm@43414: wenzelm@43414: object Token_Markup wenzelm@43414: { wenzelm@43502: /* font operations */ wenzelm@43502: wenzelm@43502: private def font_metrics(font: Font): LineMetrics = wenzelm@43502: font.getLineMetrics("", new FontRenderContext(null, false, false)) wenzelm@43502: wenzelm@43502: private def imitate_font(family: String, font: Font): Font = wenzelm@43502: { wenzelm@43502: val font1 = new Font (family, font.getStyle, font.getSize) wenzelm@43502: font1.deriveFont(font_metrics(font).getAscent / font_metrics(font1).getAscent * font.getSize) wenzelm@43502: } wenzelm@43502: wenzelm@43502: private def transform_font(font: Font, transform: AffineTransform): Font = wenzelm@43502: { wenzelm@43502: import scala.collection.JavaConversions._ wenzelm@43502: font.deriveFont(Map(TextAttribute.TRANSFORM -> new TransformAttribute(transform))) wenzelm@43502: } wenzelm@43502: wenzelm@43502: wenzelm@43443: /* extended syntax styles */ wenzelm@43414: wenzelm@43440: private val plain_range: Int = JEditToken.ID_COUNT wenzelm@43488: private val full_range = 6 * plain_range + 1 wenzelm@43414: private def check_range(i: Int) { require(0 <= i && i < plain_range) } wenzelm@43414: wenzelm@43414: def subscript(i: Byte): Byte = { check_range(i); (i + plain_range).toByte } wenzelm@43414: def superscript(i: Byte): Byte = { check_range(i); (i + 2 * plain_range).toByte } wenzelm@43460: def bold(i: Byte): Byte = { check_range(i); (i + 3 * plain_range).toByte } wenzelm@43487: def user_font(idx: Int, i: Byte): Byte = { check_range(i); (i + (4 + idx) * plain_range).toByte } wenzelm@43487: val hidden: Byte = (6 * plain_range).toByte wenzelm@43487: wenzelm@43487: private def font_style(style: SyntaxStyle, f: Font => Font): SyntaxStyle = wenzelm@43487: new SyntaxStyle(style.getForegroundColor, style.getBackgroundColor, f(style.getFont)) wenzelm@43414: wenzelm@43482: private def script_style(style: SyntaxStyle, i: Int): SyntaxStyle = wenzelm@43482: { wenzelm@43502: font_style(style, font0 => wenzelm@43502: { wenzelm@43502: import scala.collection.JavaConversions._ wenzelm@43502: val font1 = font0.deriveFont(Map(TextAttribute.SUPERSCRIPT -> new java.lang.Integer(i))) wenzelm@43502: wenzelm@43502: def shift(y: Float): Font = wenzelm@43502: transform_font(font1, AffineTransform.getTranslateInstance(0.0, y.toDouble)) wenzelm@43502: wenzelm@43502: val m0 = font_metrics(font0) wenzelm@43502: val m1 = font_metrics(font1) wenzelm@43502: val a = m1.getAscent - m0.getAscent wenzelm@43502: val b = (m1.getDescent + m1.getLeading) - (m0.getDescent + m0.getLeading) wenzelm@43502: if (a > 0.0f) shift(a) wenzelm@43502: else if (b > 0.0f) shift(- b) wenzelm@43502: else font1 wenzelm@43502: }) wenzelm@43482: } wenzelm@43482: wenzelm@43482: private def bold_style(style: SyntaxStyle): SyntaxStyle = wenzelm@43487: font_style(style, _.deriveFont(Font.BOLD)) wenzelm@43482: wenzelm@43661: class Style_Extender extends SyntaxUtilities.StyleExtender wenzelm@43482: { wenzelm@44355: val max_user_fonts = 2 wenzelm@44355: if (Symbol.font_names.length > max_user_fonts) wenzelm@44355: error("Too many user symbol fonts (max " + max_user_fonts + " permitted): " + wenzelm@44355: Symbol.font_names.mkString(", ")) wenzelm@43487: wenzelm@43482: override def extendStyles(styles: Array[SyntaxStyle]): Array[SyntaxStyle] = wenzelm@43482: { wenzelm@43482: val new_styles = new Array[SyntaxStyle](full_range) wenzelm@43482: for (i <- 0 until plain_range) { wenzelm@43482: val style = styles(i) wenzelm@43482: new_styles(i) = style wenzelm@43482: new_styles(subscript(i.toByte)) = script_style(style, -1) wenzelm@43482: new_styles(superscript(i.toByte)) = script_style(style, 1) wenzelm@43482: new_styles(bold(i.toByte)) = bold_style(style) wenzelm@44355: for (idx <- 0 until max_user_fonts) wenzelm@44355: new_styles(user_font(idx, i.toByte)) = style wenzelm@43695: for ((family, idx) <- Symbol.font_index) wenzelm@43502: new_styles(user_font(idx, i.toByte)) = font_style(style, imitate_font(family, _)) wenzelm@43482: } wenzelm@43502: new_styles(hidden) = wenzelm@43502: new SyntaxStyle(Color.white, null, wenzelm@43502: { val font = styles(0).getFont wenzelm@43502: transform_font(new Font(font.getFamily, 0, 1), wenzelm@43502: AffineTransform.getScaleInstance(1.0, font.getSize.toDouble)) }) wenzelm@43482: new_styles wenzelm@43482: } wenzelm@43482: } wenzelm@43482: wenzelm@43661: def extended_styles(text: CharSequence): Map[Text.Offset, Byte => Byte] = wenzelm@43443: { wenzelm@44238: // FIXME Symbol.bsub_decoded etc. wenzelm@43482: def ctrl_style(sym: String): Option[Byte => Byte] = wenzelm@44238: if (sym == Symbol.sub_decoded || sym == Symbol.isub_decoded) Some(subscript(_)) wenzelm@44238: else if (sym == Symbol.sup_decoded || sym == Symbol.isup_decoded) Some(superscript(_)) wenzelm@44238: else if (sym == Symbol.bold_decoded) Some(bold(_)) wenzelm@43482: else None wenzelm@43455: wenzelm@43482: var result = Map[Text.Offset, Byte => Byte]() wenzelm@43482: def mark(start: Text.Offset, stop: Text.Offset, style: Byte => Byte) wenzelm@43482: { wenzelm@43482: for (i <- start until stop) result += (i -> style) wenzelm@43482: } wenzelm@43482: var offset = 0 wenzelm@43482: var ctrl = "" wenzelm@43675: for (sym <- Symbol.iterator(text)) { wenzelm@43482: if (ctrl_style(sym).isDefined) ctrl = sym wenzelm@43482: else if (ctrl != "") { wenzelm@43695: if (Symbol.is_controllable(sym) && sym != "\"" && !Symbol.fonts.isDefinedAt(sym)) { wenzelm@43482: mark(offset - ctrl.length, offset, _ => hidden) wenzelm@43482: mark(offset, offset + sym.length, ctrl_style(ctrl).get) wenzelm@43482: } wenzelm@43482: ctrl = "" wenzelm@43443: } wenzelm@43695: Symbol.lookup_font(sym) match { wenzelm@43488: case Some(idx) => mark(offset, offset + sym.length, user_font(idx, _)) wenzelm@43487: case _ => wenzelm@43487: } wenzelm@43482: offset += sym.length wenzelm@43443: } wenzelm@43482: result wenzelm@43443: } wenzelm@43443: wenzelm@43414: wenzelm@43414: /* token marker */ wenzelm@43414: wenzelm@43414: private val isabelle_rules = new ParserRuleSet("isabelle", "MAIN") wenzelm@43414: wenzelm@43429: private class Line_Context(val context: Scan.Context) wenzelm@43429: extends TokenMarker.LineContext(isabelle_rules, null) wenzelm@43414: { wenzelm@43414: override def hashCode: Int = context.hashCode wenzelm@43414: override def equals(that: Any): Boolean = wenzelm@43414: that match { wenzelm@43414: case other: Line_Context => context == other.context wenzelm@43414: case _ => false wenzelm@43414: } wenzelm@43414: } wenzelm@43414: wenzelm@43452: class Marker extends TokenMarker wenzelm@43452: { wenzelm@43452: override def markTokens(context: TokenMarker.LineContext, wenzelm@43452: handler: TokenHandler, line: Segment): TokenMarker.LineContext = wenzelm@43452: { wenzelm@43553: val context1 = wenzelm@43553: if (Isabelle.session.is_ready) { wenzelm@43553: val syntax = Isabelle.session.current_syntax() wenzelm@43553: wenzelm@43553: val ctxt = wenzelm@43553: context match { wenzelm@43553: case c: Line_Context => c.context wenzelm@43553: case _ => Scan.Finished wenzelm@43553: } wenzelm@43553: val (tokens, ctxt1) = syntax.scan_context(line, ctxt) wenzelm@43553: val context1 = new Line_Context(ctxt1) wenzelm@43553: wenzelm@43661: val extended = extended_styles(line) wenzelm@43553: wenzelm@43553: var offset = 0 wenzelm@43553: for (token <- tokens) { wenzelm@43553: val style = Isabelle_Markup.token_markup(syntax, token) wenzelm@43553: val length = token.source.length wenzelm@43553: val end_offset = offset + length wenzelm@43553: if ((offset until end_offset) exists extended.isDefinedAt) { wenzelm@43553: for (i <- offset until end_offset) { wenzelm@43553: val style1 = wenzelm@43553: extended.get(i) match { wenzelm@43553: case None => style wenzelm@43553: case Some(ext) => ext(style) wenzelm@43553: } wenzelm@43553: handler.handleToken(line, style1, i, 1, context1) wenzelm@43553: } wenzelm@43553: } wenzelm@43553: else handler.handleToken(line, style, offset, length, context1) wenzelm@43553: offset += length wenzelm@43553: } wenzelm@43553: handler.handleToken(line, JEditToken.END, line.count, 0, context1) wenzelm@43553: context1 wenzelm@43452: } wenzelm@43553: else { wenzelm@43553: val context1 = new Line_Context(Scan.Finished) wenzelm@43553: handler.handleToken(line, JEditToken.NULL, 0, line.count, context1) wenzelm@43553: handler.handleToken(line, JEditToken.END, line.count, 0, context1) wenzelm@43553: context1 wenzelm@43414: } wenzelm@43452: val context2 = context1.intern wenzelm@43452: handler.setLineContext(context2) wenzelm@43452: context2 wenzelm@43452: } wenzelm@43452: } wenzelm@43452: wenzelm@43416: wenzelm@43452: /* mode provider */ wenzelm@43452: wenzelm@43452: class Mode_Provider(orig_provider: ModeProvider) extends ModeProvider wenzelm@43452: { wenzelm@43452: for (mode <- orig_provider.getModes) addMode(mode) wenzelm@43452: wenzelm@43452: val isabelle_token_marker = new Token_Markup.Marker wenzelm@43452: wenzelm@43452: override def loadMode(mode: Mode, xmh: XModeHandler) wenzelm@43452: { wenzelm@43452: super.loadMode(mode, xmh) wenzelm@43452: if (mode.getName == "isabelle") wenzelm@43452: mode.setTokenMarker(isabelle_token_marker) wenzelm@43414: } wenzelm@43452: } wenzelm@43414: } wenzelm@43414: