zoukankan      html  css  js  c++  java
  • Vim中如何移动光标

    一、问题

    明显的,在normal模式下,通过hjkl四个按键进行移动,但是之类的问题是vim如何移动光标而不是用户怎么移动光标。在bash界面中,我们通过通过方向键来移动光标位置。在vim中,vim是完全控制了当前终端,假设你获得了终端的控制权,你将如何控制光标在整个终端的任意位置进行移动呢?

    二、通过strace看光标移动时系统调用

    其中的32200进程是一个vim进程,当在vim中执行一次移动(按下j向下移动)时,通过strace可以看到vim向终端写入了大量眼花缭乱的、令人晕眩的字符串输出。
    tsecer@harry: strace -s 1000 -e write -p 32200
    Process 32200 attached
    write(1, "33[?25l33[m33[48;5;234m33[60;183Hj33[24;12H", 38) = 38
    write(1, "33[60;183H 33[25;9H33[1;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mfile33[m33[48;5;234m 33[2;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mtext33[m33[48;5;234m 33[3;9H33[1m33[38;5;161m33[48;5;236mx33[m33[48;5;234m33[1m33[38;5;161mt033[m33[48;5;234m:33[4;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mglobl33[m33[48;5;234m 33[5;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mtype33[m33[48;5;234m 33[6;9H33[38;5;208m33[48;5;236mo33[m33[48;5;234m33[2C:33[7;9H33[1m33[38;5;161m33[48;5;236m033[m33[48;5;234m: 33[8;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mfile33[m33[48;5;234m 33[9;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mloc33[m33[48;5;234m 33[10;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mcfi_startproc33[m33[48;5;234m 33[11;9H33[38;5;208m33[48;5;236mp33[m33[48;5;234m33[2C33[38;5;208mh33[m33[48;5;234m33[12;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mcfi_def_cfa_offset33[m33[48;5;234m 33[13;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;"..., 2031) = 2031
    write(1, "33[1m33[38;5;161m33[48;5;235m.LC033[m33[48;5;234m33[48;5;235m: 33[1C 33[m33[48;5;234m33[26;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mstring33[m33[48;5;234m 33[27;9H33[48;5;236m:33[m33[48;5;234m 33[28;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mstring33[m33[48;5;234m 33[29;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mtext33[m33[48;5;234m 33[30;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mglobl33[m33[48;5;234m 33[31;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mtype33[m33[48;5;234m 33[32;9H33[48;5;236m:33[m33[48;5;234m 33[33;9H33[1m33[38;5;161m33[48;5;236m133[m33[48;5;234m: 33[34;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mloc33[m33[48;5;234m 33[35;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mcfi_startproc33[m33[48;5;234m 33[36;9H33[38;5;208m33[48;5;236mp33[m"..., 2033) = 2033
    write(1, "33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[50;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mloc33[m33[48;5;234m 33[51;9H33[38;5;208m33[48;5;236mm33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[52;9H33[38;5;208m33[48;5;236mm33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[53;9H33[38;5;208m33[48;5;236mm33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[54;9H33[38;5;208m33[48;5;236mc33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[55;9H33[1m33[38;5;161m33[48;5;236m.33[m33[48;5;234m33[1m33[38;5;161mloc33[m33[48;5;234m 33[56;9H33[38;5;208m33[48;5;236mm33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[57;9H33[38;5;208m33[48;5;236mm33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[58;9H33[38;5;208m33[48;5;236mm33[m33[48;5;234m33[2C33[38;5;208ml33[m33[48;5;234m33[59;186H33[1m33[38;5;232m33[48;5;144m533[m33[48;5;234m33[38;5;232m33[48;5;144m:33[25;9H33[?25h", 810) = 810

    三、这些字符串的意义

    这些其实是早期终端定义的一些转义字符串序列,也就是通过特殊的序列表示对终端的控制(而不是字符串本身)。在vim输出中,比较明显的就是"33[",这个就是文档中说明的CSI (Control Sequence Introducer) sequences。对于数字类型的参数,通过分号(";")分割。
    其中最常见的就是H未设置光标位置,对应的描述为
    CSI n ; m H
    CUP
    Cursor Position Moves the cursor to row n, column m. The values are 1-based, and default to 1 (top left corner) if omitted. A sequence such as CSI ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as CSI 17H and CSI 17;1H
    另一个m对应的意义为设置颜色
    CSI n m
    SGR
    Select Graphic Rendition Sets the appearance of the following characters.

    四、putty中对这些代码的处理

    提醒一下,这些所谓的转义内容并不是由操作系统(驱动)处理的,而是由终端处理的。由于现在已经很难找到(也没必要)实体的终端,所以这些控制序列是由终端模拟器(SecureCRT、putty)来完成。在putty的代码中,我们可以看到对这些序列的处理流程。

    putty-0.75 erminal.h
    enum {
    TOPLEVEL,
    SEEN_ESC,
    SEEN_CSI,
    SEEN_OSC,
    SEEN_OSC_W,

    DO_CTRLS,

    SEEN_OSC_P,
    OSC_STRING, OSC_MAYBE_ST,
    VT52_ESC,
    VT52_Y1,
    VT52_Y2,
    VT52_FG,
    VT52_BG
    } termstate;

    代码可以看到,当遇到分号(;)时会增加参数的数量,在遇到ESC之后遇到([)进入CSI状态
    putty-0.75 erminal.c
    /*
    * Remove everything currently in `inbuf' and stick it up on the
    * in-memory display. There's a big state machine in here to
    * process escape sequences...
    */
    static void term_out(Terminal *term)
    {
    ……
    term->termstate = TOPLEVEL;
    switch (ANSI(c, term->esc_query)) {
    case '[': /* enter CSI mode */
    term->termstate = SEEN_CSI;
    term->esc_nargs = 1;
    term->esc_args[0] = ARG_DEFAULT;
    term->esc_query = 0;
    break;
    ……
    case SEEN_CSI:
    term->termstate = TOPLEVEL; /* default */
    if (isdigit(c)) {
    if (term->esc_nargs <= ARGS_MAX) {
    if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)
    term->esc_args[term->esc_nargs - 1] = 0;
    if (term->esc_args[term->esc_nargs - 1] <=
    UINT_MAX / 10 &&
    term->esc_args[term->esc_nargs - 1] * 10 <=
    UINT_MAX - c - '0')
    term->esc_args[term->esc_nargs - 1] =
    10 * term->esc_args[term->esc_nargs - 1] +
    c - '0';
    else
    term->esc_args[term->esc_nargs - 1] = UINT_MAX;
    }
    term->termstate = SEEN_CSI;
    }else if (c == ';') {
    if (term->esc_nargs < ARGS_MAX)
    term->esc_args[term->esc_nargs++] = ARG_DEFAULT;
    term->termstate = SEEN_CSI;
    }
    ……
    } else
    #define CLAMP(arg, lim) ((arg) = ((arg) > (lim)) ? (lim) : (arg))
    switch (ANSI(c, term->esc_query)) {
    case 'A': /* CUU: move up N lines */
    CLAMP(term->esc_args[0], term->rows);
    move(term, term->curs.x,
    term->curs.y - def(term->esc_args[0], 1), 1);
    seen_disp_event(term);
    break;
    case 'e': /* VPR: move down N lines */
    compatibility(ANSI);
    /* FALLTHROUGH */
    case 'B': /* CUD: Cursor down */
    CLAMP(term->esc_args[0], term->rows);
    move(term, term->curs.x,
    term->curs.y + def(term->esc_args[0], 1), 1);
    seen_disp_event(term);
    break;
    ……
    case 'H': /* CUP */
    case 'f': /* HVP: set horz and vert posns at once */
    if (term->esc_nargs < 2)
    term->esc_args[1] = ARG_DEFAULT;
    CLAMP(term->esc_args[0], term->rows);
    CLAMP(term->esc_args[1], term->cols);
    move(term, def(term->esc_args[1], 1) - 1,
    ((term->dec_om ? term->marg_t : 0) +
    def(term->esc_args[0], 1) - 1),
    (term->dec_om ? 2 : 0));
    seen_disp_event(term);
    break;
    ……
    }


    五、通过程序测试下转移的效果

    是不是感觉有一种通过字符串脚本来编程控制终端的感觉?:)
    tsecer@harry: cat term.esc.cpp
    #include <unistd.h>
    #include <stdio.h>

    int main(int argc, const char *argv[])
    {
    for (int lin = 0; lin < 200; lin++)
    {
    for(int row = 0; row < 200; row++)
    {
    char buff[100] = {};
    int icount = snprintf(buff, sizeof buff, "33[%d;%dH", lin, row);
    write(1, buff, icount);
    usleep(10000);
    }
    }
    return 0;
    }

  • 相关阅读:
    C语言 百炼成钢1
    C语言 位运算
    GPS nmealib学习 问题
    c语言 GPS nmealib学习笔记
    ubuntu 12.04 以固定 IP 地址连接网络并配置DNS
    WGS84 2 GCJ-02
    【转】地球坐标系 (WGS-84) 到火星坐标系 (GCJ-02) 的转换算法 C#
    【转】地球坐标系 (WGS-84) 到火星坐标系 (GCJ-02) 的转换算法 C语言
    nmealib-0.5.3 问题 Build Error: undefined reference to `ceil'
    ArcGIS for WPF 访问外部资源
  • 原文地址:https://www.cnblogs.com/tsecer/p/14772287.html
Copyright © 2011-2022 走看看