186 }) |
186 }) |
187 delay_flush.invoke() |
187 delay_flush.invoke() |
188 } |
188 } |
189 |
189 |
190 |
190 |
191 /* tooltips -- see $ISABELLE_HOME/src/Tools/jEdit/rendering.scala */ |
191 /* hover */ |
192 |
192 |
193 def hover(uri: String, pos: Line.Position): Option[(Line.Range, List[String])] = |
193 def hover(uri: String, pos: Line.Position): Option[(Line.Range, List[String])] = |
194 for { |
194 for { |
195 model <- state.value.models.get(uri) |
195 model <- state.value.models.get(uri) |
196 snapshot = model.snapshot() |
196 rendering = model.rendering(options) |
197 offset <- model.doc.offset(pos, text_length) |
197 offset <- model.doc.offset(pos, text_length) |
198 info <- tooltip(snapshot, Text.Range(offset, offset + 1)) |
198 info <- rendering.tooltip(Text.Range(offset, offset + 1)) |
199 } yield { |
199 } yield { |
200 val start = model.doc.position(info.range.start, text_length) |
200 val start = model.doc.position(info.range.start, text_length) |
201 val stop = model.doc.position(info.range.stop, text_length) |
201 val stop = model.doc.position(info.range.stop, text_length) |
202 val s = Pretty.string_of(info.info, margin = tooltip_margin.toDouble) |
202 val s = Pretty.string_of(info.info, margin = rendering.tooltip_margin) |
203 (Line.Range(start, stop), List("```\n" + s + "\n```")) |
203 (Line.Range(start, stop), List("```\n" + s + "\n```")) |
204 } |
204 } |
205 |
|
206 private val tooltip_descriptions = |
|
207 Map( |
|
208 Markup.CITATION -> "citation", |
|
209 Markup.TOKEN_RANGE -> "inner syntax token", |
|
210 Markup.FREE -> "free variable", |
|
211 Markup.SKOLEM -> "skolem variable", |
|
212 Markup.BOUND -> "bound variable", |
|
213 Markup.VAR -> "schematic variable", |
|
214 Markup.TFREE -> "free type variable", |
|
215 Markup.TVAR -> "schematic type variable") |
|
216 |
|
217 private val tooltip_elements = |
|
218 Markup.Elements(Markup.LANGUAGE, Markup.EXPRESSION, Markup.TIMING, Markup.ENTITY, |
|
219 Markup.SORTING, Markup.TYPING, Markup.CLASS_PARAMETER, Markup.ML_TYPING, |
|
220 Markup.ML_BREAKPOINT, Markup.PATH, Markup.DOC, Markup.URL, Markup.MARKDOWN_PARAGRAPH, |
|
221 Markup.Markdown_List.name) ++ Markup.Elements(tooltip_descriptions.keySet) |
|
222 |
|
223 private def pretty_typing(kind: String, body: XML.Body): XML.Tree = |
|
224 Pretty.block(XML.Text(kind) :: Pretty.brk(1) :: body) |
|
225 |
|
226 private def timing_threshold: Double = options.real("jedit_timing_threshold") |
|
227 |
|
228 def tooltip(snapshot: Document.Snapshot, range: Text.Range): Option[Text.Info[XML.Body]] = |
|
229 { |
|
230 def add(prev: Text.Info[(Timing, Vector[(Boolean, XML.Tree)])], |
|
231 r0: Text.Range, p: (Boolean, XML.Tree)): Text.Info[(Timing, Vector[(Boolean, XML.Tree)])] = |
|
232 { |
|
233 val r = snapshot.convert(r0) |
|
234 val (t, info) = prev.info |
|
235 if (prev.range == r) |
|
236 Text.Info(r, (t, info :+ p)) |
|
237 else Text.Info(r, (t, Vector(p))) |
|
238 } |
|
239 |
|
240 val results = |
|
241 snapshot.cumulate[Text.Info[(Timing, Vector[(Boolean, XML.Tree)])]]( |
|
242 range, Text.Info(range, (Timing.zero, Vector.empty)), tooltip_elements, _ => |
|
243 { |
|
244 case (Text.Info(r, (t1, info)), Text.Info(_, XML.Elem(Markup.Timing(t2), _))) => |
|
245 Some(Text.Info(r, (t1 + t2, info))) |
|
246 |
|
247 case (prev, Text.Info(r, XML.Elem(Markup.Entity(kind, name), _))) |
|
248 if kind != "" && |
|
249 kind != Markup.ML_DEF && |
|
250 kind != Markup.ML_OPEN && |
|
251 kind != Markup.ML_STRUCTURE => |
|
252 val kind1 = Word.implode(Word.explode('_', kind)) |
|
253 val txt1 = |
|
254 if (name == "") kind1 |
|
255 else if (kind1 == "") quote(name) |
|
256 else kind1 + " " + quote(name) |
|
257 val t = prev.info._1 |
|
258 val txt2 = |
|
259 if (kind == Markup.COMMAND && t.elapsed.seconds >= timing_threshold) |
|
260 "\n" + t.message |
|
261 else "" |
|
262 Some(add(prev, r, (true, XML.Text(txt1 + txt2)))) |
|
263 |
|
264 case (prev, Text.Info(r, XML.Elem(Markup.Doc(name), _))) => |
|
265 val text = "doc " + quote(name) |
|
266 Some(add(prev, r, (true, XML.Text(text)))) |
|
267 |
|
268 case (prev, Text.Info(r, XML.Elem(Markup.Url(name), _))) => |
|
269 Some(add(prev, r, (true, XML.Text("URL " + quote(name))))) |
|
270 |
|
271 case (prev, Text.Info(r, XML.Elem(Markup(name, _), body))) |
|
272 if name == Markup.SORTING || name == Markup.TYPING => |
|
273 Some(add(prev, r, (true, pretty_typing("::", body)))) |
|
274 |
|
275 case (prev, Text.Info(r, XML.Elem(Markup(Markup.CLASS_PARAMETER, _), body))) => |
|
276 Some(add(prev, r, (true, Pretty.block(0, body)))) |
|
277 |
|
278 case (prev, Text.Info(r, XML.Elem(Markup(Markup.ML_TYPING, _), body))) => |
|
279 Some(add(prev, r, (false, pretty_typing("ML:", body)))) |
|
280 |
|
281 case (prev, Text.Info(r, XML.Elem(Markup.Language(language, _, _, _), _))) => |
|
282 val lang = Word.implode(Word.explode('_', language)) |
|
283 Some(add(prev, r, (true, XML.Text("language: " + lang)))) |
|
284 |
|
285 case (prev, Text.Info(r, XML.Elem(Markup.Expression(kind), _))) => |
|
286 val descr = if (kind == "") "expression" else "expression: " + kind |
|
287 Some(add(prev, r, (true, XML.Text(descr)))) |
|
288 |
|
289 case (prev, Text.Info(r, XML.Elem(Markup(Markup.MARKDOWN_PARAGRAPH, _), _))) => |
|
290 Some(add(prev, r, (true, XML.Text("Markdown: paragraph")))) |
|
291 case (prev, Text.Info(r, XML.Elem(Markup.Markdown_List(kind), _))) => |
|
292 Some(add(prev, r, (true, XML.Text("Markdown: " + kind)))) |
|
293 |
|
294 case (prev, Text.Info(r, XML.Elem(Markup(name, _), _))) => |
|
295 tooltip_descriptions.get(name). |
|
296 map(descr => add(prev, r, (true, XML.Text(descr)))) |
|
297 }).map(_.info) |
|
298 |
|
299 results.map(_.info).flatMap(res => res._2.toList) match { |
|
300 case Nil => None |
|
301 case tips => |
|
302 val r = Text.Range(results.head.range.start, results.last.range.stop) |
|
303 val all_tips = (tips.filter(_._1) ++ tips.filter(!_._1).lastOption.toList).map(_._2) |
|
304 Some(Text.Info(r, Library.separate(Pretty.fbrk, all_tips))) |
|
305 } |
|
306 } |
|
307 |
|
308 def tooltip_margin: Int = options.int("jedit_tooltip_margin") |
|
309 |
205 |
310 |
206 |
311 /* main loop */ |
207 /* main loop */ |
312 |
208 |
313 def start() |
209 def start() |