mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-03 17:24:29 +00:00 
			
		
		
		
	Merge pull request #26813 from VanaIgr/screen-pos-speedup
perf: make screen size and position calculations more efficient N/A patches for version.c: vim-patch:9.1.0037: Calling get_breakindent_win() repeatedly when computing virtcol vim-patch:9.1.0038: Unnecessary loop in getvcol()
This commit is contained in:
		@@ -137,6 +137,8 @@ The following new APIs and features were added.
 | 
			
		||||
  • Treesitter highlighting now parses injections incrementally during
 | 
			
		||||
    screen redraws only for the line range being rendered. This significantly
 | 
			
		||||
    improves performance in large files with many injections.
 | 
			
		||||
  • 'breakindent' performance is significantly improved for wrapped lines.
 | 
			
		||||
  • Cursor movement, insertion with [count] and |screenpos()| are now faster.
 | 
			
		||||
 | 
			
		||||
• |vim.iter()| provides a generic iterator interface for tables and Lua
 | 
			
		||||
  iterators |for-in|.
 | 
			
		||||
 
 | 
			
		||||
@@ -257,6 +257,7 @@ bool arabic_maycombine(int two)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check whether we are dealing with Arabic combining characters.
 | 
			
		||||
/// Returns false for negative values.
 | 
			
		||||
/// Note: these are NOT really composing characters!
 | 
			
		||||
///
 | 
			
		||||
/// @param one First character.
 | 
			
		||||
 
 | 
			
		||||
@@ -141,17 +141,18 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    chartabsize_T cts;
 | 
			
		||||
    init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
 | 
			
		||||
    while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) {
 | 
			
		||||
      // Count a tab for what it's worth (if list mode not on)
 | 
			
		||||
      csize = win_lbr_chartabsize(&cts, &head);
 | 
			
		||||
      MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
      cts.cts_vcol += csize;
 | 
			
		||||
    CharsizeArg arg;
 | 
			
		||||
    CSType cstype = init_charsize_arg(&arg, curwin, pos->lnum, line);
 | 
			
		||||
    StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
    col = 0;
 | 
			
		||||
    while (col <= wcol && *ci.ptr != NUL) {
 | 
			
		||||
      CharSize cs = win_charsize(cstype, col, ci.ptr, ci.chr.value, &arg);
 | 
			
		||||
      csize = cs.width;
 | 
			
		||||
      head = cs.head;
 | 
			
		||||
      col += cs.width;
 | 
			
		||||
      ci = utfc_next(ci);
 | 
			
		||||
    }
 | 
			
		||||
    col = cts.cts_vcol;
 | 
			
		||||
    idx = (int)(cts.cts_ptr - line);
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
    idx = (int)(ci.ptr - line);
 | 
			
		||||
 | 
			
		||||
    // Handle all the special cases.  The virtual_active() check
 | 
			
		||||
    // is needed to ensure that a virtual position off the end of
 | 
			
		||||
 
 | 
			
		||||
@@ -1336,30 +1336,30 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
 | 
			
		||||
 | 
			
		||||
  if (start_col > 0 && col_rows == 0) {
 | 
			
		||||
    char *prev_ptr = ptr;
 | 
			
		||||
    chartabsize_T cts;
 | 
			
		||||
    int charsize = 0;
 | 
			
		||||
    int head = 0;
 | 
			
		||||
    CharSize cs = { 0 };
 | 
			
		||||
 | 
			
		||||
    init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, ptr);
 | 
			
		||||
    cts.cts_max_head_vcol = start_col;
 | 
			
		||||
    while (cts.cts_vcol < start_col && *cts.cts_ptr != NUL) {
 | 
			
		||||
      head = 0;
 | 
			
		||||
      charsize = win_lbr_chartabsize(&cts, &head);
 | 
			
		||||
      cts.cts_vcol += charsize;
 | 
			
		||||
      prev_ptr = cts.cts_ptr;
 | 
			
		||||
      MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
    CharsizeArg arg;
 | 
			
		||||
    CSType cstype = init_charsize_arg(&arg, wp, lnum, line);
 | 
			
		||||
    arg.max_head_vcol = start_col;
 | 
			
		||||
    int vcol = wlv.vcol;
 | 
			
		||||
    StrCharInfo ci = utf_ptr2StrCharInfo(ptr);
 | 
			
		||||
    while (vcol < start_col && *ci.ptr != NUL) {
 | 
			
		||||
      cs = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg);
 | 
			
		||||
      vcol += cs.width;
 | 
			
		||||
      prev_ptr = ci.ptr;
 | 
			
		||||
      ci = utfc_next(ci);
 | 
			
		||||
      if (wp->w_p_list) {
 | 
			
		||||
        in_multispace = *prev_ptr == ' ' && (*cts.cts_ptr == ' '
 | 
			
		||||
        in_multispace = *prev_ptr == ' ' && (*ci.ptr == ' '
 | 
			
		||||
                                             || (prev_ptr > line && prev_ptr[-1] == ' '));
 | 
			
		||||
        if (!in_multispace) {
 | 
			
		||||
          multispace_pos = 0;
 | 
			
		||||
        } else if (cts.cts_ptr >= line + leadcol
 | 
			
		||||
        } else if (ci.ptr >= line + leadcol
 | 
			
		||||
                   && wp->w_p_lcs_chars.multispace != NULL) {
 | 
			
		||||
          multispace_pos++;
 | 
			
		||||
          if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
 | 
			
		||||
            multispace_pos = 0;
 | 
			
		||||
          }
 | 
			
		||||
        } else if (cts.cts_ptr < line + leadcol
 | 
			
		||||
        } else if (ci.ptr < line + leadcol
 | 
			
		||||
                   && wp->w_p_lcs_chars.leadmultispace != NULL) {
 | 
			
		||||
          multispace_pos++;
 | 
			
		||||
          if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
 | 
			
		||||
@@ -1368,9 +1368,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    wlv.vcol = cts.cts_vcol;
 | 
			
		||||
    ptr = cts.cts_ptr;
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
    wlv.vcol = vcol;
 | 
			
		||||
    ptr = ci.ptr;
 | 
			
		||||
    int charsize = cs.width;
 | 
			
		||||
    int head = cs.head;
 | 
			
		||||
 | 
			
		||||
    // When:
 | 
			
		||||
    // - 'cuc' is set, or
 | 
			
		||||
@@ -2081,13 +2082,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
 | 
			
		||||
            && vim_isbreak(mb_c) && !vim_isbreak((uint8_t)(*ptr))) {
 | 
			
		||||
          int mb_off = utf_head_off(line, ptr - 1);
 | 
			
		||||
          char *p = ptr - (mb_off + 1);
 | 
			
		||||
          chartabsize_T cts;
 | 
			
		||||
 | 
			
		||||
          init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p);
 | 
			
		||||
          // do not want virtual text to be counted here
 | 
			
		||||
          cts.cts_has_virt_text = false;
 | 
			
		||||
          wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1;
 | 
			
		||||
          clear_chartabsize_arg(&cts);
 | 
			
		||||
          CharsizeArg arg;
 | 
			
		||||
          // lnum == 0, do not want virtual text to be counted here
 | 
			
		||||
          CSType cstype = init_charsize_arg(&arg, wp, 0, line);
 | 
			
		||||
          wlv.n_extra = win_charsize(cstype, wlv.vcol, p, utf_ptr2CharInfo(p).value,
 | 
			
		||||
                                     &arg).width - 1;
 | 
			
		||||
 | 
			
		||||
          if (on_last_col && mb_c != TAB) {
 | 
			
		||||
            // Do not continue search/match highlighting over the
 | 
			
		||||
 
 | 
			
		||||
@@ -1679,33 +1679,37 @@ void change_indent(int type, int amount, int round, int replaced, bool call_chan
 | 
			
		||||
  } else {
 | 
			
		||||
    // Compute the screen column where the cursor should be.
 | 
			
		||||
    vcol = get_indent() - vcol;
 | 
			
		||||
    curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);
 | 
			
		||||
    int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol);
 | 
			
		||||
    curwin->w_virtcol = end_vcol;
 | 
			
		||||
 | 
			
		||||
    // Advance the cursor until we reach the right screen column.
 | 
			
		||||
    int last_vcol = 0;
 | 
			
		||||
    char *ptr = get_cursor_line_ptr();
 | 
			
		||||
    chartabsize_T cts;
 | 
			
		||||
    init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr);
 | 
			
		||||
    while (cts.cts_vcol <= (int)curwin->w_virtcol) {
 | 
			
		||||
      last_vcol = cts.cts_vcol;
 | 
			
		||||
      if (cts.cts_vcol > 0) {
 | 
			
		||||
        MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
      }
 | 
			
		||||
      if (*cts.cts_ptr == NUL) {
 | 
			
		||||
    new_cursor_col = 0;
 | 
			
		||||
    char *const line = get_cursor_line_ptr();
 | 
			
		||||
    vcol = 0;
 | 
			
		||||
    if (*line != NUL) {
 | 
			
		||||
      CharsizeArg arg;
 | 
			
		||||
      CSType cstype = init_charsize_arg(&arg, curwin, 0, line);
 | 
			
		||||
      StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
      while (true) {
 | 
			
		||||
        int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
        if (next_vcol > end_vcol) {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      cts.cts_vcol += lbr_chartabsize(&cts);
 | 
			
		||||
        vcol = next_vcol;
 | 
			
		||||
        ci = utfc_next(ci);
 | 
			
		||||
        if (*ci.ptr == NUL) {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      new_cursor_col = (int)(ci.ptr - line);
 | 
			
		||||
    }
 | 
			
		||||
    vcol = last_vcol;
 | 
			
		||||
    new_cursor_col = (int)(cts.cts_ptr - cts.cts_line);
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
    // May need to insert spaces to be able to position the cursor on
 | 
			
		||||
    // the right screen column.
 | 
			
		||||
    if (vcol != (int)curwin->w_virtcol) {
 | 
			
		||||
      curwin->w_cursor.col = (colnr_T)new_cursor_col;
 | 
			
		||||
      size_t i = (size_t)(curwin->w_virtcol - vcol);
 | 
			
		||||
      ptr = xmallocz(i);
 | 
			
		||||
      char *ptr = xmallocz(i);
 | 
			
		||||
      memset(ptr, ' ', i);
 | 
			
		||||
      new_cursor_col += (int)i;
 | 
			
		||||
      ins_str(ptr);
 | 
			
		||||
@@ -4347,14 +4351,16 @@ static bool ins_tab(void)
 | 
			
		||||
    getvcol(curwin, cursor, &want_vcol, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    char *tab = "\t";
 | 
			
		||||
    chartabsize_T cts;
 | 
			
		||||
    init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab);
 | 
			
		||||
    int32_t tab_v = (uint8_t)(*tab);
 | 
			
		||||
 | 
			
		||||
    CharsizeArg arg;
 | 
			
		||||
    CSType cstype = init_charsize_arg(&arg, curwin, 0, tab);
 | 
			
		||||
 | 
			
		||||
    // Use as many TABs as possible.  Beware of 'breakindent', 'showbreak'
 | 
			
		||||
    // and 'linebreak' adding extra virtual columns.
 | 
			
		||||
    while (ascii_iswhite(*ptr)) {
 | 
			
		||||
      int i = lbr_chartabsize(&cts);
 | 
			
		||||
      if (cts.cts_vcol + i > want_vcol) {
 | 
			
		||||
      int i = win_charsize(cstype, vcol, tab, tab_v, &arg).width;
 | 
			
		||||
      if (vcol + i > want_vcol) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (*ptr != TAB) {
 | 
			
		||||
@@ -4369,23 +4375,18 @@ static bool ins_tab(void)
 | 
			
		||||
      }
 | 
			
		||||
      fpos.col++;
 | 
			
		||||
      ptr++;
 | 
			
		||||
      cts.cts_vcol += i;
 | 
			
		||||
      vcol += i;
 | 
			
		||||
    }
 | 
			
		||||
    vcol = cts.cts_vcol;
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
    if (change_col >= 0) {
 | 
			
		||||
      int repl_off = 0;
 | 
			
		||||
      // Skip over the spaces we need.
 | 
			
		||||
      init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr);
 | 
			
		||||
      while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') {
 | 
			
		||||
        cts.cts_vcol += lbr_chartabsize(&cts);
 | 
			
		||||
        cts.cts_ptr++;
 | 
			
		||||
      cstype = init_charsize_arg(&arg, curwin, 0, ptr);
 | 
			
		||||
      while (vcol < want_vcol && *ptr == ' ') {
 | 
			
		||||
        vcol += win_charsize(cstype, vcol, ptr, (uint8_t)(' '), &arg).width;
 | 
			
		||||
        ptr++;
 | 
			
		||||
        repl_off++;
 | 
			
		||||
      }
 | 
			
		||||
      ptr = cts.cts_ptr;
 | 
			
		||||
      vcol = cts.cts_vcol;
 | 
			
		||||
      clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
      if (vcol > want_vcol) {
 | 
			
		||||
        // Must have a char with 'showbreak' just before it.
 | 
			
		||||
@@ -4556,8 +4557,6 @@ static int ins_digraph(void)
 | 
			
		||||
// Returns the char to be inserted, or NUL if none found.
 | 
			
		||||
int ins_copychar(linenr_T lnum)
 | 
			
		||||
{
 | 
			
		||||
  char *ptr;
 | 
			
		||||
 | 
			
		||||
  if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
 | 
			
		||||
    vim_beep(BO_COPY);
 | 
			
		||||
    return NUL;
 | 
			
		||||
@@ -4565,24 +4564,22 @@ int ins_copychar(linenr_T lnum)
 | 
			
		||||
 | 
			
		||||
  // try to advance to the cursor column
 | 
			
		||||
  validate_virtcol();
 | 
			
		||||
  int const end_vcol = curwin->w_virtcol;
 | 
			
		||||
  char *line = ml_get(lnum);
 | 
			
		||||
  char *prev_ptr = line;
 | 
			
		||||
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  init_chartabsize_arg(&cts, curwin, lnum, 0, line, line);
 | 
			
		||||
  while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) {
 | 
			
		||||
    prev_ptr = cts.cts_ptr;
 | 
			
		||||
    cts.cts_vcol += lbr_chartabsize_adv(&cts);
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  CSType cstype = init_charsize_arg(&arg, curwin, lnum, line);
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  int vcol = 0;
 | 
			
		||||
  while (vcol < end_vcol && *ci.ptr != NUL) {
 | 
			
		||||
    vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
    if (vcol > end_vcol) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    ci = utfc_next(ci);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (cts.cts_vcol > curwin->w_virtcol) {
 | 
			
		||||
    ptr = prev_ptr;
 | 
			
		||||
  } else {
 | 
			
		||||
    ptr = cts.cts_ptr;
 | 
			
		||||
  }
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
  int c = utf_ptr2char(ptr);
 | 
			
		||||
  int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value;
 | 
			
		||||
  if (c == NUL) {
 | 
			
		||||
    vim_beep(BO_COPY);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -2502,20 +2502,22 @@ static int vgetorpeek(bool advance)
 | 
			
		||||
              // we are expecting to truncate the trailing
 | 
			
		||||
              // white-space, so find the last non-white
 | 
			
		||||
              // character -- webb
 | 
			
		||||
              if (did_ai
 | 
			
		||||
                  && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) {
 | 
			
		||||
              if (did_ai && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) {
 | 
			
		||||
                curwin->w_wcol = 0;
 | 
			
		||||
                ptr = get_cursor_line_ptr();
 | 
			
		||||
                chartabsize_T cts;
 | 
			
		||||
                init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, ptr, ptr);
 | 
			
		||||
                while (cts.cts_ptr < ptr + curwin->w_cursor.col) {
 | 
			
		||||
                  if (!ascii_iswhite(*cts.cts_ptr)) {
 | 
			
		||||
                    curwin->w_wcol = cts.cts_vcol;
 | 
			
		||||
                char *endptr = ptr + curwin->w_cursor.col;
 | 
			
		||||
 | 
			
		||||
                CharsizeArg arg;
 | 
			
		||||
                CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, ptr);
 | 
			
		||||
                StrCharInfo ci = utf_ptr2StrCharInfo(ptr);
 | 
			
		||||
                int vcol = 0;
 | 
			
		||||
                while (ci.ptr < endptr) {
 | 
			
		||||
                  if (!ascii_iswhite(ci.chr.value)) {
 | 
			
		||||
                    curwin->w_wcol = vcol;
 | 
			
		||||
                  }
 | 
			
		||||
                  cts.cts_vcol += lbr_chartabsize(&cts);
 | 
			
		||||
                  cts.cts_ptr += utfc_ptr2len(cts.cts_ptr);
 | 
			
		||||
                  vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
                  ci = utfc_next(ci);
 | 
			
		||||
                }
 | 
			
		||||
                clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
                curwin->w_wrow = curwin->w_cline_row
 | 
			
		||||
                                 + curwin->w_wcol / curwin->w_width_inner;
 | 
			
		||||
 
 | 
			
		||||
@@ -1246,18 +1246,19 @@ int get_lisp_indent(void)
 | 
			
		||||
      curwin->w_cursor.col = pos->col;
 | 
			
		||||
      colnr_T col = pos->col;
 | 
			
		||||
 | 
			
		||||
      char *that = get_cursor_line_ptr();
 | 
			
		||||
      char *line = get_cursor_line_ptr();
 | 
			
		||||
 | 
			
		||||
      char *line = that;
 | 
			
		||||
      chartabsize_T cts;
 | 
			
		||||
      init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
 | 
			
		||||
      while (*cts.cts_ptr != NUL && col > 0) {
 | 
			
		||||
        cts.cts_vcol += lbr_chartabsize_adv(&cts);
 | 
			
		||||
      CharsizeArg arg;
 | 
			
		||||
      CSType cstype = init_charsize_arg(&arg, curwin, pos->lnum, line);
 | 
			
		||||
 | 
			
		||||
      StrCharInfo sci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
      amount = 0;
 | 
			
		||||
      while (*sci.ptr != NUL && col > 0) {
 | 
			
		||||
        amount += win_charsize(cstype, amount, sci.ptr, sci.chr.value, &arg).width;
 | 
			
		||||
        sci = utfc_next(sci);
 | 
			
		||||
        col--;
 | 
			
		||||
      }
 | 
			
		||||
      amount = cts.cts_vcol;
 | 
			
		||||
      that = cts.cts_ptr;
 | 
			
		||||
      clear_chartabsize_arg(&cts);
 | 
			
		||||
      char *that = sci.ptr;
 | 
			
		||||
 | 
			
		||||
      // Some keywords require "body" indenting rules (the
 | 
			
		||||
      // non-standard-lisp ones are Scheme special forms):
 | 
			
		||||
@@ -1272,15 +1273,10 @@ int get_lisp_indent(void)
 | 
			
		||||
        }
 | 
			
		||||
        colnr_T firsttry = amount;
 | 
			
		||||
 | 
			
		||||
        init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line),
 | 
			
		||||
                             amount, line, that);
 | 
			
		||||
        while (ascii_iswhite(*cts.cts_ptr)) {
 | 
			
		||||
          cts.cts_vcol += lbr_chartabsize(&cts);
 | 
			
		||||
          cts.cts_ptr++;
 | 
			
		||||
        while (ascii_iswhite(*that)) {
 | 
			
		||||
          amount += win_charsize(cstype, amount, that, (uint8_t)(*that), &arg).width;
 | 
			
		||||
          that++;
 | 
			
		||||
        }
 | 
			
		||||
        that = cts.cts_ptr;
 | 
			
		||||
        amount = cts.cts_vcol;
 | 
			
		||||
        clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
        if (*that && (*that != ';')) {
 | 
			
		||||
          // Not a comment line.
 | 
			
		||||
@@ -1292,37 +1288,38 @@ int get_lisp_indent(void)
 | 
			
		||||
 | 
			
		||||
          parencount = 0;
 | 
			
		||||
 | 
			
		||||
          init_chartabsize_arg(&cts, curwin,
 | 
			
		||||
                               (colnr_T)(that - line), amount, line, that);
 | 
			
		||||
          if (((*that != '"') && (*that != '\'') && (*that != '#')
 | 
			
		||||
               && (((uint8_t)(*that) < '0') || ((uint8_t)(*that) > '9')))) {
 | 
			
		||||
          CharInfo ci = utf_ptr2CharInfo(that);
 | 
			
		||||
          if (((ci.value != '"') && (ci.value != '\'') && (ci.value != '#')
 | 
			
		||||
               && ((ci.value < '0') || (ci.value > '9')))) {
 | 
			
		||||
            int quotecount = 0;
 | 
			
		||||
            while (*cts.cts_ptr
 | 
			
		||||
                   && (!ascii_iswhite(*cts.cts_ptr) || quotecount || parencount)) {
 | 
			
		||||
              if (*cts.cts_ptr == '"') {
 | 
			
		||||
            while (*that && (!ascii_iswhite(ci.value) || quotecount || parencount)) {
 | 
			
		||||
              if (ci.value == '"') {
 | 
			
		||||
                quotecount = !quotecount;
 | 
			
		||||
              }
 | 
			
		||||
              if (((*cts.cts_ptr == '(') || (*cts.cts_ptr == '[')) && !quotecount) {
 | 
			
		||||
              if (((ci.value == '(') || (ci.value == '[')) && !quotecount) {
 | 
			
		||||
                parencount++;
 | 
			
		||||
              }
 | 
			
		||||
              if (((*cts.cts_ptr == ')') || (*cts.cts_ptr == ']')) && !quotecount) {
 | 
			
		||||
              if (((ci.value == ')') || (ci.value == ']')) && !quotecount) {
 | 
			
		||||
                parencount--;
 | 
			
		||||
              }
 | 
			
		||||
              if ((*cts.cts_ptr == '\\') && (*(cts.cts_ptr + 1) != NUL)) {
 | 
			
		||||
                cts.cts_vcol += lbr_chartabsize_adv(&cts);
 | 
			
		||||
              if ((ci.value == '\\') && (*(that + 1) != NUL)) {
 | 
			
		||||
                amount += win_charsize(cstype, amount, that, ci.value, &arg).width;
 | 
			
		||||
                StrCharInfo next_sci = utfc_next((StrCharInfo){ that, ci });
 | 
			
		||||
                that = next_sci.ptr;
 | 
			
		||||
                ci = next_sci.chr;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              cts.cts_vcol += lbr_chartabsize_adv(&cts);
 | 
			
		||||
              amount += win_charsize(cstype, amount, that, ci.value, &arg).width;
 | 
			
		||||
              StrCharInfo next_sci = utfc_next((StrCharInfo){ that, ci });
 | 
			
		||||
              that = next_sci.ptr;
 | 
			
		||||
              ci = next_sci.chr;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          while (ascii_iswhite(*cts.cts_ptr)) {
 | 
			
		||||
            cts.cts_vcol += lbr_chartabsize(&cts);
 | 
			
		||||
            cts.cts_ptr++;
 | 
			
		||||
          while (ascii_iswhite(*that)) {
 | 
			
		||||
            amount += win_charsize(cstype, amount, that, (uint8_t)(*that), &arg).width;
 | 
			
		||||
            that++;
 | 
			
		||||
          }
 | 
			
		||||
          that = cts.cts_ptr;
 | 
			
		||||
          amount = cts.cts_vcol;
 | 
			
		||||
          clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
          if (!*that || (*that == ';')) {
 | 
			
		||||
            amount = firsttry;
 | 
			
		||||
 
 | 
			
		||||
@@ -111,10 +111,13 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__clang__) || defined(__GNUC__)
 | 
			
		||||
# define EXPECT(cond, value) __builtin_expect((cond), (value))
 | 
			
		||||
# define UNREACHABLE __builtin_unreachable()
 | 
			
		||||
#elif defined(_MSVC_VER)
 | 
			
		||||
#elif defined(_MSC_VER)
 | 
			
		||||
# define EXPECT(cond, value) (cond)
 | 
			
		||||
# define UNREACHABLE __assume(false)
 | 
			
		||||
#else
 | 
			
		||||
# define EXPECT(cond, value) (cond)
 | 
			
		||||
# define UNREACHABLE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										170
									
								
								src/nvim/mbyte.c
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								src/nvim/mbyte.c
									
									
									
									
									
								
							@@ -528,6 +528,74 @@ int utf_ptr2cells(const char *p)
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Convert a UTF-8 byte sequence to a character number.
 | 
			
		||||
/// Doesn't handle ascii! only multibyte and illegal sequences.
 | 
			
		||||
///
 | 
			
		||||
/// @param[in]  p      String to convert.
 | 
			
		||||
/// @param[in]  len    Length of the character in bytes, 0 or 1 if illegal.
 | 
			
		||||
///
 | 
			
		||||
/// @return Unicode codepoint. A negative value When the sequence is illegal.
 | 
			
		||||
int32_t utf_ptr2CharInfo_impl(uint8_t const *p, uintptr_t const len)
 | 
			
		||||
  FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
 | 
			
		||||
{
 | 
			
		||||
// uint8_t is a reminder for clang to use smaller cmp
 | 
			
		||||
#define CHECK \
 | 
			
		||||
  do { \
 | 
			
		||||
    if (EXPECT((uint8_t)(cur & 0xC0U) != 0x80U, false)) { \
 | 
			
		||||
      return -1; \
 | 
			
		||||
    } \
 | 
			
		||||
  } while (0)
 | 
			
		||||
 | 
			
		||||
  static uint32_t const corrections[] = {
 | 
			
		||||
    (1U << 31),  // invalid - set invalid bits (safe to add as first 2 bytes
 | 
			
		||||
    (1U << 31),  // won't affect highest bit in normal ret)
 | 
			
		||||
    -(0x80U + (0xC0U << 6)),  // multibyte - subtract added UTF8 bits (1..10xxx and 10xxx)
 | 
			
		||||
    -(0x80U + (0x80U << 6) + (0xE0U << 12)),
 | 
			
		||||
    -(0x80U + (0x80U << 6) + (0x80U << 12) + (0xF0U << 18)),
 | 
			
		||||
    -(0x80U + (0x80U << 6) + (0x80U << 12) + (0x80U << 18) + (0xF8U << 24)),
 | 
			
		||||
    -(0x80U + (0x80U << 6) + (0x80U << 12) + (0x80U << 18) + (0x80U << 24)),  // + (0xFCU << 30)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // len is 0-6, but declared uintptr_t to avoid zeroing out upper bits
 | 
			
		||||
  uint32_t const corr = corrections[len];
 | 
			
		||||
  uint8_t cur;
 | 
			
		||||
 | 
			
		||||
  // reading second byte unconditionally, safe for invalid
 | 
			
		||||
  // as it cannot be the last byte, not safe for ascii
 | 
			
		||||
  uint32_t code_point = ((uint32_t)p[0] << 6) + (cur = p[1]);
 | 
			
		||||
  CHECK;
 | 
			
		||||
  if ((uint32_t)len < 3) {
 | 
			
		||||
    goto ret;  // len == 0, 1, 2
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  code_point = (code_point << 6) + (cur = p[2]);
 | 
			
		||||
  CHECK;
 | 
			
		||||
  if ((uint32_t)len == 3) {
 | 
			
		||||
    goto ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  code_point = (code_point << 6) + (cur = p[3]);
 | 
			
		||||
  CHECK;
 | 
			
		||||
  if ((uint32_t)len == 4) {
 | 
			
		||||
    goto ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  code_point = (code_point << 6) + (cur = p[4]);
 | 
			
		||||
  CHECK;
 | 
			
		||||
  if ((uint32_t)len == 5) {
 | 
			
		||||
    goto ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  code_point = (code_point << 6) + (cur = p[5]);
 | 
			
		||||
  CHECK;
 | 
			
		||||
  // len == 6
 | 
			
		||||
 | 
			
		||||
ret:
 | 
			
		||||
  return (int32_t)(code_point + corr);
 | 
			
		||||
 | 
			
		||||
#undef CHECK
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Like utf_ptr2cells(), but limit string length to "size".
 | 
			
		||||
/// For an empty string or truncated character returns 1.
 | 
			
		||||
int utf_ptr2cells_len(const char *p, int size)
 | 
			
		||||
@@ -597,45 +665,62 @@ size_t mb_string2cells_len(const char *str, size_t size)
 | 
			
		||||
///
 | 
			
		||||
/// @return Unicode codepoint or byte value.
 | 
			
		||||
int utf_ptr2char(const char *const p_in)
 | 
			
		||||
  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
 | 
			
		||||
  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
 | 
			
		||||
{
 | 
			
		||||
  uint8_t *p = (uint8_t *)p_in;
 | 
			
		||||
  if (p[0] < 0x80) {  // Be quick for ASCII.
 | 
			
		||||
    return p[0];
 | 
			
		||||
 | 
			
		||||
  uint32_t const v0 = p[0];
 | 
			
		||||
  if (EXPECT(v0 < 0x80U, true)) {  // Be quick for ASCII.
 | 
			
		||||
    return (int)v0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const uint8_t len = utf8len_tab_zero[p[0]];
 | 
			
		||||
  if (len > 1 && (p[1] & 0xc0) == 0x80) {
 | 
			
		||||
    if (len == 2) {
 | 
			
		||||
      return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
 | 
			
		||||
  const uint8_t len = utf8len_tab[v0];
 | 
			
		||||
  if (EXPECT(len < 2, false)) {
 | 
			
		||||
    return (int)v0;
 | 
			
		||||
  }
 | 
			
		||||
    if ((p[2] & 0xc0) == 0x80) {
 | 
			
		||||
      if (len == 3) {
 | 
			
		||||
        return (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
 | 
			
		||||
                + (p[2] & 0x3f));
 | 
			
		||||
      }
 | 
			
		||||
      if ((p[3] & 0xc0) == 0x80) {
 | 
			
		||||
        if (len == 4) {
 | 
			
		||||
          return (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
 | 
			
		||||
                  + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f));
 | 
			
		||||
        }
 | 
			
		||||
        if ((p[4] & 0xc0) == 0x80) {
 | 
			
		||||
          if (len == 5) {
 | 
			
		||||
            return (((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
 | 
			
		||||
                    + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
 | 
			
		||||
                    + (p[4] & 0x3f));
 | 
			
		||||
          }
 | 
			
		||||
          if ((p[5] & 0xc0) == 0x80 && len == 6) {
 | 
			
		||||
            return (((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
 | 
			
		||||
                    + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
 | 
			
		||||
                    + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // Illegal value: just return the first byte.
 | 
			
		||||
  return p[0];
 | 
			
		||||
 | 
			
		||||
#define CHECK(v) \
 | 
			
		||||
  do { \
 | 
			
		||||
    if (EXPECT((uint8_t)((v) & 0xC0U) != 0x80U, false)) { \
 | 
			
		||||
      return (int)v0; \
 | 
			
		||||
    } \
 | 
			
		||||
  } while (0)
 | 
			
		||||
#define LEN_RETURN(len_v, result) \
 | 
			
		||||
  do { \
 | 
			
		||||
    if (len == (len_v)) { \
 | 
			
		||||
      return (int)(result); \
 | 
			
		||||
    } \
 | 
			
		||||
  } while (0)
 | 
			
		||||
#define S(s) ((uint32_t)0x80U << (s))
 | 
			
		||||
 | 
			
		||||
  uint32_t const v1 = p[1];
 | 
			
		||||
  CHECK(v1);
 | 
			
		||||
  LEN_RETURN(2, (v0 << 6) + v1 - ((0xC0U << 6) + S(0)));
 | 
			
		||||
 | 
			
		||||
  uint32_t const v2 = p[2];
 | 
			
		||||
  CHECK(v2);
 | 
			
		||||
  LEN_RETURN(3, (v0 << 12) + (v1 << 6) + v2 - ((0xE0U << 12) + S(6) + S(0)));
 | 
			
		||||
 | 
			
		||||
  uint32_t const v3 = p[3];
 | 
			
		||||
  CHECK(v3);
 | 
			
		||||
  LEN_RETURN(4, (v0 << 18) + (v1 << 12) + (v2 << 6) + v3
 | 
			
		||||
             - ((0xF0U << 18) + S(12) + S(6) + S(0)));
 | 
			
		||||
 | 
			
		||||
  uint32_t const v4 = p[4];
 | 
			
		||||
  CHECK(v4);
 | 
			
		||||
  LEN_RETURN(5, (v0 << 24) + (v1 << 18) + (v2 << 12) + (v3 << 6) + v4
 | 
			
		||||
             - ((0xF8U << 24) + S(18) + S(12) + S(6) + S(0)));
 | 
			
		||||
 | 
			
		||||
  uint32_t const v5 = p[5];
 | 
			
		||||
  CHECK(v5);
 | 
			
		||||
  // len == 6
 | 
			
		||||
  return (int)((v0 << 30) + (v1 << 24) + (v2 << 18) + (v3 << 12) + (v4 << 6) + v5
 | 
			
		||||
               // - (0xFCU << 30)
 | 
			
		||||
               - (S(24) + S(18) + S(12) + S(6) + S(0)));
 | 
			
		||||
 | 
			
		||||
#undef S
 | 
			
		||||
#undef CHECK
 | 
			
		||||
#undef LEN_RETURN
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert a UTF-8 byte sequence to a wide character.
 | 
			
		||||
@@ -722,6 +807,16 @@ bool utf_composinglike(const char *p1, const char *p2)
 | 
			
		||||
  return arabic_combine(utf_ptr2char(p1), c2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check if the next character is a composing character when it
 | 
			
		||||
/// comes after the first. For Arabic sometimes "ab" is replaced with "c", which
 | 
			
		||||
/// behaves like a composing character.
 | 
			
		||||
/// returns false for negative values
 | 
			
		||||
bool utf_char_composinglike(int32_t const first, int32_t const next)
 | 
			
		||||
  FUNC_ATTR_PURE
 | 
			
		||||
{
 | 
			
		||||
  return utf_iscomposing(next) || arabic_combine(first, next);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the screen char at the beginning of a string
 | 
			
		||||
///
 | 
			
		||||
/// Caller is expected to check for things like unprintable chars etc
 | 
			
		||||
@@ -988,9 +1083,10 @@ int utf_char2bytes(const int c, char *const buf)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return true if "c" is a composing UTF-8 character.  This means it will be
 | 
			
		||||
// drawn on top of the preceding character.
 | 
			
		||||
// Based on code from Markus Kuhn.
 | 
			
		||||
/// Return true if "c" is a composing UTF-8 character.
 | 
			
		||||
/// This means it will be drawn on top of the preceding character.
 | 
			
		||||
/// Based on code from Markus Kuhn.
 | 
			
		||||
/// Returns false for negative values.
 | 
			
		||||
bool utf_iscomposing(int c)
 | 
			
		||||
{
 | 
			
		||||
  return intable(combining, ARRAY_SIZE(combining), c);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include "nvim/cmdexpand_defs.h"  // IWYU pragma: keep
 | 
			
		||||
#include "nvim/eval/typval_defs.h"  // IWYU pragma: keep
 | 
			
		||||
#include "nvim/macros_defs.h"
 | 
			
		||||
#include "nvim/mbyte_defs.h"  // IWYU pragma: keep
 | 
			
		||||
#include "nvim/types_defs.h"  // IWYU pragma: keep
 | 
			
		||||
 | 
			
		||||
@@ -13,6 +14,10 @@
 | 
			
		||||
# include "mbyte.h.generated.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  kInvalidByteCells = 4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Return byte length of character that starts with byte "b".
 | 
			
		||||
// Returns 1 for a single-byte character.
 | 
			
		||||
// MB_BYTE2LEN_CHECK() can be used to count a special key as one byte.
 | 
			
		||||
@@ -44,3 +49,72 @@ extern const uint8_t utf8len_tab[256];
 | 
			
		||||
// multi-byte characters if needed. Only use with "p" > "s" !
 | 
			
		||||
#define MB_PTR_BACK(s, p) \
 | 
			
		||||
  (p -= utf_head_off((char *)(s), (char *)(p) - 1) + 1)
 | 
			
		||||
 | 
			
		||||
static inline CharInfo utf_ptr2CharInfo(char const *p_in)
 | 
			
		||||
  REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
 | 
			
		||||
 | 
			
		||||
/// Convert a UTF-8 byte sequence to a Unicode code point.
 | 
			
		||||
/// Handles ascii, multibyte sequiences and illegal sequences.
 | 
			
		||||
///
 | 
			
		||||
/// @param[in]  p_in  String to convert.
 | 
			
		||||
///
 | 
			
		||||
/// @return information abouth the character. When the sequence is illegal,
 | 
			
		||||
/// 'value' is negative, 'len' is 1.
 | 
			
		||||
static inline CharInfo utf_ptr2CharInfo(char const *const p_in)
 | 
			
		||||
{
 | 
			
		||||
  uint8_t const *const p = (uint8_t const *)p_in;
 | 
			
		||||
  uint8_t const first = *p;
 | 
			
		||||
  if (first < 0x80) {
 | 
			
		||||
    return (CharInfo){ .value = first, .len = 1 };
 | 
			
		||||
  } else {
 | 
			
		||||
    int len = utf8len_tab[first];
 | 
			
		||||
    int32_t const code_point = utf_ptr2CharInfo_impl(p, (uintptr_t)len);
 | 
			
		||||
    if (code_point < 0) {
 | 
			
		||||
      len = 1;
 | 
			
		||||
    }
 | 
			
		||||
    return (CharInfo){ .value = code_point, .len = len };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline StrCharInfo utfc_next(StrCharInfo cur)
 | 
			
		||||
  REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE;
 | 
			
		||||
 | 
			
		||||
/// Return information about the next character.
 | 
			
		||||
/// Composing and combining characters are
 | 
			
		||||
/// considered a part of the current character.
 | 
			
		||||
///
 | 
			
		||||
/// @param[in] cur  Pointer to the current character. Must not point to NUL
 | 
			
		||||
/// @param[in] cur_char Decoded charater at 'cur'.
 | 
			
		||||
static inline StrCharInfo utfc_next(StrCharInfo cur)
 | 
			
		||||
{
 | 
			
		||||
  int32_t prev_code = cur.chr.value;
 | 
			
		||||
  uint8_t *next = (uint8_t *)(cur.ptr + cur.chr.len);
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (EXPECT(*next < 0x80U, true)) {
 | 
			
		||||
      return (StrCharInfo){
 | 
			
		||||
        .ptr = (char *)next,
 | 
			
		||||
        .chr = (CharInfo){ .value = *next, .len = 1 },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    uint8_t const next_len = utf8len_tab[*next];
 | 
			
		||||
    int32_t const next_code = utf_ptr2CharInfo_impl(next, (uintptr_t)next_len);
 | 
			
		||||
    if (!utf_char_composinglike(prev_code, next_code)) {
 | 
			
		||||
      return (StrCharInfo){
 | 
			
		||||
        .ptr = (char *)next,
 | 
			
		||||
        .chr = (CharInfo){ .value = next_code, .len = (next_code < 0 ? 1 : next_len) },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    prev_code = next_code;
 | 
			
		||||
    next += next_len;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline StrCharInfo utf_ptr2StrCharInfo(char *ptr)
 | 
			
		||||
  REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE;
 | 
			
		||||
 | 
			
		||||
static inline StrCharInfo utf_ptr2StrCharInfo(char *ptr)
 | 
			
		||||
{
 | 
			
		||||
  return (StrCharInfo){ .ptr = ptr, .chr = utf_ptr2CharInfo(ptr) };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "nvim/iconv_defs.h"
 | 
			
		||||
 | 
			
		||||
@@ -55,3 +56,13 @@ typedef struct {
 | 
			
		||||
  bool vc_fail;   ///< What to do with invalid characters: if true, fail,
 | 
			
		||||
                  ///< otherwise use '?'.
 | 
			
		||||
} vimconv_T;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int32_t value;  ///< code point
 | 
			
		||||
  int len;        ///< length in bytes
 | 
			
		||||
} CharInfo;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  char *ptr;     ///< pointer to the first byte of the character
 | 
			
		||||
  CharInfo chr;  ///< the character
 | 
			
		||||
} StrCharInfo;
 | 
			
		||||
 
 | 
			
		||||
@@ -1755,22 +1755,23 @@ colnr_T vcol2col(win_T *wp, linenr_T lnum, colnr_T vcol, colnr_T *coladdp)
 | 
			
		||||
{
 | 
			
		||||
  // try to advance to the specified column
 | 
			
		||||
  char *line = ml_get_buf(wp->w_buffer, lnum);
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
 | 
			
		||||
  while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) {
 | 
			
		||||
    int size = win_lbr_chartabsize(&cts, NULL);
 | 
			
		||||
    if (cts.cts_vcol + size > vcol) {
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  CSType cstype = init_charsize_arg(&arg, wp, lnum, line);
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  int cur_vcol = 0;
 | 
			
		||||
  while (cur_vcol < vcol && *ci.ptr != NUL) {
 | 
			
		||||
    int next_vcol = cur_vcol + win_charsize(cstype, cur_vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
    if (next_vcol > vcol) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    cts.cts_vcol += size;
 | 
			
		||||
    MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
    cur_vcol = next_vcol;
 | 
			
		||||
    ci = utfc_next(ci);
 | 
			
		||||
  }
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
  if (coladdp != NULL) {
 | 
			
		||||
    *coladdp = vcol - cts.cts_vcol;
 | 
			
		||||
    *coladdp = vcol - cur_vcol;
 | 
			
		||||
  }
 | 
			
		||||
  return (colnr_T)(cts.cts_ptr - line);
 | 
			
		||||
  return (colnr_T)(ci.ptr - line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set UI mouse depending on current mode and 'mouse'.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										130
									
								
								src/nvim/ops.c
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								src/nvim/ops.c
									
									
									
									
									
								
							@@ -387,17 +387,18 @@ static void shift_block(oparg_T *oap, int amount)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(vim): is passing bd.textstart for start of the line OK?
 | 
			
		||||
    chartabsize_T cts;
 | 
			
		||||
    init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
 | 
			
		||||
                         bd.start_vcol, bd.textstart, bd.textstart);
 | 
			
		||||
    while (ascii_iswhite(*cts.cts_ptr)) {
 | 
			
		||||
      incr = lbr_chartabsize_adv(&cts);
 | 
			
		||||
    CharsizeArg arg;
 | 
			
		||||
    CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, bd.textstart);
 | 
			
		||||
    StrCharInfo ci = utf_ptr2StrCharInfo(bd.textstart);
 | 
			
		||||
    int vcol = bd.start_vcol;
 | 
			
		||||
    while (ascii_iswhite(ci.chr.value)) {
 | 
			
		||||
      incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
      ci = utfc_next(ci);
 | 
			
		||||
      total += incr;
 | 
			
		||||
      cts.cts_vcol += incr;
 | 
			
		||||
      vcol += incr;
 | 
			
		||||
    }
 | 
			
		||||
    bd.textstart = cts.cts_ptr;
 | 
			
		||||
    bd.start_vcol = cts.cts_vcol;
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
    bd.textstart = ci.ptr;
 | 
			
		||||
    bd.start_vcol = vcol;
 | 
			
		||||
 | 
			
		||||
    int tabs = 0;
 | 
			
		||||
    int spaces = 0;
 | 
			
		||||
@@ -448,16 +449,13 @@ static void shift_block(oparg_T *oap, int amount)
 | 
			
		||||
    // The character's column is in "bd.start_vcol".
 | 
			
		||||
    colnr_T non_white_col = bd.start_vcol;
 | 
			
		||||
 | 
			
		||||
    chartabsize_T cts;
 | 
			
		||||
    init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
 | 
			
		||||
                         non_white_col, bd.textstart, non_white);
 | 
			
		||||
    while (ascii_iswhite(*cts.cts_ptr)) {
 | 
			
		||||
      incr = lbr_chartabsize_adv(&cts);
 | 
			
		||||
      cts.cts_vcol += incr;
 | 
			
		||||
    CharsizeArg arg;
 | 
			
		||||
    CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, bd.textstart);
 | 
			
		||||
    while (ascii_iswhite(*non_white)) {
 | 
			
		||||
      incr = win_charsize(cstype, non_white_col, non_white, (uint8_t)(*non_white), &arg).width;
 | 
			
		||||
      non_white_col += incr;
 | 
			
		||||
      non_white++;
 | 
			
		||||
    }
 | 
			
		||||
    non_white_col = cts.cts_vcol;
 | 
			
		||||
    non_white = cts.cts_ptr;
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
    const colnr_T block_space_width = non_white_col - oap->start_vcol;
 | 
			
		||||
    // We will shift by "total" or "block_space_width", whichever is less.
 | 
			
		||||
@@ -478,19 +476,17 @@ static void shift_block(oparg_T *oap, int amount)
 | 
			
		||||
    if (bd.startspaces) {
 | 
			
		||||
      verbatim_copy_width -= bd.start_char_vcols;
 | 
			
		||||
    }
 | 
			
		||||
    init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width,
 | 
			
		||||
                         bd.textstart, verbatim_copy_end);
 | 
			
		||||
    while (cts.cts_vcol < destination_col) {
 | 
			
		||||
      incr = lbr_chartabsize(&cts);
 | 
			
		||||
      if (cts.cts_vcol + incr > destination_col) {
 | 
			
		||||
    cstype = init_charsize_arg(&arg, curwin, 0, bd.textstart);
 | 
			
		||||
    StrCharInfo ci = utf_ptr2StrCharInfo(verbatim_copy_end);
 | 
			
		||||
    while (verbatim_copy_width < destination_col) {
 | 
			
		||||
      incr = win_charsize(cstype, verbatim_copy_width, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
      if (verbatim_copy_width + incr > destination_col) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      cts.cts_vcol += incr;
 | 
			
		||||
      MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
      verbatim_copy_width += incr;
 | 
			
		||||
      ci = utfc_next(ci);
 | 
			
		||||
    }
 | 
			
		||||
    verbatim_copy_width = cts.cts_vcol;
 | 
			
		||||
    verbatim_copy_end = cts.cts_ptr;
 | 
			
		||||
    clear_chartabsize_arg(&cts);
 | 
			
		||||
    verbatim_copy_end = ci.ptr;
 | 
			
		||||
 | 
			
		||||
    // If "destination_col" is different from the width of the initial
 | 
			
		||||
    // part of the line that will be copied, it means we encountered a tab
 | 
			
		||||
@@ -3250,19 +3246,19 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
 | 
			
		||||
      }
 | 
			
		||||
      // get the old line and advance to the position to insert at
 | 
			
		||||
      char *oldp = get_cursor_line_ptr();
 | 
			
		||||
      size_t oldlen = strlen(oldp);
 | 
			
		||||
      chartabsize_T cts;
 | 
			
		||||
      init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, oldp, oldp);
 | 
			
		||||
 | 
			
		||||
      while (cts.cts_vcol < col && *cts.cts_ptr != NUL) {
 | 
			
		||||
        // Count a tab for what it's worth (if list mode not on)
 | 
			
		||||
        incr = lbr_chartabsize_adv(&cts);
 | 
			
		||||
        cts.cts_vcol += incr;
 | 
			
		||||
      CharsizeArg arg;
 | 
			
		||||
      CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, oldp);
 | 
			
		||||
      StrCharInfo ci = utf_ptr2StrCharInfo(oldp);
 | 
			
		||||
      vcol = 0;
 | 
			
		||||
      while (vcol < col && *ci.ptr != NUL) {
 | 
			
		||||
        incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
        vcol += incr;
 | 
			
		||||
        ci = utfc_next(ci);
 | 
			
		||||
      }
 | 
			
		||||
      vcol = cts.cts_vcol;
 | 
			
		||||
      char *ptr = cts.cts_ptr;
 | 
			
		||||
      size_t oldlen = (size_t)(ci.ptr - oldp) + strlen(ci.ptr);
 | 
			
		||||
      char *ptr = ci.ptr;
 | 
			
		||||
      bd.textcol = (colnr_T)(ptr - oldp);
 | 
			
		||||
      clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
      shortline = (vcol < col) || (vcol == col && !*ptr);
 | 
			
		||||
 | 
			
		||||
@@ -3286,16 +3282,15 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
 | 
			
		||||
      yanklen = (int)strlen(y_array[i]);
 | 
			
		||||
 | 
			
		||||
      if ((flags & PUT_BLOCK_INNER) == 0) {
 | 
			
		||||
        // calculate number of spaces required to fill right side of
 | 
			
		||||
        // block
 | 
			
		||||
        // calculate number of spaces required to fill right side of block
 | 
			
		||||
        spaces = y_width + 1;
 | 
			
		||||
        init_chartabsize_arg(&cts, curwin, 0, 0, y_array[i], y_array[i]);
 | 
			
		||||
        for (int j = 0; j < yanklen; j++) {
 | 
			
		||||
          spaces -= lbr_chartabsize(&cts);
 | 
			
		||||
          cts.cts_ptr++;
 | 
			
		||||
          cts.cts_vcol = 0;
 | 
			
		||||
 | 
			
		||||
        cstype = init_charsize_arg(&arg, curwin, 0, y_array[i]);
 | 
			
		||||
        ci = utf_ptr2StrCharInfo(y_array[i]);
 | 
			
		||||
        while (*ci.ptr != NUL) {
 | 
			
		||||
          spaces -= win_charsize(cstype, 0, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
          ci = utfc_next(ci);
 | 
			
		||||
        }
 | 
			
		||||
        clear_chartabsize_arg(&cts);
 | 
			
		||||
        if (spaces < 0) {
 | 
			
		||||
          spaces = 0;
 | 
			
		||||
        }
 | 
			
		||||
@@ -4228,25 +4223,25 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
 | 
			
		||||
  char *line = ml_get(lnum);
 | 
			
		||||
  char *prev_pstart = line;
 | 
			
		||||
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line);
 | 
			
		||||
  while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) {
 | 
			
		||||
    // Count a tab for what it's worth (if list mode not on)
 | 
			
		||||
    incr = lbr_chartabsize(&cts);
 | 
			
		||||
    cts.cts_vcol += incr;
 | 
			
		||||
    if (ascii_iswhite(*cts.cts_ptr)) {
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  CSType cstype = init_charsize_arg(&arg, curwin, lnum, line);
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  int vcol = bdp->start_vcol;
 | 
			
		||||
  while (vcol < oap->start_vcol && *ci.ptr != NUL) {
 | 
			
		||||
    incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
    vcol += incr;
 | 
			
		||||
    if (ascii_iswhite(ci.chr.value)) {
 | 
			
		||||
      bdp->pre_whitesp += incr;
 | 
			
		||||
      bdp->pre_whitesp_c++;
 | 
			
		||||
    } else {
 | 
			
		||||
      bdp->pre_whitesp = 0;
 | 
			
		||||
      bdp->pre_whitesp_c = 0;
 | 
			
		||||
    }
 | 
			
		||||
    prev_pstart = cts.cts_ptr;
 | 
			
		||||
    MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
    prev_pstart = ci.ptr;
 | 
			
		||||
    ci = utfc_next(ci);
 | 
			
		||||
  }
 | 
			
		||||
  bdp->start_vcol = cts.cts_vcol;
 | 
			
		||||
  char *pstart = cts.cts_ptr;
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
  bdp->start_vcol = vcol;
 | 
			
		||||
  char *pstart = ci.ptr;
 | 
			
		||||
 | 
			
		||||
  bdp->start_char_vcols = incr;
 | 
			
		||||
  if (bdp->start_vcol < oap->start_vcol) {      // line too short
 | 
			
		||||
@@ -4283,17 +4278,18 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, line, pend);
 | 
			
		||||
      cstype = init_charsize_arg(&arg, curwin, lnum, line);
 | 
			
		||||
      ci = utf_ptr2StrCharInfo(pend);
 | 
			
		||||
      vcol = bdp->end_vcol;
 | 
			
		||||
      char *prev_pend = pend;
 | 
			
		||||
      while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) {
 | 
			
		||||
        // Count a tab for what it's worth (if list mode not on)
 | 
			
		||||
        prev_pend = cts.cts_ptr;
 | 
			
		||||
        incr = lbr_chartabsize_adv(&cts);
 | 
			
		||||
        cts.cts_vcol += incr;
 | 
			
		||||
      while (vcol <= oap->end_vcol && *ci.ptr != NUL) {
 | 
			
		||||
        prev_pend = ci.ptr;
 | 
			
		||||
        incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
 | 
			
		||||
        vcol += incr;
 | 
			
		||||
        ci = utfc_next(ci);
 | 
			
		||||
      }
 | 
			
		||||
      bdp->end_vcol = cts.cts_vcol;
 | 
			
		||||
      pend = cts.cts_ptr;
 | 
			
		||||
      clear_chartabsize_arg(&cts);
 | 
			
		||||
      bdp->end_vcol = vcol;
 | 
			
		||||
      pend = ci.ptr;
 | 
			
		||||
 | 
			
		||||
      if (bdp->end_vcol <= oap->end_vcol
 | 
			
		||||
          && (!is_del
 | 
			
		||||
 
 | 
			
		||||
@@ -51,48 +51,21 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col)
 | 
			
		||||
  return ptr2cells(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return the number of characters the string 's' will take on the screen,
 | 
			
		||||
/// taking into account the size of a tab.
 | 
			
		||||
///
 | 
			
		||||
/// @param s
 | 
			
		||||
///
 | 
			
		||||
/// @return Number of characters the string will take on the screen.
 | 
			
		||||
int linetabsize_str(char *s)
 | 
			
		||||
{
 | 
			
		||||
  return linetabsize_col(0, s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Like linetabsize_str(), but "s" starts at column "startcol".
 | 
			
		||||
/// Like linetabsize_str(), but "s" starts at virtual column "startvcol".
 | 
			
		||||
///
 | 
			
		||||
/// @param startcol
 | 
			
		||||
/// @param s
 | 
			
		||||
///
 | 
			
		||||
/// @return Number of characters the string will take on the screen.
 | 
			
		||||
int linetabsize_col(int startcol, char *s)
 | 
			
		||||
int linetabsize_col(int startvcol, char *s)
 | 
			
		||||
{
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  init_chartabsize_arg(&cts, curwin, 0, startcol, s, s);
 | 
			
		||||
  while (*cts.cts_ptr != NUL) {
 | 
			
		||||
    cts.cts_vcol += lbr_chartabsize_adv(&cts);
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  CSType const cstype = init_charsize_arg(&arg, curwin, 0, s);
 | 
			
		||||
  if (cstype == kCharsizeFast) {
 | 
			
		||||
    return linesize_fast(&arg, startvcol, MAXCOL);
 | 
			
		||||
  } else {
 | 
			
		||||
    return linesize_regular(&arg, startvcol, MAXCOL);
 | 
			
		||||
  }
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
  return cts.cts_vcol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Like linetabsize_str(), but for a given window instead of the current one.
 | 
			
		||||
///
 | 
			
		||||
/// @param wp
 | 
			
		||||
/// @param line
 | 
			
		||||
/// @param len
 | 
			
		||||
///
 | 
			
		||||
/// @return Number of characters the string will take on the screen.
 | 
			
		||||
int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
 | 
			
		||||
{
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
 | 
			
		||||
  win_linetabsize_cts(&cts, len);
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
  return cts.cts_vcol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return the number of cells line "lnum" of window "wp" will take on the
 | 
			
		||||
@@ -102,127 +75,80 @@ int linetabsize(win_T *wp, linenr_T lnum)
 | 
			
		||||
  return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void win_linetabsize_cts(chartabsize_T *cts, colnr_T len)
 | 
			
		||||
{
 | 
			
		||||
  for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len);
 | 
			
		||||
       MB_PTR_ADV(cts->cts_ptr)) {
 | 
			
		||||
    cts->cts_vcol += win_lbr_chartabsize(cts, NULL);
 | 
			
		||||
  }
 | 
			
		||||
  // check for inline virtual text after the end of the line
 | 
			
		||||
  if (len == MAXCOL && cts->cts_has_virt_text && *cts->cts_ptr == NUL) {
 | 
			
		||||
    win_lbr_chartabsize(cts, NULL);
 | 
			
		||||
    cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Prepare the structure passed to chartabsize functions.
 | 
			
		||||
/// Prepare the structure passed to charsize functions.
 | 
			
		||||
///
 | 
			
		||||
/// "line" is the start of the line, "ptr" is the first relevant character.
 | 
			
		||||
/// "line" is the start of the line.
 | 
			
		||||
/// When "lnum" is zero do not use inline virtual text.
 | 
			
		||||
void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line,
 | 
			
		||||
                          char *ptr)
 | 
			
		||||
CSType init_charsize_arg(CharsizeArg *cts, win_T *wp, linenr_T lnum, char *line)
 | 
			
		||||
{
 | 
			
		||||
  cts->cts_win = wp;
 | 
			
		||||
  cts->cts_vcol = col;
 | 
			
		||||
  cts->cts_line = line;
 | 
			
		||||
  cts->cts_ptr = ptr;
 | 
			
		||||
  cts->cts_max_head_vcol = 0;
 | 
			
		||||
  cts->cts_cur_text_width_left = 0;
 | 
			
		||||
  cts->cts_cur_text_width_right = 0;
 | 
			
		||||
  cts->cts_has_virt_text = false;
 | 
			
		||||
  cts->cts_row = lnum - 1;
 | 
			
		||||
  cts->win = wp;
 | 
			
		||||
  cts->line = line;
 | 
			
		||||
  cts->max_head_vcol = 0;
 | 
			
		||||
  cts->cur_text_width_left = 0;
 | 
			
		||||
  cts->cur_text_width_right = 0;
 | 
			
		||||
  cts->virt_row = -1;
 | 
			
		||||
  cts->indent_width = INT_MIN;
 | 
			
		||||
  cts->use_tabstop = !wp->w_p_list || wp->w_p_lcs_chars.tab1;
 | 
			
		||||
 | 
			
		||||
  if (cts->cts_row >= 0 && wp->w_buffer->b_virt_text_inline > 0) {
 | 
			
		||||
    marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter);
 | 
			
		||||
    MTKey mark = marktree_itr_current(cts->cts_iter);
 | 
			
		||||
    if (mark.pos.row == cts->cts_row) {
 | 
			
		||||
      cts->cts_has_virt_text = true;
 | 
			
		||||
  if (lnum > 0 && wp->w_buffer->b_virt_text_inline > 0) {
 | 
			
		||||
    marktree_itr_get(wp->w_buffer->b_marktree, lnum - 1, 0, cts->iter);
 | 
			
		||||
    MTKey mark = marktree_itr_current(cts->iter);
 | 
			
		||||
    if (mark.pos.row == lnum - 1) {
 | 
			
		||||
      cts->virt_row = lnum - 1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (cts->virt_row >= 0
 | 
			
		||||
      || (wp->w_p_wrap && (wp->w_p_lbr || wp->w_p_bri || *get_showbreak_value(wp) != NUL))) {
 | 
			
		||||
    return kCharsizeRegular;
 | 
			
		||||
  } else {
 | 
			
		||||
    return kCharsizeFast;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Free any allocated item in "cts".
 | 
			
		||||
void clear_chartabsize_arg(chartabsize_T *cts)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// like win_chartabsize(), but also check for line breaks on the screen
 | 
			
		||||
///
 | 
			
		||||
/// @param cts
 | 
			
		||||
///
 | 
			
		||||
/// @return The number of characters taken up on the screen.
 | 
			
		||||
int lbr_chartabsize(chartabsize_T *cts)
 | 
			
		||||
{
 | 
			
		||||
  if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
 | 
			
		||||
      && !curwin->w_p_bri && !cts->cts_has_virt_text) {
 | 
			
		||||
    if (curwin->w_p_wrap) {
 | 
			
		||||
      return win_nolbr_chartabsize(cts, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol);
 | 
			
		||||
  }
 | 
			
		||||
  return win_lbr_chartabsize(cts, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Call lbr_chartabsize() and advance the pointer.
 | 
			
		||||
///
 | 
			
		||||
/// @param cts
 | 
			
		||||
///
 | 
			
		||||
/// @return The number of characters take up on the screen.
 | 
			
		||||
int lbr_chartabsize_adv(chartabsize_T *cts)
 | 
			
		||||
{
 | 
			
		||||
  int retval = lbr_chartabsize(cts);
 | 
			
		||||
  MB_PTR_ADV(cts->cts_ptr);
 | 
			
		||||
  return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the number of characters taken up on the screen indicated by "cts".
 | 
			
		||||
/// "cts->cts_cur_text_width_left" and "cts->cts_cur_text_width_right" are set
 | 
			
		||||
/// Get the number of characters taken up on the screen for the given cts and position.
 | 
			
		||||
/// "cts->cur_text_width_left" and "cts->cur_text_width_right" are set
 | 
			
		||||
/// to the extra size for inline virtual text.
 | 
			
		||||
/// This function is used very often, keep it fast!!!!
 | 
			
		||||
///
 | 
			
		||||
/// If "headp" not NULL, set "*headp" to the size of 'showbreak'/'breakindent'
 | 
			
		||||
/// included in the return value.
 | 
			
		||||
/// When "cts->cts_max_head_vcol" is positive, only count in "*headp" the size
 | 
			
		||||
/// of 'showbreak'/'breakindent' before "cts->cts_max_head_vcol".
 | 
			
		||||
/// When "cts->cts_max_head_vcol" is negative, only count in "*headp" the size
 | 
			
		||||
/// When "cts->max_head_vcol" is positive, only count in "head" the size
 | 
			
		||||
/// of 'showbreak'/'breakindent' before "cts->max_head_vcol".
 | 
			
		||||
/// When "cts->max_head_vcol" is negative, only count in "head" the size
 | 
			
		||||
/// of 'showbreak'/'breakindent' before where cursor should be placed.
 | 
			
		||||
///
 | 
			
		||||
/// Warning: "*headp" may not be set if it's 0, init to 0 before calling.
 | 
			
		||||
int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
CharSize charsize_regular(CharsizeArg *cts, char *const cur, colnr_T const vcol,
 | 
			
		||||
                          int32_t const cur_char)
 | 
			
		||||
{
 | 
			
		||||
  win_T *wp = cts->cts_win;
 | 
			
		||||
  char *line = cts->cts_line;  // start of the line
 | 
			
		||||
  char *s = cts->cts_ptr;
 | 
			
		||||
  colnr_T vcol = cts->cts_vcol;
 | 
			
		||||
  cts->cur_text_width_left = 0;
 | 
			
		||||
  cts->cur_text_width_right = 0;
 | 
			
		||||
 | 
			
		||||
  win_T *wp = cts->win;
 | 
			
		||||
  buf_T *buf = wp->w_buffer;
 | 
			
		||||
  char *line = cts->line;
 | 
			
		||||
  bool const use_tabstop = cur_char == TAB && cts->use_tabstop;
 | 
			
		||||
  int mb_added = 0;
 | 
			
		||||
 | 
			
		||||
  cts->cts_cur_text_width_left = 0;
 | 
			
		||||
  cts->cts_cur_text_width_right = 0;
 | 
			
		||||
 | 
			
		||||
  // No 'linebreak', 'showbreak' and 'breakindent': return quickly.
 | 
			
		||||
  if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL
 | 
			
		||||
      && !cts->cts_has_virt_text) {
 | 
			
		||||
    if (wp->w_p_wrap) {
 | 
			
		||||
      return win_nolbr_chartabsize(cts, headp);
 | 
			
		||||
    }
 | 
			
		||||
    return win_chartabsize(wp, s, vcol);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool has_lcs_eol = wp->w_p_list && wp->w_p_lcs_chars.eol != NUL;
 | 
			
		||||
 | 
			
		||||
  // First get normal size, without 'linebreak' or inline virtual text
 | 
			
		||||
  int size = win_chartabsize(wp, s, vcol);
 | 
			
		||||
  if (*s == NUL && !has_lcs_eol) {
 | 
			
		||||
    size = 0;  // NUL is not displayed
 | 
			
		||||
  int size;
 | 
			
		||||
  int is_doublewidth = false;
 | 
			
		||||
  if (use_tabstop) {
 | 
			
		||||
    size = tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array);
 | 
			
		||||
  } else if (*cur == NUL && !has_lcs_eol) {
 | 
			
		||||
    size = 0;
 | 
			
		||||
  } else if (cur_char < 0) {
 | 
			
		||||
    size = kInvalidByteCells;
 | 
			
		||||
  } else {
 | 
			
		||||
    size = char2cells(cur_char);
 | 
			
		||||
    is_doublewidth = size == 2 && cur_char > 0x80;
 | 
			
		||||
  }
 | 
			
		||||
  bool is_doublewidth = size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1;
 | 
			
		||||
 | 
			
		||||
  if (cts->cts_has_virt_text) {
 | 
			
		||||
  if (cts->virt_row >= 0) {
 | 
			
		||||
    int tab_size = size;
 | 
			
		||||
    int col = (int)(s - line);
 | 
			
		||||
    int col = (int)(cur - line);
 | 
			
		||||
    while (true) {
 | 
			
		||||
      MTKey mark = marktree_itr_current(cts->cts_iter);
 | 
			
		||||
      if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
 | 
			
		||||
      MTKey mark = marktree_itr_current(cts->iter);
 | 
			
		||||
      if (mark.pos.row != cts->virt_row || mark.pos.col > col) {
 | 
			
		||||
        break;
 | 
			
		||||
      } else if (mark.pos.col == col) {
 | 
			
		||||
        if (!mt_end(mark) && mark.flags & (MT_FLAG_DECOR_VIRT_TEXT_INLINE)) {
 | 
			
		||||
@@ -231,15 +157,15 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
          while (vt) {
 | 
			
		||||
            if (!(vt->flags & kVTIsLines) && vt->pos == kVPosInline) {
 | 
			
		||||
              if (mt_right(mark)) {
 | 
			
		||||
                cts->cts_cur_text_width_right += vt->width;
 | 
			
		||||
                cts->cur_text_width_right += vt->width;
 | 
			
		||||
              } else {
 | 
			
		||||
                cts->cts_cur_text_width_left += vt->width;
 | 
			
		||||
                cts->cur_text_width_left += vt->width;
 | 
			
		||||
              }
 | 
			
		||||
              size += vt->width;
 | 
			
		||||
              if (*s == TAB) {
 | 
			
		||||
              if (use_tabstop) {
 | 
			
		||||
                // tab size changes because of the inserted text
 | 
			
		||||
                size -= tab_size;
 | 
			
		||||
                tab_size = win_chartabsize(wp, s, vcol + size);
 | 
			
		||||
                tab_size = tabstop_padding(vcol + size, buf->b_p_ts, buf->b_p_vts_array);
 | 
			
		||||
                size += tab_size;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
@@ -247,7 +173,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter);
 | 
			
		||||
      marktree_itr_next(wp->w_buffer->b_marktree, cts->iter);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -257,16 +183,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
    mb_added = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char *const sbr = get_showbreak_value(wp);
 | 
			
		||||
 | 
			
		||||
  // May have to add something for 'breakindent' and/or 'showbreak'
 | 
			
		||||
  // string at the start of a screen line.
 | 
			
		||||
  int head = mb_added;
 | 
			
		||||
  char *const sbr = get_showbreak_value(wp);
 | 
			
		||||
  // When "size" is 0, no new screen line is started.
 | 
			
		||||
  if (size > 0 && wp->w_p_wrap && (*sbr != NUL || wp->w_p_bri)) {
 | 
			
		||||
    int col_off_prev = win_col_off(wp);
 | 
			
		||||
    int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp);
 | 
			
		||||
    colnr_T wcol = vcol + col_off_prev;
 | 
			
		||||
    colnr_T max_head_vcol = cts->cts_max_head_vcol;
 | 
			
		||||
    colnr_T max_head_vcol = cts->max_head_vcol;
 | 
			
		||||
    int added = 0;
 | 
			
		||||
 | 
			
		||||
    // cells taken by 'showbreak'/'breakindent' before current char
 | 
			
		||||
@@ -277,12 +204,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
      if (wcol >= width2 && width2 > 0) {
 | 
			
		||||
        wcol %= width2;
 | 
			
		||||
      }
 | 
			
		||||
      head_prev = cts->indent_width;
 | 
			
		||||
      if (head_prev == INT_MIN) {
 | 
			
		||||
        head_prev = 0;
 | 
			
		||||
        if (*sbr != NUL) {
 | 
			
		||||
          head_prev += vim_strsize(sbr);
 | 
			
		||||
        }
 | 
			
		||||
        if (wp->w_p_bri) {
 | 
			
		||||
          head_prev += get_breakindent_win(wp, line);
 | 
			
		||||
        }
 | 
			
		||||
        cts->indent_width = head_prev;
 | 
			
		||||
      }
 | 
			
		||||
      if (wcol < head_prev) {
 | 
			
		||||
        head_prev -= wcol;
 | 
			
		||||
        wcol += head_prev;
 | 
			
		||||
@@ -298,13 +230,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
 | 
			
		||||
    if (wcol + size > wp->w_width) {
 | 
			
		||||
      // cells taken by 'showbreak'/'breakindent' halfway current char
 | 
			
		||||
      int head_mid = 0;
 | 
			
		||||
      int head_mid = cts->indent_width;
 | 
			
		||||
      if (head_mid == INT_MIN) {
 | 
			
		||||
        head_mid = 0;
 | 
			
		||||
        if (*sbr != NUL) {
 | 
			
		||||
          head_mid += vim_strsize(sbr);
 | 
			
		||||
        }
 | 
			
		||||
        if (wp->w_p_bri) {
 | 
			
		||||
          head_mid += get_breakindent_win(wp, line);
 | 
			
		||||
        }
 | 
			
		||||
        cts->indent_width = head_mid;
 | 
			
		||||
      }
 | 
			
		||||
      if (head_mid > 0 && wcol + size > wp->w_width_inner) {
 | 
			
		||||
        // Calculate effective window width.
 | 
			
		||||
        int prev_rem = wp->w_width_inner - wcol;
 | 
			
		||||
@@ -323,7 +259,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
          head += (max_head_vcol - (vcol + head_prev + prev_rem)
 | 
			
		||||
                   + width2 - 1) / width2 * head_mid;
 | 
			
		||||
        } else if (max_head_vcol < 0) {
 | 
			
		||||
          int off = virt_text_cursor_off(cts, *s == NUL);
 | 
			
		||||
          int off = virt_text_cursor_off(cts, *cur == NUL);
 | 
			
		||||
          if (off >= prev_rem) {
 | 
			
		||||
            if (size > off) {
 | 
			
		||||
              head += (1 + (off - prev_rem) / width) * head_mid;
 | 
			
		||||
@@ -338,19 +274,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
    size += added;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (headp != NULL) {
 | 
			
		||||
    *headp = head;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char *s = cur;
 | 
			
		||||
  colnr_T vcol_start = 0;  // start from where to consider linebreak
 | 
			
		||||
  // If 'linebreak' set check at a blank before a non-blank if the line
 | 
			
		||||
  // needs a break here
 | 
			
		||||
  if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) {
 | 
			
		||||
    char *t = cts->cts_line;
 | 
			
		||||
    char *t = cts->line;
 | 
			
		||||
    while (vim_isbreak((uint8_t)t[0])) {
 | 
			
		||||
      t++;
 | 
			
		||||
    }
 | 
			
		||||
    vcol_start = (colnr_T)(t - cts->cts_line);
 | 
			
		||||
    vcol_start = (colnr_T)(t - cts->line);
 | 
			
		||||
  }
 | 
			
		||||
  if (wp->w_p_lbr && vcol_start <= vcol
 | 
			
		||||
      && vim_isbreak((uint8_t)s[0])
 | 
			
		||||
@@ -388,39 +321,50 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return size;
 | 
			
		||||
  return (CharSize){ .width = size, .head = head };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and
 | 
			
		||||
/// 'wrap' is on.  This means we need to check for a double-byte character that
 | 
			
		||||
/// doesn't fit at the end of the screen line.
 | 
			
		||||
/// Like charsize_regular(), except it doesn't handle virtual text,
 | 
			
		||||
/// linebreak, breakindent and showbreak. Handles normal characters, tabs and wrapping.
 | 
			
		||||
/// This function is always inlined.
 | 
			
		||||
///
 | 
			
		||||
/// @param cts
 | 
			
		||||
/// @param headp
 | 
			
		||||
///
 | 
			
		||||
/// @return The number of characters take up on the screen.
 | 
			
		||||
static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp)
 | 
			
		||||
/// @see charsize_regular
 | 
			
		||||
/// @see charsize_fast
 | 
			
		||||
static inline CharSize charsize_fast_impl(win_T *const wp, bool use_tabstop, colnr_T const vcol,
 | 
			
		||||
                                          int32_t const cur_char)
 | 
			
		||||
  FUNC_ATTR_PURE FUNC_ATTR_ALWAYS_INLINE
 | 
			
		||||
{
 | 
			
		||||
  win_T *wp = cts->cts_win;
 | 
			
		||||
  char *s = cts->cts_ptr;
 | 
			
		||||
  colnr_T col = cts->cts_vcol;
 | 
			
		||||
  // A tab gets expanded, depending on the current column
 | 
			
		||||
  if (cur_char == TAB && use_tabstop) {
 | 
			
		||||
    return (CharSize){
 | 
			
		||||
      .width = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
 | 
			
		||||
                               wp->w_buffer->b_p_vts_array)
 | 
			
		||||
    };
 | 
			
		||||
  } else {
 | 
			
		||||
    int width;
 | 
			
		||||
    if (cur_char < 0) {
 | 
			
		||||
      width = kInvalidByteCells;
 | 
			
		||||
    } else {
 | 
			
		||||
      width = char2cells(cur_char);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
 | 
			
		||||
    return tabstop_padding(col,
 | 
			
		||||
                           wp->w_buffer->b_p_ts,
 | 
			
		||||
                           wp->w_buffer->b_p_vts_array);
 | 
			
		||||
    // If a double-width char doesn't fit at the end of a line, it wraps to the next line,
 | 
			
		||||
    // and the last column displays a '>'.
 | 
			
		||||
    if (width == 2 && cur_char >= 0x80 && wp->w_p_wrap && in_win_border(wp, vcol)) {
 | 
			
		||||
      return (CharSize){ .width = 3, .head = 1 };
 | 
			
		||||
    } else {
 | 
			
		||||
      return (CharSize){ .width = width };
 | 
			
		||||
    }
 | 
			
		||||
  int n = ptr2cells(s);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  // Add one cell for a double-width character in the last column of the
 | 
			
		||||
  // window, displayed with a ">".
 | 
			
		||||
  if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) {
 | 
			
		||||
    if (headp != NULL) {
 | 
			
		||||
      *headp = 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 3;
 | 
			
		||||
  }
 | 
			
		||||
  return n;
 | 
			
		||||
/// Like charsize_regular(), except it doesn't handle virtual text,
 | 
			
		||||
/// linebreak, breakindent and showbreak. Handles normal characters, tabs and wrapping.
 | 
			
		||||
/// Can be used if CSType is kCharsizeFast.
 | 
			
		||||
CharSize charsize_fast(CharsizeArg *cts, colnr_T const vcol, int32_t const cur_char)
 | 
			
		||||
  FUNC_ATTR_PURE
 | 
			
		||||
{
 | 
			
		||||
  return charsize_fast_impl(cts->win, cts->use_tabstop, vcol, cur_char);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check that virtual column "vcol" is in the rightmost column of window "wp".
 | 
			
		||||
@@ -451,19 +395,63 @@ static bool in_win_border(win_T *wp, colnr_T vcol)
 | 
			
		||||
  return (vcol - width1) % width2 == width2 - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Calculate virtual column until the given 'len'.
 | 
			
		||||
///
 | 
			
		||||
/// @param arg  Argument to charsize functions.
 | 
			
		||||
/// @param vcol Starting virtual column.
 | 
			
		||||
/// @param len  First byte of the end character, or MAXCOL.
 | 
			
		||||
///
 | 
			
		||||
/// @return virtual column before the character at 'len',
 | 
			
		||||
/// or full size of the line if 'len' is MAXCOL.
 | 
			
		||||
int linesize_regular(CharsizeArg *const arg, int vcol, colnr_T const len)
 | 
			
		||||
{
 | 
			
		||||
  char *const line = arg->line;
 | 
			
		||||
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  while (ci.ptr - line < len && *ci.ptr != NUL) {
 | 
			
		||||
    vcol += charsize_regular(arg, ci.ptr, vcol, ci.chr.value).width;
 | 
			
		||||
    ci = utfc_next(ci);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check for inline virtual text after the end of the line.
 | 
			
		||||
  if (len == MAXCOL && arg->virt_row >= 0) {
 | 
			
		||||
    (void)charsize_regular(arg, ci.ptr, vcol, ci.chr.value);
 | 
			
		||||
    vcol += arg->cur_text_width_left + arg->cur_text_width_right;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return vcol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Like win_linesize_regular, but can be used when CStype is kCharsizeFast.
 | 
			
		||||
///
 | 
			
		||||
/// @see win_linesize_regular
 | 
			
		||||
int linesize_fast(CharsizeArg const *const arg, int vcol, colnr_T const len)
 | 
			
		||||
{
 | 
			
		||||
  win_T *const wp = arg->win;
 | 
			
		||||
  bool const use_tabstop = arg->use_tabstop;
 | 
			
		||||
 | 
			
		||||
  char *const line = arg->line;
 | 
			
		||||
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  while (ci.ptr - line < len && *ci.ptr != NUL) {
 | 
			
		||||
    vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width;
 | 
			
		||||
    ci = utfc_next(ci);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return vcol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get how many virtual columns inline virtual text should offset the cursor.
 | 
			
		||||
///
 | 
			
		||||
/// @param cts     should contain information stored by win_lbr_chartabsize()
 | 
			
		||||
///                about widths of left and right gravity virtual text
 | 
			
		||||
/// @param on_NUL  whether this is the end of the line
 | 
			
		||||
static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL)
 | 
			
		||||
static int virt_text_cursor_off(CharsizeArg *cts, bool on_NUL)
 | 
			
		||||
{
 | 
			
		||||
  int off = 0;
 | 
			
		||||
  if (!on_NUL || !(State & MODE_NORMAL)) {
 | 
			
		||||
    off += cts->cts_cur_text_width_left;
 | 
			
		||||
    off += cts->cur_text_width_left;
 | 
			
		||||
  }
 | 
			
		||||
  if (!on_NUL && (State & MODE_NORMAL)) {
 | 
			
		||||
    off += cts->cts_cur_text_width_right;
 | 
			
		||||
    off += cts->cur_text_width_right;
 | 
			
		||||
  }
 | 
			
		||||
  return off;
 | 
			
		||||
}
 | 
			
		||||
@@ -482,115 +470,53 @@ static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL)
 | 
			
		||||
/// @param end
 | 
			
		||||
void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end)
 | 
			
		||||
{
 | 
			
		||||
  char *ptr;     // points to current char
 | 
			
		||||
  char *posptr;  // points to char at pos->col
 | 
			
		||||
  int incr;
 | 
			
		||||
  int head;
 | 
			
		||||
  colnr_T *vts = wp->w_buffer->b_p_vts_array;
 | 
			
		||||
  int ts = (int)wp->w_buffer->b_p_ts;
 | 
			
		||||
  char *const line = ml_get_buf(wp->w_buffer, pos->lnum);  // start of the line
 | 
			
		||||
  int const end_col = pos->col;
 | 
			
		||||
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  bool on_NUL = false;
 | 
			
		||||
  CSType const cstype = init_charsize_arg(&arg, wp, pos->lnum, line);
 | 
			
		||||
  arg.max_head_vcol = -1;
 | 
			
		||||
 | 
			
		||||
  colnr_T vcol = 0;
 | 
			
		||||
  char *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum);  // start of the line
 | 
			
		||||
 | 
			
		||||
  if (pos->col == MAXCOL) {
 | 
			
		||||
    // continue until the NUL
 | 
			
		||||
    posptr = NULL;
 | 
			
		||||
  } else {
 | 
			
		||||
    // In a few cases the position can be beyond the end of the line.
 | 
			
		||||
    for (colnr_T i = 0; i < pos->col; i++) {
 | 
			
		||||
      if (ptr[i] == NUL) {
 | 
			
		||||
        pos->col = i;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    posptr = ptr + pos->col;
 | 
			
		||||
    posptr -= utf_head_off(line, posptr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  bool on_NUL = false;
 | 
			
		||||
  init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
 | 
			
		||||
  cts.cts_max_head_vcol = -1;
 | 
			
		||||
 | 
			
		||||
  // This function is used very often, do some speed optimizations.
 | 
			
		||||
  // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set
 | 
			
		||||
  // and there are no virtual text use a simple loop.
 | 
			
		||||
  // Also use this when 'list' is set but tabs take their normal size.
 | 
			
		||||
  if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL))
 | 
			
		||||
      && !wp->w_p_lbr
 | 
			
		||||
      && *get_showbreak_value(wp) == NUL
 | 
			
		||||
      && !wp->w_p_bri
 | 
			
		||||
      && !cts.cts_has_virt_text) {
 | 
			
		||||
  CharSize char_size;
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  if (cstype == kCharsizeFast) {
 | 
			
		||||
    bool const use_tabstop = arg.use_tabstop;
 | 
			
		||||
    while (true) {
 | 
			
		||||
      head = 0;
 | 
			
		||||
      int c = (uint8_t)(*ptr);
 | 
			
		||||
 | 
			
		||||
      // make sure we don't go past the end of the line
 | 
			
		||||
      if (c == NUL) {
 | 
			
		||||
        // NUL at end of line only takes one column
 | 
			
		||||
        incr = 1;
 | 
			
		||||
      if (*ci.ptr == NUL) {
 | 
			
		||||
        // if cursor is at NUL, it is treated like 1 cell char
 | 
			
		||||
        char_size = (CharSize){ .width = 1 };
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // A tab gets expanded, depending on the current column
 | 
			
		||||
      if (c == TAB) {
 | 
			
		||||
        incr = tabstop_padding(vcol, ts, vts);
 | 
			
		||||
      } else {
 | 
			
		||||
        // For utf-8, if the byte is >= 0x80, need to look at
 | 
			
		||||
        // further bytes to find the cell width.
 | 
			
		||||
        if (c >= 0x80) {
 | 
			
		||||
          incr = utf_ptr2cells(ptr);
 | 
			
		||||
        } else {
 | 
			
		||||
          incr = byte2cells(c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If a double-cell char doesn't fit at the end of a line
 | 
			
		||||
        // it wraps to the next line, it's like this char is three
 | 
			
		||||
        // cells wide.
 | 
			
		||||
        if ((incr == 2)
 | 
			
		||||
            && wp->w_p_wrap
 | 
			
		||||
            && (MB_BYTE2LEN((uint8_t)(*ptr)) > 1)
 | 
			
		||||
            && in_win_border(wp, vcol)) {
 | 
			
		||||
          incr++;
 | 
			
		||||
          head = 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ((posptr != NULL) && (ptr >= posptr)) {
 | 
			
		||||
        // character at pos->col
 | 
			
		||||
      char_size = charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value);
 | 
			
		||||
      StrCharInfo const next = utfc_next(ci);
 | 
			
		||||
      if (next.ptr - line > end_col) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      vcol += incr;
 | 
			
		||||
      MB_PTR_ADV(ptr);
 | 
			
		||||
      ci = next;
 | 
			
		||||
      vcol += char_size.width;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    while (true) {
 | 
			
		||||
      // A tab gets expanded, depending on the current column
 | 
			
		||||
      // Other things also take up space.
 | 
			
		||||
      head = 0;
 | 
			
		||||
      incr = win_lbr_chartabsize(&cts, &head);
 | 
			
		||||
 | 
			
		||||
      // make sure we don't go past the end of the line
 | 
			
		||||
      if (*cts.cts_ptr == NUL) {
 | 
			
		||||
        // NUL at end of line only takes one column, unless there is virtual text
 | 
			
		||||
        incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right);
 | 
			
		||||
      char_size = charsize_regular(&arg, ci.ptr, vcol, ci.chr.value);
 | 
			
		||||
      if (*ci.ptr == NUL) {
 | 
			
		||||
        // if cursor is at NUL, it is treated like 1 cell char unless there is virtual text
 | 
			
		||||
        char_size.width = MAX(1, arg.cur_text_width_left + arg.cur_text_width_right);
 | 
			
		||||
        on_NUL = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ((posptr != NULL) && (cts.cts_ptr >= posptr)) {
 | 
			
		||||
        // character at pos->col
 | 
			
		||||
      StrCharInfo const next = utfc_next(ci);
 | 
			
		||||
      if (next.ptr - line > end_col) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      ci = next;
 | 
			
		||||
      vcol += char_size.width;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
      cts.cts_vcol += incr;
 | 
			
		||||
      MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
    }
 | 
			
		||||
    vcol = cts.cts_vcol;
 | 
			
		||||
    ptr = cts.cts_ptr;
 | 
			
		||||
  }
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
  int head = char_size.head;
 | 
			
		||||
  int incr = char_size.width;
 | 
			
		||||
 | 
			
		||||
  if (start != NULL) {
 | 
			
		||||
    *start = vcol + head;
 | 
			
		||||
@@ -601,7 +527,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (cursor != NULL) {
 | 
			
		||||
    if ((*ptr == TAB)
 | 
			
		||||
    if (ci.chr.value == TAB
 | 
			
		||||
        && (State & MODE_NORMAL)
 | 
			
		||||
        && !wp->w_p_list
 | 
			
		||||
        && !virtual_active()
 | 
			
		||||
@@ -609,7 +535,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
 | 
			
		||||
      // cursor at end
 | 
			
		||||
      *cursor = vcol + incr - 1;
 | 
			
		||||
    } else {
 | 
			
		||||
      vcol += virt_text_cursor_off(&cts, on_NUL);
 | 
			
		||||
      vcol += virt_text_cursor_off(&arg, on_NUL);
 | 
			
		||||
      // cursor at start
 | 
			
		||||
      *cursor = vcol + head;
 | 
			
		||||
    }
 | 
			
		||||
@@ -798,14 +724,18 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight)
 | 
			
		||||
int plines_win_nofold(win_T *wp, linenr_T lnum)
 | 
			
		||||
{
 | 
			
		||||
  char *s = ml_get_buf(wp->w_buffer, lnum);
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  init_chartabsize_arg(&cts, wp, lnum, 0, s, s);
 | 
			
		||||
  if (*s == NUL && !cts.cts_has_virt_text) {
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  CSType const cstype = init_charsize_arg(&arg, wp, lnum, s);
 | 
			
		||||
  if (*s == NUL && arg.virt_row < 0) {
 | 
			
		||||
    return 1;  // be quick for an empty line
 | 
			
		||||
  }
 | 
			
		||||
  win_linetabsize_cts(&cts, (colnr_T)MAXCOL);
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
  int64_t col = cts.cts_vcol;
 | 
			
		||||
 | 
			
		||||
  int64_t col;
 | 
			
		||||
  if (cstype == kCharsizeFast) {
 | 
			
		||||
    col = linesize_fast(&arg, 0, MAXCOL);
 | 
			
		||||
  } else {
 | 
			
		||||
    col = linesize_regular(&arg, 0, MAXCOL);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If list mode is on, then the '$' at the end of the line may take up one
 | 
			
		||||
  // extra column.
 | 
			
		||||
@@ -844,26 +774,33 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
 | 
			
		||||
 | 
			
		||||
  char *line = ml_get_buf(wp->w_buffer, lnum);
 | 
			
		||||
 | 
			
		||||
  colnr_T col = 0;
 | 
			
		||||
  chartabsize_T cts;
 | 
			
		||||
  CharsizeArg cts;
 | 
			
		||||
  CSType const cstype = init_charsize_arg(&cts, wp, lnum, line);
 | 
			
		||||
 | 
			
		||||
  init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
 | 
			
		||||
  while (*cts.cts_ptr != NUL && --column >= 0) {
 | 
			
		||||
    cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
 | 
			
		||||
    MB_PTR_ADV(cts.cts_ptr);
 | 
			
		||||
  colnr_T vcol = 0;
 | 
			
		||||
  StrCharInfo ci = utf_ptr2StrCharInfo(line);
 | 
			
		||||
  if (cstype == kCharsizeFast) {
 | 
			
		||||
    bool const use_tabstop = cts.use_tabstop;
 | 
			
		||||
    while (*ci.ptr != NUL && --column >= 0) {
 | 
			
		||||
      vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width;
 | 
			
		||||
      ci = utfc_next(ci);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    while (*ci.ptr != NUL && --column >= 0) {
 | 
			
		||||
      vcol += charsize_regular(&cts, ci.ptr, vcol, ci.chr.value).width;
 | 
			
		||||
      ci = utfc_next(ci);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not
 | 
			
		||||
  // If current char is a TAB, and the TAB is not displayed as ^I, and we're not
 | 
			
		||||
  // in MODE_INSERT state, then col must be adjusted so that it represents the
 | 
			
		||||
  // last screen position of the TAB.  This only fixes an error when the TAB
 | 
			
		||||
  // wraps from one screen line to the next (when 'columns' is not a multiple
 | 
			
		||||
  // of 'ts') -- webb.
 | 
			
		||||
  col = cts.cts_vcol;
 | 
			
		||||
  if (*cts.cts_ptr == TAB && (State & MODE_NORMAL)
 | 
			
		||||
      && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
 | 
			
		||||
    col += win_lbr_chartabsize(&cts, NULL) - 1;
 | 
			
		||||
  colnr_T col = vcol;
 | 
			
		||||
  if (ci.chr.value == TAB && (State & MODE_NORMAL) && cts.use_tabstop) {
 | 
			
		||||
    col += win_charsize(cstype, col, ci.ptr, ci.chr.value, &cts).width - 1;
 | 
			
		||||
  }
 | 
			
		||||
  clear_chartabsize_arg(&cts);
 | 
			
		||||
 | 
			
		||||
  // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
 | 
			
		||||
  int width = wp->w_width_inner - win_col_off(wp);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,25 +4,96 @@
 | 
			
		||||
#include <stdint.h>  // IWYU pragma: keep
 | 
			
		||||
 | 
			
		||||
#include "nvim/marktree_defs.h"
 | 
			
		||||
#include "nvim/mbyte_defs.h"
 | 
			
		||||
#include "nvim/pos_defs.h"  // IWYU pragma: keep
 | 
			
		||||
#include "nvim/types_defs.h"
 | 
			
		||||
 | 
			
		||||
/// Argument for lbr_chartabsize().
 | 
			
		||||
typedef bool CSType;
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  kCharsizeRegular,
 | 
			
		||||
  kCharsizeFast,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Argument for char size functions.
 | 
			
		||||
typedef struct {
 | 
			
		||||
  win_T *cts_win;
 | 
			
		||||
  char *cts_line;                ///< start of the line
 | 
			
		||||
  char *cts_ptr;                 ///< current position in line
 | 
			
		||||
  int cts_row;
 | 
			
		||||
  win_T *win;
 | 
			
		||||
  char *line;                ///< start of the line
 | 
			
		||||
 | 
			
		||||
  bool cts_has_virt_text;        ///< true if if there is inline virtual text
 | 
			
		||||
  int cts_cur_text_width_left;   ///< width of virtual text left of cursor
 | 
			
		||||
  int cts_cur_text_width_right;  ///< width of virtual text right of cursor
 | 
			
		||||
  MarkTreeIter cts_iter[1];
 | 
			
		||||
  bool use_tabstop;          ///< use tabstop for tab insted of counting it as ^I
 | 
			
		||||
  int indent_width;          ///< width of showbreak and breakindent on wrapped lines
 | 
			
		||||
                             ///  INT_MIN if not yet calculated
 | 
			
		||||
 | 
			
		||||
  int cts_vcol;                  ///< virtual column at current position
 | 
			
		||||
  int cts_max_head_vcol;         ///< see win_lbr_chartabsize()
 | 
			
		||||
} chartabsize_T;
 | 
			
		||||
  int virt_row;              ///< line number, -1 if no virtual text
 | 
			
		||||
  int cur_text_width_left;   ///< width of virtual text left of cursor
 | 
			
		||||
  int cur_text_width_right;  ///< width of virtual text right of cursor
 | 
			
		||||
 | 
			
		||||
  int max_head_vcol;         ///< see charsize_regular()
 | 
			
		||||
  MarkTreeIter iter[1];
 | 
			
		||||
} CharsizeArg;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int width;
 | 
			
		||||
  int head;  // size of breakindent etc. before the character (included in width)
 | 
			
		||||
} CharSize;
 | 
			
		||||
 | 
			
		||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
			
		||||
# include "plines.h.generated.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline CharSize win_charsize(CSType cstype, int vcol, char *ptr, int32_t chr,
 | 
			
		||||
                                    CharsizeArg *arg)
 | 
			
		||||
  REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
 | 
			
		||||
 | 
			
		||||
/// Get the number of cells taken up on the screen by the given character at vcol.
 | 
			
		||||
/// "arg->cur_text_width_left" and "arg->cur_text_width_right" are set
 | 
			
		||||
/// to the extra size for inline virtual text.
 | 
			
		||||
///
 | 
			
		||||
/// When "arg->max_head_vcol" is positive, only count in "head" the size
 | 
			
		||||
/// of 'showbreak'/'breakindent' before "arg->max_head_vcol".
 | 
			
		||||
/// When "arg->max_head_vcol" is negative, only count in "head" the size
 | 
			
		||||
/// of 'showbreak'/'breakindent' before where cursor should be placed.
 | 
			
		||||
static inline CharSize win_charsize(CSType cstype, int vcol, char *ptr, int32_t chr,
 | 
			
		||||
                                    CharsizeArg *arg)
 | 
			
		||||
{
 | 
			
		||||
  if (cstype == kCharsizeFast) {
 | 
			
		||||
    return charsize_fast(arg, vcol, chr);
 | 
			
		||||
  } else {
 | 
			
		||||
    return charsize_regular(arg, ptr, vcol, chr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int linetabsize_str(char *s)
 | 
			
		||||
  REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
 | 
			
		||||
 | 
			
		||||
/// Return the number of characters the string 's' will take on the screen,
 | 
			
		||||
/// taking into account the size of a tab.
 | 
			
		||||
///
 | 
			
		||||
/// @param s
 | 
			
		||||
///
 | 
			
		||||
/// @return Number of characters the string will take on the screen.
 | 
			
		||||
static inline int linetabsize_str(char *s)
 | 
			
		||||
{
 | 
			
		||||
  return linetabsize_col(0, s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
 | 
			
		||||
  REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
 | 
			
		||||
 | 
			
		||||
/// Like linetabsize_str(), but for a given window instead of the current one.
 | 
			
		||||
///
 | 
			
		||||
/// @param wp
 | 
			
		||||
/// @param line
 | 
			
		||||
/// @param len
 | 
			
		||||
///
 | 
			
		||||
/// @return Number of characters the string will take on the screen.
 | 
			
		||||
static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
 | 
			
		||||
{
 | 
			
		||||
  CharsizeArg arg;
 | 
			
		||||
  CSType const cstype = init_charsize_arg(&arg, wp, lnum, line);
 | 
			
		||||
  if (cstype == kCharsizeFast) {
 | 
			
		||||
    return linesize_fast(&arg, 0, len);
 | 
			
		||||
  } else {
 | 
			
		||||
    return linesize_regular(&arg, 0, len);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										359
									
								
								test/benchmark/screenpos_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								test/benchmark/screenpos_spec.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,359 @@
 | 
			
		||||
local helpers = require('test.functional.helpers')(after_each)
 | 
			
		||||
local api = helpers.api
 | 
			
		||||
local Screen = require('test.functional.ui.screen')
 | 
			
		||||
 | 
			
		||||
local function rand_utf8(count, seed)
 | 
			
		||||
  math.randomseed(seed)
 | 
			
		||||
  local symbols = { 'i', 'À', 'Ⴀ', '𐀀' }
 | 
			
		||||
  local s = ''
 | 
			
		||||
  for _ = 1, count do
 | 
			
		||||
    s = s .. symbols[math.random(1, #symbols)]
 | 
			
		||||
  end
 | 
			
		||||
  return s
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local width, height = 100, 50
 | 
			
		||||
 | 
			
		||||
local benchmark_chars = {
 | 
			
		||||
  {
 | 
			
		||||
    name = 'ascii',
 | 
			
		||||
    line = function(count, _)
 | 
			
		||||
      return ('i'):rep(count)
 | 
			
		||||
    end,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name = '2 byte utf-8',
 | 
			
		||||
    line = function(count, _)
 | 
			
		||||
      return ('À'):rep(count)
 | 
			
		||||
    end,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name = 'random 1-4 byte utf-8',
 | 
			
		||||
    line = function(count, i)
 | 
			
		||||
      return rand_utf8(count, 123456 + i)
 | 
			
		||||
    end,
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local benchmark_lines = {
 | 
			
		||||
  {
 | 
			
		||||
    name = 'long line',
 | 
			
		||||
    lines = function(line)
 | 
			
		||||
      return { line(width * height - 1, 1) }
 | 
			
		||||
    end,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name = 'multiple lines',
 | 
			
		||||
    lines = function(line)
 | 
			
		||||
      local lines = {}
 | 
			
		||||
      for i = 1, height - 1 do
 | 
			
		||||
        table.insert(lines, line(width, i))
 | 
			
		||||
      end
 | 
			
		||||
      table.insert(lines, line(width - 1, height))
 | 
			
		||||
      return lines
 | 
			
		||||
    end,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name = 'multiple wrapped lines',
 | 
			
		||||
    lines = function(line)
 | 
			
		||||
      local lines = {}
 | 
			
		||||
      local count = math.floor(height / 2)
 | 
			
		||||
      for i = 1, count - 1 do
 | 
			
		||||
        table.insert(lines, line(width * 2, i))
 | 
			
		||||
      end
 | 
			
		||||
      table.insert(lines, line(width * 2 - 1, count))
 | 
			
		||||
      return lines
 | 
			
		||||
    end,
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local N = 10000
 | 
			
		||||
 | 
			
		||||
local function benchmark(lines, expected_value)
 | 
			
		||||
  local lnum = #lines
 | 
			
		||||
 | 
			
		||||
  local results = helpers.exec_lua(
 | 
			
		||||
    [==[
 | 
			
		||||
    local N, lnum = ...
 | 
			
		||||
 | 
			
		||||
    local values = {}
 | 
			
		||||
    local stats = {} -- time in ns
 | 
			
		||||
    for i = 1, N do
 | 
			
		||||
      local tic = vim.uv.hrtime()
 | 
			
		||||
      local result = vim.fn.screenpos(0, lnum, 999999)
 | 
			
		||||
      local toc = vim.uv.hrtime()
 | 
			
		||||
      table.insert(values, result)
 | 
			
		||||
      table.insert(stats, toc - tic)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    return { values, stats }
 | 
			
		||||
  ]==],
 | 
			
		||||
    N,
 | 
			
		||||
    lnum
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  for _, value in ipairs(results[1]) do
 | 
			
		||||
    helpers.eq(expected_value, value)
 | 
			
		||||
  end
 | 
			
		||||
  local stats = results[2]
 | 
			
		||||
  table.sort(stats)
 | 
			
		||||
 | 
			
		||||
  local us = 1 / 1000
 | 
			
		||||
  print(
 | 
			
		||||
    string.format(
 | 
			
		||||
      'min, 25%%, median, 75%%, max:\n\t%0.2fus,\t%0.2fus,\t%0.2fus,\t%0.2fus,\t%0.2fus',
 | 
			
		||||
      stats[1] * us,
 | 
			
		||||
      stats[1 + math.floor(#stats * 0.25)] * us,
 | 
			
		||||
      stats[1 + math.floor(#stats * 0.5)] * us,
 | 
			
		||||
      stats[1 + math.floor(#stats * 0.75)] * us,
 | 
			
		||||
      stats[#stats] * us
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function benchmarks(benchmark_results)
 | 
			
		||||
  describe('screenpos() perf', function()
 | 
			
		||||
    before_each(helpers.clear)
 | 
			
		||||
 | 
			
		||||
    -- no breakindent
 | 
			
		||||
    for li, lines_type in ipairs(benchmark_lines) do
 | 
			
		||||
      for ci, chars_type in ipairs(benchmark_chars) do
 | 
			
		||||
        local name = 'for ' .. lines_type.name .. ', ' .. chars_type.name .. ', nobreakindent'
 | 
			
		||||
 | 
			
		||||
        local lines = lines_type.lines(chars_type.line)
 | 
			
		||||
        local result = benchmark_results[li][ci]
 | 
			
		||||
 | 
			
		||||
        it(name, function()
 | 
			
		||||
          local screen = Screen.new(width, height + 1)
 | 
			
		||||
          screen:attach()
 | 
			
		||||
          api.nvim_buf_set_lines(0, 0, 1, false, lines)
 | 
			
		||||
          -- for smaller screen expect (last line always different, first line same as others)
 | 
			
		||||
          helpers.feed('G$')
 | 
			
		||||
          screen:expect(result.screen)
 | 
			
		||||
          benchmark(lines, result.value)
 | 
			
		||||
        end)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- breakindent
 | 
			
		||||
    for li, lines_type in ipairs(benchmark_lines) do
 | 
			
		||||
      for ci, chars_type in ipairs(benchmark_chars) do
 | 
			
		||||
        local name = 'for ' .. lines_type.name .. ', ' .. chars_type.name .. ', breakindent'
 | 
			
		||||
 | 
			
		||||
        local lines = lines_type.lines(chars_type.line)
 | 
			
		||||
        local result = benchmark_results[li][ci]
 | 
			
		||||
 | 
			
		||||
        it(name, function()
 | 
			
		||||
          local screen = Screen.new(width, height + 1)
 | 
			
		||||
          screen:attach()
 | 
			
		||||
          api.nvim_buf_set_lines(0, 0, 1, false, lines)
 | 
			
		||||
          helpers.command('set breakindent')
 | 
			
		||||
          -- for smaller screen expect (last line always different, first line same as others)
 | 
			
		||||
          helpers.feed('G$')
 | 
			
		||||
          screen:expect(result.screen)
 | 
			
		||||
          benchmark(lines, result.value)
 | 
			
		||||
        end)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local ascii_results = {
 | 
			
		||||
  screen = [=[
 | 
			
		||||
  iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii|*49
 | 
			
		||||
  iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii^i |
 | 
			
		||||
                                                                                                      |
 | 
			
		||||
  ]=],
 | 
			
		||||
  value = { col = 100, curscol = 100, endcol = 100, row = 50 },
 | 
			
		||||
}
 | 
			
		||||
local two_byte_results = {
 | 
			
		||||
  screen = [=[
 | 
			
		||||
  ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ|*49
 | 
			
		||||
  ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ^À |
 | 
			
		||||
                                                                                                      |
 | 
			
		||||
  ]=],
 | 
			
		||||
  value = { col = 100, curscol = 100, endcol = 100, row = 50 },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
benchmarks({
 | 
			
		||||
  {
 | 
			
		||||
    ascii_results,
 | 
			
		||||
    two_byte_results,
 | 
			
		||||
    { -- random
 | 
			
		||||
      screen = [=[
 | 
			
		||||
  Ⴀ𐀀ii𐀀ႠÀ𐀀i𐀀𐀀iÀÀÀiÀ𐀀Ⴀ𐀀ႠiiႠ𐀀iii𐀀ÀႠÀႠႠÀiiÀႠÀiႠi𐀀ÀÀ𐀀𐀀Ⴀ𐀀𐀀Ⴀ𐀀iÀႠ𐀀i𐀀ÀÀႠiiÀ𐀀Ⴀ𐀀À𐀀ii𐀀ÀÀ𐀀ÀႠႠ𐀀𐀀𐀀𐀀ii𐀀ႠႠiႠႠ𐀀ႠÀ𐀀ÀÀiiႠ|
 | 
			
		||||
  𐀀𐀀ÀႠÀ𐀀𐀀iႠÀ𐀀𐀀i𐀀𐀀𐀀𐀀ႠႠႠiiiiÀÀ𐀀ႠÀႠÀi𐀀i𐀀ႠႠÀiÀႠi𐀀𐀀À𐀀iÀ𐀀ÀÀÀ𐀀𐀀iÀႠ𐀀𐀀Ⴀ𐀀𐀀ÀiႠiiႠႠÀÀÀÀi𐀀ÀÀႠ𐀀Àiii𐀀iႠႠႠi𐀀ႠÀi𐀀iႠ𐀀ႠiႠ|
 | 
			
		||||
  ႠႠႠÀi𐀀ÀiႠÀiႠiႠ𐀀ÀiiiÀiiÀÀႠ𐀀À𐀀ÀႠÀÀÀႠ𐀀iႠႠ𐀀iႠÀ𐀀À𐀀ႠiÀÀÀÀiiÀiÀiÀႠiÀi𐀀iÀÀႠ𐀀𐀀ႠÀ𐀀iႠ𐀀i𐀀i𐀀ÀႠi𐀀iႠÀÀiiႠ𐀀𐀀ႠiÀiÀiiÀ|
 | 
			
		||||
  iႠ𐀀iiႠÀႠ𐀀iÀÀÀႠÀiÀ𐀀𐀀𐀀ÀႠiႠiÀiႠiႠ𐀀𐀀ႠႠi𐀀ႠႠ𐀀𐀀Ⴀ𐀀ÀiiႠÀႠႠ𐀀iÀiÀÀ𐀀𐀀𐀀ႠႠ𐀀iii𐀀À𐀀Ⴀ𐀀iႠii𐀀i𐀀i𐀀ÀiÀi𐀀Ⴀ𐀀𐀀i𐀀iÀÀiႠ𐀀Ⴀ𐀀ÀÀÀÀ|
 | 
			
		||||
  𐀀ÀႠiÀႠÀÀÀႠ𐀀À𐀀𐀀𐀀i𐀀Ài𐀀𐀀iႠ𐀀𐀀ÀႠ𐀀𐀀À𐀀𐀀ႠÀÀႠႠႠiႠÀÀii𐀀Ⴀii𐀀Ⴀi𐀀Ài𐀀Ⴀ𐀀𐀀𐀀𐀀iÀ𐀀ႠÀiÀ𐀀ႠႠi𐀀i𐀀ႠႠÀႠ𐀀ႠÀႠ𐀀ÀႠiiႠ𐀀𐀀Ⴀ𐀀Ⴀ𐀀𐀀À𐀀𐀀Ài|
 | 
			
		||||
  iÀÀiÀÀiႠÀiiႠiÀiiiÀႠÀ𐀀Ài𐀀iiÀÀÀiႠႠiiႠ𐀀iÀ𐀀𐀀𐀀ႠÀÀ𐀀iiႠ𐀀Ⴀ𐀀ႠÀႠ𐀀Ài𐀀i𐀀ÀiÀႠႠ𐀀ÀႠiiႠÀ𐀀𐀀Ⴀii𐀀𐀀i𐀀𐀀ÀÀiÀ𐀀i𐀀𐀀i𐀀ÀiiiႠÀÀÀ|
 | 
			
		||||
  ÀÀႠႠÀႠႠÀiiiÀႠÀႠႠi𐀀ႠÀ𐀀iႠÀ𐀀ႠÀiiÀÀÀÀႠ𐀀𐀀À𐀀iiÀ𐀀𐀀iႠႠ𐀀iiႠႠ𐀀ÀÀÀ𐀀Ⴀ𐀀À𐀀ÀႠi𐀀ႠÀႠÀႠႠiÀ𐀀iÀÀႠ𐀀ႠႠ𐀀𐀀iႠႠႠ𐀀𐀀À𐀀iÀÀi𐀀iႠႠ𐀀𐀀|
 | 
			
		||||
  ÀÀiiႠÀ𐀀Ài𐀀iiÀ𐀀iÀႠÀÀi𐀀𐀀ႠႠ𐀀iiႠႠiÀ𐀀iႠÀÀiႠ𐀀ÀÀiiႠႠiÀÀ𐀀À𐀀iႠÀÀႠiႠ𐀀iႠ𐀀ÀiiႠÀÀႠÀÀiÀ𐀀𐀀ႠႠÀႠÀÀႠ𐀀À𐀀ႠႠi𐀀À𐀀𐀀ႠႠ𐀀iႠi𐀀Ⴀ|
 | 
			
		||||
  ÀÀႠ𐀀𐀀ÀႠႠႠÀႠiႠႠႠÀiiiႠiiiÀi𐀀𐀀iႠႠiiÀiႠႠႠÀÀiii𐀀ÀiႠÀi𐀀i𐀀𐀀ÀiÀiÀÀiÀ𐀀iÀÀÀiÀႠႠÀiÀ𐀀À𐀀iႠÀႠ𐀀ii𐀀𐀀iႠiÀiႠ𐀀Ⴀ𐀀Ⴀ𐀀𐀀ႠiÀႠ|
 | 
			
		||||
  ÀႠii𐀀i𐀀iÀÀÀÀÀ𐀀ÀiÀ𐀀𐀀ÀႠiiiiiÀႠ𐀀ÀÀÀႠi𐀀iႠiႠiÀႠႠ𐀀𐀀ii𐀀𐀀iÀÀÀႠiiÀ𐀀iÀiÀ𐀀Ⴀ𐀀Ⴀ𐀀i𐀀iÀႠiÀ𐀀i𐀀ႠiႠÀ𐀀iႠÀႠiႠiÀÀÀÀ𐀀Ⴀ𐀀iႠႠÀ|
 | 
			
		||||
  𐀀Ⴀ𐀀ႠÀ𐀀iႠiÀi𐀀i𐀀𐀀𐀀ႠÀႠiÀႠႠ𐀀ÀiÀiႠi𐀀ÀႠi𐀀𐀀ÀÀi𐀀À𐀀ÀiiႠ𐀀iႠÀÀÀiii𐀀ႠiႠ𐀀𐀀𐀀ႠႠiႠ𐀀𐀀iiႠiiiii𐀀𐀀𐀀𐀀𐀀ÀÀÀÀi𐀀𐀀ႠႠ𐀀iÀ𐀀ÀiiÀii|
 | 
			
		||||
  Ⴀ𐀀𐀀iႠi𐀀Ⴀ𐀀ႠႠiÀÀႠi𐀀ÀiႠႠႠ𐀀ÀႠiႠႠiႠႠÀ𐀀ႠiiiÀ𐀀𐀀𐀀ÀÀiႠi𐀀ႠÀ𐀀ÀႠÀi𐀀ႠiÀႠiiႠႠÀႠiÀÀႠÀÀÀÀ𐀀iÀႠႠiႠႠ𐀀ÀÀÀႠiÀÀiÀႠ𐀀iÀႠiiႠ𐀀|
 | 
			
		||||
  iÀႠi𐀀ÀႠÀÀÀiÀÀႠ𐀀À𐀀𐀀𐀀ႠiiiႠiiႠ𐀀À𐀀iii𐀀À𐀀ႠႠi𐀀iiႠÀ𐀀𐀀i𐀀ႠÀ𐀀𐀀i𐀀ÀiႠÀÀiÀÀi𐀀𐀀Ⴀ𐀀À𐀀i𐀀iÀႠႠႠ𐀀ÀÀÀႠÀÀႠႠႠႠႠi𐀀𐀀iiႠÀi𐀀𐀀ႠႠ|
 | 
			
		||||
  iiႠႠႠ𐀀ÀႠ𐀀Àiii𐀀ÀÀii𐀀À𐀀iiႠÀႠiiႠ𐀀Ⴀ𐀀ÀႠ𐀀iÀiႠiiÀÀiÀÀiႠiႠႠiႠ𐀀Ⴀi𐀀𐀀ÀÀÀÀiႠii𐀀ႠÀ𐀀Ⴀ𐀀i𐀀ႠiႠႠ𐀀i𐀀ႠiႠႠ𐀀ÀiႠ𐀀iÀi𐀀𐀀ÀÀ𐀀𐀀i|
 | 
			
		||||
  iÀÀi𐀀ÀÀÀ𐀀ÀႠ𐀀ÀÀÀ𐀀𐀀𐀀iiႠÀÀႠ𐀀ႠÀ𐀀iiiÀÀiiႠ𐀀𐀀ႠiiႠiÀiႠႠÀ𐀀ႠiႠÀ𐀀i𐀀𐀀𐀀ႠÀ𐀀Ⴀ𐀀ÀÀÀႠႠ𐀀𐀀À𐀀iiႠႠႠႠႠÀ𐀀𐀀iႠႠiiÀiႠÀiÀႠiÀ𐀀À𐀀i|
 | 
			
		||||
  ÀႠႠ𐀀ÀႠi𐀀i𐀀ႠÀÀÀiiiႠ𐀀ႠÀႠÀÀႠ𐀀ÀÀiÀiÀÀႠÀႠiႠiÀ𐀀iႠÀ𐀀𐀀𐀀ÀႠႠ𐀀ႠႠ𐀀ÀႠႠႠii𐀀ÀÀÀÀiÀi𐀀ႠႠÀiႠiႠÀႠ𐀀ÀiႠႠiÀႠi𐀀𐀀𐀀ÀÀiiÀÀ𐀀𐀀ÀႠ|
 | 
			
		||||
  À𐀀ÀÀÀÀ𐀀ÀÀ𐀀i𐀀ႠÀႠiႠÀÀ𐀀ႠiÀÀiႠÀႠႠÀ𐀀iÀ𐀀i𐀀𐀀ႠÀÀÀႠႠiÀiiiiႠႠi𐀀ÀႠi𐀀i𐀀i𐀀𐀀𐀀ႠÀႠiiiÀi𐀀ÀႠi𐀀iiÀÀႠÀÀႠiiÀ𐀀À𐀀ÀÀ𐀀ႠÀႠÀ𐀀iႠ|
 | 
			
		||||
  Ⴀ𐀀ÀiႠ𐀀iiÀ𐀀À𐀀𐀀À𐀀ÀÀiiiiiiႠႠiiÀi𐀀iÀ𐀀ÀÀiႠ𐀀ÀႠÀÀÀiÀႠႠ𐀀À𐀀𐀀iÀ𐀀𐀀ii𐀀ႠÀ𐀀ii𐀀𐀀iႠÀ𐀀ÀÀÀiÀii𐀀iႠ𐀀ႠႠ𐀀ÀႠiiiႠႠႠi𐀀ÀÀiÀ𐀀𐀀À|
 | 
			
		||||
  ႠiÀ𐀀ႠÀ𐀀iÀႠi𐀀ÀiiiÀÀ𐀀ÀႠ𐀀ႠႠႠÀႠ𐀀ႠÀiiÀi𐀀i𐀀𐀀iႠÀႠiႠiiÀÀ𐀀𐀀𐀀Ài𐀀𐀀iiႠiႠÀႠ𐀀iiႠÀi𐀀ႠႠႠi𐀀ÀÀiÀႠ𐀀𐀀ÀiႠÀiiÀÀႠ𐀀ႠiiÀႠႠÀiÀ|
 | 
			
		||||
  i𐀀𐀀Ài𐀀𐀀ႠÀiÀÀႠႠi𐀀𐀀ÀÀႠÀÀႠႠႠႠ𐀀ÀiႠiiႠႠÀiiiႠႠÀ𐀀Ⴀii𐀀i𐀀ႠÀ𐀀ÀÀႠႠiÀiÀÀiႠÀiiiiÀႠiiÀႠႠႠÀÀi𐀀À𐀀ႠႠ𐀀Ⴀi𐀀ႠႠ𐀀𐀀𐀀iႠÀÀႠ𐀀ႠႠ|
 | 
			
		||||
  iÀÀႠႠႠiiÀii𐀀𐀀ႠÀiii𐀀ႠÀ𐀀ႠiiÀ𐀀iႠ𐀀ÀႠ𐀀Ⴀ𐀀𐀀Ⴀi𐀀ÀiႠÀႠ𐀀ÀÀi𐀀ႠiÀÀÀÀႠÀႠiႠiႠႠ𐀀𐀀ÀႠÀႠ𐀀𐀀ÀÀ𐀀ႠႠ𐀀𐀀i𐀀Ⴀii𐀀iii𐀀Ⴀi𐀀Ⴀ𐀀Ⴀi𐀀𐀀ÀႠi|
 | 
			
		||||
  À𐀀𐀀ÀႠÀiiiÀiiÀႠi𐀀iiiႠiiႠÀÀ𐀀ႠÀ𐀀ႠႠႠ𐀀i𐀀ÀÀiႠiiiႠiiÀÀiႠ𐀀À𐀀i𐀀Ⴀ𐀀ႠÀiႠႠ𐀀ÀႠႠ𐀀𐀀ÀiႠÀÀႠiiႠi𐀀i𐀀ႠÀÀiႠ𐀀Ⴀ𐀀iႠႠ𐀀ÀÀÀiÀÀii|
 | 
			
		||||
  ႠÀ𐀀𐀀ႠiႠÀ𐀀ÀÀÀႠႠÀႠ𐀀ÀiႠii𐀀𐀀ÀႠ𐀀iÀÀiiÀiÀÀ𐀀iႠÀiÀ𐀀𐀀À𐀀Ⴀ𐀀𐀀iiႠႠ𐀀ႠÀႠÀiÀiÀ𐀀iႠႠiÀi𐀀Ⴀ𐀀iiiÀႠ𐀀ÀႠiÀiiiႠiiÀႠÀiႠiÀÀiႠÀi|
 | 
			
		||||
  𐀀𐀀ÀÀi𐀀À𐀀𐀀ÀႠႠႠ𐀀À𐀀𐀀À𐀀ÀiႠႠ𐀀𐀀ႠÀiႠiႠÀ𐀀ÀiႠiiii𐀀iႠiႠÀ𐀀ÀႠ𐀀𐀀iÀႠiÀi𐀀𐀀ႠÀiႠiÀႠႠ𐀀iႠ𐀀ÀႠÀii𐀀ႠÀႠii𐀀Ⴀ𐀀ÀÀi𐀀ÀÀiÀiÀႠ𐀀ÀႠႠ|
 | 
			
		||||
  𐀀iiii𐀀iႠÀÀႠiÀႠ𐀀ႠiÀiႠi𐀀iႠႠႠÀႠႠÀႠ𐀀iÀႠႠii𐀀iii𐀀À𐀀ႠႠႠÀiÀÀ𐀀iiiiႠႠႠi𐀀𐀀iiႠ𐀀ႠiႠ𐀀i𐀀𐀀ÀÀႠႠ𐀀iႠiႠÀÀÀႠႠÀ𐀀iÀႠႠႠႠÀ𐀀iÀ|
 | 
			
		||||
  ÀÀÀ𐀀ÀÀႠiႠÀiiÀÀ𐀀Ⴀ𐀀Àii𐀀ႠႠ𐀀iÀÀ𐀀𐀀Ⴀ𐀀iႠ𐀀iႠÀႠႠ𐀀ÀÀ𐀀𐀀À𐀀ÀႠiႠÀiÀ𐀀iÀi𐀀ႠiႠ𐀀i𐀀iiႠႠiႠÀÀ𐀀Ⴀ𐀀ႠႠ𐀀𐀀𐀀ႠႠiႠႠ𐀀ႠႠႠ𐀀ÀiႠႠi𐀀iÀÀÀ|
 | 
			
		||||
  ÀÀႠiiÀÀÀႠÀ𐀀𐀀iÀÀÀiÀi𐀀iႠiႠ𐀀iÀÀÀ𐀀𐀀𐀀iÀiÀÀiÀÀi𐀀i𐀀𐀀ႠÀ𐀀ii𐀀𐀀Ⴀ𐀀À𐀀iÀÀႠ𐀀iÀÀÀႠÀÀ𐀀𐀀iႠ𐀀ႠiÀႠi𐀀𐀀𐀀iႠ𐀀ႠႠႠi𐀀𐀀ÀÀÀ𐀀ÀႠÀiii|
 | 
			
		||||
  iiႠi𐀀ÀÀiႠ𐀀𐀀ႠႠÀ𐀀𐀀iÀႠiႠÀႠÀiÀiႠÀ𐀀𐀀ႠiÀႠႠႠႠ𐀀iiႠÀÀ𐀀ÀiÀ𐀀Ⴀ𐀀ÀiႠႠ𐀀À𐀀ႠiiႠiႠ𐀀iႠ𐀀ÀÀ𐀀ÀÀiႠÀi𐀀ÀႠii𐀀𐀀𐀀ÀÀ𐀀iႠ𐀀iႠÀႠႠiii𐀀|
 | 
			
		||||
  iÀiႠÀi𐀀À𐀀𐀀iiÀ𐀀𐀀𐀀Ài𐀀𐀀ႠÀÀ𐀀ii𐀀𐀀i𐀀ii𐀀Ⴀ𐀀𐀀𐀀ႠÀႠ𐀀ÀႠ𐀀iÀÀÀႠÀÀ𐀀𐀀iႠႠiÀ𐀀ÀႠ𐀀iiÀÀiႠႠႠႠ𐀀Ⴀ𐀀Ⴀ𐀀À𐀀iႠႠiႠ𐀀iiii𐀀Ⴀi𐀀ÀiႠ𐀀ÀÀii|
 | 
			
		||||
  ႠÀii𐀀ÀႠႠ𐀀𐀀i𐀀iiႠ𐀀i𐀀Ⴀ𐀀À𐀀𐀀ႠႠiiÀiiÀi𐀀ii𐀀𐀀iiiÀiiႠiÀ𐀀𐀀ÀÀÀ𐀀ÀႠ𐀀Ài𐀀À𐀀ÀiiÀÀ𐀀Ⴀ𐀀ႠႠiiÀÀႠ𐀀𐀀i𐀀𐀀ႠႠ𐀀𐀀𐀀𐀀ႠiႠ𐀀ႠႠÀ𐀀ႠÀÀÀÀÀ|
 | 
			
		||||
  iႠႠႠ𐀀ÀႠ𐀀𐀀ÀiÀ𐀀À𐀀iiÀ𐀀𐀀iႠiiႠႠÀiႠÀႠ𐀀ႠÀÀ𐀀iiÀ𐀀𐀀ႠÀiÀ𐀀iႠiႠÀႠiiiႠiiႠႠႠiiÀ𐀀iÀ𐀀iႠႠ𐀀Ⴀi𐀀ႠႠÀiႠ𐀀i𐀀𐀀𐀀iiႠiÀ𐀀ÀႠႠÀÀÀ𐀀i𐀀|
 | 
			
		||||
  ÀÀÀii𐀀ႠiႠႠiႠ𐀀ႠiႠ𐀀ÀÀi𐀀𐀀ÀÀႠႠiÀÀiÀႠÀ𐀀ႠÀႠ𐀀𐀀𐀀iႠiႠiႠ𐀀À𐀀𐀀ÀႠ𐀀𐀀iiÀႠ𐀀i𐀀𐀀𐀀iiႠÀ𐀀𐀀ÀiႠi𐀀ႠiÀ𐀀iÀႠÀiÀႠႠႠ𐀀ÀႠ𐀀Ⴀ𐀀À𐀀ႠÀiii|
 | 
			
		||||
  𐀀ÀÀ𐀀𐀀iႠႠ𐀀ႠiႠii𐀀𐀀ႠႠÀÀႠÀႠiÀႠႠ𐀀ÀႠႠÀ𐀀ii𐀀𐀀𐀀ii𐀀𐀀Ⴀii𐀀𐀀ÀÀÀiႠÀiiiiiႠiÀႠႠÀ𐀀𐀀ႠÀႠÀiiiႠ𐀀ÀÀႠÀi𐀀ႠiÀiÀi𐀀ÀႠiႠiiႠ𐀀iÀÀÀ|
 | 
			
		||||
  ႠiiÀii𐀀ÀÀi𐀀𐀀ႠÀÀႠႠ𐀀ii𐀀ÀႠiႠႠÀ𐀀𐀀ႠႠiႠႠii𐀀iÀiiiႠ𐀀iiႠÀÀÀÀ𐀀ႠÀi𐀀iႠi𐀀ii𐀀Ⴀ𐀀ႠÀiii𐀀𐀀ÀÀÀiiႠÀ𐀀Ⴀ𐀀ႠÀႠႠ𐀀𐀀𐀀𐀀𐀀À𐀀ÀႠႠi𐀀ႠႠ|
 | 
			
		||||
  ÀႠ𐀀iiႠႠႠÀÀ𐀀iÀiÀ𐀀ႠႠÀiÀႠÀ𐀀ÀÀÀiႠ𐀀𐀀ႠÀ𐀀ÀႠÀÀ𐀀𐀀𐀀𐀀𐀀ÀÀ𐀀𐀀iÀႠႠiႠiÀiiiႠiÀÀiႠÀ𐀀𐀀Ài𐀀iႠÀ𐀀ႠÀÀÀ𐀀𐀀𐀀ÀႠiiႠ𐀀ÀႠÀÀ𐀀iÀႠÀႠ𐀀À𐀀|
 | 
			
		||||
  𐀀Ⴀ𐀀ႠႠႠႠႠႠႠiiႠ𐀀ÀÀ𐀀iÀႠiႠÀÀႠÀ𐀀i𐀀𐀀Ⴀ𐀀ႠႠÀႠႠ𐀀ႠႠ𐀀𐀀ÀႠႠiÀÀÀÀÀiႠÀ𐀀ႠÀÀ𐀀iÀi𐀀iႠ𐀀Ⴀ𐀀Ⴀii𐀀iႠႠႠႠႠႠi𐀀iÀÀ𐀀ႠÀiÀႠiÀ𐀀𐀀ii𐀀𐀀𐀀À|
 | 
			
		||||
  iႠi𐀀ÀႠi𐀀Ⴀ𐀀Àiii𐀀Ⴀii𐀀Ⴀii𐀀𐀀Ⴀ𐀀ႠÀÀii𐀀ႠႠ𐀀i𐀀𐀀ႠiiႠÀÀiႠÀiႠႠÀႠÀÀiÀi𐀀iႠႠ𐀀ႠÀ𐀀iႠÀÀ𐀀i𐀀𐀀ÀiႠႠÀiÀiiiႠႠႠ𐀀À𐀀ÀÀiÀÀႠÀႠ𐀀ÀႠ|
 | 
			
		||||
  𐀀𐀀Ⴀ𐀀ႠÀÀ𐀀iiÀi𐀀𐀀iiÀÀ𐀀𐀀𐀀iႠ𐀀À𐀀iႠႠႠÀႠiÀÀiÀႠiiiÀiÀÀႠႠႠÀÀႠႠiÀiႠႠႠႠÀiÀႠiÀ𐀀À𐀀À𐀀𐀀iiiႠ𐀀𐀀𐀀ÀႠ𐀀ÀiÀÀiႠÀÀႠႠÀႠiiႠi𐀀i𐀀|
 | 
			
		||||
  iÀiiႠiiiiႠÀ𐀀ÀÀÀiÀi𐀀iiiႠ𐀀𐀀ႠÀiႠÀႠiႠÀiႠÀႠiÀႠÀႠÀÀÀÀiÀႠi𐀀ႠiႠi𐀀Ⴀ𐀀À𐀀i𐀀𐀀ႠiiÀႠ𐀀ႠÀႠႠႠii𐀀𐀀iiiiii𐀀À𐀀iÀiiÀႠÀႠiႠi𐀀|
 | 
			
		||||
  À𐀀i𐀀ႠÀiႠ𐀀ႠÀႠ𐀀𐀀ႠႠiႠiiiႠÀႠÀႠႠÀ𐀀𐀀Ⴀ𐀀𐀀i𐀀ႠÀ𐀀iႠႠiႠiႠ𐀀Ⴀiii𐀀𐀀À𐀀Ⴀ𐀀ႠÀÀႠÀ𐀀iÀႠÀiႠÀÀ𐀀Ⴀii𐀀ႠiiiiႠÀ𐀀Ài𐀀𐀀ႠiႠÀÀ𐀀𐀀ႠiႠႠÀÀ|
 | 
			
		||||
  Ⴀ𐀀ÀႠႠ𐀀𐀀iႠႠ𐀀iÀÀiÀ𐀀ႠÀ𐀀𐀀𐀀𐀀iႠ𐀀À𐀀ႠႠ𐀀Ⴀ𐀀𐀀iႠiႠႠ𐀀ႠႠÀÀႠႠÀÀႠ𐀀𐀀ႠÀÀii𐀀𐀀𐀀ÀÀႠ𐀀i𐀀Ⴀ𐀀iiiÀÀÀႠiÀiÀ𐀀ii𐀀𐀀iႠႠႠii𐀀iiႠႠi𐀀ÀÀ𐀀i|
 | 
			
		||||
  𐀀ÀÀ𐀀𐀀ႠÀ𐀀ႠႠႠÀ𐀀Ⴀ𐀀ii𐀀Ⴀ𐀀𐀀ႠႠ𐀀À𐀀𐀀𐀀ႠiႠႠႠ𐀀ႠÀi𐀀𐀀Ⴀ𐀀Ài𐀀ႠÀÀi𐀀À𐀀iႠiႠႠ𐀀iiÀiႠႠÀ𐀀À𐀀iiႠႠႠႠ𐀀ÀÀႠႠႠiÀႠ𐀀i𐀀i𐀀iiÀ𐀀i𐀀ႠiÀÀÀiÀ|
 | 
			
		||||
  Ⴀii𐀀i𐀀ႠiÀiiÀÀÀ𐀀Àii𐀀ႠÀႠi𐀀ႠႠiႠႠi𐀀i𐀀𐀀iႠႠ𐀀𐀀iႠ𐀀iႠႠ𐀀ÀiiႠiႠiii𐀀ÀÀÀi𐀀ႠiÀႠႠႠÀႠႠႠႠႠႠÀiiÀႠi𐀀ÀÀiÀႠ𐀀ÀiႠႠÀ𐀀𐀀iiÀ𐀀𐀀À|
 | 
			
		||||
  iႠႠiႠiiႠÀÀႠ𐀀iÀÀiÀ𐀀iiႠÀ𐀀i𐀀ႠႠ𐀀iႠႠ𐀀À𐀀𐀀iiႠႠႠ𐀀ႠiႠi𐀀iႠ𐀀ႠႠÀiႠ𐀀𐀀Ⴀ𐀀Ⴀi𐀀iႠႠÀ𐀀À𐀀ÀႠႠ𐀀ÀႠႠi𐀀Ⴀi𐀀iÀႠÀ𐀀À𐀀ႠÀ𐀀ႠÀÀi𐀀Ⴀ𐀀iiÀ|
 | 
			
		||||
  ႠႠႠ𐀀ႠiÀႠႠiiiiiiႠi𐀀i𐀀ႠÀ𐀀i𐀀𐀀ႠႠÀႠi𐀀ÀÀÀÀႠ𐀀ႠႠ𐀀i𐀀iiÀ𐀀Ài𐀀𐀀i𐀀i𐀀𐀀ÀႠႠႠii𐀀ÀiiÀiႠiႠ𐀀iiႠႠႠႠ𐀀i𐀀ii𐀀iiÀÀ𐀀𐀀ÀႠ𐀀ÀႠ𐀀iÀ𐀀𐀀|
 | 
			
		||||
  iႠÀiႠii𐀀𐀀ÀiႠႠiiÀ𐀀ÀÀ𐀀𐀀ႠÀႠ𐀀iႠiiႠiiÀi𐀀ႠႠႠiÀi𐀀𐀀ÀႠÀÀႠi𐀀iÀႠÀႠÀ𐀀𐀀À𐀀𐀀À𐀀ႠiÀÀi𐀀iÀÀ𐀀ÀႠႠႠi𐀀iႠႠi𐀀iiႠႠႠÀiÀ𐀀𐀀Ⴀ𐀀ÀÀ𐀀À|
 | 
			
		||||
  ÀiႠÀÀႠÀÀÀႠႠÀႠii𐀀i𐀀i𐀀iiႠiÀiÀÀÀႠႠiႠiiÀÀÀႠÀႠÀÀÀႠii𐀀Ⴀ𐀀Ⴀi𐀀ÀႠႠiÀÀႠi𐀀Ⴀ𐀀𐀀ÀႠႠ𐀀iႠႠ𐀀iÀiÀÀႠÀÀ𐀀i𐀀𐀀ÀႠiÀႠႠ𐀀𐀀ÀႠႠiႠÀi|
 | 
			
		||||
  ÀiႠÀiiiÀႠ𐀀𐀀iႠ𐀀𐀀iÀÀÀႠÀႠiÀiÀi𐀀Ⴀ𐀀À𐀀iiႠ𐀀ÀiÀႠႠ𐀀iiiႠ𐀀Ài𐀀𐀀𐀀𐀀𐀀𐀀𐀀ÀႠÀ𐀀ÀiÀ𐀀ÀÀ𐀀iႠႠ𐀀Ⴀ𐀀i𐀀𐀀iii𐀀𐀀𐀀𐀀ႠႠi𐀀ii𐀀𐀀ႠႠႠ𐀀ÀiႠÀႠ|
 | 
			
		||||
  À𐀀ႠÀ𐀀𐀀𐀀À𐀀ÀiÀႠiiႠႠÀႠႠiႠÀÀ𐀀𐀀i𐀀𐀀𐀀ႠiÀႠÀÀ𐀀𐀀𐀀À𐀀ႠႠiÀiÀi𐀀ႠiÀiiႠÀ𐀀ÀiiiႠႠiႠ𐀀ႠiÀ𐀀ÀႠÀÀi𐀀ႠiႠiiႠiÀiiႠÀႠiiÀi𐀀Ⴀ𐀀𐀀iႠi|
 | 
			
		||||
  ÀÀ𐀀iÀÀÀ𐀀Ⴀ𐀀𐀀ÀႠႠ𐀀Ⴀ𐀀Ⴀ𐀀ႠÀ𐀀i𐀀ÀÀiÀÀ𐀀À𐀀𐀀𐀀iÀiႠiiÀႠÀiႠii𐀀𐀀iÀii𐀀Ⴀ𐀀ႠÀႠiႠiႠ𐀀ÀႠÀ𐀀i𐀀iႠႠ𐀀ႠႠႠÀÀÀii𐀀Ⴀ𐀀𐀀i𐀀i𐀀𐀀iႠi𐀀À𐀀𐀀^Ⴀ |
 | 
			
		||||
                                                                                                      |
 | 
			
		||||
      ]=],
 | 
			
		||||
      value = { col = 100, curscol = 100, endcol = 100, row = 50 },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    ascii_results,
 | 
			
		||||
    two_byte_results,
 | 
			
		||||
    { -- random
 | 
			
		||||
      screen = [=[
 | 
			
		||||
  Ⴀ𐀀ii𐀀ႠÀ𐀀i𐀀𐀀iÀÀÀiÀ𐀀Ⴀ𐀀ႠiiႠ𐀀iii𐀀ÀႠÀႠႠÀiiÀႠÀiႠi𐀀ÀÀ𐀀𐀀Ⴀ𐀀𐀀Ⴀ𐀀iÀႠ𐀀i𐀀ÀÀႠiiÀ𐀀Ⴀ𐀀À𐀀ii𐀀ÀÀ𐀀ÀႠႠ𐀀𐀀𐀀𐀀ii𐀀ႠႠiႠႠ𐀀ႠÀ𐀀ÀÀiiႠ|
 | 
			
		||||
  iiႠ𐀀iÀÀiႠႠÀi𐀀ႠႠÀ𐀀𐀀ÀiiiiÀiႠ𐀀iႠÀiႠiႠႠÀiÀ𐀀ႠiiႠႠÀ𐀀Àii𐀀ႠÀႠiႠÀႠÀႠii𐀀Ài𐀀ႠႠ𐀀ÀÀÀi𐀀ÀÀÀ𐀀iႠ𐀀iႠÀ𐀀iႠi𐀀ÀiÀ𐀀ႠႠiÀ𐀀𐀀Ⴀi|
 | 
			
		||||
  iÀiiiႠÀÀ𐀀ႠႠႠi𐀀À𐀀𐀀iiiÀÀiiÀႠÀ𐀀À𐀀ႠႠ𐀀𐀀ႠႠႠi𐀀iiÀႠ𐀀ႠႠႠÀiႠiႠiÀÀÀi𐀀iႠ𐀀ÀÀiႠ𐀀iÀÀi𐀀i𐀀𐀀ÀiÀႠ𐀀𐀀iႠ𐀀ÀÀiÀÀႠ𐀀𐀀ႠႠ𐀀𐀀𐀀𐀀Ⴀ𐀀𐀀|
 | 
			
		||||
  ÀႠiႠiÀ𐀀i𐀀ႠႠiႠႠÀ𐀀ÀÀÀÀ𐀀𐀀ÀႠႠ𐀀ႠÀÀiႠ𐀀i𐀀Ⴀ𐀀ÀႠi𐀀ႠÀႠÀ𐀀ႠႠ𐀀i𐀀iႠÀi𐀀i𐀀𐀀À𐀀iÀiႠႠႠ𐀀ÀiÀႠÀ𐀀ÀÀÀi𐀀𐀀𐀀ႠÀi𐀀𐀀À𐀀À𐀀𐀀iiႠiÀi𐀀i𐀀Ⴀ|
 | 
			
		||||
  Ⴀ𐀀i𐀀𐀀ÀiÀႠႠႠႠႠÀÀႠႠÀႠ𐀀ii𐀀ÀႠiႠiii𐀀i𐀀i𐀀𐀀𐀀À𐀀ii𐀀iÀiiiÀÀႠiiiႠiiႠÀ𐀀À𐀀𐀀ÀႠ𐀀iÀÀiiÀiÀ𐀀iႠi𐀀𐀀À𐀀ÀiiႠ𐀀iÀ𐀀𐀀iႠႠÀÀႠႠiiÀ|
 | 
			
		||||
  𐀀ÀiႠႠÀ𐀀𐀀𐀀i𐀀i𐀀i𐀀ႠÀ𐀀ÀiiÀႠ𐀀ÀÀÀi𐀀ႠÀiÀႠi𐀀ႠÀiiÀÀÀiiiÀiႠႠiÀ𐀀ႠႠ𐀀iÀႠÀႠႠiÀÀႠÀႠÀÀii𐀀Ⴀi𐀀iiÀÀÀiႠ𐀀i𐀀𐀀i𐀀iiÀ𐀀𐀀𐀀ႠÀiႠ𐀀|
 | 
			
		||||
  i𐀀ÀႠiႠi𐀀ႠiႠ𐀀Ⴀi𐀀ႠÀ𐀀𐀀𐀀ႠÀiiiii𐀀Ⴀ𐀀iiiÀiiÀ𐀀𐀀𐀀À𐀀𐀀Ⴀ𐀀ႠÀ𐀀ႠႠႠiÀÀÀÀii𐀀i𐀀ÀiiႠÀiÀ𐀀iႠႠiÀႠii𐀀i𐀀Ⴀ𐀀𐀀iႠႠÀ𐀀ႠiiiႠႠÀÀ𐀀iÀႠ|
 | 
			
		||||
  Ⴀ𐀀𐀀ႠႠ𐀀À𐀀ÀႠ𐀀ÀႠÀ𐀀𐀀iႠႠÀÀiÀႠ𐀀ÀiÀႠi𐀀ႠÀ𐀀𐀀𐀀𐀀Ⴀ𐀀iႠÀ𐀀iÀ𐀀iÀ𐀀iÀÀႠi𐀀iÀႠi𐀀ႠiiႠÀ𐀀À𐀀ႠႠÀÀi𐀀ႠႠ𐀀iiႠÀiႠ𐀀𐀀𐀀𐀀ႠႠႠÀႠiÀႠiÀÀ𐀀À|
 | 
			
		||||
  ÀႠÀÀ𐀀i𐀀iႠÀÀÀႠ𐀀𐀀ÀႠÀÀiii𐀀𐀀iiÀiiႠÀÀႠiÀiÀÀ𐀀i𐀀i𐀀ႠiႠႠiႠÀiiÀႠ𐀀ႠႠÀiÀႠ𐀀𐀀iÀ𐀀Ⴀ𐀀iÀ𐀀ႠÀÀႠÀÀÀ𐀀𐀀i𐀀𐀀À𐀀𐀀ii𐀀À𐀀𐀀ႠÀ𐀀ႠႠႠ𐀀𐀀|
 | 
			
		||||
  ÀÀÀÀiႠiႠႠႠiႠ𐀀ႠÀÀÀ𐀀ÀÀiႠÀ𐀀ÀiႠÀႠÀႠႠÀÀႠiÀႠႠiiႠÀ𐀀ႠႠÀiႠ𐀀iÀႠ𐀀Ⴀ𐀀Ⴀ𐀀iႠÀႠi𐀀𐀀Ⴀ𐀀iÀ𐀀ÀႠ𐀀ÀÀႠ𐀀Ⴀi𐀀iႠÀ𐀀𐀀𐀀𐀀i𐀀i𐀀𐀀𐀀ÀႠiÀÀ𐀀i|
 | 
			
		||||
  𐀀𐀀iiÀ𐀀ÀႠ𐀀𐀀𐀀𐀀𐀀Àiii𐀀𐀀𐀀Ⴀ𐀀𐀀i𐀀ÀÀ𐀀iiÀiiiiÀ𐀀iႠiႠiÀႠÀႠÀiႠႠႠႠႠႠႠႠႠÀiiႠiÀႠÀ𐀀iiႠÀႠiႠႠÀiႠ𐀀iႠ𐀀iiႠÀ𐀀𐀀Àii𐀀i𐀀ÀႠÀÀiÀÀ|
 | 
			
		||||
  𐀀𐀀𐀀i𐀀iÀ𐀀𐀀iÀ𐀀Ài𐀀𐀀ႠႠ𐀀ႠÀi𐀀𐀀ÀÀiiiႠiႠ𐀀iႠÀႠÀ𐀀ႠÀ𐀀ႠiiiiÀiႠÀiiiႠႠÀ𐀀Ⴀ𐀀𐀀𐀀ÀႠiÀႠiiiiႠiႠ𐀀Ⴀ𐀀ÀႠÀii𐀀i𐀀Ⴀ𐀀À𐀀iႠႠ𐀀iႠiiii𐀀|
 | 
			
		||||
  iႠÀÀႠÀÀ𐀀𐀀𐀀iiiÀ𐀀À𐀀iÀÀi𐀀À𐀀ÀႠÀiÀii𐀀ႠႠii𐀀ႠႠ𐀀𐀀ÀႠ𐀀ÀÀ𐀀ÀÀÀÀi𐀀ÀႠ𐀀À𐀀ÀiiႠ𐀀ÀÀႠiႠ𐀀Ⴀ𐀀ÀÀ𐀀ႠႠ𐀀ႠႠÀ𐀀ႠႠႠ𐀀iiÀႠÀႠiႠi𐀀ii𐀀Ài|
 | 
			
		||||
  Ⴀ𐀀ႠÀ𐀀Ⴀi𐀀iÀ𐀀Ⴀ𐀀𐀀iÀiÀ𐀀iႠi𐀀ႠÀiႠႠiÀ𐀀iႠiÀႠi𐀀𐀀iႠiႠႠ𐀀ÀÀi𐀀iÀႠႠ𐀀ႠÀ𐀀𐀀𐀀ႠႠiÀÀiႠႠႠ𐀀𐀀ႠႠႠႠÀiႠ𐀀Ài𐀀iÀႠii𐀀ÀႠii𐀀Ⴀ𐀀ÀႠÀ𐀀Ⴀi|
 | 
			
		||||
  𐀀ႠႠႠႠႠ𐀀Ⴀ𐀀ÀÀ𐀀𐀀iÀÀႠiÀiiႠÀiႠ𐀀𐀀ÀÀiÀႠ𐀀ÀÀႠ𐀀À𐀀À𐀀iႠႠÀiႠÀiiiႠÀiÀÀÀ𐀀iႠႠႠÀÀiႠႠႠ𐀀ii𐀀ii𐀀iႠႠii𐀀iႠÀi𐀀𐀀𐀀ii𐀀ÀႠႠiႠÀ𐀀ႠႠ|
 | 
			
		||||
  𐀀iႠ𐀀ႠႠႠÀÀ𐀀iiiÀÀႠii𐀀i𐀀ÀÀႠ𐀀ႠႠiႠႠႠiႠ𐀀i𐀀À𐀀ႠႠႠi𐀀Ài𐀀ႠÀ𐀀ÀႠiiiiႠiiႠႠi𐀀𐀀ÀႠ𐀀ႠiÀÀ𐀀iiiႠÀiႠi𐀀À𐀀𐀀i𐀀𐀀iiႠ𐀀À𐀀iiႠႠÀ𐀀iႠ|
 | 
			
		||||
  ႠႠÀÀႠႠ𐀀iÀႠ𐀀iႠႠÀÀ𐀀ÀiႠ𐀀iÀႠႠႠ𐀀𐀀À𐀀ii𐀀𐀀À𐀀𐀀ÀႠႠႠÀႠiiႠ𐀀𐀀ii𐀀ႠႠÀႠiႠi𐀀ႠႠ𐀀i𐀀𐀀𐀀𐀀Ⴀ𐀀iÀiႠႠ𐀀À𐀀ÀႠႠႠÀÀ𐀀𐀀iÀႠi𐀀𐀀ÀiႠႠÀÀiÀÀ|
 | 
			
		||||
  𐀀ႠÀiÀ𐀀ႠႠ𐀀i𐀀ႠႠ𐀀𐀀𐀀𐀀𐀀𐀀iႠÀႠႠiiÀႠ𐀀𐀀i𐀀ႠႠ𐀀ႠႠႠ𐀀𐀀iÀiÀႠ𐀀À𐀀À𐀀𐀀ႠiiiႠiiiႠiႠ𐀀ÀiiiႠ𐀀À𐀀ÀႠႠÀ𐀀À𐀀𐀀𐀀ႠiႠi𐀀ႠÀÀႠiiႠႠႠႠ𐀀ÀႠ𐀀𐀀|
 | 
			
		||||
  Ⴀ𐀀iÀÀႠ𐀀ႠႠÀႠÀ𐀀ႠႠÀiiÀÀÀႠ𐀀i𐀀Ⴀ𐀀ႠÀ𐀀𐀀iႠႠႠႠiiႠႠႠÀiႠÀiiႠÀ𐀀Ⴀ𐀀Ⴀi𐀀𐀀ႠÀ𐀀ÀႠႠ𐀀ÀႠÀႠ𐀀Ⴀ𐀀iႠi𐀀ÀႠႠii𐀀ÀႠÀ𐀀Ⴀ𐀀Ài𐀀À𐀀ÀÀႠiႠႠii𐀀|
 | 
			
		||||
  ÀႠႠႠÀႠႠii𐀀Ài𐀀Ⴀi𐀀i𐀀i𐀀Ⴀ𐀀ÀÀÀiÀÀi𐀀ÀÀÀ𐀀i𐀀iÀ𐀀𐀀ႠiÀi𐀀𐀀𐀀ႠÀiÀ𐀀𐀀iÀÀႠႠ𐀀ႠႠႠႠÀÀÀÀiiÀ𐀀iiႠႠႠi𐀀ႠÀi𐀀ÀႠႠÀ𐀀ႠiiႠ𐀀𐀀i𐀀Ⴀi𐀀𐀀À|
 | 
			
		||||
  ÀႠiiÀi𐀀ႠÀÀiÀႠi𐀀ÀႠႠ𐀀ႠiÀiiႠiiႠႠ𐀀ii𐀀i𐀀𐀀𐀀𐀀i𐀀ႠႠÀႠÀÀiÀÀÀႠÀႠႠÀÀႠ𐀀ÀÀ𐀀ÀÀႠÀ𐀀𐀀ÀiႠ𐀀ႠႠ𐀀iÀ𐀀Ⴀ𐀀iÀႠႠ𐀀iÀiiii𐀀ii𐀀𐀀ႠႠႠ𐀀Ⴀ|
 | 
			
		||||
  𐀀ÀiÀႠÀiiÀÀiiႠႠႠi𐀀ÀÀiႠ𐀀iႠႠÀ𐀀𐀀𐀀ÀႠႠÀ𐀀ႠÀÀႠ𐀀ÀÀ𐀀𐀀Ⴀ𐀀ÀÀ𐀀ÀႠ𐀀ႠÀiÀ𐀀iႠ𐀀ႠႠ𐀀𐀀À𐀀iii𐀀iiႠÀႠiႠÀႠ𐀀Ⴀ𐀀i𐀀𐀀ÀႠႠi𐀀𐀀ႠႠ𐀀𐀀𐀀𐀀À𐀀Ⴀ𐀀|
 | 
			
		||||
  𐀀iiÀႠiႠiÀႠ𐀀i𐀀iii𐀀Ⴀ𐀀i𐀀iÀÀi𐀀Ⴀii𐀀ÀiÀiiiÀႠÀ𐀀ÀÀႠ𐀀Ⴀ𐀀iiÀi𐀀i𐀀𐀀i𐀀ႠiiiÀႠႠႠiiÀ𐀀À𐀀𐀀iÀ𐀀iႠÀႠÀÀi𐀀ႠiÀႠÀ𐀀𐀀iÀÀ𐀀i𐀀𐀀ÀÀႠ𐀀|
 | 
			
		||||
  ÀiÀႠႠႠႠii𐀀ÀÀ𐀀𐀀𐀀Ⴀi𐀀À𐀀ÀႠiiÀi𐀀Ⴀii𐀀iÀÀ𐀀ႠiႠ𐀀ႠiiiႠÀÀiÀÀÀÀ𐀀ႠႠii𐀀À𐀀ÀiÀi𐀀ÀÀi𐀀iႠiÀi𐀀ÀiÀi𐀀ÀiÀႠ𐀀i𐀀Ⴀi𐀀𐀀𐀀ႠႠ𐀀ႠÀႠÀႠi|
 | 
			
		||||
  i𐀀ႠiÀႠႠÀÀ𐀀𐀀ii𐀀ÀÀ𐀀iÀiÀႠÀiiii𐀀ÀiÀႠi𐀀i𐀀𐀀i𐀀𐀀iႠ𐀀iÀi𐀀ÀÀÀÀiႠiÀႠÀÀႠiiÀÀႠႠi𐀀iႠiiႠi𐀀Ⴀ𐀀𐀀ÀႠႠÀႠiႠႠÀ𐀀iiÀႠႠႠ𐀀𐀀Ⴀi𐀀Ⴀi|
 | 
			
		||||
  ii𐀀iÀÀÀÀÀÀiÀ𐀀À𐀀iiႠiႠႠi𐀀À𐀀ÀႠÀ𐀀ႠႠ𐀀𐀀𐀀iႠႠiiႠÀÀႠÀiiႠÀႠႠÀ𐀀𐀀Ⴀ𐀀ÀÀÀÀႠ𐀀𐀀𐀀ႠႠÀႠ𐀀ÀiႠiÀႠiÀÀ𐀀ii𐀀iiiÀႠÀႠႠ𐀀ႠÀiÀÀ𐀀ႠႠႠÀ|
 | 
			
		||||
  𐀀𐀀À𐀀𐀀iÀႠ𐀀ႠiႠÀÀ𐀀iÀÀ𐀀À𐀀iÀÀႠႠÀiii𐀀À𐀀ÀႠÀႠႠÀႠႠi𐀀ÀÀÀi𐀀À𐀀ႠiÀi𐀀i𐀀i𐀀ÀiÀÀiÀÀ𐀀𐀀À𐀀ႠÀ𐀀ႠÀႠ𐀀ႠiÀ𐀀𐀀ÀiÀÀ𐀀𐀀𐀀À𐀀Ⴀi𐀀i𐀀i𐀀Ài|
 | 
			
		||||
  𐀀𐀀iႠ𐀀i𐀀ÀႠႠÀ𐀀iÀ𐀀ÀiႠႠi𐀀iiႠÀ𐀀ÀiiÀႠ𐀀Ⴀ𐀀ÀÀiÀiႠi𐀀À𐀀𐀀iÀiÀiႠi𐀀ႠႠႠi𐀀À𐀀ÀႠႠ𐀀Ⴀ𐀀ÀÀႠiÀiÀ𐀀Ⴀ𐀀ÀႠiႠႠÀÀÀi𐀀i𐀀Ⴀi𐀀À𐀀ii𐀀ႠÀ𐀀Ⴀ|
 | 
			
		||||
  ႠiႠ𐀀iÀႠႠ𐀀i𐀀À𐀀iÀÀ𐀀𐀀ÀႠႠÀႠÀ𐀀iiiÀ𐀀i𐀀iÀ𐀀ႠႠ𐀀iÀႠ𐀀ႠÀi𐀀iiii𐀀iႠႠ𐀀ÀiÀ𐀀Àii𐀀Ⴀ𐀀𐀀ႠiÀii𐀀𐀀Ⴀ𐀀𐀀ႠiႠ𐀀iႠiႠi𐀀iiiႠႠႠi𐀀iiÀi𐀀Ⴀ|
 | 
			
		||||
  i𐀀i𐀀ÀÀÀ𐀀ÀiÀႠiÀiiႠÀÀÀiÀiiii𐀀i𐀀ÀÀiiiႠÀiÀႠÀiႠ𐀀iiႠiႠႠiÀi𐀀ႠႠ𐀀ÀႠiႠ𐀀ႠÀiiႠÀ𐀀ÀႠႠ𐀀ႠiÀi𐀀À𐀀𐀀iiÀ𐀀𐀀ÀiႠႠiႠ𐀀ÀႠiÀÀႠ𐀀i|
 | 
			
		||||
  Ài𐀀𐀀𐀀iÀi𐀀Ài𐀀À𐀀ႠႠ𐀀ႠÀiiÀႠ𐀀i𐀀i𐀀𐀀ႠiႠÀ𐀀𐀀Ⴀ𐀀iÀ𐀀ÀÀႠiႠႠiÀ𐀀iÀ𐀀ႠiÀÀÀÀႠiiÀ𐀀𐀀ÀႠႠiÀ𐀀iiÀ𐀀À𐀀iÀiÀÀ𐀀iÀiÀÀiiÀ𐀀ÀႠႠÀiiÀÀႠ|
 | 
			
		||||
  𐀀ႠÀႠiႠႠÀ𐀀ÀiiÀ𐀀iÀႠႠႠႠiÀÀi𐀀iÀi𐀀iiiႠ𐀀iႠ𐀀𐀀𐀀𐀀ÀÀÀႠi𐀀iႠi𐀀ႠÀႠႠ𐀀𐀀À𐀀iiÀႠ𐀀𐀀ႠႠ𐀀𐀀ÀiႠÀÀÀႠ𐀀𐀀ÀiႠ𐀀𐀀iÀÀiÀ𐀀ႠÀi𐀀𐀀ႠႠႠႠ𐀀Ⴀi|
 | 
			
		||||
  iÀi𐀀ႠႠÀ𐀀𐀀i𐀀Àii𐀀ÀiÀÀiÀiÀÀ𐀀ÀÀ𐀀À𐀀ႠႠႠÀÀÀႠii𐀀ႠÀÀႠႠi𐀀ႠႠiႠႠ𐀀Ⴀ𐀀ÀiÀiiii𐀀ÀiႠÀiiiiႠႠiiႠÀÀÀႠÀႠ𐀀𐀀𐀀iiႠႠ𐀀ႠÀႠ𐀀iႠႠ𐀀𐀀Ⴀ|
 | 
			
		||||
  À𐀀À𐀀ႠႠႠiÀ𐀀ÀႠÀႠႠiiႠii𐀀ႠÀÀÀ𐀀iႠiiiiiÀ𐀀ÀÀiÀÀႠ𐀀𐀀iiႠi𐀀𐀀Ài𐀀ႠÀÀiႠႠႠႠ𐀀iႠiႠႠႠႠႠÀÀႠiiÀiႠ𐀀iÀiiႠiiii𐀀ii𐀀À𐀀ႠÀ𐀀Ⴀ𐀀ÀႠ|
 | 
			
		||||
  ÀႠÀiiiiႠiiႠiÀi𐀀𐀀ႠÀÀ𐀀Ài𐀀Ài𐀀ÀiႠÀ𐀀ႠႠႠႠ𐀀ႠiÀ𐀀iႠႠÀႠi𐀀Ài𐀀ႠiiႠ𐀀Ⴀii𐀀ÀÀÀႠ𐀀ÀÀÀႠÀiÀႠiႠiႠi𐀀𐀀À𐀀𐀀𐀀ÀႠiႠႠႠႠÀiiÀႠ𐀀À𐀀𐀀À|
 | 
			
		||||
  iÀ𐀀ÀႠႠÀiÀi𐀀Ài𐀀ÀiႠႠÀ𐀀iႠÀ𐀀i𐀀ÀiiÀÀÀႠÀÀႠ𐀀À𐀀À𐀀ÀÀÀÀႠi𐀀iႠÀ𐀀𐀀ÀႠiÀiႠႠiÀ𐀀ႠႠÀ𐀀𐀀Ⴀ𐀀ÀÀ𐀀ႠႠÀÀiÀi𐀀𐀀𐀀À𐀀ÀႠ𐀀iႠႠ𐀀𐀀𐀀i𐀀ႠÀ𐀀Ⴀ|
 | 
			
		||||
  Ⴀi𐀀ÀÀ𐀀ႠÀiÀi𐀀i𐀀ÀႠ𐀀Ⴀ𐀀ÀႠ𐀀ႠÀႠႠႠ𐀀𐀀ÀiiiiႠႠi𐀀ႠÀႠÀ𐀀Ⴀ𐀀i𐀀À𐀀𐀀𐀀Ⴀ𐀀ÀiÀÀႠႠiiႠiÀiႠႠÀiÀÀႠႠÀÀႠÀ𐀀ႠiႠ𐀀𐀀i𐀀i𐀀𐀀ÀႠÀႠႠႠÀÀiiÀ𐀀|
 | 
			
		||||
  ႠႠႠiiÀႠႠiÀႠ𐀀ÀiႠႠÀႠiÀႠႠÀÀi𐀀ÀÀiÀ𐀀𐀀i𐀀i𐀀iiÀÀiႠ𐀀Ⴀ𐀀𐀀𐀀ÀiiႠ𐀀Ài𐀀iiiiÀiႠႠii𐀀Ⴀi𐀀iႠႠ𐀀ÀÀႠ𐀀iÀႠႠႠiÀ𐀀𐀀iÀႠiႠÀ𐀀ÀႠÀiႠ𐀀À|
 | 
			
		||||
  𐀀ႠႠႠiႠႠiiii𐀀𐀀i𐀀Àiiii𐀀À𐀀Ⴀi𐀀iႠ𐀀ႠiÀiÀႠi𐀀𐀀ÀiÀiiÀÀÀ𐀀𐀀i𐀀À𐀀ÀႠÀiiÀႠ𐀀ႠႠ𐀀𐀀Ⴀ𐀀ÀÀiÀ𐀀iႠ𐀀𐀀iÀÀႠi𐀀iႠiÀ𐀀ႠႠ𐀀ÀÀႠiÀ𐀀ÀႠႠÀႠ|
 | 
			
		||||
  Ⴀii𐀀𐀀ႠÀiႠႠÀÀ𐀀ÀÀÀÀÀÀÀႠiႠႠÀÀi𐀀ÀiႠÀ𐀀𐀀i𐀀ႠÀii𐀀Ⴀ𐀀𐀀À𐀀𐀀ÀiÀ𐀀i𐀀𐀀ႠÀiÀÀႠiiႠႠiႠÀiÀႠÀi𐀀iÀ𐀀À𐀀𐀀ႠႠi𐀀ႠÀiÀÀÀႠÀiÀÀႠiႠ𐀀iÀ|
 | 
			
		||||
  𐀀ÀႠiÀႠႠႠÀÀႠÀႠ𐀀iiiiÀiÀÀႠ𐀀Ⴀiii𐀀𐀀iiႠiÀ𐀀𐀀i𐀀ÀiiÀႠ𐀀𐀀Ⴀ𐀀Ⴀ𐀀Ⴀii𐀀ႠiႠÀiႠႠÀÀÀႠÀ𐀀Ⴀ𐀀𐀀𐀀À𐀀Ⴀ𐀀Ⴀ𐀀ႠÀ𐀀ႠႠiႠ𐀀𐀀ÀiiiÀ𐀀ÀiÀiႠÀ𐀀À|
 | 
			
		||||
  i𐀀ႠiႠi𐀀ii𐀀𐀀iiiႠႠÀÀiiii𐀀ÀiႠႠÀi𐀀ÀÀÀÀiÀiiႠ𐀀ÀႠiႠႠiÀႠ𐀀ÀႠႠ𐀀ÀÀÀ𐀀ႠÀ𐀀À𐀀iႠi𐀀iÀÀi𐀀iÀÀiႠႠ𐀀ႠiÀÀiÀ𐀀iႠ𐀀ႠÀႠÀii𐀀𐀀ႠႠi𐀀|
 | 
			
		||||
  iႠ𐀀À𐀀𐀀Ài𐀀ÀႠ𐀀Ⴀ𐀀𐀀ႠÀ𐀀ႠႠႠiÀ𐀀ÀiႠႠႠÀႠÀႠiÀႠi𐀀ÀÀÀႠÀiÀႠÀÀÀiii𐀀𐀀ÀiiႠÀi𐀀iÀ𐀀À𐀀ÀiiÀÀÀiÀiÀÀi𐀀iiiiÀ𐀀ÀႠႠiiႠi𐀀iiႠ𐀀À𐀀𐀀|
 | 
			
		||||
  ႠႠÀÀiႠ𐀀iႠiÀÀႠႠi𐀀ႠÀÀiႠ𐀀ႠႠÀÀÀii𐀀𐀀iiႠ𐀀iႠ𐀀iႠႠ𐀀Ài𐀀iiÀÀႠႠ𐀀Ⴀ𐀀𐀀𐀀i𐀀ÀÀi𐀀𐀀ႠiÀi𐀀iÀiiႠႠÀႠႠiႠÀiႠႠႠÀÀÀ𐀀ႠÀ𐀀ႠÀႠႠiÀÀႠ𐀀|
 | 
			
		||||
  Àii𐀀i𐀀iႠÀÀႠႠÀii𐀀ႠႠiÀiÀiႠÀiႠ𐀀Ⴀi𐀀𐀀𐀀À𐀀𐀀𐀀𐀀𐀀iႠ𐀀ႠႠi𐀀i𐀀iႠ𐀀ႠႠÀႠ𐀀iႠႠႠiႠႠ𐀀ႠႠႠႠ𐀀ႠÀÀႠႠႠႠÀ𐀀ႠÀႠÀiiÀiÀiÀႠi𐀀𐀀𐀀𐀀À𐀀𐀀𐀀À|
 | 
			
		||||
  ႠiÀiႠi𐀀𐀀ÀiႠiႠÀ𐀀iÀii𐀀ႠÀ𐀀𐀀ႠÀiÀÀ𐀀ႠÀÀႠ𐀀iÀiႠ𐀀𐀀Ⴀ𐀀ႠႠႠÀ𐀀iÀႠiႠÀÀ𐀀ႠÀႠႠႠႠÀ𐀀𐀀À𐀀Ⴀ𐀀À𐀀iႠi𐀀i𐀀Ⴀi𐀀Ⴀ𐀀𐀀iiiႠiႠ𐀀À𐀀ႠÀ𐀀𐀀iÀÀi|
 | 
			
		||||
  Ⴀi𐀀ÀÀiÀi𐀀iႠÀ𐀀𐀀ႠiႠÀႠÀ𐀀iÀÀÀႠ𐀀𐀀iÀÀiႠ𐀀ii𐀀i𐀀𐀀iiiႠႠႠÀÀiႠÀ𐀀i𐀀ÀiႠÀÀႠႠi𐀀ႠÀ𐀀À𐀀iiႠႠ𐀀𐀀iÀႠÀi𐀀À𐀀𐀀iÀ𐀀ii𐀀𐀀À𐀀iÀ𐀀𐀀iÀi𐀀|
 | 
			
		||||
  iႠÀiiiႠ𐀀ႠiÀႠႠႠႠ𐀀À𐀀ႠႠ𐀀ႠႠi𐀀𐀀𐀀ႠÀÀ𐀀𐀀Ⴀ𐀀iÀ𐀀ႠiႠÀ𐀀ႠႠiÀi𐀀ÀiÀႠႠÀ𐀀𐀀iiÀႠ𐀀ႠႠi𐀀𐀀ÀႠÀiႠႠiÀÀ𐀀iÀÀÀ𐀀𐀀ÀႠii𐀀ÀiiÀႠÀႠÀi𐀀𐀀iႠ|
 | 
			
		||||
  iÀ𐀀ႠÀi𐀀iႠႠii𐀀ႠÀ𐀀Ài𐀀𐀀iႠ𐀀iÀi𐀀À𐀀iÀÀiÀ𐀀ÀÀiiiÀႠႠi𐀀ႠiiiႠi𐀀iÀÀ𐀀𐀀ႠႠႠÀiiႠÀႠÀႠiႠi𐀀ႠÀÀ𐀀Ⴀ𐀀i𐀀ႠÀÀ𐀀iÀ𐀀Ⴀ𐀀iÀ𐀀Ⴀ𐀀ႠÀႠÀÀ𐀀|
 | 
			
		||||
  ႠÀ𐀀𐀀ÀiÀiႠiႠႠi𐀀𐀀𐀀ÀႠႠi𐀀À𐀀i𐀀𐀀𐀀𐀀iiÀÀÀÀ𐀀Ⴀ𐀀ii𐀀i𐀀iÀi𐀀ႠႠ𐀀iÀ𐀀𐀀𐀀ႠႠႠ𐀀𐀀𐀀𐀀i𐀀𐀀ႠiႠ𐀀i𐀀iႠi𐀀i𐀀ÀႠ𐀀iႠႠႠ𐀀À𐀀𐀀iiႠi𐀀ÀÀÀiii^𐀀 |
 | 
			
		||||
                                                                                                      |
 | 
			
		||||
      ]=],
 | 
			
		||||
      value = { col = 100, curscol = 100, endcol = 100, row = 50 },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    ascii_results,
 | 
			
		||||
    two_byte_results,
 | 
			
		||||
    { -- random
 | 
			
		||||
      screen = [=[
 | 
			
		||||
  Ⴀ𐀀ii𐀀ႠÀ𐀀i𐀀𐀀iÀÀÀiÀ𐀀Ⴀ𐀀ႠiiႠ𐀀iii𐀀ÀႠÀႠႠÀiiÀႠÀiႠi𐀀ÀÀ𐀀𐀀Ⴀ𐀀𐀀Ⴀ𐀀iÀႠ𐀀i𐀀ÀÀႠiiÀ𐀀Ⴀ𐀀À𐀀ii𐀀ÀÀ𐀀ÀႠႠ𐀀𐀀𐀀𐀀ii𐀀ႠႠiႠႠ𐀀ႠÀ𐀀ÀÀiiႠ|
 | 
			
		||||
  𐀀𐀀ÀႠÀ𐀀𐀀iႠÀ𐀀𐀀i𐀀𐀀𐀀𐀀ႠႠႠiiiiÀÀ𐀀ႠÀႠÀi𐀀i𐀀ႠႠÀiÀႠi𐀀𐀀À𐀀iÀ𐀀ÀÀÀ𐀀𐀀iÀႠ𐀀𐀀Ⴀ𐀀𐀀ÀiႠiiႠႠÀÀÀÀi𐀀ÀÀႠ𐀀Àiii𐀀iႠႠႠi𐀀ႠÀi𐀀iႠ𐀀ႠiႠ|
 | 
			
		||||
  iiႠ𐀀iÀÀiႠႠÀi𐀀ႠႠÀ𐀀𐀀ÀiiiiÀiႠ𐀀iႠÀiႠiႠႠÀiÀ𐀀ႠiiႠႠÀ𐀀Àii𐀀ႠÀႠiႠÀႠÀႠii𐀀Ài𐀀ႠႠ𐀀ÀÀÀi𐀀ÀÀÀ𐀀iႠ𐀀iႠÀ𐀀iႠi𐀀ÀiÀ𐀀ႠႠiÀ𐀀𐀀Ⴀi|
 | 
			
		||||
  À𐀀𐀀iÀiÀÀÀÀႠႠႠ𐀀iÀÀiႠ𐀀À𐀀ႠÀiiႠ𐀀iiႠႠ𐀀iÀiႠႠÀႠÀ𐀀Ài𐀀iႠ𐀀𐀀iiႠÀႠiÀÀÀiÀiiÀ𐀀i𐀀ÀÀႠ𐀀𐀀𐀀i𐀀𐀀ႠႠi𐀀À𐀀iႠi𐀀ႠႠiiiÀႠ𐀀ႠÀiÀiႠႠ|
 | 
			
		||||
  iÀiiiႠÀÀ𐀀ႠႠႠi𐀀À𐀀𐀀iiiÀÀiiÀႠÀ𐀀À𐀀ႠႠ𐀀𐀀ႠႠႠi𐀀iiÀႠ𐀀ႠႠႠÀiႠiႠiÀÀÀi𐀀iႠ𐀀ÀÀiႠ𐀀iÀÀi𐀀i𐀀𐀀ÀiÀႠ𐀀𐀀iႠ𐀀ÀÀiÀÀႠ𐀀𐀀ႠႠ𐀀𐀀𐀀𐀀Ⴀ𐀀𐀀|
 | 
			
		||||
  𐀀ÀÀႠÀ𐀀ÀÀiÀÀÀႠiiႠiiÀႠÀiႠÀiÀiႠႠ𐀀ÀÀÀႠiiÀႠ𐀀iÀi𐀀ႠႠ𐀀𐀀ÀÀ𐀀ÀiÀÀႠi𐀀iÀႠ𐀀À𐀀ႠႠÀ𐀀Ⴀiii𐀀ႠiiႠiÀႠႠiႠÀႠ𐀀ႠÀÀႠ𐀀À𐀀ÀiÀÀႠႠÀÀ|
 | 
			
		||||
  ÀႠiႠiÀ𐀀i𐀀ႠႠiႠႠÀ𐀀ÀÀÀÀ𐀀𐀀ÀႠႠ𐀀ႠÀÀiႠ𐀀i𐀀Ⴀ𐀀ÀႠi𐀀ႠÀႠÀ𐀀ႠႠ𐀀i𐀀iႠÀi𐀀i𐀀𐀀À𐀀iÀiႠႠႠ𐀀ÀiÀႠÀ𐀀ÀÀÀi𐀀𐀀𐀀ႠÀi𐀀𐀀À𐀀À𐀀𐀀iiႠiÀi𐀀i𐀀Ⴀ|
 | 
			
		||||
  𐀀𐀀i𐀀ÀႠႠ𐀀𐀀𐀀iႠႠ𐀀À𐀀ÀႠiÀ𐀀𐀀ႠÀi𐀀𐀀iiiႠ𐀀𐀀iႠÀÀ𐀀ႠiiÀႠႠÀ𐀀𐀀ႠÀႠႠÀiႠႠÀႠÀႠiႠႠ𐀀𐀀𐀀iႠႠႠiႠႠii𐀀ÀႠi𐀀ÀÀႠႠi𐀀À𐀀Ⴀ𐀀ÀÀ𐀀Ⴀ𐀀iႠiiႠႠ|
 | 
			
		||||
  Ⴀ𐀀i𐀀𐀀ÀiÀႠႠႠႠႠÀÀႠႠÀႠ𐀀ii𐀀ÀႠiႠiii𐀀i𐀀i𐀀𐀀𐀀À𐀀ii𐀀iÀiiiÀÀႠiiiႠiiႠÀ𐀀À𐀀𐀀ÀႠ𐀀iÀÀiiÀiÀ𐀀iႠi𐀀𐀀À𐀀ÀiiႠ𐀀iÀ𐀀𐀀iႠႠÀÀႠႠiiÀ|
 | 
			
		||||
  i𐀀𐀀𐀀ÀÀi𐀀ႠႠႠႠႠÀiiÀ𐀀𐀀ii𐀀Ⴀ𐀀Ài𐀀iႠiÀÀႠÀ𐀀ÀႠiႠÀi𐀀𐀀iiႠ𐀀i𐀀ႠÀiႠii𐀀𐀀À𐀀𐀀ႠႠÀႠiÀiႠÀÀi𐀀i𐀀ႠÀiႠႠႠ𐀀𐀀ÀiႠႠႠÀÀi𐀀ÀႠႠÀiႠ𐀀ႠÀ|
 | 
			
		||||
  𐀀ÀiႠႠÀ𐀀𐀀𐀀i𐀀i𐀀i𐀀ႠÀ𐀀ÀiiÀႠ𐀀ÀÀÀi𐀀ႠÀiÀႠi𐀀ႠÀiiÀÀÀiiiÀiႠႠiÀ𐀀ႠႠ𐀀iÀႠÀႠႠiÀÀႠÀႠÀÀii𐀀Ⴀi𐀀iiÀÀÀiႠ𐀀i𐀀𐀀i𐀀iiÀ𐀀𐀀𐀀ႠÀiႠ𐀀|
 | 
			
		||||
  À𐀀ႠႠႠႠ𐀀ÀiႠႠiÀ𐀀i𐀀ÀႠÀႠiiÀiÀÀiႠ𐀀𐀀𐀀Ⴀ𐀀ÀႠi𐀀𐀀iႠႠႠiiႠÀi𐀀𐀀𐀀iႠÀÀÀႠi𐀀À𐀀iiiႠÀႠiÀ𐀀iႠ𐀀ii𐀀𐀀𐀀ÀႠႠÀÀႠႠႠႠiÀi𐀀Àiii𐀀ii𐀀𐀀À|
 | 
			
		||||
  i𐀀ÀႠiႠi𐀀ႠiႠ𐀀Ⴀi𐀀ႠÀ𐀀𐀀𐀀ႠÀiiiii𐀀Ⴀ𐀀iiiÀiiÀ𐀀𐀀𐀀À𐀀𐀀Ⴀ𐀀ႠÀ𐀀ႠႠႠiÀÀÀÀii𐀀i𐀀ÀiiႠÀiÀ𐀀iႠႠiÀႠii𐀀i𐀀Ⴀ𐀀𐀀iႠႠÀ𐀀ႠiiiႠႠÀÀ𐀀iÀႠ|
 | 
			
		||||
  𐀀iii𐀀ÀႠiႠÀ𐀀𐀀i𐀀ÀႠ𐀀𐀀ႠႠÀiႠ𐀀𐀀iႠ𐀀ႠiiႠiiႠÀ𐀀𐀀ႠiÀÀႠÀiÀႠ𐀀ÀႠ𐀀ႠÀi𐀀Ⴀi𐀀𐀀𐀀𐀀𐀀À𐀀𐀀𐀀i𐀀iÀ𐀀À𐀀ÀÀÀ𐀀ႠႠ𐀀iiÀ𐀀ÀÀÀႠÀ𐀀ႠÀႠÀiÀiiÀႠ|
 | 
			
		||||
  Ⴀ𐀀𐀀ႠႠ𐀀À𐀀ÀႠ𐀀ÀႠÀ𐀀𐀀iႠႠÀÀiÀႠ𐀀ÀiÀႠi𐀀ႠÀ𐀀𐀀𐀀𐀀Ⴀ𐀀iႠÀ𐀀iÀ𐀀iÀ𐀀iÀÀႠi𐀀iÀႠi𐀀ႠiiႠÀ𐀀À𐀀ႠႠÀÀi𐀀ႠႠ𐀀iiႠÀiႠ𐀀𐀀𐀀𐀀ႠႠႠÀႠiÀႠiÀÀ𐀀À|
 | 
			
		||||
  ÀႠ𐀀iiiÀÀ𐀀ÀႠiႠ𐀀ႠႠႠ𐀀iÀ𐀀ႠiႠ𐀀i𐀀ÀÀiႠ𐀀ÀiiႠႠiÀÀ𐀀ÀiႠiÀ𐀀i𐀀ÀiÀ𐀀ÀႠiÀiႠႠi𐀀iႠÀiÀÀႠႠiÀiႠÀႠi𐀀𐀀ႠiÀႠii𐀀ႠiiႠi𐀀Ⴀi𐀀ÀiÀÀ𐀀|
 | 
			
		||||
  ÀႠÀÀ𐀀i𐀀iႠÀÀÀႠ𐀀𐀀ÀႠÀÀiii𐀀𐀀iiÀiiႠÀÀႠiÀiÀÀ𐀀i𐀀i𐀀ႠiႠႠiႠÀiiÀႠ𐀀ႠႠÀiÀႠ𐀀𐀀iÀ𐀀Ⴀ𐀀iÀ𐀀ႠÀÀႠÀÀÀ𐀀𐀀i𐀀𐀀À𐀀𐀀ii𐀀À𐀀𐀀ႠÀ𐀀ႠႠႠ𐀀𐀀|
 | 
			
		||||
  Ⴀ𐀀i𐀀𐀀i𐀀𐀀Ⴀ𐀀iÀ𐀀ÀiႠiiÀÀiÀÀÀiÀiiႠႠ𐀀iႠiÀi𐀀Ⴀ𐀀ႠÀ𐀀Ⴀ𐀀𐀀𐀀ႠÀႠ𐀀ႠiiႠiiiႠÀÀiÀ𐀀ÀÀ𐀀ÀႠÀ𐀀iiÀÀiÀiÀÀÀႠႠÀÀii𐀀ႠÀÀiႠiÀÀ𐀀iiiÀ|
 | 
			
		||||
  ÀÀÀÀiႠiႠႠႠiႠ𐀀ႠÀÀÀ𐀀ÀÀiႠÀ𐀀ÀiႠÀႠÀႠႠÀÀႠiÀႠႠiiႠÀ𐀀ႠႠÀiႠ𐀀iÀႠ𐀀Ⴀ𐀀Ⴀ𐀀iႠÀႠi𐀀𐀀Ⴀ𐀀iÀ𐀀ÀႠ𐀀ÀÀႠ𐀀Ⴀi𐀀iႠÀ𐀀𐀀𐀀𐀀i𐀀i𐀀𐀀𐀀ÀႠiÀÀ𐀀i|
 | 
			
		||||
  i𐀀iÀ𐀀i𐀀iႠႠႠÀÀiiiႠi𐀀iÀÀ𐀀iႠÀÀ𐀀ii𐀀i𐀀𐀀ÀÀÀÀiÀiiÀiiiÀiÀi𐀀𐀀ႠႠÀႠႠÀiÀÀႠiႠႠiÀႠÀiႠႠ𐀀ႠႠႠÀႠÀႠ𐀀iiႠႠႠ𐀀iÀ𐀀iႠ𐀀iÀiÀiÀi|
 | 
			
		||||
  𐀀𐀀iiÀ𐀀ÀႠ𐀀𐀀𐀀𐀀𐀀Àiii𐀀𐀀𐀀Ⴀ𐀀𐀀i𐀀ÀÀ𐀀iiÀiiiiÀ𐀀iႠiႠiÀႠÀႠÀiႠႠႠႠႠႠႠႠႠÀiiႠiÀႠÀ𐀀iiႠÀႠiႠႠÀiႠ𐀀iႠ𐀀iiႠÀ𐀀𐀀Àii𐀀i𐀀ÀႠÀÀiÀÀ|
 | 
			
		||||
  Ⴀ𐀀iÀiÀÀÀÀႠi𐀀ႠႠi𐀀ႠႠႠ𐀀ႠႠ𐀀iÀÀÀiÀi𐀀Ài𐀀𐀀𐀀ႠÀiႠiႠ𐀀𐀀Àiii𐀀ÀÀiႠÀiႠ𐀀𐀀i𐀀ÀÀiiÀiႠႠi𐀀À𐀀ÀႠÀႠiiiiii𐀀Ⴀ𐀀ÀႠ𐀀ႠႠႠ𐀀𐀀iႠႠႠႠiÀ|
 | 
			
		||||
  𐀀𐀀𐀀i𐀀iÀ𐀀𐀀iÀ𐀀Ài𐀀𐀀ႠႠ𐀀ႠÀi𐀀𐀀ÀÀiiiႠiႠ𐀀iႠÀႠÀ𐀀ႠÀ𐀀ႠiiiiÀiႠÀiiiႠႠÀ𐀀Ⴀ𐀀𐀀𐀀ÀႠiÀႠiiiiႠiႠ𐀀Ⴀ𐀀ÀႠÀii𐀀i𐀀Ⴀ𐀀À𐀀iႠႠ𐀀iႠiiii𐀀|
 | 
			
		||||
  ÀÀÀႠႠÀ𐀀À𐀀À𐀀iႠ𐀀𐀀𐀀À𐀀ႠႠ𐀀ÀiÀiÀi𐀀Ⴀ𐀀iiႠiiÀႠÀႠ𐀀ႠÀi𐀀𐀀iÀiÀႠi𐀀À𐀀𐀀ÀႠ𐀀𐀀iႠÀ𐀀Ⴀ𐀀ÀiÀÀႠÀiÀ𐀀ႠႠႠiiÀi𐀀ÀႠiÀÀÀiႠႠiÀÀiÀႠÀႠÀ|
 | 
			
		||||
  iႠÀÀႠÀÀ𐀀𐀀𐀀iiiÀ𐀀À𐀀iÀÀi𐀀À𐀀ÀႠÀiÀii𐀀ႠႠii𐀀ႠႠ𐀀𐀀ÀႠ𐀀ÀÀ𐀀ÀÀÀÀi𐀀ÀႠ𐀀À𐀀ÀiiႠ𐀀ÀÀႠiႠ𐀀Ⴀ𐀀ÀÀ𐀀ႠႠ𐀀ႠႠÀ𐀀ႠႠႠ𐀀iiÀႠÀႠiႠi𐀀ii𐀀Ài|
 | 
			
		||||
  ÀiÀႠ𐀀À𐀀𐀀ii𐀀𐀀ႠႠiiiiႠiÀiႠiÀiÀ𐀀ÀiႠÀiiÀÀÀ𐀀𐀀Ⴀ𐀀ႠႠ𐀀iÀiႠ𐀀ii𐀀ႠÀ𐀀ႠiႠÀiiႠÀႠ𐀀Ài𐀀Àii𐀀iiÀiႠÀiႠႠÀiÀ𐀀i𐀀ႠÀ𐀀iÀÀႠii𐀀iÀ𐀀|
 | 
			
		||||
  Ⴀ𐀀ႠÀ𐀀Ⴀi𐀀iÀ𐀀Ⴀ𐀀𐀀iÀiÀ𐀀iႠi𐀀ႠÀiႠႠiÀ𐀀iႠiÀႠi𐀀𐀀iႠiႠႠ𐀀ÀÀi𐀀iÀႠႠ𐀀ႠÀ𐀀𐀀𐀀ႠႠiÀÀiႠႠႠ𐀀𐀀ႠႠႠႠÀiႠ𐀀Ài𐀀iÀႠii𐀀ÀႠii𐀀Ⴀ𐀀ÀႠÀ𐀀Ⴀi|
 | 
			
		||||
  À𐀀iႠႠiႠi𐀀iႠii𐀀Ⴀi𐀀ii𐀀iႠÀ𐀀ႠÀÀi𐀀iÀ𐀀iÀ𐀀ႠÀႠiႠÀႠi𐀀Ⴀ𐀀ÀႠႠiႠ𐀀iႠiÀ𐀀À𐀀ÀႠi𐀀𐀀iÀi𐀀À𐀀Ⴀ𐀀ႠႠi𐀀𐀀𐀀iႠÀ𐀀ÀႠÀ𐀀i𐀀i𐀀iÀႠÀÀÀ𐀀iႠ𐀀|
 | 
			
		||||
  𐀀ႠႠႠႠႠ𐀀Ⴀ𐀀ÀÀ𐀀𐀀iÀÀႠiÀiiႠÀiႠ𐀀𐀀ÀÀiÀႠ𐀀ÀÀႠ𐀀À𐀀À𐀀iႠႠÀiႠÀiiiႠÀiÀÀÀ𐀀iႠႠႠÀÀiႠႠႠ𐀀ii𐀀ii𐀀iႠႠii𐀀iႠÀi𐀀𐀀𐀀ii𐀀ÀႠႠiႠÀ𐀀ႠႠ|
 | 
			
		||||
  𐀀iiႠi𐀀ÀႠ𐀀ÀiႠÀÀiÀÀÀÀႠ𐀀ÀiiႠi𐀀iႠi𐀀ÀႠÀ𐀀𐀀𐀀iÀiÀ𐀀ႠiÀ𐀀ႠႠÀÀi𐀀Ⴀ𐀀i𐀀ႠÀႠiႠiႠiii𐀀ÀႠႠ𐀀𐀀ÀႠ𐀀𐀀ii𐀀ÀiiÀÀႠiÀ𐀀ÀÀÀiÀႠÀ𐀀Ài𐀀Ⴀ|
 | 
			
		||||
  𐀀iႠ𐀀ႠႠႠÀÀ𐀀iiiÀÀႠii𐀀i𐀀ÀÀႠ𐀀ႠႠiႠႠႠiႠ𐀀i𐀀À𐀀ႠႠႠi𐀀Ài𐀀ႠÀ𐀀ÀႠiiiiႠiiႠႠi𐀀𐀀ÀႠ𐀀ႠiÀÀ𐀀iiiႠÀiႠi𐀀À𐀀𐀀i𐀀𐀀iiႠ𐀀À𐀀iiႠႠÀ𐀀iႠ|
 | 
			
		||||
  ÀႠ𐀀ႠႠႠÀii𐀀iiႠႠ𐀀iႠ𐀀iiႠii𐀀𐀀𐀀iiႠiÀ𐀀ႠႠÀÀÀiႠႠ𐀀À𐀀ႠႠႠ𐀀ÀiႠ𐀀ÀiႠ𐀀i𐀀ÀÀ𐀀Ⴀ𐀀Ⴀ𐀀iÀ𐀀iiiÀႠiÀႠiÀႠႠÀႠii𐀀ႠႠႠÀii𐀀i𐀀iiႠiÀ𐀀𐀀|
 | 
			
		||||
  ႠႠÀÀႠႠ𐀀iÀႠ𐀀iႠႠÀÀ𐀀ÀiႠ𐀀iÀႠႠႠ𐀀𐀀À𐀀ii𐀀𐀀À𐀀𐀀ÀႠႠႠÀႠiiႠ𐀀𐀀ii𐀀ႠႠÀႠiႠi𐀀ႠႠ𐀀i𐀀𐀀𐀀𐀀Ⴀ𐀀iÀiႠႠ𐀀À𐀀ÀႠႠႠÀÀ𐀀𐀀iÀႠi𐀀𐀀ÀiႠႠÀÀiÀÀ|
 | 
			
		||||
  ÀiႠii𐀀ÀÀႠi𐀀ÀႠi𐀀À𐀀ii𐀀iႠi𐀀ÀႠ𐀀À𐀀ÀÀႠႠ𐀀i𐀀ႠiiႠ𐀀ÀÀ𐀀À𐀀i𐀀𐀀i𐀀Ⴀ𐀀Ⴀi𐀀𐀀𐀀i𐀀Ài𐀀𐀀ÀႠႠi𐀀ÀiÀႠÀiiiÀÀႠႠi𐀀ÀiÀiႠÀÀiÀ𐀀𐀀ÀiiiiÀ|
 | 
			
		||||
  𐀀ႠÀiÀ𐀀ႠႠ𐀀i𐀀ႠႠ𐀀𐀀𐀀𐀀𐀀𐀀iႠÀႠႠiiÀႠ𐀀𐀀i𐀀ႠႠ𐀀ႠႠႠ𐀀𐀀iÀiÀႠ𐀀À𐀀À𐀀𐀀ႠiiiႠiiiႠiႠ𐀀ÀiiiႠ𐀀À𐀀ÀႠႠÀ𐀀À𐀀𐀀𐀀ႠiႠi𐀀ႠÀÀႠiiႠႠႠႠ𐀀ÀႠ𐀀𐀀|
 | 
			
		||||
  Ⴀ𐀀ႠႠÀ𐀀iႠ𐀀ႠiiႠi𐀀ÀႠႠ𐀀iÀiÀÀiႠi𐀀iiÀ𐀀ႠႠႠiÀႠ𐀀ႠႠႠÀ𐀀ႠiႠ𐀀𐀀ÀႠÀÀ𐀀ÀႠႠiႠ𐀀𐀀iiÀÀ𐀀À𐀀iႠiÀ𐀀iÀ𐀀𐀀iiiiii𐀀ÀiႠ𐀀𐀀i𐀀Ài𐀀À𐀀𐀀i𐀀Ⴀ|
 | 
			
		||||
  Ⴀ𐀀iÀÀႠ𐀀ႠႠÀႠÀ𐀀ႠႠÀiiÀÀÀႠ𐀀i𐀀Ⴀ𐀀ႠÀ𐀀𐀀iႠႠႠႠiiႠႠႠÀiႠÀiiႠÀ𐀀Ⴀ𐀀Ⴀi𐀀𐀀ႠÀ𐀀ÀႠႠ𐀀ÀႠÀႠ𐀀Ⴀ𐀀iႠi𐀀ÀႠႠii𐀀ÀႠÀ𐀀Ⴀ𐀀Ài𐀀À𐀀ÀÀႠiႠႠii𐀀|
 | 
			
		||||
  ႠÀႠႠႠiႠ𐀀Ⴀ𐀀Ⴀ𐀀ႠiiႠÀ𐀀ÀiiÀi𐀀iÀÀiiiiÀႠÀႠÀႠiiႠ𐀀𐀀ႠÀÀ𐀀𐀀À𐀀ÀႠႠ𐀀𐀀iii𐀀iiÀ𐀀ႠiႠႠÀ𐀀ÀÀii𐀀ႠÀႠi𐀀𐀀Ⴀi𐀀ÀႠႠ𐀀ÀÀÀ𐀀iÀႠႠ𐀀ÀÀi𐀀i|
 | 
			
		||||
  ÀႠႠႠÀႠႠii𐀀Ài𐀀Ⴀi𐀀i𐀀i𐀀Ⴀ𐀀ÀÀÀiÀÀi𐀀ÀÀÀ𐀀i𐀀iÀ𐀀𐀀ႠiÀi𐀀𐀀𐀀ႠÀiÀ𐀀𐀀iÀÀႠႠ𐀀ႠႠႠႠÀÀÀÀiiÀ𐀀iiႠႠႠi𐀀ႠÀi𐀀ÀႠႠÀ𐀀ႠiiႠ𐀀𐀀i𐀀Ⴀi𐀀𐀀À|
 | 
			
		||||
  𐀀À𐀀Ⴀ𐀀𐀀iÀÀiႠiiÀႠÀiÀႠÀႠႠ𐀀iႠႠÀiႠႠႠႠ𐀀iႠÀÀႠႠiႠ𐀀ÀiႠႠi𐀀𐀀ႠiÀ𐀀ÀiÀi𐀀i𐀀Ⴀi𐀀ႠÀiiÀႠÀi𐀀Ài𐀀ÀÀÀi𐀀𐀀ÀႠi𐀀ႠiႠÀiÀiႠ𐀀ii𐀀𐀀𐀀À|
 | 
			
		||||
  ÀႠiiÀi𐀀ႠÀÀiÀႠi𐀀ÀႠႠ𐀀ႠiÀiiႠiiႠႠ𐀀ii𐀀i𐀀𐀀𐀀𐀀i𐀀ႠႠÀႠÀÀiÀÀÀႠÀႠႠÀÀႠ𐀀ÀÀ𐀀ÀÀႠÀ𐀀𐀀ÀiႠ𐀀ႠႠ𐀀iÀ𐀀Ⴀ𐀀iÀႠႠ𐀀iÀiiii𐀀ii𐀀𐀀ႠႠႠ𐀀Ⴀ|
 | 
			
		||||
  ႠÀ𐀀ႠႠÀÀiႠႠ𐀀iႠႠႠÀႠ𐀀ႠႠ𐀀iႠ𐀀𐀀𐀀𐀀ႠiÀ𐀀ႠႠÀ𐀀Ⴀ𐀀ÀÀii𐀀Ⴀ𐀀ÀÀ𐀀ÀÀႠ𐀀ႠÀiÀ𐀀𐀀À𐀀À𐀀𐀀iႠi𐀀ÀႠႠႠႠ𐀀ႠႠÀÀ𐀀𐀀𐀀ÀÀiiႠÀÀ𐀀ႠႠႠiÀႠÀႠႠiÀႠ𐀀|
 | 
			
		||||
  𐀀ÀiÀႠÀiiÀÀiiႠႠႠi𐀀ÀÀiႠ𐀀iႠႠÀ𐀀𐀀𐀀ÀႠႠÀ𐀀ႠÀÀႠ𐀀ÀÀ𐀀𐀀Ⴀ𐀀ÀÀ𐀀ÀႠ𐀀ႠÀiÀ𐀀iႠ𐀀ႠႠ𐀀𐀀À𐀀iii𐀀iiႠÀႠiႠÀႠ𐀀Ⴀ𐀀i𐀀𐀀ÀႠႠi𐀀𐀀ႠႠ𐀀𐀀𐀀𐀀À𐀀Ⴀ𐀀|
 | 
			
		||||
  Àiiiii𐀀iႠÀÀÀiiႠiii𐀀𐀀ÀiÀႠÀÀiႠႠ𐀀iiÀÀႠ𐀀ႠiÀႠ𐀀𐀀ii𐀀iႠÀ𐀀iiႠ𐀀Ⴀ𐀀𐀀i𐀀Ⴀ𐀀i𐀀𐀀𐀀ÀႠiႠiႠi𐀀iiiÀii𐀀𐀀Àii𐀀À𐀀Ⴀ𐀀𐀀Ⴀ𐀀i𐀀ႠÀႠii𐀀Ⴀ|
 | 
			
		||||
  𐀀iiÀႠiႠiÀႠ𐀀i𐀀iii𐀀Ⴀ𐀀i𐀀iÀÀi𐀀Ⴀii𐀀ÀiÀiiiÀႠÀ𐀀ÀÀႠ𐀀Ⴀ𐀀iiÀi𐀀i𐀀𐀀i𐀀ႠiiiÀႠႠႠiiÀ𐀀À𐀀𐀀iÀ𐀀iႠÀႠÀÀi𐀀ႠiÀႠÀ𐀀𐀀iÀÀ𐀀i𐀀𐀀ÀÀႠ𐀀|
 | 
			
		||||
  𐀀iႠႠÀ𐀀ÀႠÀ𐀀iႠ𐀀Àii𐀀i𐀀𐀀ÀÀi𐀀𐀀𐀀iiႠÀ𐀀ii𐀀Ⴀ𐀀𐀀iႠi𐀀iÀ𐀀À𐀀ႠႠ𐀀À𐀀i𐀀𐀀iႠiiÀÀႠႠႠiÀÀiÀÀ𐀀𐀀𐀀À𐀀𐀀𐀀ႠÀႠ𐀀iÀ𐀀𐀀Ⴀ𐀀ႠႠii𐀀𐀀ÀÀႠÀi𐀀𐀀i|
 | 
			
		||||
  ÀiÀႠႠႠႠii𐀀ÀÀ𐀀𐀀𐀀Ⴀi𐀀À𐀀ÀႠiiÀi𐀀Ⴀii𐀀iÀÀ𐀀ႠiႠ𐀀ႠiiiႠÀÀiÀÀÀÀ𐀀ႠႠii𐀀À𐀀ÀiÀi𐀀ÀÀi𐀀iႠiÀi𐀀ÀiÀi𐀀ÀiÀႠ𐀀i𐀀Ⴀi𐀀𐀀𐀀ႠႠ𐀀ႠÀႠÀႠi|
 | 
			
		||||
  À𐀀𐀀i𐀀Ài𐀀𐀀ႠiႠÀႠiiႠiႠÀ𐀀𐀀ÀiÀ𐀀𐀀ÀÀႠႠႠ𐀀ႠiÀႠႠÀ𐀀Ⴀi𐀀𐀀ÀiÀ𐀀À𐀀iႠi𐀀𐀀ÀÀ𐀀iႠiႠႠ𐀀ÀÀ𐀀𐀀ÀiÀÀ𐀀ÀÀ𐀀i𐀀ÀÀ𐀀𐀀ÀÀႠii𐀀Ⴀ𐀀Ⴀ𐀀iiÀÀÀi𐀀À|
 | 
			
		||||
  i𐀀ႠiÀႠႠÀÀ𐀀𐀀ii𐀀ÀÀ𐀀iÀiÀႠÀiiii𐀀ÀiÀႠi𐀀i𐀀𐀀i𐀀𐀀iႠ𐀀iÀi𐀀ÀÀÀÀiႠiÀႠÀÀႠiiÀÀႠႠi𐀀iႠiiႠi𐀀Ⴀ𐀀𐀀ÀႠႠÀႠiႠႠÀ𐀀iiÀႠႠႠ𐀀𐀀Ⴀi𐀀Ⴀi|
 | 
			
		||||
  ႠÀႠ𐀀ÀႠ𐀀iႠÀ𐀀ႠiႠii𐀀ÀႠÀÀ𐀀i𐀀Ⴀi𐀀ႠiÀ𐀀ႠÀÀÀiÀÀÀႠ𐀀𐀀ႠiiႠ𐀀ÀႠiiÀiiႠႠi𐀀ÀiiႠ𐀀iÀႠÀi𐀀À𐀀ÀiÀÀÀi𐀀ÀÀႠႠiÀiႠ𐀀ii𐀀ႠÀiႠÀႠႠi^i |
 | 
			
		||||
                                                                                                      |
 | 
			
		||||
      ]=],
 | 
			
		||||
      value = { col = 100, curscol = 100, endcol = 100, row = 50 },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
		Reference in New Issue
	
	Block a user