📄 vi_mode.c
字号:
/* vi_mode.c -- A vi emulation mode for Bash. Derived from code written by Jeff Sparkes (jsparkes@bnr.ca). *//* Copyright (C) 1988, 1991 Free Software Foundation, Inc. This file is part of the GNU Readline Library (the Library), a set of routines for providing Emacs style line input to programs that ask for it. The Library 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 2 of the License, or (at your option) any later version. The Library 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* **************************************************************** *//* *//* VI Emulation Mode *//* *//* **************************************************************** */#if defined (VI_MODE)/* Some standard library routines. */#include "sysdep.h"#include <stdio.h>#include "readline.h"#include "history.h"#ifndef digit#define digit(c) ((c) >= '0' && (c) <= '9')#endif#ifndef isletter#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))#endif#ifndef digit_value#define digit_value(c) ((c) - '0')#endif#ifndef member#define member(c, s) ((c) ? index ((s), (c)) : 0)#endif#ifndef isident#define isident(c) ((isletter(c) || digit(c) || c == '_'))#endif#ifndef exchange#define exchange(x, y) {int temp = x; x = y; y = temp;}#endif/* Variables imported from readline.c */extern int rl_point, rl_end, rl_mark, rl_done;extern FILE *rl_instream;extern int rl_line_buffer_len, rl_explicit_arg, rl_numeric_arg;extern Keymap keymap;extern char *rl_prompt;extern char *rl_line_buffer;extern int rl_arg_sign;extern char *xmalloc (), *xrealloc ();extern void rl_extend_line_buffer ();/* Last string searched for from `/' or `?'. */static char *vi_last_search = (char *)NULL;static int vi_histpos;/* Non-zero means enter insertion mode. */int vi_doing_insert = 0;/* String inserted into the line by rl_vi_comment (). */char *rl_vi_comment_begin = (char *)NULL;/* *** UNCLEAN *** *//* Command keys which do movement for xxx_to commands. */static char *vi_motion = " hl^$0ftFt;,%wbeWBE|";/* Keymap used for vi replace characters. Created dynamically since rarely used. */static Keymap vi_replace_map = (Keymap)NULL;/* The number of characters inserted in the last replace operation. */static int vi_replace_count = 0;/* Yank the nth arg from the previous line into this line at point. */rl_vi_yank_arg (count) int count;{ /* 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);}/* With an argument, move back that many history lines, else move to the beginning of history. */rl_vi_fetch_history (count, c) int count, c;{ extern int rl_explicit_arg; int current = where_history (); /* 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) { int wanted = history_base + current - count; if (wanted <= 0) rl_beginning_of_history (0, 0); else rl_get_previous_history (wanted); } else rl_beginning_of_history (count, 0);}/* Search again for the last thing searched for. */rl_vi_search_again (ignore, key) int ignore, key;{ switch (key) { case 'n': rl_vi_dosearch (vi_last_search, -1); break; case 'N': rl_vi_dosearch (vi_last_search, 1); break; }}/* Do a vi style search. */rl_vi_search (count, key) int count, key;{ int dir, c, save_pos; char *p; switch (key) { case '?': dir = 1; break; case '/': dir = -1; break; default: ding (); return; } vi_histpos = where_history (); maybe_save_line (); save_pos = rl_point; /* Reuse the line input buffer to read the search string. */ rl_line_buffer[0] = 0; rl_end = rl_point = 0; p = (char *)alloca (2 + (rl_prompt ? strlen (rl_prompt) : 0)); sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key); rl_message (p, 0, 0); while (c = rl_read_key ()) { switch (c) { case CTRL('H'): case RUBOUT: if (rl_point == 0) { maybe_unsave_line (); rl_clear_message (); rl_point = save_pos; return; } case CTRL('W'): case CTRL('U'): rl_dispatch (c, keymap); break; case ESC: case RETURN: case NEWLINE: goto dosearch; break; case CTRL('C'): maybe_unsave_line (); rl_clear_message (); rl_point = 0; ding (); return; default: rl_insert (1, c); break; } rl_redisplay (); } dosearch: if (vi_last_search) free (vi_last_search); vi_last_search = savestring (rl_line_buffer); rl_vi_dosearch (rl_line_buffer, dir);}/* Search for STRING in the history list. DIR is < 0 for searching backwards. POS is an absolute index into the history list at which point to begin searching. If the first character of STRING is `^', the string must match a prefix of a history line, otherwise a full substring match is performed. */static intvi_history_search_pos (string, dir, pos) char *string; int dir, pos;{ int ret, old = where_history (); history_set_pos (pos); if (*string == '^') ret = history_search_prefix (string + 1, dir); else ret = history_search (string, dir); if (ret == -1) { history_set_pos (old); return (-1); } ret = where_history (); history_set_pos (old); return ret;}rl_vi_dosearch (string, dir) char *string; int dir;{ int old, save = vi_histpos; HIST_ENTRY *h; if (string == 0 || *string == 0 || vi_histpos < 0) { ding (); return; } if ((save = vi_history_search_pos (string, dir, vi_histpos + dir)) == -1) { maybe_unsave_line (); rl_clear_message (); rl_point = 0; ding (); return; } vi_histpos = save; old = where_history (); history_set_pos (vi_histpos); h = current_history (); history_set_pos (old); { int line_len = strlen (h->line); if (line_len >= rl_line_buffer_len) rl_extend_line_buffer (line_len); strcpy (rl_line_buffer, h->line); } rl_undo_list = (UNDO_LIST *)h->data; rl_end = strlen (rl_line_buffer); rl_point = 0; rl_clear_message ();}/* Completion, from vi's point of view. */rl_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);}/* Previous word in vi mode. */rl_vi_prev_word (count, key) int count, key;{ if (count < 0) { rl_vi_next_word (-count, key); return; } if (rl_point == 0) { ding (); return; } if (uppercase_p (key)) rl_vi_bWord (count); else rl_vi_bword (count);}/* Next word in vi mode. */rl_vi_next_word (count, key) int count;{ if (count < 0) { rl_vi_prev_word (-count, key); return; } if (rl_point >= (rl_end - 1)) { ding (); return; } if (uppercase_p (key)) rl_vi_fWord (count); else rl_vi_fword (count);}/* Move to the end of the ?next? word. */rl_vi_end_word (count, key) int count, key;{ if (count < 0) { ding (); return; } if (uppercase_p (key)) rl_vi_eWord (count); else rl_vi_eword (count);}/* Move forward a word the way that 'W' does. */rl_vi_fWord (count) int count;{ 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++; }}rl_vi_bWord (count) int count;{ 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++; } }}rl_vi_eWord (count) int count;{ 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])); 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--; } }}rl_vi_fword (count) int count;{ while (count-- && rl_point < (rl_end - 1)) { /* Move to white space (really non-identifer). */ if (isident (rl_line_buffer[rl_point])) { while (isident (rl_line_buffer[rl_point]) && rl_point < rl_end) rl_point++; } else /* if (!whitespace (rl_line_buffer[rl_point])) */ { while (!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++; }}rl_vi_bword (count) int count;{ 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 = isident (rl_line_buffer[rl_point - 1]); if ((isident (rl_line_buffer[rl_point]) && !last_is_ident) || (!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 (isident (rl_line_buffer[rl_point])) while (--rl_point >= 0 && isident (rl_line_buffer[rl_point])); else while (--rl_point >= 0 && !isident (rl_line_buffer[rl_point]) && !whitespace (rl_line_buffer[rl_point])); rl_point++; } }}rl_vi_eword (count) int count;{ 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 (isident (rl_line_buffer[rl_point])) while (++rl_point < rl_end && isident (rl_line_buffer[rl_point])); else while (++rl_point < rl_end && !isident (rl_line_buffer[rl_point]) && !whitespace (rl_line_buffer[rl_point])); } rl_point--; }}rl_vi_insert_beg (){ rl_beg_of_line (); rl_vi_insertion_mode (); return 0;}rl_vi_append_mode (){ if (rl_point < rl_end) rl_point += 1; rl_vi_insertion_mode (); return 0;}rl_vi_append_eol (){ rl_end_of_line (); rl_vi_append_mode (); return 0;}/* What to do in the case of C-d. */rl_vi_eof_maybe (count, c) int count, c;{ rl_newline (1, '\n');}/* Insertion mode stuff. *//* Switching from one mode to the other really just involves switching keymaps. */rl_vi_insertion_mode (){ keymap = vi_insertion_keymap;}rl_vi_movement_mode (){ if (rl_point > 0) rl_backward (1); keymap = vi_movement_keymap; vi_done_inserting ();}vi_done_inserting (){ if (vi_doing_insert) { rl_end_undo_group (); vi_doing_insert = 0; }}rl_vi_arg_digit (count, c) int count, c;{ if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg) rl_beg_of_line (); else rl_digit_argument (count, c);}rl_vi_change_case (count, ignore) int count, ignore;{ char c = 0; /* Don't try this on an empty line. */ if (rl_point >= rl_end) return; while (count-- && rl_point < rl_end) { if (uppercase_p (rl_line_buffer[rl_point]))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -