patx/twig

version 2. clean up key bindings

Commit dc74faf · patx · 2026-06-14T18:42:22-04:00

Changeset
dc74fafd22dcbc570ae68f79d6dcf99e8985dde1
Parents
7a2992f0f6f790e8c45b99c858ff316786e860b5

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/Makefile b/Makefile
index e0af79a..439c03a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ APPDIR ?= $(DATADIR)/applications
 ICONDIR ?= $(DATADIR)/icons/hicolor
 MANDIR ?= $(DATADIR)/man
 PACKAGE ?= twig
-VERSION ?= 0.1.0
+VERSION ?= 0.1.1
 DEB_ARCH ?= all
 DEB_BUILD_DIR ?= build/deb
 DEB_ROOT ?= $(DEB_BUILD_DIR)/$(PACKAGE)
diff --git a/README.md b/README.md
index a6e1824..1836367 100644
--- a/README.md
+++ b/README.md
@@ -64,18 +64,19 @@ Twig intentionally has no toolbar or menu bar. Use these keyboard shortcuts:
 | `Ctrl+P` | Print |
 | `Ctrl+Q` | Quit Twig, prompting for unsaved files |
 | `Ctrl+Z` | Undo |
-| `Ctrl+Shift+Z` or `Ctrl+Y` | Redo |
+| `Ctrl+Shift+Z` | Redo |
 | `Ctrl+X` | Cut |
 | `Ctrl+C` | Copy |
 | `Ctrl+V` | Paste |
 | `Ctrl+A` | Select all |
+| `Ctrl+D` | Delete the selected lines or current line |
 | `Ctrl+F` | Open or focus Find and Replace |
-| `Ctrl+G` or `F3` | Find next match |
-| `Ctrl+Shift+G` or `Shift+F3` | Find previous match |
-| `Ctrl+H` or `Ctrl+R` | Open Find and Replace with the replace field focused |
+| `Ctrl+G` | Find next match |
+| `Ctrl+Shift+G` | Find previous match |
+| `Ctrl+R` | Open Find and Replace with the replace field focused |
 | `Ctrl+J` | Jump to line |
-| `Ctrl+Shift++` | Increase editor font size |
-| `Ctrl+Shift+-` | Decrease editor font size |
+| `Ctrl++` | Increase editor font size |
+| `Ctrl+-` | Decrease editor font size |
 | `Tab` with selected lines | Indent selected lines with spaces |
 | `Shift+Tab` with selected lines | Unindent selected lines |
 | `Enter` in Find | Find next match |
diff --git a/dist/twig_0.1.0_all.deb b/dist/twig_0.1.0_all.deb
deleted file mode 100644
index 8fe1d3e..0000000
Binary files a/dist/twig_0.1.0_all.deb and /dev/null differ
diff --git a/dist/twig_0.1.1_all.deb b/dist/twig_0.1.1_all.deb
new file mode 100644
index 0000000..94934bd
Binary files /dev/null and b/dist/twig_0.1.1_all.deb differ
diff --git a/docs/index.html b/docs/index.html
index 4567507..bbe04b5 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -60,7 +60,7 @@
   <img src="https://gitman.io/patx/twig/raw/docs/logo.png" alt="Twig logo">
 </div>
 <p><strong>Twig is an ultra-minimal GTK code editor</strong> for Debian/Ubuntu based Linux distros. No toolbar or menu bar, just code. <a href="https://gitman.io/patx/twig/raw/docs/screenshot.png" target="_blank">View screenshot</a>.</p>
-<p>Download the <a href="https://gitman.io/patx/twig/raw/dist/twig_0.1.0_all.deb" target="_blank">.deb</a>. You can also install Twig from <a href="https://gitman.io/patx/twig" target="_blank">source</a>.</p>
+<p>Download the <a href="https://gitman.io/patx/twig/raw/dist/twig_0.1.1_all.deb" target="_blank">.deb</a>. You can also install Twig from <a href="https://gitman.io/patx/twig" target="_blank">source</a>.</p>
 
 <h2>Features</h2>
 <ul>
@@ -78,19 +78,18 @@
 <table>
   <tr><td><code>Ctrl+N</code> / <code>Ctrl+T</code></td><td>New window</td></tr>
   <tr><td><code>Ctrl+O</code></td><td>Open file(s)</td></tr>
-  <tr><td><code>Ctrl+S</code></td><td>Save</td></tr>
-  <tr><td><code>Ctrl+Shift+S</code></td><td>Save as</td></tr>
+  <tr><td><code>Ctrl+S</code> / <code>Ctrl+Shift+S</code></td><td>Save / Save as</td></tr>
   <tr><td><code>Ctrl+W</code></td><td>Close window</td></tr>
   <tr><td><code>Ctrl+P</code></td><td>Print</td></tr>
   <tr><td><code>Ctrl+Q</code></td><td>Quit</td></tr>
-  <tr><td><code>Ctrl+Z</code></td><td>Undo</td></tr>
-  <tr><td><code>Ctrl+Shift+Z</code> / <code>Ctrl+Y</code></td><td>Redo</td></tr>
+  <tr><td><code>Ctrl+Z</code> / <code>Ctrl+Shift+Z</code></td><td>Undo / Redo</td></tr>
   <tr><td><code>Ctrl+X</code> / <code>Ctrl+C</code> / <code>Ctrl+V</code></td><td>Cut / Copy / Paste</td></tr>
   <tr><td><code>Ctrl+A</code></td><td>Select all</td></tr>
+  <tr><td><code>Ctrl+D</code></td><td>Delete line(s)</td></tr>
   <tr><td><code>Ctrl+F / Ctrl+R</code></td><td>Find / Replace</td></tr>
   <tr><td><code>Ctrl+G</code> / <code>Ctrl+Shift+G</code></td><td>Find next / previous</td></tr>
   <tr><td><code>Ctrl+J</code></td><td>Jump to line</td></tr>
-  <tr><td><code>Ctrl+Shift++</code> / <code>Ctrl+Shift+-</code></td><td>Increase / decrease editor font size</td></tr>
+  <tr><td><code>Ctrl++</code> / <code>Ctrl+-</code></td><td>Increase / decrease editor font size</td></tr>
   <tr><td><code>Tab</code> / <code>Shift+Tab</code></td><td>Indent / Unindent</td></tr>
 </table>
 
diff --git a/docs/twig.1 b/docs/twig.1
index 6f16875..c2c7d6b 100644
--- a/docs/twig.1
+++ b/docs/twig.1
@@ -1,4 +1,4 @@
-.TH TWIG 1 "June 2026" "Twig 0.1.0" "User Commands"
+.TH TWIG 1 "June 2026" "Twig 0.1.1" "User Commands"
 .SH NAME
 twig \- lightweight GTK code editor
 .SH SYNOPSIS
@@ -23,6 +23,9 @@ Save the current file as a new path.
 .B Ctrl+F
 Open or focus find and replace.
 .TP
+.B Ctrl+D
+Delete the selected lines or current line.
+.TP
 .B Ctrl+Q
 Quit Twig, prompting for unsaved files.
 .SH AUTHOR
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 084f1e7..889686c 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -10,14 +10,19 @@ spec.loader.exec_module(twig)
 
 
 class HelperTests(unittest.TestCase):
-    def test_detect_language_from_filename(self):
-        self.assertEqual(twig.detect_language("example.py"), "python")
-        self.assertEqual(twig.detect_language("index.html"), "html")
-        self.assertEqual(twig.detect_language("style.css"), "css")
-        self.assertEqual(twig.detect_language("script.js"), "javascript")
-        self.assertEqual(twig.detect_language("README.md"), "markdown")
-        self.assertEqual(twig.detect_language("Makefile"), "config")
-        self.assertEqual(twig.detect_language("notes.txt"), "plain")
+    def test_gtksourceview_guesses_language_from_filename(self):
+        manager = twig.GtkSource.LanguageManager.get_default()
+
+        def language_id(filename):
+            language = manager.guess_language(filename, None)
+            return language.get_id() if language else None
+
+        self.assertEqual(language_id("example.py"), "python")
+        self.assertEqual(language_id("index.html"), "html")
+        self.assertEqual(language_id("style.css"), "css")
+        self.assertEqual(language_id("script.js"), "js")
+        self.assertEqual(language_id("README.md"), "markdown")
+        self.assertIsNone(language_id("notes.txt"))
 
     def test_clamp_font_size(self):
         self.assertEqual(twig.clamp_font_size(1), twig.MIN_EDITOR_FONT_SIZE)
diff --git a/twig.py b/twig.py
index 5ff64c3..7349bcc 100755
--- a/twig.py
+++ b/twig.py
@@ -2,6 +2,7 @@
 """Twig: a tiny GTK code editor for lightweight Linux desktops."""
 
 import os
+import shutil
 import sys
 from pathlib import Path
 
@@ -39,6 +40,23 @@ EDITOR_FONT_STEP = 1
 HEADERBAR_DESKTOPS = {"gnome", "pantheon"}
 
 
+def clamp_font_size(size):
+    return min(MAX_EDITOR_FONT_SIZE, max(MIN_EDITOR_FONT_SIZE, size))
+
+
+def line_count_for_text(text):
+    return text.count("\n") + 1
+
+
+def select_print_command(is_available=None):
+    if is_available is None:
+        is_available = lambda command: shutil.which(command) is not None
+    for command in ("lpr", "lp"):
+        if is_available(command):
+            return command
+    return None
+
+
 def should_use_headerbar():
     desktop_names = (
         os.environ.get("XDG_CURRENT_DESKTOP", ""),
@@ -189,6 +207,7 @@ class TwigWindow(Gtk.ApplicationWindow):
             "copy": self.on_copy,
             "paste": self.on_paste,
             "select-all": self.on_select_all,
+            "delete-line": self.on_delete_line,
         }
         for name, callback in actions.items():
             action = Gio.SimpleAction.new(name, None)
@@ -260,10 +279,7 @@ class TwigWindow(Gtk.ApplicationWindow):
         self.view.queue_draw()
 
     def _change_editor_font_size(self, delta):
-        new_size = min(
-            MAX_EDITOR_FONT_SIZE,
-            max(MIN_EDITOR_FONT_SIZE, self.editor_font_size + delta),
-        )
+        new_size = clamp_font_size(self.editor_font_size + delta)
         if new_size != self.editor_font_size:
             self.editor_font_size = new_size
             self._apply_editor_font_size()
@@ -489,6 +505,32 @@ class TwigWindow(Gtk.ApplicationWindow):
         self.buffer.end_user_action()
         return True
 
+    def delete_selected_lines(self):
+        if self.buffer.get_has_selection():
+            start, last_line = self.selected_line_bounds()
+        else:
+            cursor = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+            start = self.buffer.get_iter_at_line(cursor.get_line())
+            last_line = cursor.get_line()
+
+        if last_line + 1 < self.buffer.get_line_count():
+            end = self.buffer.get_iter_at_line(last_line + 1)
+        else:
+            end = self.buffer.get_end_iter()
+            if start.get_line() > 0:
+                start.backward_char()
+
+        cursor_offset = start.get_offset()
+        self.buffer.begin_user_action()
+        self.buffer.delete(start, end)
+        self.buffer.end_user_action()
+
+        target_offset = min(cursor_offset, self.buffer.get_end_iter().get_offset())
+        target = self.buffer.get_iter_at_offset(target_offset)
+        self.buffer.place_cursor(target)
+        self.view.scroll_to_iter(target, 0.15, False, 0.0, 0.0)
+        return True
+
     def jump_to_line(self, line_number):
         line_count = self.buffer.get_line_count()
         line_index = max(0, min(line_number - 1, line_count - 1))
@@ -625,23 +667,24 @@ class TwigWindow(Gtk.ApplicationWindow):
     def on_select_all(self, *_args):
         self.buffer.select_range(self.buffer.get_start_iter(), self.buffer.get_end_iter())
 
+    def on_delete_line(self, *_args):
+        self.delete_selected_lines()
+
     def _is_editor_font_shortcut(self, event):
         state = event.state & Gtk.accelerator_get_default_mod_mask()
-        required = Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK
-        if (state & required) != required:
+        if not state & Gdk.ModifierType.CONTROL_MASK:
             return False
         return event.keyval in (
             Gdk.KEY_plus,
             Gdk.KEY_equal,
             Gdk.KEY_KP_Add,
             Gdk.KEY_minus,
-            Gdk.KEY_underscore,
             Gdk.KEY_KP_Subtract,
         )
 
     def on_key_press(self, _view, event):
         if self._is_editor_font_shortcut(event):
-            if event.keyval in (Gdk.KEY_minus, Gdk.KEY_underscore, Gdk.KEY_KP_Subtract):
+            if event.keyval in (Gdk.KEY_minus, Gdk.KEY_KP_Subtract):
                 return self._change_editor_font_size(-EDITOR_FONT_STEP)
             return self._change_editor_font_size(EDITOR_FONT_STEP)
         if event.keyval == Gdk.KEY_Tab and self.buffer.get_has_selection():
@@ -785,16 +828,17 @@ class TwigApp(Gtk.Application):
             "app.quit": ["<Primary>q"],
             "win.print": ["<Primary>p"],
             "win.find": ["<Primary>f"],
-            "win.replace": ["<Primary>h", "<Primary>r"],
-            "win.find-next": ["F3", "<Primary>g"],
-            "win.find-prev": ["<Shift>F3", "<Primary><Shift>g"],
+            "win.replace": ["<Primary>r"],
+            "win.find-next": ["<Primary>g"],
+            "win.find-prev": ["<Primary><Shift>g"],
             "win.jump-to": ["<Primary>j"],
             "win.undo": ["<Primary>z"],
-            "win.redo": ["<Primary>y", "<Primary><Shift>z"],
+            "win.redo": ["<Primary><Shift>z"],
             "win.cut": ["<Primary>x"],
             "win.copy": ["<Primary>c"],
             "win.paste": ["<Primary>v"],
             "win.select-all": ["<Primary>a"],
+            "win.delete-line": ["<Primary>d"],
         }
         for action, accels in shortcuts.items():
             self.set_accels_for_action(action, accels)