Kaydet (Commit) f403e7e8 authored tarafından Colomban Wendling's avatar Colomban Wendling

Merge pull request #188 from artros/feature/keep-edit-history-on-reload

Maintain edit history on document reload

Conflicts:
 * src/callbacks.c: document_reload_prompt().

Amendments:
 * src/document.c: document_redo(), document_undo(): for loop style.
...@@ -2646,6 +2646,9 @@ use_gio_unsafe_file_saving Whether to use GIO as the unsafe file t ...@@ -2646,6 +2646,9 @@ use_gio_unsafe_file_saving Whether to use GIO as the unsafe file t
correctly on some complex setups. correctly on some complex setups.
gio_unsafe_save_backup Make a backup when using GIO unsafe file false immediately gio_unsafe_save_backup Make a backup when using GIO unsafe file false immediately
saving. Backup is named `filename~`. saving. Backup is named `filename~`.
keep_edit_history_on_reload Whether to maintain the edit history when true immediately
reloading a file, and allow the operation
to be reverted.
**Filetype related** **Filetype related**
extract_filetype_regex Regex to extract filetype name from file See below. immediately extract_filetype_regex Regex to extract filetype name from file See below. immediately
via capture group one. via capture group one.
...@@ -3370,8 +3373,7 @@ Close all Ctrl-Shift-W Closes all open files. ...@@ -3370,8 +3373,7 @@ Close all Ctrl-Shift-W Closes all open files.
Close Ctrl-W (C) Closes the current file. Close Ctrl-W (C) Closes the current file.
Reload file Ctrl-R (C) Reloads the current file. All unsaved changes Reload file Ctrl-R (C) Reloads the current file.
will be lost.
Print Ctrl-P (C) Prints the current file. Print Ctrl-P (C) Prints the current file.
......
...@@ -1215,6 +1215,8 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename ...@@ -1215,6 +1215,8 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename
gchar *locale_filename = NULL; gchar *locale_filename = NULL;
GeanyFiletype *use_ft; GeanyFiletype *use_ft;
FileData filedata; FileData filedata;
UndoReloadData *undo_reload_data;
gboolean add_undo_reload_action;
g_return_val_if_fail(doc == NULL || doc->is_valid, NULL); g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
...@@ -1274,8 +1276,31 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename ...@@ -1274,8 +1276,31 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename
monitor_file_setup(doc); monitor_file_setup(doc);
} }
sci_set_undo_collection(doc->editor->sci, FALSE); /* avoid creation of an undo action */ if (! reload || ! file_prefs.keep_edit_history_on_reload)
sci_empty_undo_buffer(doc->editor->sci); {
sci_set_undo_collection(doc->editor->sci, FALSE); /* avoid creation of an undo action */
sci_empty_undo_buffer(doc->editor->sci);
undo_reload_data = NULL;
}
else
{
undo_reload_data = (UndoReloadData*) g_malloc(sizeof(UndoReloadData));
/* We will be adding a UNDO_RELOAD action to the undo stack that undoes
* this reload. To do that, we keep collecting undo actions during
* reloading, and at the end add an UNDO_RELOAD action that performs
* all these actions in bulk. To keep track of how many undo actions
* were added during this time, we compare the current undo-stack height
* with its height at the end of the process. Note that g_trash_stack_height()
* is O(N), which is a little ugly, but this seems like the most maintainable
* option. */
undo_reload_data->actions_count = g_trash_stack_height(&doc->priv->undo_actions);
/* We use add_undo_reload_action to track any changes to the document that
* require adding an undo action to revert the reload, but that do not
* generate an undo action themselves. */
add_undo_reload_action = FALSE;
}
/* add the text to the ScintillaObject */ /* add the text to the ScintillaObject */
sci_set_readonly(doc->editor->sci, FALSE); /* to allow replacing text */ sci_set_readonly(doc->editor->sci, FALSE); /* to allow replacing text */
...@@ -1284,11 +1309,28 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename ...@@ -1284,11 +1309,28 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename
/* detect & set line endings */ /* detect & set line endings */
editor_mode = utils_get_line_endings(filedata.data, filedata.len); editor_mode = utils_get_line_endings(filedata.data, filedata.len);
if (undo_reload_data)
{
undo_reload_data->eol_mode = editor_get_eol_char_mode(doc->editor);
/* Force adding an undo-reload action if the EOL mode changed. */
if (editor_mode != undo_reload_data->eol_mode)
add_undo_reload_action = TRUE;
}
sci_set_eol_mode(doc->editor->sci, editor_mode); sci_set_eol_mode(doc->editor->sci, editor_mode);
g_free(filedata.data); g_free(filedata.data);
sci_set_undo_collection(doc->editor->sci, TRUE); sci_set_undo_collection(doc->editor->sci, TRUE);
/* If reloading and the current and new encodings or BOM states differ,
* add appropriate undo actions. */
if (undo_reload_data)
{
if (! utils_str_equal(doc->encoding, filedata.enc))
document_undo_add(doc, UNDO_ENCODING, g_strdup(doc->encoding));
if (doc->has_bom != filedata.bom)
document_undo_add(doc, UNDO_BOM, GINT_TO_POINTER(doc->has_bom));
}
doc->priv->mtime = filedata.mtime; /* get the modification time from file and keep it */ doc->priv->mtime = filedata.mtime; /* get the modification time from file and keep it */
g_free(doc->encoding); /* if reloading, free old encoding */ g_free(doc->encoding); /* if reloading, free old encoding */
doc->encoding = filedata.enc; doc->encoding = filedata.enc;
...@@ -1314,7 +1356,34 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename ...@@ -1314,7 +1356,34 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename
} }
else else
{ /* reloading */ { /* reloading */
document_undo_clear(doc); if (undo_reload_data)
{
/* Calculate the number of undo actions that are part of the reloading
* process, and add the UNDO_RELOAD action. */
undo_reload_data->actions_count =
g_trash_stack_height(&doc->priv->undo_actions) - undo_reload_data->actions_count;
/* We only add an undo-reload action if the document has actually changed.
* At the time of writing, this condition is moot because sci_set_text
* generates an undo action even when the text hasn't really changed, so
* actions_count is always greater than zero. In the future this might change.
* It's arguable whether we should add an undo-reload action unconditionally,
* especially since it's possible (if unlikely) that there had only
* been "invisible" changes to the document, such as changes in encoding and
* EOL mode, but for the time being that's how we roll. */
if (undo_reload_data->actions_count > 0 || add_undo_reload_action)
document_undo_add(doc, UNDO_RELOAD, undo_reload_data);
else
g_free(undo_reload_data);
/* We didn't save the document per-se, but its contents are now
* synchronized with the file on disk, hence set a save point here.
* We need to do this in this case only, because we don't clear
* Scintilla's undo stack. */
sci_set_savepoint(doc->editor->sci);
}
else
document_undo_clear(doc);
use_ft = ft; use_ft = ft;
} }
...@@ -1458,8 +1527,9 @@ gboolean document_reload_prompt(GeanyDocument *doc, const gchar *forced_enc) ...@@ -1458,8 +1527,9 @@ gboolean document_reload_prompt(GeanyDocument *doc, const gchar *forced_enc)
forced_enc = doc->encoding; forced_enc = doc->encoding;
base_name = g_path_get_basename(doc->file_name); base_name = g_path_get_basename(doc->file_name);
/* don't prompt if file hasn't been edited at all */ /* don't prompt if edit history is maintained, or if file hasn't been edited at all */
prompt = doc->changed || (document_can_undo(doc) || document_can_redo(doc)); prompt = !file_prefs.keep_edit_history_on_reload &&
(doc->changed || (document_can_undo(doc) || document_can_redo(doc)));
if (!prompt || dialogs_show_question_full(NULL, _("_Reload"), GTK_STOCK_CANCEL, if (!prompt || dialogs_show_question_full(NULL, _("_Reload"), GTK_STOCK_CANCEL,
doc->changed ? _("Any unsaved changes will be lost.") : doc->changed ? _("Any unsaved changes will be lost.") :
...@@ -2715,7 +2785,9 @@ void document_undo_clear_stack(GTrashStack **stack) ...@@ -2715,7 +2785,9 @@ void document_undo_clear_stack(GTrashStack **stack)
{ {
switch (a->type) switch (a->type)
{ {
case UNDO_ENCODING: g_free(a->data); break; case UNDO_ENCODING:
case UNDO_RELOAD:
g_free(a->data); break;
default: break; default: break;
} }
g_free(a); g_free(a);
...@@ -2837,6 +2909,29 @@ void document_undo(GeanyDocument *doc) ...@@ -2837,6 +2909,29 @@ void document_undo(GeanyDocument *doc)
g_free(action->data); g_free(action->data);
break; break;
} }
case UNDO_RELOAD:
{
UndoReloadData *data = (UndoReloadData*)action->data;
gint eol_mode = data->eol_mode;
guint i;
/* We reuse 'data' for the redo action, so read the current EOL mode
* into it before proceeding. */
data->eol_mode = editor_get_eol_char_mode(doc->editor);
/* Undo the rest of the actions which are part of the reloading process. */
for (i = 0; i < data->actions_count; i++)
document_undo(doc);
/* Restore the previous EOL mode. */
sci_set_eol_mode(doc->editor->sci, eol_mode);
/* This might affect the status bar and document menu, so update them. */
ui_update_statusbar(doc, -1);
ui_document_show_hide(doc);
document_redo_add(doc, UNDO_RELOAD, data);
break;
}
default: break; default: break;
} }
} }
...@@ -2905,6 +3000,29 @@ void document_redo(GeanyDocument *doc) ...@@ -2905,6 +3000,29 @@ void document_redo(GeanyDocument *doc)
g_free(action->data); g_free(action->data);
break; break;
} }
case UNDO_RELOAD:
{
UndoReloadData *data = (UndoReloadData*)action->data;
gint eol_mode = data->eol_mode;
guint i;
/* We reuse 'data' for the undo action, so read the current EOL mode
* into it before proceeding. */
data->eol_mode = editor_get_eol_char_mode(doc->editor);
/* Redo the rest of the actions which are part of the reloading process. */
for (i = 0; i < data->actions_count; i++)
document_redo(doc);
/* Restore the previous EOL mode. */
sci_set_eol_mode(doc->editor->sci, eol_mode);
/* This might affect the status bar and document menu, so update them. */
ui_update_statusbar(doc, -1);
ui_document_show_hide(doc);
document_undo_add_internal(doc, UNDO_RELOAD, data);
break;
}
default: break; default: break;
} }
} }
......
...@@ -64,6 +64,7 @@ typedef struct GeanyFilePrefs ...@@ -64,6 +64,7 @@ typedef struct GeanyFilePrefs
gboolean use_gio_unsafe_file_saving; /* whether to use GIO as the unsafe backend */ gboolean use_gio_unsafe_file_saving; /* whether to use GIO as the unsafe backend */
gchar *extract_filetype_regex; /* regex to extract filetype on opening */ gchar *extract_filetype_regex; /* regex to extract filetype on opening */
gboolean tab_close_switch_to_mru; gboolean tab_close_switch_to_mru;
gboolean keep_edit_history_on_reload; /* Keep undo stack upon, and allow undoing of, document reloading. */
} }
GeanyFilePrefs; GeanyFilePrefs;
......
...@@ -34,9 +34,17 @@ enum ...@@ -34,9 +34,17 @@ enum
UNDO_SCINTILLA = 0, UNDO_SCINTILLA = 0,
UNDO_ENCODING, UNDO_ENCODING,
UNDO_BOM, UNDO_BOM,
UNDO_RELOAD,
UNDO_ACTIONS_MAX UNDO_ACTIONS_MAX
}; };
typedef struct UndoReloadData
{
guint actions_count; /* How many following undo/redo actions need to be applied. */
gint eol_mode; /* End-Of-Line mode before/after reloading. */
}
UndoReloadData;
typedef enum typedef enum
{ {
FILE_OK, FILE_OK,
......
...@@ -224,6 +224,8 @@ static void init_pref_groups(void) ...@@ -224,6 +224,8 @@ static void init_pref_groups(void)
"gio_unsafe_save_backup", FALSE); "gio_unsafe_save_backup", FALSE);
stash_group_add_boolean(group, &file_prefs.use_gio_unsafe_file_saving, stash_group_add_boolean(group, &file_prefs.use_gio_unsafe_file_saving,
"use_gio_unsafe_file_saving", TRUE); "use_gio_unsafe_file_saving", TRUE);
stash_group_add_boolean(group, &file_prefs.keep_edit_history_on_reload,
"keep_edit_history_on_reload", TRUE);
/* for backwards-compatibility */ /* for backwards-compatibility */
stash_group_add_integer(group, &editor_prefs.indentation->hard_tab_width, stash_group_add_integer(group, &editor_prefs.indentation->hard_tab_width,
"indent_hard_tab_width", 8); "indent_hard_tab_width", 8);
......
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