From fb1e59fe1e1aa2cde13a2ccc39182323ca28640c Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Tue, 21 Jul 2026 12:35:48 +0100 Subject: [PATCH 1/9] Call DocumentView.open_document directly with string --- src/Application.vala | 6 +- src/MainWindow.vala | 50 ++++------------- src/Widgets/DocumentView.vala | 102 +++++++++++++++++++--------------- src/Widgets/SourceView.vala | 18 +++--- 4 files changed, 83 insertions(+), 93 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index 56581cb32..c661dc0a6 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -179,16 +179,16 @@ namespace Scratch { var window = get_last_window (); foreach (var file in files) { bool is_folder; + string path = file.get_uri (); if (Scratch.Services.FileHandler.can_open_file (file, out is_folder)) { if (is_folder) { window.open_folder (file); } else { debug ("Files length: %d\n", files.length); - var doc = new Scratch.Services.Document (window.actions, file); if (location_jump_manager.has_selection_range != null && files.length == 1) { - window.open_document_at_selected_range.begin (doc, true, location_jump_manager.range); + window.document_view.open_document.begin (path, true, -2, location_jump_manager.range); } else { - window.open_document.begin (doc); + window.document_view.open_document.begin (path); } } } diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 2e948dc0e..235390372 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -463,8 +463,7 @@ namespace Scratch { bool is_folder; //TODO Handle folders dropped here if (Scratch.Services.FileHandler.can_open_file (file, out is_folder) && !is_folder) { - Scratch.Services.Document doc = new Scratch.Services.Document (actions, file); - document_view.open_document.begin (doc); + document_view.open_document.begin (filename); } } @@ -481,10 +480,9 @@ namespace Scratch { folder_manager_view.activate.connect ((a) => { var file = new Scratch.FolderManager.File (a); - var doc = new Scratch.Services.Document (actions, file.file); - + //TODO Foldermanager should only show text files so is this check required if (file.is_valid_textfile) { - open_document.begin (doc); + document_view.open_document.begin (a); } else { open_binary (file.file); } @@ -676,14 +674,12 @@ namespace Scratch { focused_file = file; } //TODO Check files valid (settings could have been manually altered) - var doc = new Scratch.Services.Document (actions, file); - if (doc.exists () || !doc.is_file_temporary) { - if (restore_override != null && (file.get_path () == restore_override.file.get_path ())) { - yield open_document_at_selected_range (doc, true, restore_override.range, true); - was_restore_overriden = true; - } else { - yield open_document (doc, was_restore_overriden ? false : is_focused, pos); - } + // We already know the file exists and a restore override path is not going to match a temporary file + if (restore_override != null && (file.get_path () == restore_override.file.get_path ())) { + yield document_view.open_document (uri, true, -2, restore_override.range); + was_restore_overriden = true; + } else { + yield document_view.open_document (uri, was_restore_overriden ? false : is_focused, pos); } } } @@ -776,26 +772,9 @@ namespace Scratch { folder_manager_view.open_folder (foldermanager_file); } - public async void open_document (Scratch.Services.Document doc, - bool focus = true, - int cursor_position = 0) { - FolderManager.ProjectFolderItem? project = folder_manager_view.get_project_for_file (doc.file); - doc.source_view.project = project; - yield document_view.open_document (doc, focus, cursor_position); - } - public async void open_document_at_selected_range (Scratch.Services.Document doc, - bool focus = true, - SelectionRange range = SelectionRange.EMPTY, - bool is_override = false) { - if (restore_override != null && is_override == false) { - return; - } - doc.source_view.project = folder_manager_view.get_project_for_file (doc.file); - yield document_view.open_document (doc, focus, 0, range); - } // Close a document public void close_document (Scratch.Services.Document doc) { @@ -996,9 +975,7 @@ namespace Scratch { // Update last visited path Utils.last_path = Path.get_dirname (uri); // Open the file - var file = File.new_for_uri (uri); - var doc = new Scratch.Services.Document (actions, file); - open_document.begin (doc); + document_view.open_document.begin (uri); } } }); @@ -1013,10 +990,8 @@ namespace Scratch { } var new_window = new MainWindow (false); - var file = File.new_for_path (path); - var doc = new Scratch.Services.Document (new_window.actions, file); - new_window.open_document.begin (doc, true); + new_window.document_view.open_document.begin (path, true); } @@ -1271,8 +1246,7 @@ namespace Scratch { private void restore_project_docs (string project_path) { document_manager.take_restorable_paths (project_path).@foreach ((doc_path) => { - var doc = new Scratch.Services.Document (actions, File.new_for_path (doc_path)); - open_document.begin (doc); // Use this to reassociate project and document. + document_view.open_document.begin (doc_path); // Use this to reassociate project and document. return true; }); } diff --git a/src/Widgets/DocumentView.vala b/src/Widgets/DocumentView.vala index bcfa1aba8..4a9ff1877 100644 --- a/src/Widgets/DocumentView.vala +++ b/src/Widgets/DocumentView.vala @@ -293,78 +293,94 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { } public void new_document () { - var file = File.new_for_path (unsaved_file_path_builder ()); + var new_doc_path = unsaved_file_path_builder (); + var file = File.new_for_path (new_doc_path); try { file.create (FileCreateFlags.PRIVATE); - - var doc = new Services.Document (window.actions, file); // Must open document in order to unlock it. - open_document.begin (doc); + open_document.begin (new_doc_path); } catch (Error e) { critical (e.message); } } public void new_document_from_clipboard (string clipboard) { - var file = File.new_for_path (unsaved_file_path_builder ()); + var new_doc_path = unsaved_file_path_builder (); + var file = File.new_for_path (new_doc_path); // Set clipboard content try { file.create (FileCreateFlags.PRIVATE); file.replace_contents (clipboard.data, null, false, 0, null); - var doc = new Services.Document (window.actions, file); - - open_document.begin (doc); - - + open_document.begin (new_doc_path); } catch (Error e) { critical ("Cannot insert clipboard: %s", clipboard); } } - public async void open_document (Services.Document doc, bool focus = true, int cursor_position = 0, SelectionRange range = SelectionRange.EMPTY) { - for (int n = 0; n <= docs.length (); n++) { - var nth_doc = docs.nth_data (n); - if (nth_doc == null) { + // Open document from path to avoid unnecessarily creating Document objects and + // to confine Document creation to this class. + // Documents are treated the same regarding the focus, cursor_position and range parameters + // whether already open or not. + // Cursor position may have any value > -2 to set cursor. Lower values are ignored (default) + // Specifying a valid non-empty selection range overrides the cursor position + public async void open_document ( + string doc_path, + bool focus = true, + int cursor_position = -2, + SelectionRange range = SelectionRange.EMPTY + ) { + Scratch.Services.Document? doc = null; // The document to be created and/or focused + // Check whether document already open + bool found = false; + for (int n = 0; n <= docs.length (); n++) { + doc = docs.nth_data (n); + //TODO Is this check necessary? + if (doc == null || doc.file == null) { + critical ("Invalid document at position %i. %s", n, doc == null ? "Null document" : "Null document file"); continue; } - if (nth_doc.file != null && nth_doc.file.get_uri () == doc.file.get_uri ()) { - if (focus) { - current_document = nth_doc; - } - - debug ("This Document was already opened! Not opening a duplicate!"); - if (range != SelectionRange.EMPTY) { - Idle.add_full (GLib.Priority.LOW, () => { // This helps ensures new tab is drawn before opening document. - current_document.source_view.select_range (range); - update_opened_files_setting (); - - return false; - }); - } - - return; + if (doc.file.get_uri () == doc_path) { + found = true; + break; } } - insert_document (doc, (int) docs.length ()); - if (focus) { - current_document = doc; + if (!found) { + // Document not already open, create and insert it now + doc = new Scratch.Services.Document ( + window.actions, + GLib.File.new_for_path (doc_path) + ); + insert_document (doc, (int) docs.length ()); + // Load contents before proceeding + yield doc.open (false); + doc.source_view.cursor_position = cursor_position; + } else { + debug ("This Document was already opened! Not opening a duplicate!"); } - yield doc.open (false); - - if (focus && doc == current_document) { + // @doc must have been assigned at this point + assert (doc != null); + // If we need to select a range we have to focus document anyway + if (focus || range != SelectionRange.EMPTY) { doc.focus (); + current_document = doc; } - if (range != SelectionRange.EMPTY) { - doc.source_view.select_range (range); - } else if (cursor_position > 0) { + if (cursor_position > -2) { doc.source_view.cursor_position = cursor_position; } + if (range != SelectionRange.EMPTY) { + Idle.add_full (GLib.Priority.LOW, () => { // This helps ensures new tab is drawn before opening document. + current_document.source_view.select_range (range); + update_opened_files_setting (); //Records changed cursor position + return false; + }); + } + update_opened_files_setting (); } @@ -517,9 +533,7 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { } public void restore_closed_tab (string path) { - var file = File.new_for_path (path); - var doc = new Services.Document (window.actions, file); - open_document.begin (doc); + open_document.begin (path); var menu = (Menu) tab_history_button.menu_model; for (var i = 0; i < menu.get_n_items (); i++) { @@ -634,9 +648,7 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { if (info == TargetType.URI_LIST) { var uris = sel.get_uris (); foreach (var filename in uris) { - var file = File.new_for_uri (filename); - var doc = new Services.Document (window.actions, file); - open_document.begin (doc); + open_document.begin (filename); } Gtk.drag_finish (ctx, true, false, time); diff --git a/src/Widgets/SourceView.vala b/src/Widgets/SourceView.vala index 068e2e171..a89194549 100644 --- a/src/Widgets/SourceView.vala +++ b/src/Widgets/SourceView.vala @@ -67,13 +67,17 @@ namespace Scratch.Widgets { } set { - Gtk.TextIter iter; - buffer.get_iter_at_offset (out iter, value); - buffer.place_cursor (iter); //Assume invalid offset handled correctly for now - Idle.add (() => { - scroll_to_iter (iter, 0.25, false, 0, 0); - return Source.REMOVE; - }); + if (value > -1) { + Gtk.TextIter iter; + // values off -1 and greater than number of characters in buffer place the cursor + // at the end of the buffer. + buffer.get_iter_at_offset (out iter, value); + buffer.place_cursor (iter); //Assume invalid offset handled correctly for now + Idle.add (() => { + scroll_to_iter (iter, 0.25, false, 0, 0); + return Source.REMOVE; + }); + } } } From e1c62c6790f0a5c66625c72a8299e1dbfaacc23d Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 12:19:39 +0100 Subject: [PATCH 2/9] Fixup fuzzy search plugin --- plugins/fuzzy-search/fuzzy-search.vala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/fuzzy-search/fuzzy-search.vala b/plugins/fuzzy-search/fuzzy-search.vala index e78692afc..4d670d016 100644 --- a/plugins/fuzzy-search/fuzzy-search.vala +++ b/plugins/fuzzy-search/fuzzy-search.vala @@ -127,10 +127,7 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Scratch.Services.A var popover = new Scratch.FuzzySearchPopover (indexer, window); popover.open_file.connect ((filepath) => { - var file = new Scratch.FolderManager.File (filepath); - var doc = new Scratch.Services.Document (window.actions, file.file); - - window.open_document.begin (doc); + window.document_view.open_document.begin (filepath); popover.popdown (); }); From bc2b7f04fada01f9df9e4c1d5e6a9da63d9c63f2 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 13:11:16 +0100 Subject: [PATCH 3/9] Fix loop bound --- src/Widgets/DocumentView.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/DocumentView.vala b/src/Widgets/DocumentView.vala index 4a9ff1877..9fdca6359 100644 --- a/src/Widgets/DocumentView.vala +++ b/src/Widgets/DocumentView.vala @@ -333,7 +333,7 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { Scratch.Services.Document? doc = null; // The document to be created and/or focused // Check whether document already open bool found = false; - for (int n = 0; n <= docs.length (); n++) { + for (int n = 0; n < docs.length (); n++) { doc = docs.nth_data (n); //TODO Is this check necessary? if (doc == null || doc.file == null) { From 3a5e5fb43ea0620555af0a9c156aa11cacba5b0c Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 13:12:04 +0100 Subject: [PATCH 4/9] Fix Document creation --- src/Services/Document.vala | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index 38717b397..dc9c1dae8 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -35,6 +35,7 @@ namespace Scratch.Services { // The parent window's actions public unowned SimpleActionGroup actions { get; set construct; } + public GLib.File start_location { get; construct; } // The TabPage that this document is a child of public unowned Hdy.TabPage tab { get; private set; } @@ -196,12 +197,12 @@ namespace Scratch.Services { private double total_delta = 0; private const double SCROLL_THRESHOLD = 1.0; - public Document (SimpleActionGroup actions, File file) { + public Document (SimpleActionGroup actions, GLib.File file) { Object ( - actions: actions + actions: actions, + start_location: file ); - this.file = file; } static construct { @@ -222,7 +223,10 @@ namespace Scratch.Services { construct { locked_icon = new ThemedIcon ("emblem-readonly-symbolic"); main_stack = new Gtk.Stack (); - source_view = new Scratch.Widgets.SourceView (); + source_view = new Scratch.Widgets.SourceView () { + location = start_location + }; + scroll = new Gtk.ScrolledWindow (null, null) { hexpand = true, @@ -252,7 +256,10 @@ namespace Scratch.Services { Gtk.propagate_event (scroll, Gtk.get_current_event ()); }); - source_file = new Gtk.SourceFile (); + source_file = new Gtk.SourceFile () { + location = start_location + }; + source_map = new Gtk.SourceMap (); outline_widget_pane = new Gtk.Paned (Gtk.Orientation.HORIZONTAL); @@ -335,8 +342,6 @@ namespace Scratch.Services { } }); - loaded = file == null; - add (main_stack); this.show_all (); } From 43aebb844fa70a0958e36756adfa1268bba9dd9d Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 13:16:14 +0100 Subject: [PATCH 5/9] Create File with new_for_commandline_arg --- src/Widgets/DocumentView.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/DocumentView.vala b/src/Widgets/DocumentView.vala index 9fdca6359..df1815be3 100644 --- a/src/Widgets/DocumentView.vala +++ b/src/Widgets/DocumentView.vala @@ -351,7 +351,7 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { // Document not already open, create and insert it now doc = new Scratch.Services.Document ( window.actions, - GLib.File.new_for_path (doc_path) + GLib.File.new_for_commandline_arg (doc_path) ); insert_document (doc, (int) docs.length ()); // Load contents before proceeding From 1e3b7425581a9b9192c6a2f5fbf883363977e0ac Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 17:51:36 +0100 Subject: [PATCH 6/9] Revert "Fix Document creation" This reverts commit 3a5e5fb43ea0620555af0a9c156aa11cacba5b0c. --- src/Services/Document.vala | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Services/Document.vala b/src/Services/Document.vala index dc9c1dae8..38717b397 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -35,7 +35,6 @@ namespace Scratch.Services { // The parent window's actions public unowned SimpleActionGroup actions { get; set construct; } - public GLib.File start_location { get; construct; } // The TabPage that this document is a child of public unowned Hdy.TabPage tab { get; private set; } @@ -197,12 +196,12 @@ namespace Scratch.Services { private double total_delta = 0; private const double SCROLL_THRESHOLD = 1.0; - public Document (SimpleActionGroup actions, GLib.File file) { + public Document (SimpleActionGroup actions, File file) { Object ( - actions: actions, - start_location: file + actions: actions ); + this.file = file; } static construct { @@ -223,10 +222,7 @@ namespace Scratch.Services { construct { locked_icon = new ThemedIcon ("emblem-readonly-symbolic"); main_stack = new Gtk.Stack (); - source_view = new Scratch.Widgets.SourceView () { - location = start_location - }; - + source_view = new Scratch.Widgets.SourceView (); scroll = new Gtk.ScrolledWindow (null, null) { hexpand = true, @@ -256,10 +252,7 @@ namespace Scratch.Services { Gtk.propagate_event (scroll, Gtk.get_current_event ()); }); - source_file = new Gtk.SourceFile () { - location = start_location - }; - + source_file = new Gtk.SourceFile (); source_map = new Gtk.SourceMap (); outline_widget_pane = new Gtk.Paned (Gtk.Orientation.HORIZONTAL); @@ -342,6 +335,8 @@ namespace Scratch.Services { } }); + loaded = file == null; + add (main_stack); this.show_all (); } From bb8d389a798fb191239a9807e13efee8877b1dea Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 19:08:41 +0100 Subject: [PATCH 7/9] Fix setting project on created document in DocumentView --- src/MainWindow.vala | 3 ++- src/Widgets/DocumentView.vala | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index e2071b7fa..7ba9fa480 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -41,6 +41,8 @@ public class Scratch.MainWindow : Hdy.Window { } public Code.Sidebar sidebar { get; private set; } + // Temporary fix to make get_project_for_file function accessible. + public FolderManager.FileView folder_manager_view; public Scratch.Application app { get; private set; } public Scratch.Widgets.DocumentView document_view { get; private set; } public SimpleActionGroup actions { get; private set; } @@ -160,7 +162,6 @@ public class Scratch.MainWindow : Hdy.Window { private Code.Terminal terminal; private Code.WelcomeView welcome_view; - private FolderManager.FileView folder_manager_view; private Gtk.Clipboard clipboard; private Gtk.EventControllerKey key_controller; private Gtk.Paned hp1; diff --git a/src/Widgets/DocumentView.vala b/src/Widgets/DocumentView.vala index df1815be3..d52ffd12a 100644 --- a/src/Widgets/DocumentView.vala +++ b/src/Widgets/DocumentView.vala @@ -353,6 +353,10 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { window.actions, GLib.File.new_for_commandline_arg (doc_path) ); + // Temporary fix, the get_project_for_file function is intended to be moved + // to GitManager instance. + var project = window.folder_manager_view.get_project_for_file (doc.file); + doc.source_view.project = project; insert_document (doc, (int) docs.length ()); // Load contents before proceeding yield doc.open (false); From 71b042d836821bcbc517d0a46e3dd28986d988b9 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 22 Jul 2026 19:12:17 +0100 Subject: [PATCH 8/9] Remove commented out code --- src/MainWindow.vala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 7ba9fa480..1835c0a2e 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -655,8 +655,6 @@ public class Scratch.MainWindow : Hdy.Window { focused_file = file; } //TODO Check files valid (settings could have been manually altered) - // var doc = new Scratch.Services.Document (actions, file); - // if (doc.exists () || !doc.is_file_temporary) { if (restore_override != null && (file.get_path () == restore_override.file.get_path ())) { @@ -674,7 +672,6 @@ public class Scratch.MainWindow : Hdy.Window { pos ); } - // } } } } From db8e3150e277169822e330d363c9859c3e29abde Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Thu, 23 Jul 2026 11:20:18 +0100 Subject: [PATCH 9/9] Cleanup cursor position setter --- src/Widgets/SourceView.vala | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Widgets/SourceView.vala b/src/Widgets/SourceView.vala index a89194549..546ff1111 100644 --- a/src/Widgets/SourceView.vala +++ b/src/Widgets/SourceView.vala @@ -67,17 +67,20 @@ namespace Scratch.Widgets { } set { - if (value > -1) { - Gtk.TextIter iter; - // values off -1 and greater than number of characters in buffer place the cursor - // at the end of the buffer. - buffer.get_iter_at_offset (out iter, value); - buffer.place_cursor (iter); //Assume invalid offset handled correctly for now - Idle.add (() => { - scroll_to_iter (iter, 0.25, false, 0, 0); - return Source.REMOVE; - }); + // Values of -1 and greater than number of characters + // in the buffer place the cursor at the end of the buffer. + // Ignore lower values + if (value <= -2) { + return; } + + Gtk.TextIter iter; + buffer.get_iter_at_offset (out iter, value); + buffer.place_cursor (iter); + Idle.add (() => { + scroll_to_iter (iter, 0.25, false, 0, 0); + return Source.REMOVE; + }); } }