[gnome-text-editor] vim: add support for vim keybindings
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-text-editor] vim: add support for vim keybindings
- Date: Fri, 12 Nov 2021 07:28:55 +0000 (UTC)
commit 010861934cbb278b92274099f7b1c2d9524d13f1
Author: Christian Hergert <chergert redhat com>
Date: Thu Nov 11 23:21:45 2021 -0800
vim: add support for vim keybindings
This is currently hidden behind the gsettings 'keybindings' key. You may
`gsettings set org.gnome.TextEditor keybindings vim` to enable it, although
in Flatpak you'll need to edit the settings file.
This uses the new GtkSourceVimIMContext along with a command bar widget on
the bottom that is only visible when Vim bindings are enabled.
Use with caution, and file bugs please (or better yet, fix them).
data/org.gnome.TextEditor.gschema.xml | 9 ++
src/TextEditor.css | 8 ++
src/editor-page-private.h | 3 +
src/editor-page-vim.c | 208 ++++++++++++++++++++++++++++++++++
src/editor-page.c | 4 +
src/editor-page.ui | 11 ++
src/meson.build | 1 +
7 files changed, 244 insertions(+)
---
diff --git a/data/org.gnome.TextEditor.gschema.xml b/data/org.gnome.TextEditor.gschema.xml
index fc4bb2c..d95c028 100644
--- a/data/org.gnome.TextEditor.gschema.xml
+++ b/data/org.gnome.TextEditor.gschema.xml
@@ -108,5 +108,14 @@
<summary>Restore session</summary>
<description>When Text Editor is run, restore the previous session.</description>
</key>
+ <key name="keybindings" type="s">
+ <choices>
+ <choice value="default"/>
+ <choice value="vim"/>
+ </choices>
+ <default>'default'</default>
+ <summary>Keybindings</summary>
+ <description>The keybindings to use within Text Editor.</description>
+ </key>
</schema>
</schemalist>
diff --git a/src/TextEditor.css b/src/TextEditor.css
index e5476c0..d4f1ff8 100644
--- a/src/TextEditor.css
+++ b/src/TextEditor.css
@@ -117,3 +117,11 @@ textview.source-map:dir(rtl) {
flap > scrolledwindow.preferences > viewport {
background: @theme_bg_color;
}
+
+label.vim-command-bar {
+ font-family: monospace;
+ min-width: 100px;
+ padding: 3px 6px;
+ background: @theme_bg_color;
+ border-top: 1px solid @borders;
+}
diff --git a/src/editor-page-private.h b/src/editor-page-private.h
index b36d843..ea6dea9 100644
--- a/src/editor-page-private.h
+++ b/src/editor-page-private.h
@@ -57,6 +57,8 @@ struct _EditorPage
EditorSearchBar *search_bar;
GtkInfoBar *changed_infobar;
GtkInfoBar *infobar;
+ GtkLabel *vim_command_bar;
+ GtkEventController *vim;
guint close_requested : 1;
guint moving : 1;
@@ -93,5 +95,6 @@ void _editor_page_move_previous_search (EditorPage *self,
gboolean hide_after_search);
void _editor_page_begin_move (EditorPage *self);
void _editor_page_end_move (EditorPage *self);
+void _editor_page_vim_init (EditorPage *self);
G_END_DECLS
diff --git a/src/editor-page-vim.c b/src/editor-page-vim.c
new file mode 100644
index 0000000..87afa2b
--- /dev/null
+++ b/src/editor-page-vim.c
@@ -0,0 +1,208 @@
+/* editor-page-vim.c
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include "editor-application-private.h"
+#include "editor-page-private.h"
+#include "editor-session.h"
+
+static void
+on_notify_command_bar_text_cb (EditorPage *self,
+ GParamSpec *pspec,
+ GtkSourceVimIMContext *im_context)
+{
+ const char *label;
+
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+
+ label = gtk_source_vim_im_context_get_command_bar_text (im_context);
+ gtk_label_set_label (self->vim_command_bar, label);
+}
+
+static void
+on_vim_write_cb (EditorPage *self,
+ GtkSourceView *view,
+ const char *path,
+ GtkSourceVimIMContext *im_context)
+{
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (GTK_SOURCE_IS_VIEW (view));
+ g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+
+ if (path != NULL)
+ _editor_page_save_as (self, path);
+ else
+ _editor_page_save (self);
+}
+
+static void
+on_vim_edit_cb (EditorPage *self,
+ GtkSourceView *view,
+ const char *path,
+ GtkSourceVimIMContext *im_context)
+{
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (GTK_SOURCE_IS_VIEW (view));
+ g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+
+ if (path != NULL)
+ {
+ GFile *file = editor_document_get_file (self->document);
+ g_autoptr(GFile) selected = NULL;
+
+ if (file != NULL && !g_path_is_absolute (path))
+ {
+ g_autoptr(GFile) parent = g_file_get_parent (file);
+ selected = g_file_get_child (parent, path);
+ }
+ else
+ {
+ selected = g_file_new_for_path (path);
+ }
+
+ editor_session_open (EDITOR_SESSION_DEFAULT,
+ EDITOR_WINDOW (gtk_widget_get_native (GTK_WIDGET (self))),
+ selected,
+ NULL);
+ }
+ else
+ {
+ _editor_page_discard_changes (self);
+ }
+}
+
+static void
+close_after_complete_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(EditorPage) self = user_data;
+
+ g_assert (EDITOR_IS_PAGE (self));
+
+ editor_session_remove_page (EDITOR_SESSION_DEFAULT, self);
+}
+
+static gboolean
+on_vim_execute_command_cb (EditorPage *self,
+ const char *command,
+ GtkSourceVimIMContext *im_context)
+{
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+
+ if (g_str_equal (command, ":q") || g_str_equal (command, ":quit"))
+ {
+ editor_session_remove_page (EDITOR_SESSION_DEFAULT, self);
+ return TRUE;
+ }
+
+ if (g_str_equal (command, ":q!") || g_str_equal (command, ":quit!"))
+ {
+ _editor_page_discard_changes_async (self, FALSE, NULL, close_after_complete_cb, g_object_ref (self));
+ return TRUE;
+ }
+
+ if (g_str_equal (command, ":wq"))
+ {
+ _editor_document_save_async (self->document, NULL, NULL, close_after_complete_cb, g_object_ref (self));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+on_keybindings_changed_cb (EditorPage *self,
+ const char *key,
+ GSettings *settings)
+{
+ g_autofree char *choice = NULL;
+
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (G_IS_SETTINGS (settings));
+
+ choice = g_settings_get_string (settings, "keybindings");
+
+ if (g_str_equal (choice, "vim"))
+ {
+ if (self->vim == NULL)
+ {
+ GtkIMContext *im_context;
+
+ im_context = gtk_source_vim_im_context_new ();
+ g_signal_connect_object (im_context,
+ "notify::command-bar-text",
+ G_CALLBACK (on_notify_command_bar_text_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (im_context,
+ "write",
+ G_CALLBACK (on_vim_write_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (im_context,
+ "edit",
+ G_CALLBACK (on_vim_edit_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (im_context,
+ "execute-command",
+ G_CALLBACK (on_vim_execute_command_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_im_context_set_client_widget (im_context, GTK_WIDGET (self->view));
+
+ self->vim = gtk_event_controller_key_new ();
+ gtk_event_controller_set_propagation_phase (self->vim, GTK_PHASE_CAPTURE);
+ gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (self->vim), im_context);
+ gtk_widget_add_controller (GTK_WIDGET (self->view), self->vim);
+
+ gtk_widget_show (GTK_WIDGET (self->vim_command_bar));
+ }
+ }
+ else
+ {
+ if (self->vim)
+ {
+ gtk_label_set_label (self->vim_command_bar, NULL);
+ gtk_widget_hide (GTK_WIDGET (self->vim_command_bar));
+ gtk_widget_remove_controller (GTK_WIDGET (self), self->vim);
+ self->vim = NULL;
+ }
+ }
+}
+
+void
+_editor_page_vim_init (EditorPage *self)
+{
+ EditorApplication *app = EDITOR_APPLICATION_DEFAULT;
+
+ g_return_if_fail (EDITOR_IS_PAGE (self));
+
+ g_signal_connect_object (app->settings,
+ "changed::keybindings",
+ G_CALLBACK (on_keybindings_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ on_keybindings_changed_cb (self, NULL, app->settings);
+}
diff --git a/src/editor-page.c b/src/editor-page.c
index 5605101..118701b 100644
--- a/src/editor-page.c
+++ b/src/editor-page.c
@@ -25,6 +25,7 @@
#include <adwaita.h>
#include <glib/gi18n.h>
+#include "editor-application-private.h"
#include "editor-info-bar-private.h"
#include "editor-page-private.h"
#include "editor-sidebar-model-private.h"
@@ -341,6 +342,8 @@ editor_page_constructed (GObject *object)
editor_page_document_notify_busy_cb (self, NULL, self->document);
editor_page_document_notify_language_cb (self, NULL, self->document);
+
+ _editor_page_vim_init (self);
}
static gboolean
@@ -627,6 +630,7 @@ editor_page_class_init (EditorPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, EditorPage, search_bar);
gtk_widget_class_bind_template_child (widget_class, EditorPage, search_revealer);
gtk_widget_class_bind_template_child (widget_class, EditorPage, view);
+ gtk_widget_class_bind_template_child (widget_class, EditorPage, vim_command_bar);
gtk_widget_class_bind_template_callback (widget_class, get_child_position_cb);
gtk_widget_class_bind_template_callback (widget_class, goto_line_entry_activate_cb);
diff --git a/src/editor-page.ui b/src/editor-page.ui
index 3deca11..97c0c14 100644
--- a/src/editor-page.ui
+++ b/src/editor-page.ui
@@ -145,6 +145,17 @@
</child>
</object>
</child>
+ <child>
+ <object class="GtkLabel" id="vim_command_bar">
+ <property name="xalign">0</property>
+ <property name="valign">end</property>
+ <property name="vexpand">false</property>
+ <property name="visible">false</property>
+ <style>
+ <class name="vim-command-bar"/>
+ </style>
+ </object>
+ </child>
</object>
</child>
</template>
diff --git a/src/meson.build b/src/meson.build
index 4769521..1f8e497 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,6 +17,7 @@ editor_sources = [
'editor-page-gsettings.c',
'editor-page-settings.c',
'editor-page-settings-provider.c',
+ 'editor-page-vim.c',
'editor-path.c',
'editor-position-label.c',
'editor-preferences-font.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]