diff --git a/src/MainWindow.vala b/src/MainWindow.vala index a4b9e7608..440e025c3 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -579,7 +579,6 @@ namespace Scratch { }); document_view.realize.connect (() => { - document_view.update_outline_visible (); update_find_actions (); }); @@ -694,6 +693,7 @@ namespace Scratch { } document_view.request_placeholder_if_empty (); + document_view.update_outline_visible (); restore_override = null; if (focused_file != null) { folder_manager_view.expand_to_path (focused_file.get_path ()); diff --git a/src/Services/Document.vala b/src/Services/Document.vala index 051ecd904..ae4bf524f 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -161,7 +161,9 @@ namespace Scratch.Services { public bool closing { get; private set; default = false; } public Gtk.Stack main_stack; - public Scratch.Widgets.SourceView source_view; + + public Scratch.Widgets.SourceView source_view { get; construct; } + private Scratch.Services.SymbolOutline? outline = null; private Scratch.Widgets.DocumentView doc_view { get { @@ -306,7 +308,7 @@ namespace Scratch.Services { public void init_tab (Hdy.TabPage tab) { this.tab = tab; - notify["tab.loading"].connect (on_tab_loading_change); + tab.notify["loading"].connect (on_tab_loading_change); tab.title = title; tab.icon = icon; @@ -815,6 +817,27 @@ namespace Scratch.Services { return this.source_view.get_selected_text (replace_newline); } + public string get_slice (int start_line, int start_col, int end_line, int end_col) { + // This is accessed from separate thread so must lock the sourceview + lock (source_view) { + Gtk.TextIter iter; + source_view.buffer.get_iter_at_line (out iter, start_line - 1); + if (start_col > 1 && !iter.forward_chars (start_col - 1)) { + return ""; + } + + var start = iter; + source_view.buffer.get_iter_at_line (out iter, end_line - 1); + + if (!iter.forward_to_line_end ()) { + return ""; + } + + var end = iter; + return source_view.buffer.get_text (start, end, false); + } + } + // Get language name public string get_language_name () { var source_buffer = (Gtk.SourceBuffer) source_view.buffer; @@ -1206,27 +1229,30 @@ namespace Scratch.Services { public void show_outline (bool show) { if (show && outline == null) { - switch (mime_type) { - case "text/x-vala": + switch (mime_type) { + case "text/x-vala": outline = new ValaSymbolOutline (this); break; - case "text/x-csrc": - case "text/x-chdr": - case "text/x-c++src": - case "text/x-c++hdr": + case "text/x-csrc": + case "text/x-chdr": + case "text/x-c++src": + case "text/x-c++hdr": outline = new CtagsSymbolOutline (this); break; - } - - if (outline != null) { - outline_widget_pane.pack2 (outline.get_widget (), false, false); - Idle.add (() => { - set_outline_width (doc_view.outline_width); - outline_widget_pane.notify["position"].connect (sync_outline_width); - outline.parse_symbols (); - return Source.REMOVE; - }); - } + } + + if (outline != null) { + outline_widget_pane.pack2 (outline.get_widget (), false, false); + Idle.add (() => { + set_outline_width (doc_view.outline_width); + outline_widget_pane.notify["position"].connect (sync_outline_width); + if (!tab.loading) { + outline.parse_symbols (); + } // else parsing will occur when tab finishes loading + + return Source.REMOVE; + }); + } } else if (!show && outline != null) { outline_widget_pane.notify["position"].disconnect (sync_outline_width); outline_widget_pane.get_child2 ().destroy (); @@ -1321,6 +1347,9 @@ namespace Scratch.Services { /* Block user editing while tab is loading */ private void on_tab_loading_change () { source_view.sensitive = !tab.loading; + if (!tab.loading && outline != null) { + outline.parse_symbols (); + } } } } diff --git a/src/SymbolPane/Vala/ValaSymbolItem.vala b/src/SymbolPane/Vala/ValaSymbolItem.vala index 96b5613a7..4cee79429 100644 --- a/src/SymbolPane/Vala/ValaSymbolItem.vala +++ b/src/SymbolPane/Vala/ValaSymbolItem.vala @@ -17,16 +17,87 @@ */ public class Scratch.Services.ValaSymbolItem : Code.Widgets.SourceList.ExpandableItem, Code.Widgets.SourceListSortable, Scratch.Services.SymbolItem { - public Vala.Symbol symbol { get; set; } + public Vala.Symbol symbol { get; construct; } public SymbolType symbol_type { get; set; default = SymbolType.OTHER; } - public ValaSymbolItem (Vala.Symbol symbol) { - this.symbol = symbol; - this.name = symbol.name; + public ValaSymbolItem (Vala.Symbol symbol, string _tooltip) { + Object ( + symbol: symbol, + tooltip: _tooltip + ); + } + + construct { if (symbol is Vala.CreationMethod) { - if (symbol.name == ".new") - this.name = ((Vala.CreationMethod)symbol).class_name; - else - this.name = "%s.%s".printf (((Vala.CreationMethod)symbol).class_name, symbol.name); + var klass = ((Vala.CreationMethod)symbol).class_name; + if (symbol.name == ".new") { + name = klass; + } else { + name = "%s.%s".printf (klass, symbol.name); + } + } else { + name = symbol.name; + } + + if (symbol is Vala.Struct) { + icon = new ThemedIcon ("lang-struct"); + symbol_type = SymbolType.STRUCT; + } else if (symbol is Vala.Class) { + if (((Vala.Class) symbol).is_abstract) { + icon = new ThemedIcon ("lang-class-abstract"); + } else { + icon = new ThemedIcon ("lang-class"); + } + + symbol_type = SymbolType.CLASS; + } else if (symbol is Vala.Constant) { + icon = new ThemedIcon ("lang-constant"); + symbol_type = SymbolType.CONSTANT; + } else if (symbol is Vala.Enum) { + icon = new ThemedIcon ("lang-enum"); + symbol_type = SymbolType.ENUM; + } else if (symbol is Vala.Field) { + icon = new ThemedIcon ("lang-property"); + symbol_type = SymbolType.PROPERTY; + } else if (symbol is Vala.Interface) { + icon = new ThemedIcon ("lang-interface"); + symbol_type = SymbolType.INTERFACE; + } else if (symbol is Vala.Property) { + if (((Vala.Property) symbol).is_abstract) { + icon = new ThemedIcon ("lang-property-abstract"); + } else if (((Vala.Property) symbol).is_virtual) { + icon = new ThemedIcon ("lang-property-virtual"); + } else { + icon = new ThemedIcon ("lang-property"); + } + + symbol_type = SymbolType.PROPERTY; + } else if (symbol is Vala.Signal) { + icon = new ThemedIcon ("lang-signal"); + symbol_type = SymbolType.SIGNAL; + } else if (symbol is Vala.CreationMethod) { + icon = new ThemedIcon ("lang-constructor"); + symbol_type = SymbolType.CONSTRUCTOR; + } else if (symbol is Vala.Method) { + if (((Vala.Method) symbol).is_abstract) { + icon = new ThemedIcon ("lang-method-abstract"); + } else if (((Vala.Method) symbol).is_virtual) { + icon = new ThemedIcon ("lang-method-virtual"); + } else if (((Vala.Method) symbol).binding == Vala.MemberBinding.STATIC) { + icon = new ThemedIcon ("lang-method-static"); + } else { + icon = new ThemedIcon ("lang-method"); + } + + symbol_type = SymbolType.METHOD; + } else if (symbol is Vala.Namespace) { + icon = new ThemedIcon ("lang-namespace"); + symbol_type = SymbolType.NAMESPACE; + } else if (symbol is Vala.ErrorDomain) { + icon = new ThemedIcon ("lang-errordomain"); + } else if (symbol is Vala.Delegate) { + icon = new ThemedIcon ("lang-delegate"); + } else { + warning (symbol.type_name); } } diff --git a/src/SymbolPane/Vala/ValaSymbolOutline.vala b/src/SymbolPane/Vala/ValaSymbolOutline.vala index 2ec2c56a0..49a96edb4 100644 --- a/src/SymbolPane/Vala/ValaSymbolOutline.vala +++ b/src/SymbolPane/Vala/ValaSymbolOutline.vala @@ -17,7 +17,7 @@ */ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline { - public const int PARSE_TIME_MAX_MSEC = 1000; + public const int PARSE_TIME_MAX_MSEC = 5000; private Code.Plugins.ValaSymbolResolver resolver; private Vala.Parser parser; private GLib.Cancellable cancellable; @@ -159,6 +159,7 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline parent.remove (item); } + // Called from separate thread private Code.Widgets.SourceList.ExpandableItem construct_tree (GLib.Cancellable cancellable) { var fields = resolver.get_properties_fields (); var symbols = resolver.get_symbols (); @@ -166,6 +167,7 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline symbols.remove_all (fields); var new_root = new Code.Widgets.SourceList.ExpandableItem (_("Symbols")); + new_root.tooltip = _("Vala symbols found in %s").printf (doc.file.get_basename ()); foreach (var symbol in symbols) { if (cancellable.is_cancelled ()) break; @@ -175,12 +177,18 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline continue; construct_child (symbol, new_root, cancellable); + Thread.yield (); } return new_root; } - private ValaSymbolItem construct_child (Vala.Symbol symbol, Code.Widgets.SourceList.ExpandableItem given_parent, GLib.Cancellable cancellable) { + private ValaSymbolItem construct_child ( + Vala.Symbol symbol, + Code.Widgets.SourceList.ExpandableItem given_parent, + GLib.Cancellable cancellable + ) { + Code.Widgets.SourceList.ExpandableItem parent; if (symbol.scope.parent_scope.owner.name == null) parent = given_parent; @@ -191,69 +199,20 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline parent = construct_child (symbol.scope.parent_scope.owner, given_parent, cancellable); } - var tree_child = new ValaSymbolItem (symbol); - if (symbol is Vala.Struct) { - tree_child.icon = new ThemedIcon ("lang-struct"); - tree_child.symbol_type = SymbolType.STRUCT; - } else if (symbol is Vala.Class) { - if (((Vala.Class) symbol).is_abstract) { - tree_child.icon = new ThemedIcon ("lang-class-abstract"); - } else { - tree_child.icon = new ThemedIcon ("lang-class"); - } - - tree_child.symbol_type = SymbolType.CLASS; - } else if (symbol is Vala.Constant) { - tree_child.icon = new ThemedIcon ("lang-constant"); - tree_child.symbol_type = SymbolType.CONSTANT; - } else if (symbol is Vala.Enum) { - tree_child.icon = new ThemedIcon ("lang-enum"); - tree_child.symbol_type = SymbolType.ENUM; - } else if (symbol is Vala.Field) { - tree_child.icon = new ThemedIcon ("lang-property"); - tree_child.symbol_type = SymbolType.PROPERTY; - } else if (symbol is Vala.Interface) { - tree_child.icon = new ThemedIcon ("lang-interface"); - tree_child.symbol_type = SymbolType.INTERFACE; - } else if (symbol is Vala.Property) { - if (((Vala.Property) symbol).is_abstract) { - tree_child.icon = new ThemedIcon ("lang-property-abstract"); - } else if (((Vala.Property) symbol).is_virtual) { - tree_child.icon = new ThemedIcon ("lang-property-virtual"); - } else { - tree_child.icon = new ThemedIcon ("lang-property"); - } - - tree_child.symbol_type = SymbolType.PROPERTY; - } else if (symbol is Vala.Signal) { - tree_child.icon = new ThemedIcon ("lang-signal"); - tree_child.symbol_type = SymbolType.SIGNAL; - } else if (symbol is Vala.CreationMethod) { - tree_child.icon = new ThemedIcon ("lang-constructor"); - tree_child.symbol_type = SymbolType.CONSTRUCTOR; - } else if (symbol is Vala.Method) { - if (((Vala.Method) symbol).is_abstract) { - tree_child.icon = new ThemedIcon ("lang-method-abstract"); - } else if (((Vala.Method) symbol).is_virtual) { - tree_child.icon = new ThemedIcon ("lang-method-virtual"); - } else if (((Vala.Method) symbol).binding == Vala.MemberBinding.STATIC) { - tree_child.icon = new ThemedIcon ("lang-method-static"); - } else { - tree_child.icon = new ThemedIcon ("lang-method"); - } - - tree_child.symbol_type = SymbolType.METHOD; - } else if (symbol is Vala.Namespace) { - tree_child.icon = new ThemedIcon ("lang-namespace"); - tree_child.symbol_type = SymbolType.NAMESPACE; - } else if (symbol is Vala.ErrorDomain) { - tree_child.icon = new ThemedIcon ("lang-errordomain"); - } else if (symbol is Vala.Delegate) { - tree_child.icon = new ThemedIcon ("lang-delegate"); - } else { - warning (symbol.type_name); - } + var tooltip = "%s%s".printf ( + doc.get_slice ( + symbol.source_reference.begin.line, + symbol.source_reference.begin.column, + symbol.source_reference.end.line, + symbol.source_reference.end.column + ), + symbol.comment != null ? "\n" + symbol.comment.content : "" + ); + var tree_child = new ValaSymbolItem ( + symbol, + tooltip + ); parent.add (tree_child); return tree_child; }