Skip to content

Commit 35e1072

Browse files
Show more information in Vala symbol tooltip (#1565)
* ValaSymbolItem: GObject style construction * ValaSymbol: Move construction code from Outline to Item * Use slice of doc for tooltip * Wait till all docs restored and loaded before updating outline visible * Correctly connect to tab.loading notification * On tab loading change parse symbols if appropriate * Correctly format show outline (); parse symbols only if not loading * Do not try to forward iter less than 1 char * Make source_view property and lock when used from separate thread * Yield thread when constructing tree * Increase allowed time for parse since UI not blocked * Fix lint --------- Co-authored-by: Leonardo Lemos <[email protected]>
1 parent 73343af commit 35e1072

File tree

4 files changed

+151
-92
lines changed

4 files changed

+151
-92
lines changed

src/MainWindow.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,6 @@ namespace Scratch {
579579
});
580580

581581
document_view.realize.connect (() => {
582-
document_view.update_outline_visible ();
583582
update_find_actions ();
584583
});
585584

@@ -694,6 +693,7 @@ namespace Scratch {
694693
}
695694

696695
document_view.request_placeholder_if_empty ();
696+
document_view.update_outline_visible ();
697697
restore_override = null;
698698
if (focused_file != null) {
699699
folder_manager_view.expand_to_path (focused_file.get_path ());

src/Services/Document.vala

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ namespace Scratch.Services {
161161
public bool closing { get; private set; default = false; }
162162

163163
public Gtk.Stack main_stack;
164-
public Scratch.Widgets.SourceView source_view;
164+
165+
public Scratch.Widgets.SourceView source_view { get; construct; }
166+
165167
private Scratch.Services.SymbolOutline? outline = null;
166168
private Scratch.Widgets.DocumentView doc_view {
167169
get {
@@ -306,7 +308,7 @@ namespace Scratch.Services {
306308

307309
public void init_tab (Hdy.TabPage tab) {
308310
this.tab = tab;
309-
notify["tab.loading"].connect (on_tab_loading_change);
311+
tab.notify["loading"].connect (on_tab_loading_change);
310312

311313
tab.title = title;
312314
tab.icon = icon;
@@ -815,6 +817,27 @@ namespace Scratch.Services {
815817
return this.source_view.get_selected_text (replace_newline);
816818
}
817819

820+
public string get_slice (int start_line, int start_col, int end_line, int end_col) {
821+
// This is accessed from separate thread so must lock the sourceview
822+
lock (source_view) {
823+
Gtk.TextIter iter;
824+
source_view.buffer.get_iter_at_line (out iter, start_line - 1);
825+
if (start_col > 1 && !iter.forward_chars (start_col - 1)) {
826+
return "";
827+
}
828+
829+
var start = iter;
830+
source_view.buffer.get_iter_at_line (out iter, end_line - 1);
831+
832+
if (!iter.forward_to_line_end ()) {
833+
return "";
834+
}
835+
836+
var end = iter;
837+
return source_view.buffer.get_text (start, end, false);
838+
}
839+
}
840+
818841
// Get language name
819842
public string get_language_name () {
820843
var source_buffer = (Gtk.SourceBuffer) source_view.buffer;
@@ -1206,27 +1229,30 @@ namespace Scratch.Services {
12061229

12071230
public void show_outline (bool show) {
12081231
if (show && outline == null) {
1209-
switch (mime_type) {
1210-
case "text/x-vala":
1232+
switch (mime_type) {
1233+
case "text/x-vala":
12111234
outline = new ValaSymbolOutline (this);
12121235
break;
1213-
case "text/x-csrc":
1214-
case "text/x-chdr":
1215-
case "text/x-c++src":
1216-
case "text/x-c++hdr":
1236+
case "text/x-csrc":
1237+
case "text/x-chdr":
1238+
case "text/x-c++src":
1239+
case "text/x-c++hdr":
12171240
outline = new CtagsSymbolOutline (this);
12181241
break;
1219-
}
1220-
1221-
if (outline != null) {
1222-
outline_widget_pane.pack2 (outline.get_widget (), false, false);
1223-
Idle.add (() => {
1224-
set_outline_width (doc_view.outline_width);
1225-
outline_widget_pane.notify["position"].connect (sync_outline_width);
1226-
outline.parse_symbols ();
1227-
return Source.REMOVE;
1228-
});
1229-
}
1242+
}
1243+
1244+
if (outline != null) {
1245+
outline_widget_pane.pack2 (outline.get_widget (), false, false);
1246+
Idle.add (() => {
1247+
set_outline_width (doc_view.outline_width);
1248+
outline_widget_pane.notify["position"].connect (sync_outline_width);
1249+
if (!tab.loading) {
1250+
outline.parse_symbols ();
1251+
} // else parsing will occur when tab finishes loading
1252+
1253+
return Source.REMOVE;
1254+
});
1255+
}
12301256
} else if (!show && outline != null) {
12311257
outline_widget_pane.notify["position"].disconnect (sync_outline_width);
12321258
outline_widget_pane.get_child2 ().destroy ();
@@ -1321,6 +1347,9 @@ namespace Scratch.Services {
13211347
/* Block user editing while tab is loading */
13221348
private void on_tab_loading_change () {
13231349
source_view.sensitive = !tab.loading;
1350+
if (!tab.loading && outline != null) {
1351+
outline.parse_symbols ();
1352+
}
13241353
}
13251354
}
13261355
}

src/SymbolPane/Vala/ValaSymbolItem.vala

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,87 @@
1717
*/
1818

1919
public class Scratch.Services.ValaSymbolItem : Code.Widgets.SourceList.ExpandableItem, Code.Widgets.SourceListSortable, Scratch.Services.SymbolItem {
20-
public Vala.Symbol symbol { get; set; }
20+
public Vala.Symbol symbol { get; construct; }
2121
public SymbolType symbol_type { get; set; default = SymbolType.OTHER; }
22-
public ValaSymbolItem (Vala.Symbol symbol) {
23-
this.symbol = symbol;
24-
this.name = symbol.name;
22+
public ValaSymbolItem (Vala.Symbol symbol, string _tooltip) {
23+
Object (
24+
symbol: symbol,
25+
tooltip: _tooltip
26+
);
27+
}
28+
29+
construct {
2530
if (symbol is Vala.CreationMethod) {
26-
if (symbol.name == ".new")
27-
this.name = ((Vala.CreationMethod)symbol).class_name;
28-
else
29-
this.name = "%s.%s".printf (((Vala.CreationMethod)symbol).class_name, symbol.name);
31+
var klass = ((Vala.CreationMethod)symbol).class_name;
32+
if (symbol.name == ".new") {
33+
name = klass;
34+
} else {
35+
name = "%s.%s".printf (klass, symbol.name);
36+
}
37+
} else {
38+
name = symbol.name;
39+
}
40+
41+
if (symbol is Vala.Struct) {
42+
icon = new ThemedIcon ("lang-struct");
43+
symbol_type = SymbolType.STRUCT;
44+
} else if (symbol is Vala.Class) {
45+
if (((Vala.Class) symbol).is_abstract) {
46+
icon = new ThemedIcon ("lang-class-abstract");
47+
} else {
48+
icon = new ThemedIcon ("lang-class");
49+
}
50+
51+
symbol_type = SymbolType.CLASS;
52+
} else if (symbol is Vala.Constant) {
53+
icon = new ThemedIcon ("lang-constant");
54+
symbol_type = SymbolType.CONSTANT;
55+
} else if (symbol is Vala.Enum) {
56+
icon = new ThemedIcon ("lang-enum");
57+
symbol_type = SymbolType.ENUM;
58+
} else if (symbol is Vala.Field) {
59+
icon = new ThemedIcon ("lang-property");
60+
symbol_type = SymbolType.PROPERTY;
61+
} else if (symbol is Vala.Interface) {
62+
icon = new ThemedIcon ("lang-interface");
63+
symbol_type = SymbolType.INTERFACE;
64+
} else if (symbol is Vala.Property) {
65+
if (((Vala.Property) symbol).is_abstract) {
66+
icon = new ThemedIcon ("lang-property-abstract");
67+
} else if (((Vala.Property) symbol).is_virtual) {
68+
icon = new ThemedIcon ("lang-property-virtual");
69+
} else {
70+
icon = new ThemedIcon ("lang-property");
71+
}
72+
73+
symbol_type = SymbolType.PROPERTY;
74+
} else if (symbol is Vala.Signal) {
75+
icon = new ThemedIcon ("lang-signal");
76+
symbol_type = SymbolType.SIGNAL;
77+
} else if (symbol is Vala.CreationMethod) {
78+
icon = new ThemedIcon ("lang-constructor");
79+
symbol_type = SymbolType.CONSTRUCTOR;
80+
} else if (symbol is Vala.Method) {
81+
if (((Vala.Method) symbol).is_abstract) {
82+
icon = new ThemedIcon ("lang-method-abstract");
83+
} else if (((Vala.Method) symbol).is_virtual) {
84+
icon = new ThemedIcon ("lang-method-virtual");
85+
} else if (((Vala.Method) symbol).binding == Vala.MemberBinding.STATIC) {
86+
icon = new ThemedIcon ("lang-method-static");
87+
} else {
88+
icon = new ThemedIcon ("lang-method");
89+
}
90+
91+
symbol_type = SymbolType.METHOD;
92+
} else if (symbol is Vala.Namespace) {
93+
icon = new ThemedIcon ("lang-namespace");
94+
symbol_type = SymbolType.NAMESPACE;
95+
} else if (symbol is Vala.ErrorDomain) {
96+
icon = new ThemedIcon ("lang-errordomain");
97+
} else if (symbol is Vala.Delegate) {
98+
icon = new ThemedIcon ("lang-delegate");
99+
} else {
100+
warning (symbol.type_name);
30101
}
31102
}
32103

src/SymbolPane/Vala/ValaSymbolOutline.vala

Lines changed: 23 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818

1919
public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline {
20-
public const int PARSE_TIME_MAX_MSEC = 1000;
20+
public const int PARSE_TIME_MAX_MSEC = 5000;
2121
private Code.Plugins.ValaSymbolResolver resolver;
2222
private Vala.Parser parser;
2323
private GLib.Cancellable cancellable;
@@ -159,13 +159,15 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline
159159
parent.remove (item);
160160
}
161161

162+
// Called from separate thread
162163
private Code.Widgets.SourceList.ExpandableItem construct_tree (GLib.Cancellable cancellable) {
163164
var fields = resolver.get_properties_fields ();
164165
var symbols = resolver.get_symbols ();
165166
// Remove fake fields created by the vala parser.
166167
symbols.remove_all (fields);
167168

168169
var new_root = new Code.Widgets.SourceList.ExpandableItem (_("Symbols"));
170+
new_root.tooltip = _("Vala symbols found in %s").printf (doc.file.get_basename ());
169171
foreach (var symbol in symbols) {
170172
if (cancellable.is_cancelled ())
171173
break;
@@ -175,12 +177,18 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline
175177
continue;
176178

177179
construct_child (symbol, new_root, cancellable);
180+
Thread.yield ();
178181
}
179182

180183
return new_root;
181184
}
182185

183-
private ValaSymbolItem construct_child (Vala.Symbol symbol, Code.Widgets.SourceList.ExpandableItem given_parent, GLib.Cancellable cancellable) {
186+
private ValaSymbolItem construct_child (
187+
Vala.Symbol symbol,
188+
Code.Widgets.SourceList.ExpandableItem given_parent,
189+
GLib.Cancellable cancellable
190+
) {
191+
184192
Code.Widgets.SourceList.ExpandableItem parent;
185193
if (symbol.scope.parent_scope.owner.name == null)
186194
parent = given_parent;
@@ -191,69 +199,20 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline
191199
parent = construct_child (symbol.scope.parent_scope.owner, given_parent, cancellable);
192200
}
193201

194-
var tree_child = new ValaSymbolItem (symbol);
195-
if (symbol is Vala.Struct) {
196-
tree_child.icon = new ThemedIcon ("lang-struct");
197-
tree_child.symbol_type = SymbolType.STRUCT;
198-
} else if (symbol is Vala.Class) {
199-
if (((Vala.Class) symbol).is_abstract) {
200-
tree_child.icon = new ThemedIcon ("lang-class-abstract");
201-
} else {
202-
tree_child.icon = new ThemedIcon ("lang-class");
203-
}
204-
205-
tree_child.symbol_type = SymbolType.CLASS;
206-
} else if (symbol is Vala.Constant) {
207-
tree_child.icon = new ThemedIcon ("lang-constant");
208-
tree_child.symbol_type = SymbolType.CONSTANT;
209-
} else if (symbol is Vala.Enum) {
210-
tree_child.icon = new ThemedIcon ("lang-enum");
211-
tree_child.symbol_type = SymbolType.ENUM;
212-
} else if (symbol is Vala.Field) {
213-
tree_child.icon = new ThemedIcon ("lang-property");
214-
tree_child.symbol_type = SymbolType.PROPERTY;
215-
} else if (symbol is Vala.Interface) {
216-
tree_child.icon = new ThemedIcon ("lang-interface");
217-
tree_child.symbol_type = SymbolType.INTERFACE;
218-
} else if (symbol is Vala.Property) {
219-
if (((Vala.Property) symbol).is_abstract) {
220-
tree_child.icon = new ThemedIcon ("lang-property-abstract");
221-
} else if (((Vala.Property) symbol).is_virtual) {
222-
tree_child.icon = new ThemedIcon ("lang-property-virtual");
223-
} else {
224-
tree_child.icon = new ThemedIcon ("lang-property");
225-
}
226-
227-
tree_child.symbol_type = SymbolType.PROPERTY;
228-
} else if (symbol is Vala.Signal) {
229-
tree_child.icon = new ThemedIcon ("lang-signal");
230-
tree_child.symbol_type = SymbolType.SIGNAL;
231-
} else if (symbol is Vala.CreationMethod) {
232-
tree_child.icon = new ThemedIcon ("lang-constructor");
233-
tree_child.symbol_type = SymbolType.CONSTRUCTOR;
234-
} else if (symbol is Vala.Method) {
235-
if (((Vala.Method) symbol).is_abstract) {
236-
tree_child.icon = new ThemedIcon ("lang-method-abstract");
237-
} else if (((Vala.Method) symbol).is_virtual) {
238-
tree_child.icon = new ThemedIcon ("lang-method-virtual");
239-
} else if (((Vala.Method) symbol).binding == Vala.MemberBinding.STATIC) {
240-
tree_child.icon = new ThemedIcon ("lang-method-static");
241-
} else {
242-
tree_child.icon = new ThemedIcon ("lang-method");
243-
}
244-
245-
tree_child.symbol_type = SymbolType.METHOD;
246-
} else if (symbol is Vala.Namespace) {
247-
tree_child.icon = new ThemedIcon ("lang-namespace");
248-
tree_child.symbol_type = SymbolType.NAMESPACE;
249-
} else if (symbol is Vala.ErrorDomain) {
250-
tree_child.icon = new ThemedIcon ("lang-errordomain");
251-
} else if (symbol is Vala.Delegate) {
252-
tree_child.icon = new ThemedIcon ("lang-delegate");
253-
} else {
254-
warning (symbol.type_name);
255-
}
202+
var tooltip = "%s%s".printf (
203+
doc.get_slice (
204+
symbol.source_reference.begin.line,
205+
symbol.source_reference.begin.column,
206+
symbol.source_reference.end.line,
207+
symbol.source_reference.end.column
208+
),
209+
symbol.comment != null ? "\n" + symbol.comment.content : ""
210+
);
256211

212+
var tree_child = new ValaSymbolItem (
213+
symbol,
214+
tooltip
215+
);
257216
parent.add (tree_child);
258217
return tree_child;
259218
}

0 commit comments

Comments
 (0)