Skip to content

Commit 5e19248

Browse files
committed
Easy mode ain't so easy anymore!
Enhanced brutal mode exit traps and easy mode shortcuts with improved user experience and error handling. brutal.c: +72/-15 brutal.h: +21/-0 edit.c: +40/-0 normal.c: +44/-15
1 parent 63ba2cd commit 5e19248

4 files changed

Lines changed: 177 additions & 30 deletions

File tree

src/nvim/brutal.c

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ BrutalMode brutal_mode = BRUTAL_NONE;
2222
uint8_t brutal_keymap[256];
2323
uint64_t brutal_esc_hold_start = 0;
2424
int brutal_ctrl_quit_count = 0;
25+
uint64_t brutal_ctrl_quit_times[5] = {0};
2526
uint64_t brutal_esc_press_times[5] = {0};
2627
int brutal_esc_press_count = 0;
2728
char brutal_easter_egg_buffer[32] = {0};
@@ -127,7 +128,12 @@ void brutal_init( void )
127128

128129
// Set selectmode to "key" so Shift+Arrow enters select mode
129130
do_cmdline_cmd( "set selectmode=key" );
131+
// Set keymodel to start selection with shift-keys
132+
do_cmdline_cmd( "set keymodel=startsel,stopsel" );
130133
}
134+
135+
// Add :Q command alias for :quit
136+
do_cmdline_cmd("command! Q quit");
131137
}
132138

133139
/// Display brutal mode startup banner
@@ -291,7 +297,58 @@ bool brutal_should_block_quit( bool force )
291297
return ( brutal_mode == BRUTAL_HARDER || brutal_mode == BRUTAL_HARDEST );
292298
}
293299

294-
/// Handle easy mode special quit sequences
300+
/// Reset the quit counter
301+
void brutal_reset_ctrl_quit_count( void )
302+
{
303+
brutal_ctrl_quit_count = 0;
304+
for ( int i = 0; i < 5; i++ ) {
305+
brutal_ctrl_quit_times[i] = 0;
306+
}
307+
}
308+
309+
/// Increment the quit counter and check time window
310+
void brutal_increment_ctrl_quit_count( void )
311+
{
312+
uint64_t now = os_hrtime();
313+
314+
// Shift timestamps (keep last 5)
315+
brutal_ctrl_quit_times[4] = brutal_ctrl_quit_times[3];
316+
brutal_ctrl_quit_times[3] = brutal_ctrl_quit_times[2];
317+
brutal_ctrl_quit_times[2] = brutal_ctrl_quit_times[1];
318+
brutal_ctrl_quit_times[1] = brutal_ctrl_quit_times[0];
319+
brutal_ctrl_quit_times[0] = now;
320+
321+
if ( brutal_ctrl_quit_count < 3 ) {
322+
brutal_ctrl_quit_count++;
323+
}
324+
}
325+
326+
/// Check if quit counter reached threshold (3) within time window
327+
bool brutal_check_ctrl_quit( void )
328+
{
329+
if ( brutal_ctrl_quit_count >= 3 ) {
330+
uint64_t now = os_hrtime();
331+
uint64_t eight_seconds_ns = 8000000000ULL; // 8 seconds
332+
333+
// Check if 3rd press back (index 2) is within 8 seconds
334+
if ( brutal_ctrl_quit_times[2] != 0 &&
335+
( now - brutal_ctrl_quit_times[2] ) <= eight_seconds_ns ) {
336+
return true;
337+
} else {
338+
// Reset if timeout expired, but keep the current press as the first one
339+
brutal_ctrl_quit_count = 1;
340+
// The current timestamp is already at [0], clear others
341+
brutal_ctrl_quit_times[1] = 0;
342+
brutal_ctrl_quit_times[2] = 0;
343+
brutal_ctrl_quit_times[3] = 0;
344+
brutal_ctrl_quit_times[4] = 0;
345+
return false;
346+
}
347+
}
348+
return false;
349+
}
350+
351+
/// Handle easy mode special quit sequences (unused now, replaced by direct checks)
295352
/// @param c The character pressed
296353
/// @return true if quit was triggered
297354
bool brutal_easy_mode_quit_check( int c )
@@ -302,32 +359,32 @@ bool brutal_easy_mode_quit_check( int c )
302359

303360
// Check for Ctrl+X, Ctrl+C, or Ctrl+Q
304361
if ( c == Ctrl_X || c == Ctrl_C || c == Ctrl_Q ) {
305-
brutal_ctrl_quit_count++;
306-
if ( brutal_ctrl_quit_count >= 3 ) {
362+
brutal_increment_ctrl_quit_count();
363+
if ( brutal_check_ctrl_quit() ) {
307364
return true; // Trigger quit
308365
}
309366
} else {
310-
brutal_ctrl_quit_count = 0; // Reset counter
367+
brutal_reset_ctrl_quit_count(); // Reset counter
311368
}
312369

313370
return false;
314371
}
315372

316-
/// Check if ESC has been pressed repeatedly (5 times within 10 seconds)
373+
/// Check if ESC has been pressed repeatedly (3 times within 10 seconds)
317374
/// @return true if threshold met
318375
bool brutal_easy_mode_esc_repeated( void )
319376
{
320377
if ( brutal_mode != BRUTAL_EASY ) {
321378
return false;
322379
}
323380

324-
// Check if we have 5 ESC presses
325-
if ( brutal_esc_press_count >= 5 ) {
381+
// Check if we have 3 ESC presses
382+
if ( brutal_esc_press_count >= 3 ) {
326383
uint64_t now = os_hrtime();
327384
uint64_t ten_seconds_ns = 10000000000ULL; // 10 seconds
328385

329-
// Check if oldest press (4th back) is within 10 seconds
330-
if ( now - brutal_esc_press_times[4] <= ten_seconds_ns ) {
386+
// Check if oldest press (2nd back) is within 10 seconds
387+
if ( now - brutal_esc_press_times[2] <= ten_seconds_ns ) {
331388
return true;
332389
}
333390
}
@@ -451,25 +508,25 @@ void brutal_handle_esc_press( void )
451508
brutal_esc_press_times[0] = now;
452509

453510
// Increment counter
454-
if ( brutal_esc_press_count < 5 ) {
511+
if ( brutal_esc_press_count < 3 ) {
455512
brutal_esc_press_count++;
456513
}
457514
}
458515

459-
/// Check if 5 ESC presses detected in EASY mode within 10 seconds
516+
/// Check if 3 ESC presses detected in EASY mode within 10 seconds
460517
bool brutal_check_repeated_esc( void )
461518
{
462519
if ( brutal_mode != BRUTAL_EASY ) {
463520
return false;
464521
}
465522

466-
if ( brutal_esc_press_count >= 5 ) {
523+
if ( brutal_esc_press_count >= 3 ) {
467524
uint64_t now = os_hrtime();
468525
uint64_t ten_seconds_ns = 10000000000ULL;
469526

470-
// Check if 5th press back is within 10 seconds
471-
if ( brutal_esc_press_times[4] != 0 &&
472-
( now - brutal_esc_press_times[4] ) <= ten_seconds_ns ) {
527+
// Check if 3rd press back (index 2) is within 10 seconds
528+
if ( brutal_esc_press_times[2] != 0 &&
529+
( now - brutal_esc_press_times[2] ) <= ten_seconds_ns ) {
473530
// Reset counter
474531
brutal_esc_press_count = 0;
475532
for ( int i = 0; i < 5; i++ ) {

src/nvim/brutal.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,34 @@ extern uint8_t brutal_keymap[256];
2121
/// Easy mode state tracking
2222
extern uint64_t brutal_esc_hold_start; ///< Unused (kept for compatibility)
2323
extern int brutal_ctrl_quit_count; ///< Count of consecutive ctrl+x/c/q presses
24+
extern uint64_t brutal_ctrl_quit_times[5]; ///< Timestamps of last 5 Ctrl-C/X/Q presses
2425
extern uint64_t brutal_esc_press_times[5]; ///< Timestamps of last 5 ESC presses
2526
extern int brutal_esc_press_count; ///< Count of ESC presses
2627

2728
/// Easter egg buffer for "fuck you let me out"
2829
extern char brutal_easter_egg_buffer[32];
2930
extern int brutal_easter_egg_pos;
3031

32+
/// Function prototypes
33+
void brutal_init(void);
34+
void brutal_show_banner(void);
35+
bool brutal_should_block_key(int c);
36+
int brutal_apply_easy_mode_mappings(int c);
37+
int brutal_remap_key(int c);
38+
bool brutal_windows_keys_active(void);
39+
bool brutal_should_block_quit(bool force);
40+
bool brutal_easy_mode_quit_check(int c);
41+
bool brutal_easy_mode_esc_repeated(void);
42+
bool brutal_check_easter_egg(void);
43+
void brutal_record_char(int c);
44+
void brutal_handle_esc_press(void);
45+
bool brutal_check_repeated_esc(void);
46+
47+
// New functions for managing quit counter explicitly
48+
void brutal_reset_ctrl_quit_count(void);
49+
void brutal_increment_ctrl_quit_count(void);
50+
bool brutal_check_ctrl_quit(void);
51+
3152
#ifdef INCLUDE_GENERATED_DECLARATIONS
3253
# include "brutal.h.generated.h"
3354
#endif

src/nvim/edit.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "nvim/ascii_defs.h"
1414
#include "nvim/autocmd.h"
1515
#include "nvim/autocmd_defs.h"
16+
#include "nvim/brutal.h"
1617
#include "nvim/buffer.h"
1718
#include "nvim/buffer_defs.h"
1819
#include "nvim/change.h"
@@ -678,6 +679,11 @@ static int insert_execute(VimState *state, int key)
678679
}
679680

680681
if (s->c == Ctrl_V || s->c == Ctrl_Q) {
682+
if (brutal_mode == BRUTAL_EASY && s->c == Ctrl_V) {
683+
// Paste from clipboard (+)
684+
do_put('+', NULL, BACKWARD, 1, PUT_CURSEND);
685+
return 1; // Stay in Insert mode
686+
}
681687
ins_ctrl_v();
682688
s->c = Ctrl_V; // pretend CTRL-V is last typed character
683689
return 1; // continue
@@ -740,6 +746,26 @@ static int insert_handle_key(InsertState *s)
740746
FALLTHROUGH;
741747

742748
case Ctrl_C: // End input mode
749+
if (s->c == Ctrl_C && brutal_mode == BRUTAL_EASY) {
750+
if (VIsual_active) {
751+
// Visual/Select active: Copy to clipboard and reset counter
752+
do_cmdline_cmd("normal! \"+y");
753+
brutal_reset_ctrl_quit_count();
754+
// Return to Insert mode
755+
return 1; // Continue in Insert mode
756+
} else {
757+
// No selection: Increment counter
758+
brutal_increment_ctrl_quit_count();
759+
if (brutal_check_ctrl_quit()) {
760+
// Exit app
761+
do_cmdline_cmd("qa!");
762+
return 0;
763+
}
764+
// Don't exit insert mode
765+
return 1;
766+
}
767+
}
768+
743769
if (s->c == Ctrl_C && cmdwin_type != 0) {
744770
// Close the cmdline window.
745771
cmdwin_result = K_IGNORE;
@@ -761,6 +787,10 @@ static int insert_handle_key(InsertState *s)
761787
return 0; // exit insert mode
762788

763789
case Ctrl_Z:
790+
if (brutal_mode == BRUTAL_EASY) {
791+
u_undo(1);
792+
return 1; // Stay in Insert mode
793+
}
764794
goto normalchar; // insert CTRL-Z as normal char
765795

766796
case Ctrl_O: // execute one command
@@ -893,6 +923,10 @@ static int insert_handle_key(InsertState *s)
893923

894924
case Ctrl_U: // delete all inserted text in current line
895925
// CTRL-X CTRL-U completes with 'completefunc'.
926+
if (brutal_mode == BRUTAL_EASY) {
927+
u_undo(1);
928+
return 1; // Stay in Insert mode
929+
}
896930
if (ctrl_x_mode_function()) {
897931
insert_do_complete(s);
898932
} else {
@@ -1137,6 +1171,12 @@ static int insert_handle_key(InsertState *s)
11371171
goto normalchar;
11381172

11391173
case Ctrl_X: // Enter CTRL-X mode
1174+
if (brutal_mode == BRUTAL_EASY && VIsual_active) {
1175+
// Cut to clipboard (+)
1176+
do_cmdline_cmd("normal! \"+d");
1177+
brutal_reset_ctrl_quit_count();
1178+
return 1; // Stay in Insert mode
1179+
}
11401180
ins_ctrl_x();
11411181
break;
11421182

src/nvim/normal.c

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,12 @@ static int normal_execute(VimState *state, int key)
12511251
// Execute the command!
12521252
// Call the command function found in the commands table.
12531253
s->ca.arg = nv_cmds[s->idx].cmd_arg;
1254+
1255+
// Reset ESC quit counter if a non-ESC command is executed in Normal mode
1256+
if (brutal_mode == BRUTAL_EASY && s->ca.cmdchar != ESC && s->ca.cmdchar != K_IGNORE && s->ca.cmdchar != K_MOUSEMOVE) {
1257+
brutal_esc_press_count = 0;
1258+
}
1259+
12541260
(nv_cmds[s->idx].cmd_func)(&s->ca);
12551261

12561262
finish:
@@ -6151,23 +6157,46 @@ static void nv_esc(cmdarg_T *cap)
61516157

61526158
// Brutal mode: Removed ESC detection (was unreliable)
61536159

6160+
// Handle ESC in Easy Mode (3x check)
6161+
if (brutal_mode == BRUTAL_EASY && !cap->arg) { // cap->arg is false for ESC
6162+
brutal_handle_esc_press();
6163+
if (brutal_check_repeated_esc()) {
6164+
do_cmdline_cmd("qa!");
6165+
return;
6166+
}
6167+
}
6168+
61546169
if (cap->arg) { // true for CTRL-C
61556170
// EASY mode: Ctrl+C in visual mode copies to clipboard
6156-
if (VIsual_active && brutal_mode == BRUTAL_EASY) {
6157-
// Set up yank operator - use unnamed register (0 means use default)
6158-
// This ensures yank works even without clipboard provider
6159-
cap->oap->regname = 0; // Use unnamed/default register
6160-
cap->oap->op_type = OP_YANK;
6161-
cap->cmdchar = 'y'; // Simulate 'y' command for do_pending_operator
6162-
6163-
// Call do_pending_operator to execute the yank on the visual selection
6164-
do_pending_operator(cap, curwin->w_curswant, false);
6165-
6166-
// Ensure clean state after operation
6167-
clearop(cap->oap);
6168-
// Clear got_int which may have been set by Ctrl+C signal
6169-
got_int = false;
6170-
return;
6171+
if (brutal_mode == BRUTAL_EASY) {
6172+
if (VIsual_active) {
6173+
// Set up yank operator - use unnamed register (0 means use default)
6174+
// This ensures yank works even without clipboard provider
6175+
cap->oap->regname = '+'; // Use + register for clipboard
6176+
cap->oap->op_type = OP_YANK;
6177+
cap->cmdchar = 'y'; // Simulate 'y' command for do_pending_operator
6178+
6179+
// Call do_pending_operator to execute the yank on the visual selection
6180+
do_pending_operator(cap, curwin->w_curswant, false);
6181+
6182+
// Ensure clean state after operation
6183+
clearop(cap->oap);
6184+
// Clear got_int which may have been set by Ctrl+C signal
6185+
got_int = false;
6186+
brutal_reset_ctrl_quit_count();
6187+
return;
6188+
} else {
6189+
// Normal mode, no selection: Increment counter
6190+
brutal_increment_ctrl_quit_count();
6191+
if (brutal_check_ctrl_quit()) {
6192+
do_cmdline_cmd("qa!");
6193+
return;
6194+
}
6195+
// Block standard Ctrl-C message
6196+
clearop(cap->oap);
6197+
got_int = false;
6198+
return;
6199+
}
61716200
}
61726201

61736202
if (restart_edit == 0 && cmdwin_type == 0 && !VIsual_active && no_reason) {

0 commit comments

Comments
 (0)