602 def timing_threshold: Double = 0.0 |
602 def timing_threshold: Double = 0.0 |
603 |
603 |
604 private sealed case class Tooltip_Info( |
604 private sealed case class Tooltip_Info( |
605 range: Text.Range, |
605 range: Text.Range, |
606 timing: Timing = Timing.zero, |
606 timing: Timing = Timing.zero, |
607 messages: List[(Long, XML.Tree)] = Nil, |
607 messages: List[(Long, XML.Elem)] = Nil, |
608 rev_infos: List[(Boolean, Int, XML.Tree)] = Nil |
608 rev_infos: List[(Boolean, Int, XML.Elem)] = Nil |
609 ) { |
609 ) { |
610 def add_timing(t: Timing): Tooltip_Info = copy(timing = timing + t) |
610 def add_timing(t: Timing): Tooltip_Info = copy(timing = timing + t) |
611 def add_message(r0: Text.Range, serial: Long, tree: XML.Tree): Tooltip_Info = { |
611 def add_message(r0: Text.Range, serial: Long, msg: XML.Elem): Tooltip_Info = { |
612 val r = snapshot.convert(r0) |
612 val r = snapshot.convert(r0) |
613 if (range == r) copy(messages = (serial -> tree) :: messages) |
613 if (range == r) copy(messages = (serial -> msg) :: messages) |
614 else copy(range = r, messages = List(serial -> tree)) |
614 else copy(range = r, messages = List(serial -> msg)) |
615 } |
615 } |
616 def add_info(r0: Text.Range, tree: XML.Tree, important: Boolean = true, ord: Int = 0): Tooltip_Info = { |
616 def add_info(r0: Text.Range, info: XML.Elem, |
|
617 important: Boolean = true, |
|
618 ord: Int = 0 |
|
619 ): Tooltip_Info = { |
617 val r = snapshot.convert(r0) |
620 val r = snapshot.convert(r0) |
618 val entry = (important, ord, tree) |
621 val entry = (important, ord, info) |
619 if (range == r) copy(rev_infos = entry :: rev_infos) |
622 if (range == r) copy(rev_infos = entry :: rev_infos) |
620 else copy (range = r, rev_infos = List(entry)) |
623 else copy (range = r, rev_infos = List(entry)) |
621 } |
624 } |
622 |
625 def add_info_text(r0: Text.Range, text: String, ord: Int = 0): Tooltip_Info = |
623 def timing_info(tree: XML.Tree): Option[XML.Tree] = |
626 add_info(r0, Pretty.string(text), ord = ord) |
624 tree match { |
627 |
625 case XML.Elem(Markup(Markup.TIMING, _), _) => |
628 def timing_info(elem: XML.Elem): Option[XML.Elem] = |
626 if (timing.elapsed.seconds >= timing_threshold) Some(XML.Text(timing.message)) else None |
629 if (elem.markup.name == Markup.TIMING) { |
627 case _ => Some(tree) |
630 if (timing.elapsed.seconds >= timing_threshold) { |
|
631 Some(Pretty.string(timing.message)) |
|
632 } |
|
633 else None |
628 } |
634 } |
629 def infos(important: Boolean = true): List[XML.Tree] = |
635 else Some(elem) |
|
636 def infos(important: Boolean = true): List[XML.Elem] = |
630 for { |
637 for { |
631 (is_important, _, tree) <- rev_infos.reverse.sortBy(_._2) if is_important == important |
638 (is_important, _, elem) <- rev_infos.reverse.sortBy(_._2) if is_important == important |
632 tree1 <- timing_info(tree) |
639 elem1 <- timing_info(elem) |
633 } yield tree1 |
640 } yield elem1 |
634 } |
641 } |
635 |
642 |
636 def perhaps_append_file(node_name: Document.Node.Name, name: String): String = |
643 def perhaps_append_file(node_name: Document.Node.Name, name: String): String = |
637 if (Path.is_valid(name)) session.resources.append_path(node_name.master_dir, Path.explode(name)) |
644 if (Path.is_valid(name)) session.resources.append_path(node_name.master_dir, Path.explode(name)) |
638 else name |
645 else name |
639 |
646 |
640 def tooltips(elements: Markup.Elements, range: Text.Range): Option[Text.Info[List[XML.Tree]]] = { |
647 def tooltips(elements: Markup.Elements, range: Text.Range): Option[Text.Info[List[XML.Elem]]] = { |
641 val results = |
648 val results = |
642 snapshot.cumulate[Tooltip_Info](range, Tooltip_Info(range), elements, command_states => |
649 snapshot.cumulate[Tooltip_Info](range, Tooltip_Info(range), elements, command_states => |
643 { |
650 { |
644 case (info, Text.Info(_, XML.Elem(Markup.Timing(t), _))) => Some(info.add_timing(t)) |
651 case (info, Text.Info(_, XML.Elem(Markup.Timing(t), _))) => Some(info.add_timing(t)) |
645 |
652 |
646 case (info, Text.Info(r0, msg @ XML.Elem(Markup(Markup.BAD, Markup.Serial(i)), body))) |
653 case (info, Text.Info(r0, msg @ XML.Elem(Markup(Markup.BAD, Markup.Serial(i)), body))) |
647 if body.nonEmpty => Some(info.add_message(r0, i, msg)) |
654 if body.nonEmpty => Some(info.add_message(r0, i, msg)) |
648 |
655 |
649 case (info, Text.Info(r0, XML.Elem(Markup(name, props), _))) |
656 case (info, Text.Info(r0, XML.Elem(Markup(name, props), _))) |
650 if Rendering.tooltip_message_elements(name) => |
657 if Rendering.tooltip_message_elements(name) => |
651 for ((i, tree) <- Command.State.get_result_proper(command_states, props)) |
658 for ((i, msg) <- Command.State.get_result_proper(command_states, props)) |
652 yield info.add_message(r0, i, tree) |
659 yield info.add_message(r0, i, msg) |
653 |
660 |
654 case (info, Text.Info(r0, XML.Elem(Markup.Entity(kind, name), _))) |
661 case (info, Text.Info(r0, XML.Elem(Markup.Entity(kind, name), _))) |
655 if kind != "" && kind != Markup.ML_DEF => |
662 if kind != "" && kind != Markup.ML_DEF => |
656 val kind1 = Word.implode(Word.explode('_', kind)) |
663 val kind1 = Word.implode(Word.explode('_', kind)) |
657 val txt1 = |
664 val txt1 = |
658 if (name == "") kind1 |
665 if (name == "") kind1 |
659 else if (kind1 == "") quote(name) |
666 else if (kind1 == "") quote(name) |
660 else kind1 + " " + quote(name) |
667 else kind1 + " " + quote(name) |
661 val info1 = info.add_info(r0, XML.Text(txt1), ord = 2) |
668 val info1 = info.add_info_text(r0, txt1, ord = 2) |
662 Some(if (kind == Markup.COMMAND) info1.add_info(r0, XML.elem(Markup.TIMING)) else info1) |
669 Some(if (kind == Markup.COMMAND) info1.add_info(r0, XML.elem(Markup.TIMING)) else info1) |
663 |
670 |
664 case (info, Text.Info(r0, XML.Elem(Markup.Path(name), _))) => |
671 case (info, Text.Info(r0, XML.Elem(Markup.Path(name), _))) => |
665 val file = perhaps_append_file(snapshot.node_name, name) |
672 val file = perhaps_append_file(snapshot.node_name, name) |
666 val text = |
673 val text = |
667 if (name == file) "file " + quote(file) |
674 if (name == file) "file " + quote(file) |
668 else "path " + quote(name) + "\nfile " + quote(file) |
675 else "path " + quote(name) + "\nfile " + quote(file) |
669 Some(info.add_info(r0, XML.Text(text))) |
676 Some(info.add_info_text(r0, text)) |
670 |
677 |
671 case (info, Text.Info(r0, XML.Elem(Markup.Doc(name), _))) => |
678 case (info, Text.Info(r0, XML.Elem(Markup.Doc(name), _))) => |
672 val text = "doc " + quote(name) |
679 val text = "doc " + quote(name) |
673 Some(info.add_info(r0, XML.Text(text))) |
680 Some(info.add_info_text(r0, text)) |
674 |
681 |
675 case (info, Text.Info(r0, XML.Elem(Markup.Url(name), _))) => |
682 case (info, Text.Info(r0, XML.Elem(Markup.Url(name), _))) => |
676 Some(info.add_info(r0, XML.Text("URL " + quote(name)))) |
683 Some(info.add_info_text(r0, "URL " + quote(name))) |
677 |
684 |
678 case (info, Text.Info(r0, XML.Elem(Markup.Command_Span(span), _))) => |
685 case (info, Text.Info(r0, XML.Elem(Markup.Command_Span(span), _))) => |
679 Some(info.add_info(r0, XML.Text("command span " + quote(span.name)))) |
686 Some(info.add_info_text(r0, "command span " + quote(span.name))) |
680 |
687 |
681 case (info, Text.Info(r0, XML.Elem(Markup(name, _), body))) |
688 case (info, Text.Info(r0, XML.Elem(Markup(name, _), body))) |
682 if name == Markup.SORTING || name == Markup.TYPING => |
689 if name == Markup.SORTING || name == Markup.TYPING => |
683 Some(info.add_info(r0, Pretty.block(XML.Text("::") :: Pretty.brk(1) :: body), ord = 3)) |
690 Some(info.add_info(r0, Pretty.block(XML.Text("::") :: Pretty.brk(1) :: body), ord = 3)) |
684 |
691 |
691 |
698 |
692 case (info, Text.Info(r0, Protocol.ML_Breakpoint(breakpoint))) => |
699 case (info, Text.Info(r0, Protocol.ML_Breakpoint(breakpoint))) => |
693 val text = |
700 val text = |
694 if (session.debugger.breakpoint_state(breakpoint)) "breakpoint (enabled)" |
701 if (session.debugger.breakpoint_state(breakpoint)) "breakpoint (enabled)" |
695 else "breakpoint (disabled)" |
702 else "breakpoint (disabled)" |
696 Some(info.add_info(r0, XML.Text(text))) |
703 Some(info.add_info_text(r0, text)) |
697 |
704 |
698 case (info, Text.Info(r0, XML.Elem(Markup.Language(lang), _))) => |
705 case (info, Text.Info(r0, XML.Elem(Markup.Language(lang), _))) => |
699 Some(info.add_info(r0, XML.Text("language: " + lang.description))) |
706 Some(info.add_info_text(r0, "language: " + lang.description)) |
700 |
707 |
701 case (info, Text.Info(r0, XML.Elem(Markup.Notation(kind, name), _))) => |
708 case (info, Text.Info(r0, XML.Elem(Markup.Notation(kind, name), _))) => |
702 val a = kind.nonEmpty |
709 val a = kind.nonEmpty |
703 val b = name.nonEmpty |
710 val b = name.nonEmpty |
704 val k = Word.implode(Word.explode('_', kind)) |
711 val k = Word.implode(Word.explode('_', kind)) |
705 val description = |
712 val description = |
706 if (!a && !b) "notation" |
713 if (!a && !b) "notation" |
707 else "notation: " + k + if_proper(a && b, " ") + if_proper(b, quote(name)) |
714 else "notation: " + k + if_proper(a && b, " ") + if_proper(b, quote(name)) |
708 Some(info.add_info(r0, XML.Text(description), ord = 1)) |
715 Some(info.add_info_text(r0, description, ord = 1)) |
709 |
716 |
710 case (info, Text.Info(r0, XML.Elem(Markup.Expression(kind), _))) => |
717 case (info, Text.Info(r0, XML.Elem(Markup.Expression(kind), _))) => |
711 val description = |
718 val description = |
712 if (kind.isEmpty) "expression" |
719 if (kind.isEmpty) "expression" |
713 else "expression: " + Word.implode(Word.explode('_', kind)) |
720 else "expression: " + Word.implode(Word.explode('_', kind)) |
714 Some(info.add_info(r0, XML.Text(description), ord = 1)) |
721 Some(info.add_info_text(r0, description, ord = 1)) |
715 |
722 |
716 case (info, Text.Info(r0, XML.Elem(Markup(Markup.MARKDOWN_PARAGRAPH, _), _))) => |
723 case (info, Text.Info(r0, XML.Elem(Markup(Markup.MARKDOWN_PARAGRAPH, _), _))) => |
717 Some(info.add_info(r0, XML.Text("Markdown: paragraph"))) |
724 Some(info.add_info_text(r0, "Markdown: paragraph")) |
718 case (info, Text.Info(r0, XML.Elem(Markup(Markup.MARKDOWN_ITEM, _), _))) => |
725 case (info, Text.Info(r0, XML.Elem(Markup(Markup.MARKDOWN_ITEM, _), _))) => |
719 Some(info.add_info(r0, XML.Text("Markdown: item"))) |
726 Some(info.add_info_text(r0, "Markdown: item")) |
720 case (info, Text.Info(r0, XML.Elem(Markup.Markdown_List(kind), _))) => |
727 case (info, Text.Info(r0, XML.Elem(Markup.Markdown_List(kind), _))) => |
721 Some(info.add_info(r0, XML.Text("Markdown: " + kind))) |
728 Some(info.add_info_text(r0, "Markdown: " + kind)) |
722 |
729 |
723 case (info, Text.Info(r0, XML.Elem(Markup(name, _), _))) => |
730 case (info, Text.Info(r0, XML.Elem(Markup(name, _), _))) => |
724 Rendering.tooltip_descriptions.get(name).map(desc => info.add_info(r0, XML.Text(desc))) |
731 Rendering.tooltip_descriptions.get(name).map(desc => info.add_info_text(r0, desc)) |
725 }).map(_.info) |
732 }).map(_.info) |
726 |
733 |
727 if (results.isEmpty) None |
734 if (results.isEmpty) None |
728 else { |
735 else { |
729 val r = Text.Range(results.head.range.start, results.last.range.stop) |
736 val r = Text.Range(results.head.range.start, results.last.range.stop) |
730 val all_tips = |
737 val all_tips = |
731 results.flatMap(_.messages).foldLeft(SortedMap.empty[Long, XML.Tree])(_ + _) |
738 results.flatMap(_.messages).foldLeft(SortedMap.empty[Long, XML.Elem])(_ + _) |
732 .iterator.map(_._2).toList ::: |
739 .iterator.map(_._2).toList ::: |
733 results.flatMap(res => res.infos()) ::: |
740 results.flatMap(res => res.infos()) ::: |
734 results.flatMap(res => res.infos(important = false)).lastOption.toList |
741 results.flatMap(res => res.infos(important = false)).lastOption.toList |
735 if (all_tips.isEmpty) None else Some(Text.Info(r, all_tips)) |
742 if (all_tips.isEmpty) None else Some(Text.Info(r, all_tips)) |
736 } |
743 } |