From 67bac7315b1027ec0e0ac7847833a1bc4c1a640e Mon Sep 17 00:00:00 2001
From: Philipp Reiter
Date: Sat, 18 Jul 2026 16:55:01 +0200
Subject: [PATCH] fix: count hyphen as punctuation
issue: https://github.com/preiter93/edtui/issues/35
---
CHANGELOG.md | 8 +++++++-
src/actions/motion.rs | 3 ++-
src/actions/select.rs | 26 ++++++++++++++++++++++++++
3 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb8a83a..3584594 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,16 @@
Unreleased
==========
-- feat: add `.` command to repeat the last change (dot-repeat)
+
+- fix: count hyphen as punctuation
Released
--------
+0.11.5 - 2026-07-18
+===================
+
+- feat: add `.` command to repeat the last change (dot-repeat)
+
0.11.4 - 2026-07-18
===================
- feat: add bindings for delete word and delete big word @acerv
diff --git a/src/actions/motion.rs b/src/actions/motion.rs
index e88a61b..6992cfd 100644
--- a/src/actions/motion.rs
+++ b/src/actions/motion.rs
@@ -474,7 +474,8 @@ pub(crate) enum CharacterClass {
impl From<&char> for CharacterClass {
fn from(value: &char) -> Self {
- if value.is_ascii_alphanumeric() {
+ // Underscore counts as a word character (matching Vim's `iskeyword`),
+ if value.is_ascii_alphanumeric() || *value == '_' {
return Self::Alphanumeric;
}
if value.is_ascii_punctuation() {
diff --git a/src/actions/select.rs b/src/actions/select.rs
index fb0ba42..8f32be1 100644
--- a/src/actions/select.rs
+++ b/src/actions/select.rs
@@ -527,6 +527,32 @@ mod tests {
assert_eq!(state.mode, EditorMode::Normal);
}
+ #[test]
+ fn test_select_inner_word_includes_underscores() {
+ // Underscore is a word character, so `viw` on the `B` of `BY` selects
+ // the whole identifier (matching Vim/nvim).
+ let mut state = EditorState::new(Lines::from("ORDER_BY_FIELD"));
+ state.cursor = Index2::new(0, 6);
+
+ SelectInnerWord.execute(&mut state);
+
+ let want = Selection::new(Index2::new(0, 0), Index2::new(0, 13));
+ assert_eq!(state.selection.unwrap(), want);
+ }
+
+ #[test]
+ fn test_select_inner_word_stops_at_hyphen() {
+ // A hyphen is punctuation, so `viw` on the `B` of `BY` selects only
+ // `BY` (matching Vim/nvim).
+ let mut state = EditorState::new(Lines::from("ORDER-BY"));
+ state.cursor = Index2::new(0, 6);
+
+ SelectInnerWord.execute(&mut state);
+
+ let want = Selection::new(Index2::new(0, 6), Index2::new(0, 7));
+ assert_eq!(state.selection.unwrap(), want);
+ }
+
#[test]
fn test_select_inner_big_word() {
let mut state = EditorState::new(Lines::from("foo.bar baz"));