Commit 9107cacb authored by Christian Hergert's avatar Christian Hergert

buffers: use metadata::libide-position for cursor position

Previously, we were relying on working history to track the
most recent cursor position. That is rather prone to failure
so instead we'll use the gvfs backend to store
metadata::libide-position with a value like "line:offset" to
restore the cursor. We will also accept just "line" which will
result in 0 being used for the offset.

Line and offset use a zero index.

This does require that gvfs is installed to work correctly,
and jhbuild has been updated to include that as a dependency.

Long term, we should investigate a metadata manager of sorts
to be attached to IdeContext so that metadata operations can
be blocked (asynchronously) during context unloading.
parent b9ba56da
......@@ -79,6 +79,8 @@ typedef struct
GtkSourceFileLoader *loader;
guint is_new : 1;
IdeWorkbenchOpenFlags flags;
guint line;
guint line_offset;
} LoadState;
typedef struct
......@@ -405,6 +407,40 @@ ide_buffer_manager_track_buffer (IdeBufferManager *self,
static void
ide_buffer_manager_save_cursor_position (IdeBufferManager *self,
IdeBuffer *buffer)
g_autofree gchar *position = NULL;
g_autoptr(GError) error = NULL;
IdeFile *file;
GFile *gfile;
GtkTextIter iter;
g_assert (IDE_IS_BUFFER_MANAGER (self));
g_assert (IDE_IS_BUFFER (buffer));
/* FIXME: We should have a metadata manager like gedit which is owned by the
* context. That would allow us to do this asynchronously and block
* on synchronization during unload.
file = ide_buffer_get_file (buffer);
if (ide_file_get_is_temporary (file))
gfile = ide_file_get_file (file);
gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter,
gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)));
position = g_strdup_printf ("%u:%u",
gtk_text_iter_get_line (&iter),
gtk_text_iter_get_line_offset (&iter));
if (!g_file_set_attribute_string (gfile, IDE_FILE_ATTRIBUTE_POSITION, position, 0, NULL, &error))
g_warning ("Failed to persist cursor position: %s", error->message);
static void
ide_buffer_manager_remove_buffer (IdeBufferManager *self,
IdeBuffer *buffer)
......@@ -433,6 +469,9 @@ ide_buffer_manager_remove_buffer (IdeBufferManager *self,
/* Stash the cursor position for next time we open the file */
ide_buffer_manager_save_cursor_position (self, buffer);
/* Stealing ownership from self->buffers */
g_ptr_array_remove_index (self->buffers, position);
......@@ -502,17 +541,16 @@ ide_buffer_manager_load_file__load_cb (GObject *object,
g_autofree gchar *guess_contents = NULL;
g_autofree gchar *content_type = NULL;
GtkSourceFileLoader *loader = (GtkSourceFileLoader *)object;
IdeBackForwardList *back_forward_list;
IdeBackForwardItem *item;
IdeBufferManager *self;
const gchar *path;
IdeContext *context;
LoadState *state;
GtkTextIter iter;
GtkTextIter end;
GError *error = NULL;
g_autoptr(GError) error = NULL;
gboolean uncertain = TRUE;
gsize i;
g_assert (G_IS_TASK (task));
g_assert (GTK_SOURCE_IS_FILE_LOADER (loader));
......@@ -547,64 +585,26 @@ ide_buffer_manager_load_file__load_cb (GObject *object,
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
g_task_return_error (task, error);
g_task_return_error (task, g_steal_pointer (&error));
g_clear_error (&error);
gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (state->buffer), FALSE);
for (i = 0; i < self->buffers->len; i++)
IdeBuffer *cur_buffer;
cur_buffer = g_ptr_array_index (self->buffers, i);
if (cur_buffer == state->buffer)
goto emit_signal;
if (state->is_new)
ide_buffer_manager_track_buffer (self, state->buffer);
* If we have a navigation item for this buffer, restore the insert mark to
* the most recent navigation point.
back_forward_list = ide_context_get_back_forward_list (context);
item = _ide_back_forward_list_find (back_forward_list, state->file);
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (state->buffer), &iter);
if (item != NULL && g_settings_get_boolean (self->settings, "restore-insert-mark"))
const gchar *fragment;
IdeUri *uri;
uri = ide_back_forward_item_get_uri (item);
fragment = ide_uri_get_fragment (uri);
if (fragment != NULL)
guint line = 0;
guint line_offset = 0;
if (sscanf (fragment, "L%u_%u", &line, &line_offset) >= 1)
IDE_TRACE_MSG ("Restoring insert mark to %u:%u", line, line_offset);
gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (state->buffer), &iter,
line, line_offset);
/* try to restore the insertion cursor */
if (g_settings_get_boolean (self->settings, "restore-insert-mark"))
gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (state->buffer), &iter,
state->line, state->line_offset);
IDE_TRACE_MSG ("Restoring insert mark to 0:0");
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (state->buffer), &iter);
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (state->buffer), &iter);
IDE_TRACE_MSG ("Restoring insert mark to %u:%u",
gtk_text_iter_get_line (&iter) + 1,
gtk_text_iter_get_line_offset (&iter) + 1);
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (state->buffer), &iter, &iter);
......@@ -621,14 +621,14 @@ ide_buffer_manager_load_file__load_cb (GObject *object,
if (content_type && !uncertain)
_ide_file_set_content_type (state->file, content_type);
if (!_ide_context_is_restoring (context))
ide_buffer_manager_set_focus_buffer (self, state->buffer);
g_signal_emit (self, signals [BUFFER_LOADED], 0, state->buffer);
g_task_return_pointer (task, g_object_ref (state->buffer), g_object_unref);
static void
......@@ -698,6 +698,19 @@ ide_buffer_manager__load_file_query_info_cb (GObject *object,
_ide_buffer_set_mtime (state->buffer, &tv);
if (file_info && g_file_info_has_attribute (file_info, IDE_FILE_ATTRIBUTE_POSITION))
const gchar *attr = g_file_info_get_attribute_string (file_info, IDE_FILE_ATTRIBUTE_POSITION);
guint line = 0;
guint line_offset = 0;
if (attr != NULL && sscanf (attr, "%u:%u", &line, &line_offset) >= 1)
state->line = line;
state->line_offset = line_offset;
create_new_view = (state->flags & IDE_WORKBENCH_OPEN_FLAGS_BACKGROUND) ? FALSE : state->is_new;
g_signal_emit (self, signals [LOAD_BUFFER], 0, state->buffer, create_new_view);
......@@ -748,7 +761,8 @@ ide_buffer_manager__load_file_read_cb (GObject *object,
g_file_query_info_async (file,
g_task_get_cancellable (task),
......@@ -27,6 +27,8 @@ G_BEGIN_DECLS
#define IDE_TYPE_FILE (ide_file_get_type())
#define IDE_FILE_ATTRIBUTE_POSITION "metadata::libide-position"
G_DECLARE_FINAL_TYPE (IdeFile, ide_file, IDE, FILE, IdeObject)
IdeFile *ide_file_new (IdeContext *context,
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment