📄 vi_mode.c
字号:
/* vi_mode.c -- A vi emulation mode for Bash. Derived from code written by Jeff Sparkes (jsparkes@bnr.ca). *//* Copyright (C) 1987-2010 Free Software Foundation, Inc. This file is part of the GNU Readline Library (Readline), a library for reading lines of text with interactive input and history editing. Readline 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. Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>.*/#define READLINE_LIBRARY/* **************************************************************** *//* *//* VI Emulation Mode *//* *//* **************************************************************** */#include "rlconf.h"#if defined (VI_MODE)#if defined (HAVE_CONFIG_H)# include <config.h>#endif#include <sys/types.h>#if defined (HAVE_STDLIB_H)# include <stdlib.h>#else# include "ansi_stdlib.h"#endif /* HAVE_STDLIB_H */#if defined (HAVE_UNISTD_H)# include <unistd.h>#endif#include <stdio.h>/* Some standard library routines. */#include "rldefs.h"#include "rlmbutil.h"#include "readline.h"#include "history.h"#include "rlprivate.h"#include "xmalloc.h"#ifndef member#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)#endifint _rl_vi_last_command = 'i'; /* default `.' puts you in insert mode */_rl_vimotion_cxt *_rl_vimvcxt = 0;/* Non-zero means enter insertion mode. */static int _rl_vi_doing_insert;/* Command keys which do movement for xxx_to commands. */static const char * const vi_motion = " hl^$0ftFT;,%wbeWBE|`";/* Keymap used for vi replace characters. Created dynamically since rarely used. */static Keymap vi_replace_map;/* The number of characters inserted in the last replace operation. */static int vi_replace_count;/* If non-zero, we have text inserted after a c[motion] command that put us implicitly into insert mode. Some people want this text to be attached to the command so that it is `redoable' with `.'. */static int vi_continued_command;static char *vi_insert_buffer;static int vi_insert_buffer_size;static int _rl_vi_last_repeat = 1;static int _rl_vi_last_arg_sign = 1;static int _rl_vi_last_motion;#if defined (HANDLE_MULTIBYTE)static char _rl_vi_last_search_mbchar[MB_LEN_MAX];static int _rl_vi_last_search_mblen;#elsestatic int _rl_vi_last_search_char;#endifstatic int _rl_vi_last_replacement;static int _rl_vi_last_key_before_insert;static int vi_redoing;/* Text modification commands. These are the `redoable' commands. */static const char * const vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";/* Arrays for the saved marks. */static int vi_mark_chars['z' - 'a' + 1];static void _rl_vi_stuff_insert PARAMS((int));static void _rl_vi_save_insert PARAMS((UNDO_LIST *));static void _rl_vi_backup PARAMS((void));static int _rl_vi_arg_dispatch PARAMS((int));static int rl_digit_loop1 PARAMS((void));static int _rl_vi_set_mark PARAMS((void));static int _rl_vi_goto_mark PARAMS((void));static void _rl_vi_append_forward PARAMS((int));static int _rl_vi_callback_getchar PARAMS((char *, int));#if defined (READLINE_CALLBACKS)static int _rl_vi_callback_set_mark PARAMS((_rl_callback_generic_arg *));static int _rl_vi_callback_goto_mark PARAMS((_rl_callback_generic_arg *));static int _rl_vi_callback_change_char PARAMS((_rl_callback_generic_arg *));static int _rl_vi_callback_char_search PARAMS((_rl_callback_generic_arg *));#endifstatic int rl_domove_read_callback PARAMS((_rl_vimotion_cxt *));static int rl_domove_motion_callback PARAMS((_rl_vimotion_cxt *));static int rl_vi_domove_getchar PARAMS((_rl_vimotion_cxt *));static int vi_change_dispatch PARAMS((_rl_vimotion_cxt *));static int vi_delete_dispatch PARAMS((_rl_vimotion_cxt *));static int vi_yank_dispatch PARAMS((_rl_vimotion_cxt *));static int vidomove_dispatch PARAMS((_rl_vimotion_cxt *));void_rl_vi_initialize_line (){ register int i, n; n = sizeof (vi_mark_chars) / sizeof (vi_mark_chars[0]); for (i = 0; i < n; i++) vi_mark_chars[i] = -1; RL_UNSETSTATE(RL_STATE_VICMDONCE);}void_rl_vi_reset_last (){ _rl_vi_last_command = 'i'; _rl_vi_last_repeat = 1; _rl_vi_last_arg_sign = 1; _rl_vi_last_motion = 0;}void_rl_vi_set_last (key, repeat, sign) int key, repeat, sign;{ _rl_vi_last_command = key; _rl_vi_last_repeat = repeat; _rl_vi_last_arg_sign = sign;}/* A convenience function that calls _rl_vi_set_last to save the last command information and enters insertion mode. */voidrl_vi_start_inserting (key, repeat, sign) int key, repeat, sign;{ _rl_vi_set_last (key, repeat, sign); rl_vi_insertion_mode (1, key);}/* Is the command C a VI mode text modification command? */int_rl_vi_textmod_command (c) int c;{ return (member (c, vi_textmod));}static void_rl_vi_stuff_insert (count) int count;{ rl_begin_undo_group (); while (count--) rl_insert_text (vi_insert_buffer); rl_end_undo_group ();}/* Bound to `.'. Called from command mode, so we know that we have to redo a text modification command. The default for _rl_vi_last_command puts you back into insert mode. */intrl_vi_redo (count, c) int count, c;{ int r; if (!rl_explicit_arg) { rl_numeric_arg = _rl_vi_last_repeat; rl_arg_sign = _rl_vi_last_arg_sign; } r = 0; vi_redoing = 1; /* If we're redoing an insert with `i', stuff in the inserted text and do not go into insertion mode. */ if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer) { _rl_vi_stuff_insert (count); /* And back up point over the last character inserted. */ if (rl_point > 0) _rl_vi_backup (); } /* Ditto for redoing an insert with `I', but move to the beginning of the line like the `I' command does. */ else if (_rl_vi_last_command == 'I' && vi_insert_buffer && *vi_insert_buffer) { rl_beg_of_line (1, 'I'); _rl_vi_stuff_insert (count); if (rl_point > 0) _rl_vi_backup (); } /* Ditto for redoing an insert with `a', but move forward a character first like the `a' command does. */ else if (_rl_vi_last_command == 'a' && vi_insert_buffer && *vi_insert_buffer) { _rl_vi_append_forward ('a'); _rl_vi_stuff_insert (count); if (rl_point > 0) _rl_vi_backup (); } /* Ditto for redoing an insert with `A', but move to the end of the line like the `A' command does. */ else if (_rl_vi_last_command == 'A' && vi_insert_buffer && *vi_insert_buffer) { rl_end_of_line (1, 'A'); _rl_vi_stuff_insert (count); if (rl_point > 0) _rl_vi_backup (); } else r = _rl_dispatch (_rl_vi_last_command, _rl_keymap); vi_redoing = 0; return (r);}/* A placeholder for further expansion. */intrl_vi_undo (count, key) int count, key;{ return (rl_undo_command (count, key));} /* Yank the nth arg from the previous line into this line at point. */intrl_vi_yank_arg (count, key) int count, key;{ /* Readline thinks that the first word on a line is the 0th, while vi thinks the first word on a line is the 1st. Compensate. */ if (rl_explicit_arg) rl_yank_nth_arg (count - 1, 0); else rl_yank_nth_arg ('$', 0); return (0);}/* With an argument, move back that many history lines, else move to the beginning of history. */intrl_vi_fetch_history (count, c) int count, c;{ int wanted; /* Giving an argument of n means we want the nth command in the history file. The command number is interpreted the same way that the bash `history' command does it -- that is, giving an argument count of 450 to this command would get the command listed as number 450 in the output of `history'. */ if (rl_explicit_arg) { wanted = history_base + where_history () - count; if (wanted <= 0) rl_beginning_of_history (0, 0); else rl_get_previous_history (wanted, c); } else rl_beginning_of_history (count, 0); return (0);}/* Search again for the last thing searched for. */intrl_vi_search_again (count, key) int count, key;{ switch (key) { case 'n': rl_noninc_reverse_search_again (count, key); break; case 'N': rl_noninc_forward_search_again (count, key); break; } return (0);}/* Do a vi style search. */intrl_vi_search (count, key) int count, key;{ switch (key) { case '?': _rl_free_saved_history_line (); rl_noninc_forward_search (count, key); break; case '/': _rl_free_saved_history_line (); rl_noninc_reverse_search (count, key); break; default: rl_ding (); break; } return (0);}/* Completion, from vi's point of view. */intrl_vi_complete (ignore, key) int ignore, key;{ if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point]))) { if (!whitespace (rl_line_buffer[rl_point + 1])) rl_vi_end_word (1, 'E'); rl_point++; } if (key == '*') rl_complete_internal ('*'); /* Expansion and replacement. */ else if (key == '=') rl_complete_internal ('?'); /* List possible completions. */ else if (key == '\\') rl_complete_internal (TAB); /* Standard Readline completion. */ else rl_complete (0, key); if (key == '*' || key == '\\') rl_vi_start_inserting (key, 1, rl_arg_sign); return (0);}/* Tilde expansion for vi mode. */intrl_vi_tilde_expand (ignore, key) int ignore, key;{ rl_tilde_expand (0, key); rl_vi_start_inserting (key, 1, rl_arg_sign); return (0);}/* Previous word in vi mode. */intrl_vi_prev_word (count, key) int count, key;{ if (count < 0) return (rl_vi_next_word (-count, key)); if (rl_point == 0) { rl_ding (); return (0); } if (_rl_uppercase_p (key)) rl_vi_bWord (count, key); else rl_vi_bword (count, key); return (0);}/* Next word in vi mode. */intrl_vi_next_word (count, key) int count, key;{ if (count < 0) return (rl_vi_prev_word (-count, key)); if (rl_point >= (rl_end - 1)) { rl_ding (); return (0); } if (_rl_uppercase_p (key)) rl_vi_fWord (count, key); else rl_vi_fword (count, key); return (0);}/* Move to the end of the ?next? word. */intrl_vi_end_word (count, key) int count, key;{ if (count < 0) { rl_ding (); return -1; } if (_rl_uppercase_p (key)) rl_vi_eWord (count, key); else rl_vi_eword (count, key); return (0);}/* Move forward a word the way that 'W' does. */intrl_vi_fWord (count, ignore) int count, ignore;{ while (count-- && rl_point < (rl_end - 1)) { /* Skip until whitespace. */ while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) rl_point++; /* Now skip whitespace. */ while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) rl_point++; } return (0);}intrl_vi_bWord (count, ignore) int count, ignore;{ while (count-- && rl_point > 0) { /* If we are at the start of a word, move back to whitespace so we will go back to the start of the previous word. */ if (!whitespace (rl_line_buffer[rl_point]) && whitespace (rl_line_buffer[rl_point - 1])) rl_point--; while (rl_point > 0 && whitespace (rl_line_buffer[rl_point])) rl_point--; if (rl_point > 0) { while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point])); rl_point++; } } return (0);}intrl_vi_eWord (count, ignore) int count, ignore;{ while (count-- && rl_point < (rl_end - 1)) { if (!whitespace (rl_line_buffer[rl_point])) rl_point++; /* Move to the next non-whitespace character (to the start of the next word). */ while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) rl_point++; if (rl_point && rl_point < rl_end) { /* Skip whitespace. */ while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) rl_point++; /* Skip until whitespace. */ while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point])) rl_point++; /* Move back to the last character of the word. */ rl_point--; } } return (0);}intrl_vi_fword (count, ignore) int count, ignore;{ while (count-- && rl_point < (rl_end - 1)) { /* Move to white space (really non-identifer). */ if (_rl_isident (rl_line_buffer[rl_point])) { while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end) rl_point++; } else /* if (!whitespace (rl_line_buffer[rl_point])) */ { while (!_rl_isident (rl_line_buffer[rl_point]) && !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) rl_point++; } /* Move past whitespace. */ while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end) rl_point++; } return (0);}intrl_vi_bword (count, ignore) int count, ignore;{ while (count-- && rl_point > 0) { int last_is_ident; /* If we are at the start of a word, move back to whitespace so we will go back to the start of the previous word. */ if (!whitespace (rl_line_buffer[rl_point]) && whitespace (rl_line_buffer[rl_point - 1])) rl_point--; /* If this character and the previous character are `opposite', move back so we don't get messed up by the rl_point++ down there in the while loop. Without this code, words like `l;' screw up the function. */ last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]); if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) || (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident)) rl_point--; while (rl_point > 0 && whitespace (rl_line_buffer[rl_point])) rl_point--; if (rl_point > 0) { if (_rl_isident (rl_line_buffer[rl_point])) while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point])); else while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) && !whitespace (rl_line_buffer[rl_point])); rl_point++; } } return (0);}intrl_vi_eword (count, ignore) int count, ignore;{ while (count-- && rl_point < rl_end - 1) { if (!whitespace (rl_line_buffer[rl_point])) rl_point++; while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) rl_point++; if (rl_point < rl_end) { if (_rl_isident (rl_line_buffer[rl_point])) while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point])); else while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point]) && !whitespace (rl_line_buffer[rl_point])); } rl_point--; } return (0);}intrl_vi_insert_beg (count, key) int count, key;{ rl_beg_of_line (1, key); rl_vi_insert_mode (1, key); return (0);}static void_rl_vi_append_forward (key) int key;{ int point; if (rl_point < rl_end) { if (MB_CUR_MAX == 1 || rl_byte_oriented) rl_point++; else { point = rl_point;#if 0 rl_forward_char (1, key);#else rl_point = _rl_forward_char_internal (1);#endif if (point == rl_point) rl_point = rl_end; } }}intrl_vi_append_mode (count, key) int count, key;{ _rl_vi_append_forward (key); rl_vi_start_inserting (key, 1, rl_arg_sign); return (0);}intrl_vi_append_eol (count, key) int count, key;{ rl_end_of_line (1, key); rl_vi_append_mode (1, key); return (0);}/* What to do in the case of C-d. */intrl_vi_eof_maybe (count, c) int count, c;{ return (rl_newline (1, '\n'));}/* Insertion mode stuff. *//* Switching from one mode to the other really just involves switching keymaps. */intrl_vi_insertion_mode (count, key) int count, key;{ _rl_keymap = vi_insertion_keymap; _rl_vi_last_key_before_insert = key; return (0);}intrl_vi_insert_mode (count, key) int count, key;{ rl_vi_start_inserting (key, 1, rl_arg_sign); return (0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -