From 4f65962ba670695d1533b141ac945c73ab74a036 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 21 May 2025 18:54:38 +0100 Subject: [PATCH 01/12] ValaSymbolItem: GObject style construction --- src/SymbolPane/Vala/ValaSymbolItem.vala | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/SymbolPane/Vala/ValaSymbolItem.vala b/src/SymbolPane/Vala/ValaSymbolItem.vala index 96b5613a7f..7b06b94563 100644 --- a/src/SymbolPane/Vala/ValaSymbolItem.vala +++ b/src/SymbolPane/Vala/ValaSymbolItem.vala @@ -17,16 +17,24 @@ */ 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; + Object ( + symbol: symbol + ); + } + + 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; } } From cea4aa6acbf04c214758bf676c4e5efbb560fcb4 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 15:29:23 +0100 Subject: [PATCH 02/12] ValaSymbol: Move construction code from Outline to Item --- src/SymbolPane/Vala/ValaSymbolItem.vala | 62 ++++++++++++++++++++++ src/SymbolPane/Vala/ValaSymbolOutline.vala | 61 --------------------- 2 files changed, 62 insertions(+), 61 deletions(-) diff --git a/src/SymbolPane/Vala/ValaSymbolItem.vala b/src/SymbolPane/Vala/ValaSymbolItem.vala index 7b06b94563..6b6b5d8e7c 100644 --- a/src/SymbolPane/Vala/ValaSymbolItem.vala +++ b/src/SymbolPane/Vala/ValaSymbolItem.vala @@ -36,6 +36,68 @@ public class Scratch.Services.ValaSymbolItem : Code.Widgets.SourceList.Expandabl } 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); + } } ~ValaSymbolItem () { diff --git a/src/SymbolPane/Vala/ValaSymbolOutline.vala b/src/SymbolPane/Vala/ValaSymbolOutline.vala index 2ec2c56a0a..98ff9879e6 100644 --- a/src/SymbolPane/Vala/ValaSymbolOutline.vala +++ b/src/SymbolPane/Vala/ValaSymbolOutline.vala @@ -192,67 +192,6 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline } 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); - } parent.add (tree_child); return tree_child; From d9c478a5226ad7186c8b742e213ca0c4bda067bd Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 15:54:33 +0100 Subject: [PATCH 03/12] Use slice of doc for tooltip --- src/Services/Document.vala | 20 ++++++++++++++++++++ src/SymbolPane/Vala/ValaSymbolItem.vala | 5 +++-- src/SymbolPane/Vala/ValaSymbolOutline.vala | 22 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index 051ecd904f..581e77a1c6 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -815,6 +815,26 @@ 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) { + var text = source_view; + Gtk.TextIter iter; + text.buffer.get_iter_at_line (out iter, start_line - 1); + if (!iter.forward_chars (start_col - 1)) { + return ""; + } + + var start = iter; + text.buffer.get_iter_at_line (out iter, end_line - 1); + + if (!iter.forward_to_line_end ()) { + return ""; + } + + var end = iter; + var slice = text.buffer.get_text (start, end, false); + return text.buffer.get_text (start, end, false); + } + // Get language name public string get_language_name () { var source_buffer = (Gtk.SourceBuffer) source_view.buffer; diff --git a/src/SymbolPane/Vala/ValaSymbolItem.vala b/src/SymbolPane/Vala/ValaSymbolItem.vala index 6b6b5d8e7c..4cee79429b 100644 --- a/src/SymbolPane/Vala/ValaSymbolItem.vala +++ b/src/SymbolPane/Vala/ValaSymbolItem.vala @@ -19,9 +19,10 @@ public class Scratch.Services.ValaSymbolItem : Code.Widgets.SourceList.ExpandableItem, Code.Widgets.SourceListSortable, Scratch.Services.SymbolItem { public Vala.Symbol symbol { get; construct; } public SymbolType symbol_type { get; set; default = SymbolType.OTHER; } - public ValaSymbolItem (Vala.Symbol symbol) { + public ValaSymbolItem (Vala.Symbol symbol, string _tooltip) { Object ( - symbol: symbol + symbol: symbol, + tooltip: _tooltip ); } diff --git a/src/SymbolPane/Vala/ValaSymbolOutline.vala b/src/SymbolPane/Vala/ValaSymbolOutline.vala index 98ff9879e6..f5466ba06b 100644 --- a/src/SymbolPane/Vala/ValaSymbolOutline.vala +++ b/src/SymbolPane/Vala/ValaSymbolOutline.vala @@ -166,6 +166,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; @@ -180,7 +181,12 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline 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,8 +197,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); + 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; } From c88a13c67b40753fa74cc6496f26f1adef9c1552 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 16:52:41 +0100 Subject: [PATCH 04/12] Wait till all docs restored and loaded before updating outline visible --- src/MainWindow.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 924c4b184c..6862ba12fd 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 ()); From e35326a45f5787fa73e9b607765828cd92014e71 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 16:55:25 +0100 Subject: [PATCH 05/12] Correctly connect to tab.loading notification --- src/Services/Document.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index 581e77a1c6..fa050ca42a 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -306,7 +306,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; From afb993dc6365601d6619d3fb60efe1fc573af1f8 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 16:56:26 +0100 Subject: [PATCH 06/12] On tab loading change parse symbols if appropriate --- src/Services/Document.vala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index fa050ca42a..d60cce01ce 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -1341,6 +1341,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 (); + } } } } From 79659e8666c14c03dd9c148d2fdd923d7350d829 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 16:57:35 +0100 Subject: [PATCH 07/12] Correctly format show outline (); parse symbols only if not loading --- src/Services/Document.vala | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index d60cce01ce..f0b3bbfe86 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -1226,27 +1226,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 (); From 2764aec9adc206ff1981292e0fff4b574cae26bc Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 22 May 2025 20:20:04 +0100 Subject: [PATCH 08/12] Do not try to forward iter less than 1 char --- src/Services/Document.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index f0b3bbfe86..bac979b522 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -819,7 +819,7 @@ namespace Scratch.Services { var text = source_view; Gtk.TextIter iter; text.buffer.get_iter_at_line (out iter, start_line - 1); - if (!iter.forward_chars (start_col - 1)) { + if (start_col > 1 && !iter.forward_chars (start_col - 1)) { return ""; } From c1dea9dab26629129b97aa356fad7b466b86c92e Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sat, 24 May 2025 12:41:42 +0100 Subject: [PATCH 09/12] Make source_view property and lock when used from separate thread --- src/Services/Document.vala | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index bac979b522..38f95fe5ee 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 { @@ -816,23 +818,24 @@ namespace Scratch.Services { } public string get_slice (int start_line, int start_col, int end_line, int end_col) { - var text = source_view; - Gtk.TextIter iter; - text.buffer.get_iter_at_line (out iter, start_line - 1); - if (start_col > 1 && !iter.forward_chars (start_col - 1)) { - return ""; - } + // 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; - text.buffer.get_iter_at_line (out iter, end_line - 1); + var start = iter; + source_view.buffer.get_iter_at_line (out iter, end_line - 1); - if (!iter.forward_to_line_end ()) { - return ""; - } + if (!iter.forward_to_line_end ()) { + return ""; + } - var end = iter; - var slice = text.buffer.get_text (start, end, false); - return text.buffer.get_text (start, end, false); + var end = iter; + return source_view.buffer.get_text (start, end, false); + } } // Get language name From b13a4c3d06c8e9a82da38a459a1a6d4f013c6b0b Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sat, 24 May 2025 12:42:25 +0100 Subject: [PATCH 10/12] Yield thread when constructing tree --- src/SymbolPane/Vala/ValaSymbolOutline.vala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SymbolPane/Vala/ValaSymbolOutline.vala b/src/SymbolPane/Vala/ValaSymbolOutline.vala index f5466ba06b..6b620f33a8 100644 --- a/src/SymbolPane/Vala/ValaSymbolOutline.vala +++ b/src/SymbolPane/Vala/ValaSymbolOutline.vala @@ -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 (); @@ -176,6 +177,7 @@ public class Scratch.Services.ValaSymbolOutline : Scratch.Services.SymbolOutline continue; construct_child (symbol, new_root, cancellable); + Thread.yield (); } return new_root; From 549a8dd45beffbfaf04f0d22f7d42c6d7016ec43 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sat, 24 May 2025 12:42:53 +0100 Subject: [PATCH 11/12] Increase allowed time for parse since UI not blocked --- src/SymbolPane/Vala/ValaSymbolOutline.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SymbolPane/Vala/ValaSymbolOutline.vala b/src/SymbolPane/Vala/ValaSymbolOutline.vala index 6b620f33a8..49a96edb4e 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; From eca622bf7fa20fadd93d2a64f2ef504a0f4d2c4c Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sat, 24 May 2025 17:36:53 +0100 Subject: [PATCH 12/12] Fix lint --- src/Services/Document.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index 38f95fe5ee..ae4bf524f5 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -162,7 +162,7 @@ namespace Scratch.Services { public Gtk.Stack main_stack; - public Scratch.Widgets.SourceView source_view { get; construct; } + public Scratch.Widgets.SourceView source_view { get; construct; } private Scratch.Services.SymbolOutline? outline = null; private Scratch.Widgets.DocumentView doc_view {