wenzelm@44162
|
1 |
/* Title: Tools/jEdit/src/isabelle_sidekick.scala
|
wenzelm@36760
|
2 |
Author: Fabian Immler, TU Munich
|
wenzelm@36760
|
3 |
Author: Makarius
|
wenzelm@36760
|
4 |
|
wenzelm@36760
|
5 |
SideKick parsers for Isabelle proof documents.
|
wenzelm@36760
|
6 |
*/
|
immler@34396
|
7 |
|
wenzelm@34429
|
8 |
package isabelle.jedit
|
immler@34396
|
9 |
|
wenzelm@34763
|
10 |
|
wenzelm@36039
|
11 |
import isabelle._
|
wenzelm@36039
|
12 |
|
immler@34478
|
13 |
import scala.collection.Set
|
immler@34478
|
14 |
import scala.collection.immutable.TreeSet
|
wenzelm@34420
|
15 |
|
immler@34396
|
16 |
import javax.swing.tree.DefaultMutableTreeNode
|
wenzelm@34704
|
17 |
import javax.swing.text.Position
|
wenzelm@34704
|
18 |
import javax.swing.Icon
|
wenzelm@34420
|
19 |
|
wenzelm@34612
|
20 |
import org.gjt.sp.jedit.{Buffer, EditPane, TextUtilities, View}
|
wenzelm@34420
|
21 |
import errorlist.DefaultErrorSource
|
wenzelm@34704
|
22 |
import sidekick.{SideKickParser, SideKickParsedData, SideKickCompletion, IAsset}
|
wenzelm@34704
|
23 |
|
immler@34396
|
24 |
|
wenzelm@36738
|
25 |
object Isabelle_Sidekick
|
wenzelm@36738
|
26 |
{
|
wenzelm@40746
|
27 |
def int_to_pos(offset: Text.Offset): Position =
|
wenzelm@36738
|
28 |
new Position { def getOffset = offset; override def toString = offset.toString }
|
wenzelm@40709
|
29 |
|
wenzelm@40746
|
30 |
class Asset(name: String, start: Text.Offset, end: Text.Offset) extends IAsset
|
wenzelm@40709
|
31 |
{
|
wenzelm@40709
|
32 |
protected var _name = name
|
wenzelm@40709
|
33 |
protected var _start = int_to_pos(start)
|
wenzelm@40709
|
34 |
protected var _end = int_to_pos(end)
|
wenzelm@40709
|
35 |
override def getIcon: Icon = null
|
wenzelm@40709
|
36 |
override def getShortString: String = _name
|
wenzelm@40709
|
37 |
override def getLongString: String = _name
|
wenzelm@40709
|
38 |
override def getName: String = _name
|
wenzelm@40709
|
39 |
override def setName(name: String) = _name = name
|
wenzelm@40709
|
40 |
override def getStart: Position = _start
|
wenzelm@40709
|
41 |
override def setStart(start: Position) = _start = start
|
wenzelm@40709
|
42 |
override def getEnd: Position = _end
|
wenzelm@40709
|
43 |
override def setEnd(end: Position) = _end = end
|
wenzelm@40709
|
44 |
override def toString = _name
|
wenzelm@40709
|
45 |
}
|
wenzelm@36738
|
46 |
}
|
wenzelm@36738
|
47 |
|
wenzelm@36738
|
48 |
|
wenzelm@36759
|
49 |
abstract class Isabelle_Sidekick(name: String) extends SideKickParser(name)
|
wenzelm@34628
|
50 |
{
|
wenzelm@34420
|
51 |
/* parsing */
|
wenzelm@34420
|
52 |
|
wenzelm@36759
|
53 |
@volatile protected var stopped = false
|
wenzelm@34506
|
54 |
override def stop() = { stopped = true }
|
wenzelm@34420
|
55 |
|
wenzelm@36759
|
56 |
def parser(data: SideKickParsedData, model: Document_Model): Unit
|
wenzelm@36759
|
57 |
|
wenzelm@34628
|
58 |
def parse(buffer: Buffer, error_source: DefaultErrorSource): SideKickParsedData =
|
wenzelm@34628
|
59 |
{
|
wenzelm@34420
|
60 |
stopped = false
|
wenzelm@34628
|
61 |
|
wenzelm@38899
|
62 |
// FIXME lock buffer (!??)
|
wenzelm@34420
|
63 |
val data = new SideKickParsedData(buffer.getName)
|
wenzelm@34720
|
64 |
val root = data.root
|
wenzelm@40709
|
65 |
data.getAsset(root).setEnd(Isabelle_Sidekick.int_to_pos(buffer.getLength))
|
immler@34478
|
66 |
|
wenzelm@34792
|
67 |
Swing_Thread.now { Document_Model(buffer) } match {
|
wenzelm@34787
|
68 |
case Some(model) =>
|
wenzelm@36759
|
69 |
parser(data, model)
|
wenzelm@34780
|
70 |
if (stopped) root.add(new DefaultMutableTreeNode("<parser stopped>"))
|
wenzelm@34780
|
71 |
case None => root.add(new DefaultMutableTreeNode("<buffer inactive>"))
|
immler@34478
|
72 |
}
|
wenzelm@34420
|
73 |
data
|
wenzelm@34420
|
74 |
}
|
wenzelm@34420
|
75 |
|
wenzelm@36738
|
76 |
|
wenzelm@34420
|
77 |
/* completion */
|
wenzelm@34420
|
78 |
|
wenzelm@34420
|
79 |
override def supportsCompletion = true
|
wenzelm@34420
|
80 |
override def canCompleteAnywhere = true
|
wenzelm@34420
|
81 |
|
wenzelm@40746
|
82 |
override def complete(pane: EditPane, caret: Text.Offset): SideKickCompletion =
|
wenzelm@34612
|
83 |
{
|
immler@34478
|
84 |
val buffer = pane.getBuffer
|
wenzelm@39914
|
85 |
Isabelle.swing_buffer_lock(buffer) {
|
wenzelm@39914
|
86 |
Document_Model(buffer) match {
|
wenzelm@39914
|
87 |
case None => null
|
wenzelm@39914
|
88 |
case Some(model) =>
|
wenzelm@39914
|
89 |
val line = buffer.getLineOfOffset(caret)
|
wenzelm@39914
|
90 |
val start = buffer.getLineStartOffset(line)
|
wenzelm@39914
|
91 |
val text = buffer.getSegment(start, caret - start)
|
immler@34478
|
92 |
|
wenzelm@39914
|
93 |
val completion = model.session.current_syntax().completion
|
wenzelm@39914
|
94 |
completion.complete(text) match {
|
wenzelm@39914
|
95 |
case None => null
|
wenzelm@39914
|
96 |
case Some((word, cs)) =>
|
wenzelm@39914
|
97 |
val ds =
|
wenzelm@39914
|
98 |
(if (Isabelle_Encoding.is_active(buffer))
|
wenzelm@44569
|
99 |
cs.map(Symbol.decode(_)).sortWith(_ < _)
|
wenzelm@39914
|
100 |
else cs).filter(_ != word)
|
wenzelm@39914
|
101 |
if (ds.isEmpty) null
|
wenzelm@39914
|
102 |
else new SideKickCompletion(
|
wenzelm@39914
|
103 |
pane.getView, word, ds.toArray.asInstanceOf[Array[Object]]) { }
|
wenzelm@39914
|
104 |
}
|
wenzelm@39914
|
105 |
}
|
wenzelm@34614
|
106 |
}
|
wenzelm@34612
|
107 |
}
|
immler@34396
|
108 |
}
|
wenzelm@36738
|
109 |
|
wenzelm@36738
|
110 |
|
wenzelm@36759
|
111 |
class Isabelle_Sidekick_Default extends Isabelle_Sidekick("isabelle")
|
wenzelm@36759
|
112 |
{
|
wenzelm@40712
|
113 |
import Thy_Syntax.Structure
|
wenzelm@40712
|
114 |
|
wenzelm@36759
|
115 |
def parser(data: SideKickParsedData, model: Document_Model)
|
wenzelm@36759
|
116 |
{
|
wenzelm@40712
|
117 |
val syntax = model.session.current_syntax()
|
wenzelm@40712
|
118 |
|
wenzelm@40746
|
119 |
def make_tree(offset: Text.Offset, entry: Structure.Entry): List[DefaultMutableTreeNode] =
|
wenzelm@40712
|
120 |
entry match {
|
wenzelm@40712
|
121 |
case Structure.Block(name, body) =>
|
wenzelm@40712
|
122 |
val node =
|
wenzelm@40712
|
123 |
new DefaultMutableTreeNode(
|
wenzelm@40744
|
124 |
new Isabelle_Sidekick.Asset(Library.first_line(name), offset, offset + entry.length))
|
wenzelm@40712
|
125 |
(offset /: body)((i, e) =>
|
wenzelm@40712
|
126 |
{
|
wenzelm@40712
|
127 |
make_tree(i, e) foreach (nd => node.add(nd))
|
wenzelm@40712
|
128 |
i + e.length
|
wenzelm@40712
|
129 |
})
|
wenzelm@40712
|
130 |
List(node)
|
wenzelm@40712
|
131 |
case Structure.Atom(command)
|
wenzelm@40715
|
132 |
if command.is_command && syntax.heading_level(command).isEmpty =>
|
wenzelm@40712
|
133 |
val node =
|
wenzelm@40712
|
134 |
new DefaultMutableTreeNode(
|
wenzelm@40712
|
135 |
new Isabelle_Sidekick.Asset(command.name, offset, offset + entry.length))
|
wenzelm@40712
|
136 |
List(node)
|
wenzelm@40712
|
137 |
case _ => Nil
|
wenzelm@40712
|
138 |
}
|
wenzelm@40712
|
139 |
|
wenzelm@40743
|
140 |
val text = Isabelle.buffer_text(model.buffer)
|
wenzelm@41044
|
141 |
val structure = Structure.parse(syntax, "theory " + model.thy_name, text)
|
wenzelm@40712
|
142 |
|
wenzelm@40712
|
143 |
make_tree(0, structure) foreach (node => data.root.add(node))
|
wenzelm@36759
|
144 |
}
|
wenzelm@36759
|
145 |
}
|
wenzelm@36738
|
146 |
|
wenzelm@36738
|
147 |
|
wenzelm@36759
|
148 |
class Isabelle_Sidekick_Raw extends Isabelle_Sidekick("isabelle-raw")
|
wenzelm@36759
|
149 |
{
|
wenzelm@36759
|
150 |
def parser(data: SideKickParsedData, model: Document_Model)
|
wenzelm@36759
|
151 |
{
|
wenzelm@36759
|
152 |
val root = data.root
|
wenzelm@38479
|
153 |
val snapshot = Swing_Thread.now { model.snapshot() } // FIXME cover all nodes (!??)
|
wenzelm@38863
|
154 |
for ((command, command_start) <- snapshot.node.command_range() if !stopped) {
|
wenzelm@38946
|
155 |
snapshot.state(command).root_markup.swing_tree(root)((info: Text.Info[Any]) =>
|
wenzelm@36759
|
156 |
{
|
wenzelm@38951
|
157 |
val range = info.range + command_start
|
wenzelm@38871
|
158 |
val content = command.source(info.range).replace('\n', ' ')
|
wenzelm@38951
|
159 |
val info_text =
|
wenzelm@38951
|
160 |
info.info match {
|
wenzelm@38951
|
161 |
case elem @ XML.Elem(_, _) =>
|
wenzelm@38951
|
162 |
Pretty.formatted(List(elem), margin = 40).mkString("\n")
|
wenzelm@38951
|
163 |
case x => x.toString
|
wenzelm@38951
|
164 |
}
|
wenzelm@36738
|
165 |
|
wenzelm@40709
|
166 |
new DefaultMutableTreeNode(
|
wenzelm@40709
|
167 |
new Isabelle_Sidekick.Asset(command.toString, range.start, range.stop) {
|
wenzelm@40709
|
168 |
override def getShortString: String = content
|
wenzelm@40709
|
169 |
override def getLongString: String = info_text
|
wenzelm@40709
|
170 |
override def toString = "\"" + content + "\" " + range.toString
|
wenzelm@40709
|
171 |
})
|
wenzelm@38797
|
172 |
})
|
wenzelm@36759
|
173 |
}
|
wenzelm@36759
|
174 |
}
|
wenzelm@36759
|
175 |
}
|
wenzelm@36738
|
176 |
|