+
+ var f
+ var l
+
+ if n isa Token then
+ f = n
+ l = n
+ else
+ assert n isa Prod
+ f = n.first_token
+ if f == null then return
+ l = n.last_token
+ if l == null then return
+ end
+
+ if include_loose_tokens then
+ if f.prev_looses.not_empty then f = f.prev_looses.first
+ if l.next_looses.not_empty then l = l.next_looses.last
+ end
+
+ if include_whole_lines then
+ f = f.first_real_token_in_line
+ l = l.last_real_token_in_line
+ end
+
+ htmlize(f, l)
+ end
+
+ private fun full_tag(anode: ANode, hv: HighlightVisitor): nullable HTMLTag
+ do
+ var tag = anode.make_tag(hv)
+ if tag == null then return null
+ var infobox = anode.infobox(hv)
+ if infobox == null and anode isa Token then
+ var pa = anode.parent
+ if pa != null then
+ var c = anode
+ if c isa TId or c isa TClassid or c isa TAttrid or c isa TokenLiteral or c isa TokenOperator or c isa TComment and pa isa ADoc then
+ infobox = pa.decorate_tag(hv, tag, anode)
+ end
+ end
+ end
+ var messages = anode.location.messages
+ if messages != null then
+ tag.css("border-bottom", "solid 2px red")
+ if infobox == null then
+ infobox = new HInfoBox(hv, "Messages")
+ end
+ var c = infobox.new_dropdown("{messages.length} message(s)", "")
+ for m in messages do
+ c.open("li").append(m.text)
+ end
+ end
+ if infobox != null then
+ tag.attach_infobox(infobox)
+ end
+ return tag