author | wenzelm |
Mon, 21 Feb 2022 20:50:01 +0100 | |
changeset 75119 | 7bf685cbc789 |
parent 73997 | 0f61cd0ce803 |
child 75195 | 596e77cda169 |
permissions | -rw-r--r-- |
65258 | 1 |
/* Title: Tools/jEdit/src/syntax_style.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
Support for extended syntax styles: subscript, superscript, bold, user fonts. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle.jedit |
|
8 |
||
9 |
||
10 |
import isabelle._ |
|
11 |
||
73909 | 12 |
import java.util.{Map => JMap} |
65258 | 13 |
import java.awt.{Font, Color} |
14 |
import java.awt.font.TextAttribute |
|
15 |
import java.awt.geom.AffineTransform |
|
16 |
||
17 |
import org.gjt.sp.util.SyntaxUtilities |
|
73987
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
18 |
import org.gjt.sp.jedit.jEdit |
65258 | 19 |
import org.gjt.sp.jedit.syntax.{Token => JEditToken, SyntaxStyle} |
65259 | 20 |
import org.gjt.sp.jedit.textarea.TextArea |
65258 | 21 |
|
22 |
||
23 |
object Syntax_Style |
|
24 |
{ |
|
65259 | 25 |
/* extended syntax styles */ |
26 |
||
65258 | 27 |
private val plain_range: Int = JEditToken.ID_COUNT |
73995 | 28 |
private val full_range: Int = 6 * plain_range |
73340 | 29 |
private def check_range(i: Int): Unit = |
30 |
require(0 <= i && i < plain_range, "bad syntax style range") |
|
65258 | 31 |
|
32 |
def subscript(i: Byte): Byte = { check_range(i); (i + plain_range).toByte } |
|
33 |
def superscript(i: Byte): Byte = { check_range(i); (i + 2 * plain_range).toByte } |
|
34 |
def bold(i: Byte): Byte = { check_range(i); (i + 3 * plain_range).toByte } |
|
35 |
def user_font(idx: Int, i: Byte): Byte = { check_range(i); (i + (4 + idx) * plain_range).toByte } |
|
73995 | 36 |
val hidden: Byte = full_range.toByte |
67128 | 37 |
val control: Byte = (hidden + JEditToken.DIGIT).toByte |
65258 | 38 |
|
39 |
private def font_style(style: SyntaxStyle, f: Font => Font): SyntaxStyle = |
|
40 |
new SyntaxStyle(style.getForegroundColor, style.getBackgroundColor, f(style.getFont)) |
|
41 |
||
42 |
private def script_style(style: SyntaxStyle, i: Int): SyntaxStyle = |
|
43 |
{ |
|
44 |
font_style(style, font0 => |
|
45 |
{ |
|
73909 | 46 |
val font1 = font0.deriveFont(JMap.of(TextAttribute.SUPERSCRIPT, java.lang.Integer.valueOf(i))) |
65258 | 47 |
|
48 |
def shift(y: Float): Font = |
|
49 |
GUI.transform_font(font1, AffineTransform.getTranslateInstance(0.0, y.toDouble)) |
|
50 |
||
51 |
val m0 = GUI.line_metrics(font0) |
|
52 |
val m1 = GUI.line_metrics(font1) |
|
53 |
val a = m1.getAscent - m0.getAscent |
|
54 |
val b = (m1.getDescent + m1.getLeading) - (m0.getDescent + m0.getLeading) |
|
55 |
if (a > 0.0f) shift(a) |
|
56 |
else if (b > 0.0f) shift(- b) |
|
57 |
else font1 |
|
58 |
}) |
|
59 |
} |
|
60 |
||
61 |
private def bold_style(style: SyntaxStyle): SyntaxStyle = |
|
62 |
font_style(style, font => font.deriveFont(if (font.isBold) Font.PLAIN else Font.BOLD)) |
|
63 |
||
64 |
val hidden_color: Color = new Color(255, 255, 255, 0) |
|
65 |
||
73987
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
66 |
def set_extender(extender: SyntaxUtilities.StyleExtender): Unit = |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
67 |
{ |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
68 |
SyntaxUtilities.setStyleExtender(extender) |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
69 |
GUI_Thread.later { jEdit.propertiesChanged } |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
70 |
} |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
71 |
|
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
72 |
object Base_Extender extends SyntaxUtilities.StyleExtender |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
73 |
{ |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
74 |
override def extendStyles(styles: Array[SyntaxStyle]): Array[SyntaxStyle] = |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
75 |
{ |
73995 | 76 |
val new_styles = Array.fill[SyntaxStyle](java.lang.Byte.MAX_VALUE)(styles(0)) |
73987
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
77 |
for (i <- 0 until full_range) { |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
78 |
new_styles(i) = styles(i % plain_range) |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
79 |
} |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
80 |
new_styles |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
81 |
} |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
82 |
} |
fc363a3b690a
build.props for isabelle.jar, including isabelle.jedit;
wenzelm
parents:
73909
diff
changeset
|
83 |
|
73997 | 84 |
object Main_Extender extends SyntaxUtilities.StyleExtender |
65258 | 85 |
{ |
86 |
val max_user_fonts = 2 |
|
87 |
if (Symbol.font_names.length > max_user_fonts) |
|
88 |
error("Too many user symbol fonts (" + max_user_fonts + " permitted): " + |
|
89 |
Symbol.font_names.mkString(", ")) |
|
90 |
||
91 |
override def extendStyles(styles: Array[SyntaxStyle]): Array[SyntaxStyle] = |
|
92 |
{ |
|
67128 | 93 |
val style0 = styles(0) |
94 |
val font0 = style0.getFont |
|
95 |
||
96 |
val new_styles = Array.fill[SyntaxStyle](java.lang.Byte.MAX_VALUE)(styles(0)) |
|
65258 | 97 |
for (i <- 0 until plain_range) { |
98 |
val style = styles(i) |
|
99 |
new_styles(i) = style |
|
100 |
new_styles(subscript(i.toByte)) = script_style(style, -1) |
|
101 |
new_styles(superscript(i.toByte)) = script_style(style, 1) |
|
102 |
new_styles(bold(i.toByte)) = bold_style(style) |
|
103 |
for (idx <- 0 until max_user_fonts) |
|
104 |
new_styles(user_font(idx, i.toByte)) = style |
|
105 |
for ((family, idx) <- Symbol.font_index) |
|
106 |
new_styles(user_font(idx, i.toByte)) = font_style(style, GUI.imitate_font(_, family)) |
|
107 |
} |
|
108 |
new_styles(hidden) = |
|
109 |
new SyntaxStyle(hidden_color, null, |
|
67128 | 110 |
GUI.transform_font(new Font(font0.getFamily, 0, 1), |
70124
af4f723823d8
visible hairline for cursor, even on OpenJDK 11 (amending 2fd73a1a0937);
wenzelm
parents:
69969
diff
changeset
|
111 |
AffineTransform.getScaleInstance(2.0, font0.getSize.toDouble))) |
67128 | 112 |
new_styles(control) = |
113 |
new SyntaxStyle(style0.getForegroundColor, style0.getBackgroundColor, |
|
114 |
{ val font_style = |
|
115 |
(if (font0.isItalic) 0 else Font.ITALIC) | |
|
116 |
(if (font0.isBold) 0 else Font.BOLD) |
|
117 |
new Font(font0.getFamily, font_style, font0.getSize) }) |
|
65258 | 118 |
new_styles |
119 |
} |
|
120 |
} |
|
121 |
||
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
122 |
private def control_style(sym: String): Option[Byte => Byte] = |
71601 | 123 |
if (sym == Symbol.sub_decoded) Some(subscript) |
124 |
else if (sym == Symbol.sup_decoded) Some(superscript) |
|
125 |
else if (sym == Symbol.bold_decoded) Some(bold) |
|
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
126 |
else None |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
127 |
|
65258 | 128 |
def extended(text: CharSequence): Map[Text.Offset, Byte => Byte] = |
129 |
{ |
|
130 |
var result = Map[Text.Offset, Byte => Byte]() |
|
73340 | 131 |
def mark(start: Text.Offset, stop: Text.Offset, style: Byte => Byte): Unit = |
65258 | 132 |
{ |
133 |
for (i <- start until stop) result += (i -> style) |
|
134 |
} |
|
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
135 |
|
65258 | 136 |
var offset = 0 |
67128 | 137 |
var control_sym = "" |
65258 | 138 |
for (sym <- Symbol.iterator(text)) { |
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
139 |
val end_offset = offset + sym.length |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
140 |
|
67128 | 141 |
if (control_style(sym).isDefined) control_sym = sym |
142 |
else if (control_sym != "") { |
|
66006
cec184536dfd
uniform notion of Symbol.is_controllable (see also 265d9300d523);
wenzelm
parents:
65997
diff
changeset
|
143 |
if (Symbol.is_controllable(sym) && !Symbol.fonts.isDefinedAt(sym)) { |
67128 | 144 |
mark(offset - control_sym.length, offset, _ => hidden) |
145 |
mark(offset, end_offset, control_style(control_sym).get) |
|
65258 | 146 |
} |
67128 | 147 |
control_sym = "" |
65258 | 148 |
} |
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
149 |
|
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
150 |
if (Symbol.is_control_encoded(sym)) { |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
151 |
val a = offset + Symbol.control_prefix.length |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
152 |
val b = end_offset - Symbol.control_suffix.length |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
153 |
mark(offset, a, _ => hidden) |
67128 | 154 |
mark(a, b, _ => control) |
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
155 |
mark(b, end_offset, _ => hidden) |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
156 |
} |
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
157 |
|
65258 | 158 |
Symbol.lookup_font(sym) match { |
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
159 |
case Some(idx) => mark(offset, end_offset, user_font(idx, _)) |
65258 | 160 |
case _ => |
161 |
} |
|
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
162 |
|
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
163 |
offset = end_offset |
65258 | 164 |
} |
67127
cf111622c9f8
font style for literal control symbols, notably for antiquotations;
wenzelm
parents:
67125
diff
changeset
|
165 |
|
65258 | 166 |
result |
167 |
} |
|
65259 | 168 |
|
169 |
||
170 |
/* editing support for control symbols */ |
|
171 |
||
73340 | 172 |
def edit_control_style(text_area: TextArea, control_sym: String): Unit = |
65259 | 173 |
{ |
174 |
GUI_Thread.assert {} |
|
175 |
||
176 |
val buffer = text_area.getBuffer |
|
177 |
||
69969 | 178 |
val control_decoded = Isabelle_Encoding.perhaps_decode(buffer, control_sym) |
65259 | 179 |
|
180 |
def update_style(text: String): String = |
|
181 |
{ |
|
182 |
val result = new StringBuilder |
|
65997
e3dc9ea67a62
output control symbols like ML version, with optionally hidden source;
wenzelm
parents:
65265
diff
changeset
|
183 |
for (sym <- Symbol.iterator(text) if !HTML.is_control(sym)) { |
65259 | 184 |
if (Symbol.is_controllable(sym)) result ++= control_decoded |
185 |
result ++= sym |
|
186 |
} |
|
187 |
result.toString |
|
188 |
} |
|
189 |
||
190 |
text_area.getSelection.foreach(sel => { |
|
191 |
val before = JEdit_Lib.point_range(buffer, sel.getStart - 1) |
|
67014 | 192 |
JEdit_Lib.get_text(buffer, before) match { |
65997
e3dc9ea67a62
output control symbols like ML version, with optionally hidden source;
wenzelm
parents:
65265
diff
changeset
|
193 |
case Some(s) if HTML.is_control(s) => |
65259 | 194 |
text_area.extendSelection(before.start, before.stop) |
195 |
case _ => |
|
196 |
} |
|
197 |
}) |
|
198 |
||
199 |
text_area.getSelection.toList match { |
|
200 |
case Nil => |
|
201 |
text_area.setSelectedText(control_decoded) |
|
202 |
case sels => |
|
203 |
JEdit_Lib.buffer_edit(buffer) { |
|
204 |
sels.foreach(sel => |
|
205 |
text_area.setSelectedText(sel, update_style(text_area.getSelectedText(sel)))) |
|
206 |
} |
|
207 |
} |
|
208 |
} |
|
65258 | 209 |
} |