7 package isabelle.jedit |
7 package isabelle.jedit |
8 |
8 |
9 |
9 |
10 import isabelle._ |
10 import isabelle._ |
11 |
11 |
12 import java.awt.BorderLayout |
12 import java.awt.{BorderLayout, Dimension} |
13 import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent} |
13 import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent, MouseEvent, MouseAdapter} |
14 |
14 import javax.swing.{JTree, JScrollPane} |
15 import scala.swing.{Button, Label, Component} |
15 import javax.swing.tree.{DefaultMutableTreeNode, TreeSelectionModel} |
|
16 import javax.swing.event.{TreeSelectionEvent, TreeSelectionListener} |
|
17 |
|
18 import scala.swing.{Button, Label, Component, SplitPane, Orientation} |
16 import scala.swing.event.ButtonClicked |
19 import scala.swing.event.ButtonClicked |
17 |
20 |
18 import org.gjt.sp.jedit.View |
21 import org.gjt.sp.jedit.View |
19 |
22 |
|
23 |
|
24 object Debugger_Dockable |
|
25 { |
|
26 sealed case class Tree_Entry(thread_name: String, debug_states: List[Debugger.Debug_State]) |
|
27 { |
|
28 override def toString: String = thread_name |
|
29 } |
|
30 } |
20 |
31 |
21 class Debugger_Dockable(view: View, position: String) extends Dockable(view, position) |
32 class Debugger_Dockable(view: View, position: String) extends Dockable(view, position) |
22 { |
33 { |
23 GUI_Thread.require {} |
34 GUI_Thread.require {} |
24 |
35 |
25 |
36 |
26 /* component state -- owned by GUI thread */ |
37 /* component state -- owned by GUI thread */ |
27 |
38 |
28 private var current_snapshot = Document.Snapshot.init |
39 private var current_snapshot = Document.Snapshot.init |
|
40 private var current_threads: Map[String, List[Debugger.Debug_State]] = Map.empty |
29 private var current_output: List[XML.Tree] = Nil |
41 private var current_output: List[XML.Tree] = Nil |
30 |
42 |
31 |
43 |
32 /* common GUI components */ |
44 /* pretty text area */ |
|
45 |
|
46 val pretty_text_area = new Pretty_Text_Area(view) |
|
47 |
|
48 override def detach_operation = pretty_text_area.detach_operation |
|
49 |
|
50 private def handle_resize() |
|
51 { |
|
52 GUI_Thread.require {} |
|
53 |
|
54 pretty_text_area.resize( |
|
55 Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) |
|
56 } |
|
57 |
|
58 private def handle_update() |
|
59 { |
|
60 GUI_Thread.require {} |
|
61 |
|
62 val new_state = Debugger.current_state() |
|
63 |
|
64 val new_snapshot = PIDE.editor.current_node_snapshot(view).getOrElse(current_snapshot) |
|
65 val new_threads = new_state.threads |
|
66 val new_output = // FIXME select by thread name |
|
67 (for ((_, results) <- new_state.output; (_, tree) <- results.iterator) |
|
68 yield tree).toList ::: List(XML.Text(new_threads.toString)) |
|
69 |
|
70 if (new_threads != current_threads) { |
|
71 val entries = |
|
72 (for ((a, b) <- new_threads.iterator) |
|
73 yield Debugger_Dockable.Tree_Entry(a, b)).toList.sortBy(_.thread_name) |
|
74 update_tree(entries) |
|
75 } |
|
76 |
|
77 if (new_output != current_output) |
|
78 pretty_text_area.update(new_snapshot, Command.Results.empty, Pretty.separate(new_output)) |
|
79 |
|
80 current_snapshot = new_snapshot |
|
81 current_threads = new_threads |
|
82 current_output = new_output |
|
83 |
|
84 revalidate() |
|
85 repaint() |
|
86 } |
|
87 |
|
88 |
|
89 /* tree view */ |
|
90 |
|
91 private val root = new DefaultMutableTreeNode("Threads") |
|
92 |
|
93 val tree = new JTree(root) |
|
94 tree.setRowHeight(0) |
|
95 tree.getSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION) |
|
96 |
|
97 private def update_tree(entries: List[Debugger_Dockable.Tree_Entry]) |
|
98 { |
|
99 tree.clearSelection |
|
100 |
|
101 root.removeAllChildren |
|
102 val entry_nodes = entries.map(entry => new DefaultMutableTreeNode(entry)) |
|
103 for (node <- entry_nodes) root.add(node) |
|
104 |
|
105 for (i <- 0 until tree.getRowCount) tree.expandRow(i) |
|
106 |
|
107 for ((entry, node) <- entries zip entry_nodes) { |
|
108 for (debug_state <- entry.debug_states) { |
|
109 val sub_node = new DefaultMutableTreeNode(debug_state.function) |
|
110 node.add(sub_node) |
|
111 } |
|
112 } |
|
113 |
|
114 tree.revalidate() |
|
115 } |
|
116 |
|
117 private def action(node: DefaultMutableTreeNode) |
|
118 { |
|
119 node.getUserObject match { |
|
120 case _ => // FIXME |
|
121 } |
|
122 } |
|
123 |
|
124 tree.addMouseListener(new MouseAdapter { |
|
125 override def mouseClicked(e: MouseEvent) |
|
126 { |
|
127 val click = tree.getPathForLocation(e.getX, e.getY) |
|
128 if (click != null && e.getClickCount == 1) { |
|
129 (click.getLastPathComponent, tree.getLastSelectedPathComponent) match { |
|
130 case (node: DefaultMutableTreeNode, node1: DefaultMutableTreeNode) if node == node1 => |
|
131 action(node) |
|
132 case _ => |
|
133 } |
|
134 } |
|
135 } |
|
136 }) |
|
137 |
|
138 val tree_view = new JScrollPane(tree) |
|
139 tree_view.setMinimumSize(new Dimension(100, 50)) |
|
140 |
|
141 |
|
142 /* controls */ |
33 |
143 |
34 private val context_label = new Label("Context:") { tooltip = "Isabelle/ML context (optional)" } |
144 private val context_label = new Label("Context:") { tooltip = "Isabelle/ML context (optional)" } |
35 private val context_field = |
145 private val context_field = |
36 new Completion_Popup.History_Text_Field("isabelle-debugger-context") |
146 new Completion_Popup.History_Text_Field("isabelle-debugger-context") |
37 { |
147 { |
58 |
168 |
59 private val eval_button = new Button("<html><b>Eval</b></html>") { |
169 private val eval_button = new Button("<html><b>Eval</b></html>") { |
60 tooltip = "Evaluate Isabelle/ML expression within optional context" |
170 tooltip = "Evaluate Isabelle/ML expression within optional context" |
61 reactions += { case ButtonClicked(_) => eval_expression() } |
171 reactions += { case ButtonClicked(_) => eval_expression() } |
62 } |
172 } |
|
173 override def focusOnDefaultComponent { eval_button.requestFocus } |
63 |
174 |
64 private def eval_expression() |
175 private def eval_expression() |
65 { |
176 { |
66 // FIXME |
177 // FIXME |
67 Output.writeln("eval context = " + quote(context_field.getText) + " expression = " + |
178 Output.writeln("eval context = " + quote(context_field.getText) + " expression = " + |
68 quote(expression_field.getText)) |
179 quote(expression_field.getText)) |
69 } |
180 } |
70 |
181 |
71 |
|
72 /* controls */ |
|
73 |
|
74 private val debugger_active = |
182 private val debugger_active = |
75 new JEdit_Options.Check_Box("ML_debugger_active", "Active", "Enable debugger at run-time") |
183 new JEdit_Options.Check_Box("ML_debugger_active", "Active", "Enable debugger at run-time") |
76 |
184 |
77 private val debugger_stepping = |
185 private val debugger_stepping = |
78 new JEdit_Options.Check_Box("ML_debugger_stepping", "Stepping", "Enable single-step mode") |
186 new JEdit_Options.Check_Box("ML_debugger_stepping", "Stepping", "Enable single-step mode") |
79 |
187 |
80 private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } |
188 private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } |
81 |
189 |
82 |
190 private val controls = |
83 /* pretty text area */ |
191 new Wrap_Panel(Wrap_Panel.Alignment.Right)( |
84 |
192 context_label, Component.wrap(context_field), |
85 val pretty_text_area = new Pretty_Text_Area(view) |
193 expression_label, Component.wrap(expression_field), eval_button, |
86 set_content(pretty_text_area) |
194 pretty_text_area.search_label, pretty_text_area.search_field, |
87 |
195 debugger_stepping, debugger_active, zoom) |
88 override def detach_operation = pretty_text_area.detach_operation |
196 add(controls.peer, BorderLayout.NORTH) |
89 |
197 |
90 |
198 |
91 private def handle_resize() |
199 /* main panel */ |
92 { |
200 |
93 GUI_Thread.require {} |
201 val main_panel = new SplitPane(Orientation.Vertical) { |
94 |
202 oneTouchExpandable = true |
95 pretty_text_area.resize( |
203 leftComponent = Component.wrap(tree_view) |
96 Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) |
204 rightComponent = Component.wrap(pretty_text_area) |
97 } |
205 } |
98 |
206 set_content(main_panel) |
99 private def handle_update() |
|
100 { |
|
101 GUI_Thread.require {} |
|
102 |
|
103 val new_snapshot = PIDE.editor.current_node_snapshot(view).getOrElse(current_snapshot) |
|
104 val new_output = // FIXME select by thread name |
|
105 (for ((_, results) <- Debugger.current_state.output; (_, tree) <- results.iterator) |
|
106 yield tree).toList ::: List(XML.Text(Debugger.current_state.threads.toString)) |
|
107 |
|
108 if (new_output != current_output) |
|
109 pretty_text_area.update(new_snapshot, Command.Results.empty, Pretty.separate(new_output)) |
|
110 |
|
111 current_snapshot = new_snapshot |
|
112 current_output = new_output |
|
113 } |
|
114 |
207 |
115 |
208 |
116 /* main */ |
209 /* main */ |
117 |
210 |
118 private val main = |
211 private val main = |