diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/api/private/defs.h | 1 | ||||
| -rw-r--r-- | src/nvim/grid_defs.h | 9 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 8 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/unpacker.c | 173 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/unpacker.h | 2 | ||||
| -rw-r--r-- | src/nvim/ui_client.c | 95 | ||||
| -rw-r--r-- | src/nvim/ui_client.h | 6 | 
7 files changed, 175 insertions, 119 deletions
| diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b1e0dd364c..9c7e59e4b3 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -36,6 +36,7 @@ typedef enum {    kMessageTypeRequest = 0,    kMessageTypeResponse = 1,    kMessageTypeNotification = 2, +  kMessageTypeRedrawEvent = 3,  } MessageType;  /// Mask for all internal calls diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 1571340849..9252b8a371 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -128,4 +128,13 @@ typedef struct {    const char *start;       ///< Location where region starts.  } StlClickRecord; +typedef struct { +  int args[3]; +  int icell; +  int ncells; +  int coloff; +  int cur_attr; +  int clear_width; +} GridLineEvent; +  #endif  // NVIM_GRID_DEFS_H diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 0bb5d800f1..1ca68979f4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -239,11 +239,13 @@ static void parse_msgpack(Channel *channel)  {    Unpacker *p = channel->rpc.unpacker;    while (unpacker_advance(p)) { -    if (p->is_ui) { -      if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { +    if (p->type == kMessageTypeRedrawEvent) { +      if (p->grid_line_event) { +        ui_client_event_raw_line(p->grid_line_event); +      } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {          p->ui_handler.fn(p->result.data.array); +        arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);        } -      arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);      } else if (p->type == kMessageTypeResponse) {        ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);        if (p->request_id != frame->request_id) { diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index efbb3110d9..d73557ec11 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -6,6 +6,7 @@  #include "nvim/memory.h"  #include "nvim/msgpack_rpc/helpers.h"  #include "nvim/msgpack_rpc/unpacker.h" +#include "nvim/ui_client.h"  #ifdef INCLUDE_GENERATED_DECLARATIONS  # include "msgpack_rpc/unpacker.c.generated.h" @@ -280,10 +281,20 @@ error:  // is relatively small, just ~10 bytes + the method name. Thus we can simply refuse  // to advance the stream beyond the header until it can be parsed in its entirety.  // -// Of course, later on, we want to specialize state 2 into sub-states depending +// Later on, we want to specialize state 2 into more sub-states depending  // on the specific method. "nvim_exec_lua" should just decode direct into lua -// objects, and "redraw/grid_line" should use a hand-rolled decoder to avoid -// a blizzard of small objects for each screen cell. +// objects. For the moment "redraw/grid_line" uses a hand-rolled decoder, +// to avoid a blizzard of small objects for each screen cell. +// +// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] +// +// Where [args] gets unpacked as an Array. Note: first {11} is not saved as a state. +// +// When method is "grid_line", we furthermore decode a cell at a time like: +// +// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]] +// +// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional  bool unpacker_advance(Unpacker *p)  { @@ -293,6 +304,7 @@ bool unpacker_advance(Unpacker *p)        return false;      }      if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) { +      p->type = kMessageTypeRedrawEvent;        p->state = 10;      } else {        p->state = p->type == kMessageTypeResponse ? 1 : 2; @@ -300,11 +312,18 @@ bool unpacker_advance(Unpacker *p)      }    } -  if (p->state == 10 || p->state == 11) { +  if (p->state >= 10 && p->state != 12) {      if (!unpacker_parse_redraw(p)) {        return false;      } -    arena_start(&p->arena, &p->reuse_blk); + +    if (p->state == 14) { +      // grid_line event already unpacked +      goto done; +    } else { +      // unpack other ui events using mpack_parse() +      arena_start(&p->arena, &p->reuse_blk); +    }    }    int result; @@ -321,6 +340,7 @@ rerun:      return false;    } +done:    switch (p->state) {    case 1:      p->error = p->result; @@ -328,28 +348,23 @@ rerun:      goto rerun;    case 2:      p->state = 0; -    p->is_ui = false;      return true;    case 12: +  case 14:      p->ncalls--;      if (p->ncalls > 0) { -      p->state = 12; +      p->state = (p->state == 14) ? 13 : 12;      } else if (p->nevents > 0) {        p->state = 11;      } else {        p->state = 0;      } -    // TODO: fold into p->type -    p->is_ui = true;      return true;    default:      abort();    }  } -// note: first {11} is not saved as a state -// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] -//  bool unpacker_parse_redraw(Unpacker *p)  {    mpack_token_t tok; @@ -357,40 +372,36 @@ bool unpacker_parse_redraw(Unpacker *p)    const char *data = p->read_ptr;    size_t size = p->read_size; +  GridLineEvent *g = p->grid_line_event; + +#define NEXT_TYPE(tok, typ) \ +  result = mpack_rtoken(&data, &size, &tok); \ +  if (result == MPACK_EOF) { \ +    return false; \ +  } else if (result || (tok.type != typ \ +                        && !(typ == MPACK_TOKEN_STR && tok.type == MPACK_TOKEN_BIN) \ +                        && !(typ == MPACK_TOKEN_SINT && tok.type == MPACK_TOKEN_UINT))) { \ +    p->state = -1; \ +    return false; \ +  } + +redo:    switch (p->state) {    case 10: -    result = mpack_rtoken(&data, &size, &tok); -    if (result == MPACK_EOF) { -      return false; -    } else if (result || tok.type != MPACK_TOKEN_ARRAY) { -      p->state = -1; -      return false; -    } - +    NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);      p->nevents = (int)tok.length;      FALLTHROUGH;    case 11: -    result = mpack_rtoken(&data, &size, &tok); -    if (result == MPACK_EOF) { -      return false; -    } else if (result || tok.type != MPACK_TOKEN_ARRAY) { -      abort(); -    } - +    NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);      p->ncalls = (int)tok.length; +      if (p->ncalls-- == 0) {        p->state = -1;        return false;      } -    result = mpack_rtoken(&data, &size, &tok); -    if (result == MPACK_EOF) { -      return false; -    } else if (result || (tok.type != MPACK_TOKEN_STR && tok.type != MPACK_TOKEN_BIN)) { -      abort(); -    } - +    NEXT_TYPE(tok, MPACK_TOKEN_STR);      if (tok.length > size) {        return false;      } @@ -402,8 +413,98 @@ bool unpacker_parse_redraw(Unpacker *p)      p->nevents--;      p->read_ptr = data;      p->read_size = size; -    p->state = 12; -    return true; +    if (p->ui_handler.fn != ui_client_event_grid_line) { +      p->state = 12; +      if (p->grid_line_event) { +        arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); +        p->grid_line_event = NULL; +      } +      return true; +    } else { +      p->state = 13; +      arena_start(&p->arena, &p->reuse_blk); +      p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true); +      g = p->grid_line_event; +      FALLTHROUGH; +    } + +  case 13: +    NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); +    int eventarrsize = (int)tok.length; +    if (eventarrsize != 4) { +      p->state = -1; +      return false; +    } + +    for (int i = 0; i < 3; i++) { +      NEXT_TYPE(tok, MPACK_TOKEN_UINT); +      g->args[i] = (int)tok.data.value.lo; +    } + +    NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); +    g->ncells = (int)tok.length; +    g->icell = 0; +    g->coloff = 0; +    g->cur_attr = -1; + +    p->read_ptr = data; +    p->read_size = size; +    p->state = 14; +    FALLTHROUGH; + +  case 14: +    assert(g->icell < g->ncells); + +    NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); +    int cellarrsize = (int)tok.length; +    if (cellarrsize < 1 || cellarrsize > 3) { +      p->state = -1; +      return false; +    } + +    NEXT_TYPE(tok, MPACK_TOKEN_STR); +    if (tok.length > size) { +      return false; +    } + +    const char *cellbuf = data; +    size_t cellsize = tok.length; +    data += cellsize; +    size -= cellsize; + +    if (cellarrsize >= 2) { +      NEXT_TYPE(tok, MPACK_TOKEN_SINT); +      g->cur_attr = (int)tok.data.value.lo; +    } + +    int repeat = 1; +    if (cellarrsize >= 3) { +      NEXT_TYPE(tok, MPACK_TOKEN_UINT); +      repeat = (int)tok.data.value.lo; +    } + +    g->clear_width = 0; +    if (g->icell == g->ncells - 1 && cellsize == 1 && cellbuf[0] == ' ' && repeat > 1) { +      g->clear_width = repeat; +    } else { +      for (int r = 0; r < repeat; r++) { +        if (g->coloff >= (int)grid_line_buf_size) { +          p->state = -1; +          return false; +        } +        memcpy(grid_line_buf_char[g->coloff], cellbuf, cellsize); +        grid_line_buf_char[g->coloff][cellsize] = NUL; +        grid_line_buf_attr[g->coloff++] = g->cur_attr; +      } +    } + +    g->icell++; +    p->read_ptr = data; +    p->read_size = size; +    if (g->icell == g->ncells) { +      return true; +    } +    goto redo;    default:      abort(); diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index 3a5476cd21..f39439be63 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -35,10 +35,10 @@ struct Unpacker {    // one length free-list of reusable blocks    ArenaMem reuse_blk; -  bool is_ui;    int nevents;    int ncalls;    UIClientHandler ui_handler; +  GridLineEvent *grid_line_event;  };  // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 5f4cb63f87..09e971f03f 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -22,11 +22,6 @@  # include "ui_events_client.generated.h"  #endif -// Temporary buffer for converting a single grid_line event -static size_t buf_size = 0; -static schar_T *buf_char = NULL; -static sattr_T *buf_attr = NULL; -  void ui_client_init(uint64_t chan)  {    Array args = ARRAY_DICT_INIT; @@ -106,88 +101,30 @@ void ui_client_event_grid_resize(Array args)    Integer height = args.items[2].data.integer;    ui_call_grid_resize(grid, width, height); -  if (buf_size < (size_t)width) { -    xfree(buf_char); -    xfree(buf_attr); -    buf_size = (size_t)width; -    buf_char = xmalloc(buf_size * sizeof(schar_T)); -    buf_attr = xmalloc(buf_size * sizeof(sattr_T)); +  if (grid_line_buf_size < (size_t)width) { +    xfree(grid_line_buf_char); +    xfree(grid_line_buf_attr); +    grid_line_buf_size = (size_t)width; +    grid_line_buf_char = xmalloc(grid_line_buf_size * sizeof(schar_T)); +    grid_line_buf_attr = xmalloc(grid_line_buf_size * sizeof(sattr_T));    }  }  void ui_client_event_grid_line(Array args) +  FUNC_ATTR_NORETURN  { -  if (args.size < 4 -      || args.items[0].type != kObjectTypeInteger -      || args.items[1].type != kObjectTypeInteger -      || args.items[2].type != kObjectTypeInteger -      || args.items[3].type != kObjectTypeArray) { -    goto error; -  } +  abort();  // unreachable +} -  Integer grid = args.items[0].data.integer; -  Integer row = args.items[1].data.integer; -  Integer startcol = args.items[2].data.integer; -  Array cells = args.items[3].data.array; +void ui_client_event_raw_line(GridLineEvent *g) +{ +  int grid = g->args[0], row = g->args[1], startcol = g->args[2]; +  Integer endcol = startcol + g->coloff; +  Integer clearcol = endcol + g->clear_width;    // TODO(hlpr98): Accommodate other LineFlags when included in grid_line    LineFlags lineflags = 0; -  size_t j = 0; -  int cur_attr = 0; -  int clear_attr = 0; -  int clear_width = 0; -  for (size_t i = 0; i < cells.size; i++) { -    if (cells.items[i].type != kObjectTypeArray) { -      goto error; -    } -    Array cell = cells.items[i].data.array; - -    if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { -      goto error; -    } -    String sstring = cell.items[0].data.string; - -    char *schar = sstring.data; -    int repeat = 1; -    if (cell.size >= 2) { -      if (cell.items[1].type != kObjectTypeInteger -          || cell.items[1].data.integer < 0) { -        goto error; -      } -      cur_attr = (int)cell.items[1].data.integer; -    } - -    if (cell.size >= 3) { -      if (cell.items[2].type != kObjectTypeInteger -          || cell.items[2].data.integer < 0) { -        goto error; -      } -      repeat = (int)cell.items[2].data.integer; -    } - -    if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { -      clear_width = repeat; -      break; -    } - -    for (int r = 0; r < repeat; r++) { -      if (j >= buf_size) { -        goto error;  // _YIKES_ -      } -      STRLCPY(buf_char[j], schar, sizeof(schar_T)); -      buf_attr[j++] = cur_attr; -    } -  } - -  Integer endcol = startcol + (int)j; -  Integer clearcol = endcol + clear_width; -  clear_attr = cur_attr; - -  ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, -                   (const schar_T *)buf_char, (const sattr_T *)buf_attr); -  return; - -error: -  ELOG("Error handling ui event 'grid_line'"); +  ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags, +                   (const schar_T *)grid_line_buf_char, grid_line_buf_attr);  } diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 41d9fa6227..311dafaa0b 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -2,12 +2,18 @@  #define NVIM_UI_CLIENT_H  #include "nvim/api/private/defs.h" +#include "nvim/grid_defs.h"  typedef struct {    const char *name;    void (*fn)(Array args);  } UIClientHandler; +// Temporary buffer for converting a single grid_line event +EXTERN size_t grid_line_buf_size INIT(= 0); +EXTERN schar_T *grid_line_buf_char INIT(= NULL); +EXTERN sattr_T *grid_line_buf_attr INIT(= NULL); +  #ifdef INCLUDE_GENERATED_DECLARATIONS  # include "ui_client.h.generated.h" | 
