diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/nvim/CMakeLists.txt | 122 | ||||
| -rw-r--r-- | src/nvim/drawline.c | 1775 | ||||
| -rw-r--r-- | src/nvim/insexpand.c | 14 | ||||
| -rw-r--r-- | src/nvim/po/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/nvim/testdir/test_breakindent.vim | 18 | ||||
| -rw-r--r-- | src/nvim/testdir/test_filetype.vim | 1 | ||||
| -rw-r--r-- | src/nvim/testdir/test_ins_complete.vim | 48 | ||||
| -rw-r--r-- | src/nvim/testdir/test_listlbr.vim | 24 | 
8 files changed, 1045 insertions, 960 deletions
| diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 9bd79a6ba7..8c23b501f9 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -1,22 +1,19 @@  add_library(main_lib INTERFACE)  add_executable(nvim main.c) -add_library(libuv_lib INTERFACE) +add_library(libuv INTERFACE)  find_package(libuv CONFIG)  if(TARGET libuv::uv_a) -  target_link_libraries(libuv_lib INTERFACE libuv::uv_a) +  target_link_libraries(libuv INTERFACE libuv::uv_a) +  mark_as_advanced(libuv_DIR)  else()    # Fall back to find module for older libuv versions that don't provide config file -  find_package(LibUV 1.28.0 REQUIRED MODULE) -  target_include_directories(libuv_lib SYSTEM BEFORE INTERFACE ${LIBUV_INCLUDE_DIRS}) -  target_link_libraries(libuv_lib INTERFACE ${LIBUV_LIBRARIES}) +  find_package(Libuv 1.28.0 REQUIRED MODULE) +  target_include_directories(libuv SYSTEM BEFORE INTERFACE ${LIBUV_INCLUDE_DIRS}) +  target_link_libraries(libuv INTERFACE ${LIBUV_LIBRARIES})  endif() -find_package(Msgpack 1.0.0 REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${MSGPACK_INCLUDE_DIRS}) -target_link_libraries(main_lib INTERFACE ${MSGPACK_LIBRARIES}) - -find_package(LibLUV 1.43.0 REQUIRED) +find_package(Libluv 1.43.0 REQUIRED)  target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBLUV_INCLUDE_DIRS})  # Use "luv" as imported library, to work around CMake using "-lluv" for  # "luv.so".  #10407 @@ -24,33 +21,28 @@ add_library(luv UNKNOWN IMPORTED)  set_target_properties(luv PROPERTIES IMPORTED_LOCATION ${LIBLUV_LIBRARIES})  target_link_libraries(main_lib INTERFACE luv) -find_package(TreeSitter REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${TreeSitter_INCLUDE_DIR}) -target_link_libraries(main_lib INTERFACE ${TreeSitter_LIBRARY}) - -find_package(unibilium 2.0 REQUIRED) -target_link_libraries(main_lib INTERFACE unibilium) - -find_package(LibTermkey 0.22 REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBTERMKEY_INCLUDE_DIR}) -target_link_libraries(main_lib INTERFACE ${LIBTERMKEY_LIBRARY}) - -find_package(libvterm 0.3 REQUIRED) -target_link_libraries(main_lib INTERFACE libvterm) -  find_package(Iconv REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${ICONV_INCLUDE_DIR}) -if(ICONV_LIBRARY) -  target_link_libraries(main_lib INTERFACE ${ICONV_LIBRARY}) -endif() +find_package(Libtermkey 0.22 REQUIRED) +find_package(Libvterm 0.3 REQUIRED) +find_package(Msgpack 1.0.0 REQUIRED) +find_package(Treesitter REQUIRED) +find_package(Unibilium 2.0 REQUIRED) + +target_link_libraries(main_lib INTERFACE +  iconv +  libtermkey +  libvterm +  msgpack +  treesitter +  unibilium)  option(ENABLE_LIBINTL "enable libintl" ON)  if(ENABLE_LIBINTL) -  # LibIntl (not Intl) selects our FindLibIntl.cmake script. #8464 -  find_package(LibIntl REQUIRED) -  target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LibIntl_INCLUDE_DIR}) -  if (LibIntl_LIBRARY) -    target_link_libraries(main_lib INTERFACE ${LibIntl_LIBRARY}) +  # Libintl (not Intl) selects our FindLibintl.cmake script. #8464 +  find_package(Libintl REQUIRED) +  target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBINTL_INCLUDE_DIR}) +  if (LIBINTL_LIBRARY) +    target_link_libraries(main_lib INTERFACE ${LIBINTL_LIBRARY})    endif()  endif() @@ -61,9 +53,9 @@ if(PREFER_LUA)    target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUA_INCLUDE_DIR})    target_link_libraries(main_lib INTERFACE ${LUA_LIBRARIES})    # Passive (not REQUIRED): if LUAJIT_FOUND is not set, fixtures for unittests is skipped. -  find_package(LuaJit) +  find_package(Luajit)  else() -  find_package(LuaJit REQUIRED) +  find_package(Luajit REQUIRED)    target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUAJIT_INCLUDE_DIRS})    target_link_libraries(main_lib INTERFACE ${LUAJIT_LIBRARIES})  endif() @@ -191,52 +183,6 @@ if(UNSIGNED_CHAR)    target_compile_options(main_lib INTERFACE -funsigned-char)  endif() -list(APPEND CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}") -check_c_source_compiles(" -#include <msgpack.h> - -int -main(void) -{ -  return MSGPACK_OBJECT_FLOAT32; -} -" MSGPACK_HAS_FLOAT32) -list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}") -if(MSGPACK_HAS_FLOAT32) -  target_compile_definitions(main_lib INTERFACE NVIM_MSGPACK_HAS_FLOAT32) -endif() - -list(APPEND CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}") -list(APPEND CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}") -check_c_source_compiles(" -#include <tree_sitter/api.h> -int -main(void) -{ -  TSQueryCursor *cursor = ts_query_cursor_new(); -  ts_query_cursor_set_match_limit(cursor, 32); -  return 0; -} -" TS_HAS_SET_MATCH_LIMIT) -if(TS_HAS_SET_MATCH_LIMIT) -  target_compile_definitions(main_lib INTERFACE NVIM_TS_HAS_SET_MATCH_LIMIT) -endif() -check_c_source_compiles(" -#include <stdlib.h> -#include <tree_sitter/api.h> -int -main(void) -{ -  ts_set_allocator(malloc, calloc, realloc, free); -  return 0; -} -" TS_HAS_SET_ALLOCATOR) -if(TS_HAS_SET_ALLOCATOR) -  target_compile_definitions(main_lib INTERFACE NVIM_TS_HAS_SET_ALLOCATOR) -endif() -list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}") -list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}") -  target_compile_definitions(main_lib INTERFACE INCLUDE_GENERATED_DECLARATIONS)  # Remove --sort-common from linker flags, as this seems to cause bugs (see #2641, #3374). @@ -260,7 +206,7 @@ endif()  if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")    if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")      target_link_libraries(nvim PRIVATE -Wl,--no-undefined -lsocket) -  elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") +  elseif(NOT APPLE)      target_link_libraries(nvim PRIVATE -Wl,--no-undefined)    endif() @@ -287,7 +233,7 @@ if(WIN32)      # Enable wmain      target_link_libraries(nvim PRIVATE -municode)    endif() -elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") +elseif(APPLE)    target_link_libraries(nvim PRIVATE "-framework CoreServices")  endif() @@ -437,7 +383,7 @@ else()  endif()  # Log level (MIN_LOG_LEVEL in log.h) -if($ENV{CI} MATCHES "true") +if($ENV{CI})    set(MIN_LOG_LEVEL 3)  endif()  if("${MIN_LOG_LEVEL}" MATCHES "^$") @@ -466,7 +412,7 @@ get_target_property(prop main_lib INTERFACE_INCLUDE_DIRECTORIES)  foreach(gen_include ${prop})    list(APPEND gen_cflags "-I${gen_include}")  endforeach() -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT) +if(APPLE AND CMAKE_OSX_SYSROOT)    list(APPEND gen_cflags "-isysroot")    list(APPEND gen_cflags "${CMAKE_OSX_SYSROOT}")  endif() @@ -718,7 +664,7 @@ if(${CMAKE_VERSION} VERSION_LESS 3.20)    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)  endif() -target_link_libraries(nvim PRIVATE main_lib PUBLIC libuv_lib) +target_link_libraries(nvim PRIVATE main_lib PUBLIC libuv)  install_helper(TARGETS nvim)  if(MSVC)    install(FILES $<TARGET_PDB_FILE:nvim> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) @@ -829,12 +775,10 @@ if(WIN32)    file(WRITE ${PROJECT_BINARY_DIR}/external_blobs.cmake ${EXTERNAL_BLOBS_SCRIPT})    add_custom_target(external_blobs      COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/external_blobs.cmake) -  set_target_properties(external_blobs PROPERTIES FOLDER deps)    add_dependencies(nvim_runtime_deps external_blobs)  else()    add_custom_target(nvim_runtime_deps)  # Stub target to avoid CMP0046.  endif() -set_target_properties(nvim_runtime_deps PROPERTIES FOLDER deps)  file(MAKE_DIRECTORY ${BINARY_LIB_DIR}) @@ -862,7 +806,7 @@ set_target_properties(      OUTPUT_NAME ${LIBNVIM_NAME}  )  target_compile_definitions(libnvim PRIVATE MAKE_LIB) -target_link_libraries(libnvim PRIVATE main_lib PUBLIC libuv_lib) +target_link_libraries(libnvim PRIVATE main_lib PUBLIC libuv)  if(CLANG_ASAN_UBSAN)    message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.") diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index bf8649afe0..dbbeabbba2 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -68,6 +68,60 @@ typedef enum {    WL_LINE,       // text in the line  } LineDrawState; +/// structure with variables passed between win_line() and other functions +typedef struct { +  LineDrawState draw_state;  ///< what to draw next + +  linenr_T lnum;             ///< line number to be drawn +  foldinfo_T foldinfo;       ///< fold info for this line + +  int startrow;              ///< first row in the window to be drawn +  int row;                   ///< row in the window, excl w_winrow + +  colnr_T vcol;              ///< virtual column, before wrapping +  int col;                   ///< visual column on screen, after wrapping +  int boguscols;             ///< nonexistent columns added to "col" to force wrapping +  int vcol_off;              ///< offset for concealed characters + +  int off;                   ///< offset relative start of line + +  int cul_attr;              ///< set when 'cursorline' active +  int line_attr;             ///< attribute for the whole line +  int line_attr_lowprio;     ///< low-priority attribute for the line + +  int fromcol;               ///< start of inverting +  int tocol;                 ///< end of inverting + +  long vcol_sbr;             ///< virtual column after showbreak +  bool need_showbreak;       ///< overlong line, skipping first x chars + +  int char_attr;             ///< attributes for next character + +  int n_extra;               ///< number of extra bytes +  char *p_extra;             ///< string of extra chars, plus NUL, only used +                             ///< when c_extra and c_final are NUL +  char *p_extra_free;        ///< p_extra buffer that needs to be freed +  int extra_attr;            ///< attributes for p_extra +  int c_extra;               ///< extra chars, all the same +  int c_final;               ///< final char, mandatory if set + +  // saved "extra" items for when draw_state becomes WL_LINE (again) +  int saved_n_extra; +  char *saved_p_extra; +  int saved_c_extra; +  int saved_c_final; +  int saved_char_attr; + +  char extra[57];            ///< sign, line number and 'fdc' must fit in here + +  hlf_T diff_hlf;            ///< type of diff highlighting + +  int n_virt_lines;          ///< nr of virtual lines +  int filler_lines;          ///< nr of filler lines to be drawn +  int filler_todo;           ///< nr of filler lines still to do + 1 +  SignTextAttrs sattrs[SIGN_SHOW_MAX];  ///< sign attributes for the sign column +} winlinevars_T; +  /// for line_putchar. Contains the state that needs to be remembered from  /// putting one character to the next.  typedef struct { @@ -300,104 +354,230 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,  }  /// Return true if CursorLineSign highlight is to be used. -static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) +static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)  {    return wp->w_p_cul           && lnum == wp->w_cursorline           && (wp->w_p_culopt_flags & CULOPT_NBR);  } -// Get information needed to display the sign in line 'lnum' in window 'wp'. -// If 'nrcol' is true, the sign is going to be displayed in the number column. -// Otherwise the sign is going to be displayed in the sign column. -// -// @param count max number of signs -// @param[out] n_extrap number of characters from pp_extra to display -// @param sign_idxp Index of the displayed sign -static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, SignTextAttrs sattrs[], -                                  int row, int startrow, int filler_lines, int filler_todo, -                                  int *c_extrap, int *c_finalp, char *extra, size_t extra_size, -                                  char **pp_extra, int *n_extrap, int *char_attrp, int sign_idx, -                                  int cul_attr) +/// Setup for drawing the 'foldcolumn', if there is one. +static void handle_foldcolumn(win_T *wp, winlinevars_T *wlv) +{ +  int fdc = compute_foldcolumn(wp, 0); +  if (fdc <= 0) { +    return; +  } + +  // Allocate a buffer, "wlv->extra[]" may already be in use. +  xfree(wlv->p_extra_free); +  wlv->p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1); +  wlv->n_extra = (int)fill_foldcolumn(wlv->p_extra_free, wp, wlv->foldinfo, wlv->lnum); +  wlv->p_extra_free[wlv->n_extra] = NUL; +  wlv->p_extra = wlv->p_extra_free; +  wlv->c_extra = NUL; +  wlv->c_final = NUL; +  if (use_cursor_line_highlight(wp, wlv->lnum)) { +    wlv->char_attr = win_hl_attr(wp, HLF_CLF); +  } else { +    wlv->char_attr = win_hl_attr(wp, HLF_FC); +  } +} + +/// Get information needed to display the sign in line "wlv->lnum" in window "wp". +/// If "nrcol" is true, the sign is going to be displayed in the number column. +/// Otherwise the sign is going to be displayed in the sign column. +static void get_sign_display_info(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, +                                  int sign_cul_attr)  {    // Draw cells with the sign value or blank. -  *c_extrap = ' '; -  *c_finalp = NUL; +  wlv->c_extra = ' '; +  wlv->c_final = NUL;    if (nrcol) { -    *n_extrap = number_width(wp) + 1; +    wlv->n_extra = number_width(wp) + 1;    } else { -    if (use_cursor_line_sign(wp, lnum)) { -      *char_attrp = win_hl_attr(wp, HLF_CLS); +    if (use_cursor_line_highlight(wp, wlv->lnum)) { +      wlv->char_attr = win_hl_attr(wp, HLF_CLS);      } else { -      *char_attrp = win_hl_attr(wp, HLF_SC); +      wlv->char_attr = win_hl_attr(wp, HLF_SC);      } -    *n_extrap = win_signcol_width(wp); +    wlv->n_extra = win_signcol_width(wp);    } -  if (row == startrow + filler_lines && filler_todo <= 0) { -    SignTextAttrs *sattr = sign_get_attr(sign_idx, sattrs, wp->w_scwidth); +  if (wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) { +    SignTextAttrs *sattr = sign_get_attr(sign_idx, wlv->sattrs, wp->w_scwidth);      if (sattr != NULL) { -      *pp_extra = sattr->text; -      if (*pp_extra != NULL) { -        *c_extrap = NUL; -        *c_finalp = NUL; +      wlv->p_extra = sattr->text; +      if (wlv->p_extra != NULL) { +        wlv->c_extra = NUL; +        wlv->c_final = NUL;          if (nrcol) {            int n, width = number_width(wp) - 2;            for (n = 0; n < width; n++) { -            extra[n] = ' '; +            wlv->extra[n] = ' ';            } -          extra[n] = NUL; -          STRCAT(extra, *pp_extra); -          STRCAT(extra, " "); -          *pp_extra = extra; -          *n_extrap = (int)strlen(*pp_extra); +          wlv->extra[n] = NUL; +          STRCAT(wlv->extra, wlv->p_extra); +          STRCAT(wlv->extra, " "); +          wlv->p_extra = wlv->extra; +          wlv->n_extra = (int)strlen(wlv->p_extra);          } else { -          size_t symbol_blen = strlen(*pp_extra); +          size_t symbol_blen = strlen(wlv->p_extra);            // TODO(oni-link): Is sign text already extended to            // full cell width? -          assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra))); +          assert((size_t)win_signcol_width(wp) >= mb_string2cells(wlv->p_extra));            // symbol(s) bytes + (filling spaces) (one byte each) -          *n_extrap = (int)symbol_blen + win_signcol_width(wp) - -                      (int)mb_string2cells(*pp_extra); +          wlv->n_extra = (int)symbol_blen + win_signcol_width(wp) - +                         (int)mb_string2cells(wlv->p_extra); -          assert(extra_size > symbol_blen); -          memset(extra, ' ', extra_size); -          memcpy(extra, *pp_extra, symbol_blen); +          assert(sizeof(wlv->extra) > symbol_blen); +          memset(wlv->extra, ' ', sizeof(wlv->extra)); +          memcpy(wlv->extra, wlv->p_extra, symbol_blen); -          *pp_extra = extra; -          (*pp_extra)[*n_extrap] = NUL; +          wlv->p_extra = wlv->extra; +          wlv->p_extra[wlv->n_extra] = NUL;          }        } -      if (use_cursor_line_sign(wp, lnum) && cul_attr > 0) { -        *char_attrp = cul_attr; +      if (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr > 0) { +        wlv->char_attr = sign_cul_attr;        } else { -        *char_attrp = sattr->hl_attr_id; +        wlv->char_attr = sattr->hl_attr_id;        }      }    }  } -static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int *line_attr, -                          int *num_attr, int *cul_attr) +static int get_sign_attrs(buf_T *buf, winlinevars_T *wlv, int *sign_num_attrp, int *sign_cul_attrp)  { -  HlPriAttr line_attrs = { *line_attr, 0 }; -  HlPriAttr num_attrs  = { *num_attr,  0 }; -  HlPriAttr cul_attrs  = { *cul_attr,  0 }; +  HlPriAttr line_attrs = { wlv->line_attr,  0 }; +  HlPriAttr num_attrs  = { *sign_num_attrp, 0 }; +  HlPriAttr cul_attrs  = { *sign_cul_attrp, 0 };    // TODO(bfredl, vigoux): line_attr should not take priority over decoration! -  int num_signs = buf_get_signattrs(buf, lnum, sattrs, &num_attrs, &line_attrs, &cul_attrs); -  decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs, &num_attrs, &line_attrs, &cul_attrs); +  int num_signs = buf_get_signattrs(buf, wlv->lnum, wlv->sattrs, &num_attrs, &line_attrs, +                                    &cul_attrs); +  decor_redraw_signs(buf, wlv->lnum - 1, &num_signs, wlv->sattrs, &num_attrs, &line_attrs, +                     &cul_attrs); -  *line_attr = line_attrs.attr_id; -  *num_attr = num_attrs.attr_id; -  *cul_attr = cul_attrs.attr_id; +  wlv->line_attr = line_attrs.attr_id; +  *sign_num_attrp = num_attrs.attr_id; +  *sign_cul_attrp = cul_attrs.attr_id;    return num_signs;  } +static inline void get_line_number_str(win_T *wp, linenr_T lnum, char *buf, size_t buf_len) +{ +  long num; +  char *fmt = "%*ld "; + +  if (wp->w_p_nu && !wp->w_p_rnu) { +    // 'number' + 'norelativenumber' +    num = (long)lnum; +  } else { +    // 'relativenumber', don't use negative numbers +    num = labs((long)get_cursor_rel_lnum(wp, lnum)); +    if (num == 0 && wp->w_p_nu && wp->w_p_rnu) { +      // 'number' + 'relativenumber' +      num = lnum; +      fmt = "%-*ld "; +    } +  } + +  snprintf(buf, buf_len, fmt, number_width(wp), num); +} + +/// Return true if CursorLineNr highlight is to be used for the number column. +/// - 'cursorline' must be set +/// - "wlv->lnum" must be the cursor line +/// - 'cursorlineopt' has "number" +/// - don't highlight filler lines (when in diff mode) +/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number +///   itself on the first screenline of the wrapped line, otherwise highlight the number column of +///   all screenlines of the wrapped line. +static bool use_cursor_line_nr(win_T *wp, winlinevars_T *wlv) +{ +  return wp->w_p_cul +         && wlv->lnum == wp->w_cursorline +         && (wp->w_p_culopt_flags & CULOPT_NBR) +         && (wlv->row == wlv->startrow + wlv->filler_lines +             || (wlv->row > wlv->startrow + wlv->filler_lines +                 && (wp->w_p_culopt_flags & CULOPT_LINE))); +} + +static int get_line_number_attr(win_T *wp, winlinevars_T *wlv) +{ +  if (use_cursor_line_nr(wp, wlv)) { +    // TODO(vim): Can we use CursorLine instead of CursorLineNr +    // when CursorLineNr isn't set? +    return win_hl_attr(wp, HLF_CLN); +  } + +  if (wp->w_p_rnu) { +    if (wlv->lnum < wp->w_cursor.lnum) { +      // Use LineNrAbove +      return win_hl_attr(wp, HLF_LNA); +    } +    if (wlv->lnum > wp->w_cursor.lnum) { +      // Use LineNrBelow +      return win_hl_attr(wp, HLF_LNB); +    } +  } + +  return win_hl_attr(wp, HLF_N); +} + +/// Display the absolute or relative line number.  After the first row fill with +/// blanks when the 'n' flag isn't in 'cpo'. +static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int sign_idx, +                            int sign_num_attr, int sign_cul_attr) +{ +  if ((wp->w_p_nu || wp->w_p_rnu) +      && (wlv->row == wlv->startrow + wlv->filler_lines +          || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { +    // If 'signcolumn' is set to 'number' and a sign is present +    // in "lnum", then display the sign instead of the line +    // number. +    if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { +      get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr); +    } else { +      // Draw the line number (empty space after wrapping). +      if (wlv->row == wlv->startrow + wlv->filler_lines) { +        get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra)); +        if (wp->w_skipcol > 0) { +          for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) { +            *wlv->p_extra = '-'; +          } +        } +        if (wp->w_p_rl) {                       // reverse line numbers +          // like rl_mirror(), but keep the space at the end +          char *p2 = skipwhite(wlv->extra); +          p2 = skiptowhite(p2) - 1; +          for (char *p1 = skipwhite(wlv->extra); p1 < p2; p1++, p2--) { +            const char t = *p1; +            *p1 = *p2; +            *p2 = t; +          } +        } +        wlv->p_extra = wlv->extra; +        wlv->c_extra = NUL; +      } else { +        wlv->c_extra = ' '; +      } +      wlv->c_final = NUL; +      wlv->n_extra = number_width(wp) + 1; +      if (sign_num_attr > 0) { +        wlv->char_attr = sign_num_attr; +      } else { +        wlv->char_attr = get_line_number_attr(wp, wlv); +      } +    } +  } +} +  /// Prepare and build the 'statuscolumn' string for line "lnum" in window "wp".  /// Fill "stcp" with the built status column string and attributes.  /// This can be called three times per win_line(), once for virt_lines, once for @@ -449,110 +629,144 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T  }  /// Get information needed to display the next segment in the 'statuscolumn'. -/// If not yet at the end, prepare for next segment and decrement "draw_state". +/// If not yet at the end, prepare for next segment and decrement "wlv->draw_state".  ///  /// @param stcp  Status column attributes -/// @param[out] draw_state  Current draw state in win_line() -static void get_statuscol_display_info(statuscol_T *stcp, LineDrawState *draw_state, int *char_attr, -                                       int *n_extrap, int *c_extrap, int *c_finalp, char **pp_extra) +/// @param[in,out]  wlv +static void get_statuscol_display_info(statuscol_T *stcp, winlinevars_T *wlv)  { -  *c_extrap = NUL; -  *c_finalp = NUL; +  wlv->c_extra = NUL; +  wlv->c_final = NUL;    do { -    *draw_state = WL_STC; -    *char_attr = stcp->cur_attr; -    *pp_extra = stcp->textp; -    *n_extrap = (int)((stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end) - stcp->textp); +    wlv->draw_state = WL_STC; +    wlv->char_attr = stcp->cur_attr; +    wlv->p_extra = stcp->textp; +    wlv->n_extra = +      (int)((stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end) - stcp->textp);      // Prepare for next highlight section if not yet at the end -    if (stcp->textp + *n_extrap < stcp->text_end) { +    if (stcp->textp + wlv->n_extra < stcp->text_end) {        int hl = stcp->hlrecp->userhl;        stcp->textp = stcp->hlrecp->start;        stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : hl > 0 ? hl : stcp->num_attr;        stcp->hlrecp++; -      *draw_state = WL_STC - 1; +      wlv->draw_state = WL_STC - 1;      }      // Skip over empty highlight sections -  } while (*n_extrap == 0 && stcp->textp < stcp->text_end); +  } while (wlv->n_extra == 0 && stcp->textp < stcp->text_end);  } -/// Return true if CursorLineNr highlight is to be used for the number column. -/// -/// - 'cursorline' must be set -/// - lnum must be the cursor line -/// - 'cursorlineopt' has "number" -/// - don't highlight filler lines (when in diff mode) -/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number -///   itself on the first screenline of the wrapped line, otherwise highlight the number column of -///   all screenlines of the wrapped line. -static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines) +static void handle_breakindent(win_T *wp, winlinevars_T *wlv)  { -  return wp->w_p_cul -         && lnum == wp->w_cursorline -         && (wp->w_p_culopt_flags & CULOPT_NBR) -         && (row == startrow + filler_lines -             || (row > startrow + filler_lines -                 && (wp->w_p_culopt_flags & CULOPT_LINE))); -} - -static inline void get_line_number_str(win_T *wp, linenr_T lnum, char *buf, size_t buf_len) -{ -  long num; -  char *fmt = "%*ld "; +  if (wp->w_briopt_sbr && wlv->draw_state == WL_BRI - 1 +      && *get_showbreak_value(wp) != NUL) { +    // draw indent after showbreak value +    wlv->draw_state = WL_BRI; +  } else if (wp->w_briopt_sbr && wlv->draw_state == WL_SBR) { +    // after the showbreak, draw the breakindent +    wlv->draw_state = WL_BRI - 1; +  } -  if (wp->w_p_nu && !wp->w_p_rnu) { -    // 'number' + 'norelativenumber' -    num = (long)lnum; -  } else { -    // 'relativenumber', don't use negative numbers -    num = labs((long)get_cursor_rel_lnum(wp, lnum)); -    if (num == 0 && wp->w_p_nu && wp->w_p_rnu) { -      // 'number' + 'relativenumber' -      num = lnum; -      fmt = "%-*ld "; +  // draw 'breakindent': indent wrapped text accordingly +  if (wlv->draw_state == WL_BRI - 1 && wlv->n_extra == 0) { +    wlv->draw_state = WL_BRI; +    // if wlv->need_showbreak is set, breakindent also applies +    if (wp->w_p_bri && (wlv->row != wlv->startrow || wlv->need_showbreak) +        && wlv->filler_lines == 0) { +      wlv->char_attr = 0; +      if (wlv->diff_hlf != (hlf_T)0) { +        wlv->char_attr = win_hl_attr(wp, (int)wlv->diff_hlf); +      } +      wlv->p_extra = NULL; +      wlv->c_extra = ' '; +      wlv->c_final = NUL; +      wlv->n_extra = get_breakindent_win(wp, ml_get_buf(wp->w_buffer, wlv->lnum, false)); +      if (wlv->row == wlv->startrow) { +        wlv->n_extra -= win_col_off2(wp); +        if (wlv->n_extra < 0) { +          wlv->n_extra = 0; +        } +      } +      if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) { +        wlv->need_showbreak = false; +      } +      // Correct end of highlighted area for 'breakindent', +      // required wen 'linebreak' is also set. +      if (wlv->tocol == wlv->vcol) { +        wlv->tocol += wlv->n_extra; +      }      }    } - -  snprintf(buf, buf_len, fmt, number_width(wp), num);  } -static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines) +static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)  { -  if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) { -    // TODO(vim): Can we use CursorLine instead of CursorLineNr -    // when CursorLineNr isn't set? -    return win_hl_attr(wp, HLF_CLN); -  } - -  if (wp->w_p_rnu) { -    if (lnum < wp->w_cursor.lnum) { -      // Use LineNrAbove -      return win_hl_attr(wp, HLF_LNA); +  if (wlv->filler_todo > wlv->filler_lines - wlv->n_virt_lines) { +    // TODO(bfredl): check this doesn't inhibit TUI-style +    //               clear-to-end-of-line. +    wlv->c_extra = ' '; +    wlv->c_final = NUL; +    if (wp->w_p_rl) { +      wlv->n_extra = wlv->col + 1; +    } else { +      wlv->n_extra = wp->w_grid.cols - wlv->col; +    } +    wlv->char_attr = 0; +  } else if (wlv->filler_todo > 0) { +    // Draw "deleted" diff line(s) +    if (char2cells(wp->w_p_fcs_chars.diff) > 1) { +      wlv->c_extra = '-'; +      wlv->c_final = NUL; +    } else { +      wlv->c_extra = wp->w_p_fcs_chars.diff; +      wlv->c_final = NUL;      } -    if (lnum > wp->w_cursor.lnum) { -      // Use LineNrBelow -      return win_hl_attr(wp, HLF_LNB); +    if (wp->w_p_rl) { +      wlv->n_extra = wlv->col + 1; +    } else { +      wlv->n_extra = wp->w_grid.cols - wlv->col;      } +    wlv->char_attr = win_hl_attr(wp, HLF_DED);    } -  return win_hl_attr(wp, HLF_N); +  char *const sbr = get_showbreak_value(wp); +  if (*sbr != NUL && wlv->need_showbreak) { +    // Draw 'showbreak' at the start of each broken line. +    wlv->p_extra = sbr; +    wlv->c_extra = NUL; +    wlv->c_final = NUL; +    wlv->n_extra = (int)strlen(sbr); +    wlv->char_attr = win_hl_attr(wp, HLF_AT); +    if (wp->w_skipcol == 0 || !wp->w_p_wrap) { +      wlv->need_showbreak = false; +    } +    wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr); +    // Correct end of highlighted area for 'showbreak', +    // required when 'linebreak' is also set. +    if (wlv->tocol == wlv->vcol) { +      wlv->tocol += wlv->n_extra; +    } +    // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. +    if (wlv->cul_attr) { +      wlv->char_attr = hl_combine_attr(wlv->cul_attr, wlv->char_attr); +    } +  }  } -static void apply_cursorline_highlight(win_T *wp, linenr_T lnum, int *line_attr, int *cul_attr, -                                       int *line_attr_lowprio) +static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv)  { -  *cul_attr = win_hl_attr(wp, HLF_CUL); -  HlAttrs ae = syn_attr2entry(*cul_attr); +  wlv->cul_attr = win_hl_attr(wp, HLF_CUL); +  HlAttrs ae = syn_attr2entry(wlv->cul_attr);    // We make a compromise here (#7383):    //  * low-priority CursorLine if fg is not set    //  * high-priority ("same as Vim" priority) CursorLine if fg is set    if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { -    *line_attr_lowprio = *cul_attr; +    wlv->line_attr_lowprio = wlv->cul_attr;    } else {      if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer) -        && qf_current_entry(wp) == lnum) { -      *line_attr = hl_combine_attr(*cul_attr, *line_attr); +        && qf_current_entry(wp) == wlv->lnum) { +      wlv->line_attr = hl_combine_attr(wlv->cul_attr, wlv->line_attr);      } else { -      *line_attr = *cul_attr; +      wlv->line_attr = wlv->cul_attr;      }    }  } @@ -604,6 +818,48 @@ static colnr_T get_leadcol(win_T *wp, const char *ptr, const char *line)    return leadcol;  } +/// Start a screen line at column zero. +static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) +{ +  wlv->col = 0; +  wlv->off = 0; + +  if (wp->w_p_rl) { +    // Rightleft window: process the text in the normal direction, but put +    // it in linebuf_char[wlv.off] from right to left.  Start at the +    // rightmost column of the window. +    wlv->col = wp->w_grid.cols - 1; +    wlv->off += wlv->col; +  } + +  if (save_extra) { +    // reset the drawing state for the start of a wrapped line +    wlv->draw_state = WL_START; +    wlv->saved_n_extra = wlv->n_extra; +    wlv->saved_p_extra = wlv->p_extra; +    wlv->saved_c_extra = wlv->c_extra; +    wlv->saved_c_final = wlv->c_final; +    wlv->saved_char_attr = wlv->char_attr; + +    wlv->n_extra = 0; +  } +} + +/// Called when wlv->draw_state is set to WL_LINE. +static void win_line_continue(winlinevars_T *wlv) +{ +  if (wlv->saved_n_extra > 0) { +    // Continue item from end of wrapped line. +    wlv->n_extra = wlv->saved_n_extra; +    wlv->c_extra = wlv->saved_c_extra; +    wlv->c_final = wlv->saved_c_final; +    wlv->p_extra = wlv->saved_p_extra; +    wlv->char_attr = wlv->saved_char_attr; +  } else { +    wlv->char_attr = 0; +  } +} +  /// Display line "lnum" of window 'wp' on the screen.  /// wp->w_virtcol needs to be valid.  /// @@ -621,34 +877,18 @@ static colnr_T get_leadcol(win_T *wp, const char *ptr, const char *line)  int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool number_only,               foldinfo_T foldinfo, DecorProviders *providers, char **provider_err)  { +  winlinevars_T wlv;                  // variables passed between functions +    int c = 0;                          // init for GCC -  colnr_T vcol = 0;                   // virtual column (for tabs) -  long vcol_sbr = -1;                 // virtual column after showbreak -  long vcol_prev = -1;                // "vcol" of previous character +  long vcol_prev = -1;                // "wlv.vcol" of previous character    char *line;                         // current line    char *ptr;                          // current position in "line" -  int row;                            // row in the window, excl w_winrow    ScreenGrid *grid = &wp->w_grid;     // grid specific to the window -  char extra[57];                     // sign, line number and 'fdc' must -                                      // fit in here -  int n_extra = 0;                    // number of extra chars -  char *p_extra = NULL;               // string of extra chars, plus NUL -  char *p_extra_free = NULL;          // p_extra needs to be freed -  int c_extra = NUL;                  // extra chars, all the same -  int c_final = NUL;                  // final char, mandatory if set -  int extra_attr = 0;                 // attributes when n_extra != 0    static char *at_end_str = "";       // used for p_extra when displaying curwin->w_p_lcs_chars.eol                                        // at end-of-line    bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; -  // saved "extra" items for when draw_state becomes WL_LINE (again) -  int saved_n_extra = 0; -  char *saved_p_extra = NULL; -  int saved_c_extra = 0; -  int saved_c_final = 0; -  int saved_char_attr = 0; -    int n_attr = 0;                       // chars with special attr    int saved_attr2 = 0;                  // char_attr saved for n_attr    int n_attr3 = 0;                      // chars with overruling special attr @@ -656,15 +896,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    int n_skip = 0;                       // nr of chars to skip for 'nowrap' -  int fromcol = -10;                    // start of inverting -  int tocol = MAXCOL;                   // end of inverting    int fromcol_prev = -2;                // start of inverting after cursor    bool noinvcur = false;                // don't invert the cursor    bool lnum_in_visual_area = false;    pos_T pos;    ptrdiff_t v; -  int char_attr = 0;                    // attributes for next character    bool attr_pri = false;                // char_attr has priority    bool area_highlighting = false;       // Visual or incsearch highlighting in this line    int vi_attr = 0;                      // attributes for Visual and incsearch highlighting @@ -697,17 +934,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    int mb_c = 0;                         // decoded multi-byte character    bool mb_utf8 = false;                 // screen char is UTF-8 char    int u8cc[MAX_MCO];                    // composing UTF-8 chars -  int filler_lines;                     // nr of filler lines to be drawn -  int filler_todo;                      // nr of filler lines still to do + 1 -  hlf_T diff_hlf = (hlf_T)0;            // type of diff highlighting    int change_start = MAXCOL;            // first col of changed area    int change_end = -1;                  // last col of changed area    bool in_multispace = false;           // in multiple consecutive spaces    int multispace_pos = 0;               // position in lcs-multispace string -  bool need_showbreak = false;          // overlong line, skip first x chars -  int line_attr = 0;                    // attribute for the whole line    int line_attr_save; -  int line_attr_lowprio = 0;            // low-priority attribute for the line    int line_attr_lowprio_save;    int prev_c = 0;                       // previous Arabic character    int prev_c1 = 0;                      // first composing char for prev_c @@ -720,7 +951,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    bool area_active = false; -  int cul_attr = 0;                     // set when 'cursorline' active    // 'cursorlineopt' has "screenline" and cursor is in this line    bool cul_screenline = false;    // margin columns for the screen line, needed for when 'cursorlineopt' @@ -728,8 +958,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    int left_curline_col = 0;    int right_curline_col = 0; -  LineDrawState draw_state = WL_START;  // what to draw next -    int match_conc      = 0;              ///< cchar for match functions    bool on_last_col    = false;    int syntax_flags    = 0; @@ -737,27 +965,32 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    int prev_syntax_id  = 0;    int conceal_attr    = win_hl_attr(wp, HLF_CONCEAL);    bool is_concealing  = false; -  int boguscols       = 0;              ///< nonexistent columns added to -                                        ///< force wrapping -  int vcol_off        = 0;              ///< offset for concealed characters    int did_wcol        = false;    int old_boguscols = 0; -#define VCOL_HLC (vcol - vcol_off) +#define VCOL_HLC (wlv.vcol - wlv.vcol_off)  #define FIX_FOR_BOGUSCOLS \    { \ -    n_extra += vcol_off; \ -    vcol -= vcol_off; \ -    vcol_off = 0; \ -    col -= boguscols; \ -    old_boguscols = boguscols; \ -    boguscols = 0; \ +    wlv.n_extra += wlv.vcol_off; \ +    wlv.vcol -= wlv.vcol_off; \ +    wlv.vcol_off = 0; \ +    wlv.col -= wlv.boguscols; \ +    old_boguscols = wlv.boguscols; \ +    wlv.boguscols = 0; \    }    if (startrow > endrow) {              // past the end already!      return startrow;    } -  row = startrow; +  CLEAR_FIELD(wlv); + +  wlv.lnum = lnum; +  wlv.foldinfo = foldinfo; +  wlv.startrow = startrow; +  wlv.row = startrow; +  wlv.fromcol = -10; +  wlv.tocol = MAXCOL; +  wlv.vcol_sbr = -1;    buf_T *buf = wp->w_buffer;    bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); @@ -859,37 +1092,37 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        if (VIsual_mode == Ctrl_V) {          // block mode          if (lnum_in_visual_area) { -          fromcol = wp->w_old_cursor_fcol; -          tocol = wp->w_old_cursor_lcol; +          wlv.fromcol = wp->w_old_cursor_fcol; +          wlv.tocol = wp->w_old_cursor_lcol;          }        } else {          // non-block mode          if (lnum > top->lnum && lnum <= bot->lnum) { -          fromcol = 0; +          wlv.fromcol = 0;          } else if (lnum == top->lnum) {            if (VIsual_mode == 'V') {       // linewise -            fromcol = 0; +            wlv.fromcol = 0;            } else { -            getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL); +            getvvcol(wp, top, (colnr_T *)&wlv.fromcol, NULL, NULL);              if (gchar_pos(top) == NUL) { -              tocol = fromcol + 1; +              wlv.tocol = wlv.fromcol + 1;              }            }          }          if (VIsual_mode != 'V' && lnum == bot->lnum) {            if (*p_sel == 'e' && bot->col == 0                && bot->coladd == 0) { -            fromcol = -10; -            tocol = MAXCOL; +            wlv.fromcol = -10; +            wlv.tocol = MAXCOL;            } else if (bot->col == MAXCOL) { -            tocol = MAXCOL; +            wlv.tocol = MAXCOL;            } else {              pos = *bot;              if (*p_sel == 'e') { -              getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL); +              getvvcol(wp, &pos, (colnr_T *)&wlv.tocol, NULL, NULL);              } else { -              getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol); -              tocol++; +              getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&wlv.tocol); +              wlv.tocol++;              }            }          } @@ -902,7 +1135,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        }        // if inverting in this line set area_highlighting -      if (fromcol >= 0) { +      if (wlv.fromcol >= 0) {          area_highlighting = true;          vi_attr = win_hl_attr(wp, HLF_V);        } @@ -914,18 +1147,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,                 && lnum <= curwin->w_cursor.lnum + search_match_lines) {        if (lnum == curwin->w_cursor.lnum) {          getvcol(curwin, &(curwin->w_cursor), -                (colnr_T *)&fromcol, NULL, NULL); +                (colnr_T *)&wlv.fromcol, NULL, NULL);        } else { -        fromcol = 0; +        wlv.fromcol = 0;        }        if (lnum == curwin->w_cursor.lnum + search_match_lines) {          pos.lnum = lnum;          pos.col = search_match_endcol; -        getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL); +        getvcol(curwin, &pos, (colnr_T *)&wlv.tocol, NULL, NULL);        }        // do at least one character; happens when past end of line -      if (fromcol == tocol && search_match_endcol) { -        tocol = fromcol + 1; +      if (wlv.fromcol == wlv.tocol && search_match_endcol) { +        wlv.tocol = wlv.fromcol + 1;        }        area_highlighting = true;        vi_attr = win_hl_attr(wp, HLF_I); @@ -935,32 +1168,32 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    int bg_attr = win_bg_attr(wp);    int linestatus = 0; -  filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus); -  if (filler_lines < 0 || linestatus < 0) { -    if (filler_lines == -1 || linestatus == -1) { +  wlv.filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus); +  if (wlv.filler_lines < 0 || linestatus < 0) { +    if (wlv.filler_lines == -1 || linestatus == -1) {        if (diff_find_change(wp, lnum, &change_start, &change_end)) { -        diff_hlf = HLF_ADD;             // added line +        wlv.diff_hlf = HLF_ADD;             // added line        } else if (change_start == 0) { -        diff_hlf = HLF_TXD;             // changed text +        wlv.diff_hlf = HLF_TXD;             // changed text        } else { -        diff_hlf = HLF_CHD;             // changed line +        wlv.diff_hlf = HLF_CHD;             // changed line        }      } else { -      diff_hlf = HLF_ADD;               // added line +      wlv.diff_hlf = HLF_ADD;               // added line      }      if (linestatus == 0) { -      filler_lines = 0; +      wlv.filler_lines = 0;      }      area_highlighting = true;    }    VirtLines virt_lines = KV_INITIAL_VALUE; -  int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines, has_fold); -  filler_lines += n_virt_lines; +  wlv.n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines, has_fold); +  wlv.filler_lines += wlv.n_virt_lines;    if (lnum == wp->w_topline) { -    filler_lines = wp->w_topfill; -    n_virt_lines = MIN(n_virt_lines, filler_lines); +    wlv.filler_lines = wp->w_topfill; +    wlv.n_virt_lines = MIN(wlv.n_virt_lines, wlv.filler_lines);    } -  filler_todo = filler_lines; +  wlv.filler_todo = wlv.filler_lines;    // Cursor line highlighting for 'cursorline' in the current window.    if (wp->w_p_cul && wp->w_p_culopt_flags != CULOPT_NBR && lnum == wp->w_cursorline @@ -969,31 +1202,29 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        && !(wp == curwin && VIsual_active)) {      cul_screenline = (wp->w_p_wrap && (wp->w_p_culopt_flags & CULOPT_SCRLINE));      if (!cul_screenline) { -      apply_cursorline_highlight(wp, lnum, &line_attr, &cul_attr, &line_attr_lowprio); +      apply_cursorline_highlight(wp, &wlv);      } else {        margin_columns_win(wp, &left_curline_col, &right_curline_col);      }      area_highlighting = true;    } -  SignTextAttrs sattrs[SIGN_SHOW_MAX];  // sign attributes for the sign column    int sign_num_attr = 0;                // sign attribute for the number column    int sign_cul_attr = 0;                // sign attribute for cursorline -  CLEAR_FIELD(sattrs); -  int num_signs = get_sign_attrs(buf, lnum, sattrs, &line_attr, &sign_num_attr, &sign_cul_attr); +  int num_signs = get_sign_attrs(buf, &wlv, &sign_num_attr, &sign_cul_attr);    // Highlight the current line in the quickfix window.    if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) { -    line_attr = win_hl_attr(wp, HLF_QFL); +    wlv.line_attr = win_hl_attr(wp, HLF_QFL);    } -  if (line_attr_lowprio || line_attr) { +  if (wlv.line_attr_lowprio || wlv.line_attr) {      area_highlighting = true;    }    if (cul_screenline) { -    line_attr_save = line_attr; -    line_attr_lowprio_save = line_attr_lowprio; +    line_attr_save = wlv.line_attr; +    line_attr_lowprio_save = wlv.line_attr_lowprio;    }    line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum, false); @@ -1061,14 +1292,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      chartabsize_T cts;      int charsize; -    init_chartabsize_arg(&cts, wp, lnum, vcol, line, ptr); +    init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, ptr);      while (cts.cts_vcol < v && *cts.cts_ptr != NUL) {        charsize = win_lbr_chartabsize(&cts, NULL);        cts.cts_vcol += charsize;        prev_ptr = cts.cts_ptr;        MB_PTR_ADV(cts.cts_ptr);      } -    vcol = cts.cts_vcol; +    wlv.vcol = cts.cts_vcol;      ptr = cts.cts_ptr;      clear_chartabsize_arg(&cts); @@ -1078,36 +1309,36 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      // - 'virtualedit' is set, or      // - the visual mode is active,      // the end of the line may be before the start of the displayed part. -    if (vcol < v && (wp->w_p_cuc -                     || draw_color_col -                     || virtual_active() -                     || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { -      vcol = (colnr_T)v; +    if (wlv.vcol < v && (wp->w_p_cuc +                         || draw_color_col +                         || virtual_active() +                         || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { +      wlv.vcol = (colnr_T)v;      }      // Handle a character that's not completely on the screen: Put ptr at      // that character but skip the first few screen characters. -    if (vcol > v) { -      vcol -= charsize; +    if (wlv.vcol > v) { +      wlv.vcol -= charsize;        ptr = prev_ptr;        // If the character fits on the screen, don't need to skip it.        // Except for a TAB.        if (utf_ptr2cells(ptr) >= charsize || *ptr == TAB) { -        n_skip = (int)(v - vcol); +        n_skip = (int)(v - wlv.vcol);        }      }      // Adjust for when the inverted text is before the screen,      // and when the start of the inverted text is before the screen. -    if (tocol <= vcol) { -      fromcol = 0; -    } else if (fromcol >= 0 && fromcol < vcol) { -      fromcol = vcol; +    if (wlv.tocol <= wlv.vcol) { +      wlv.fromcol = 0; +    } else if (wlv.fromcol >= 0 && wlv.fromcol < wlv.vcol) { +      wlv.fromcol = wlv.vcol;      }      // When w_skipcol is non-zero, first line needs 'showbreak'      if (wp->w_p_wrap) { -      need_showbreak = true; +      wlv.need_showbreak = true;      }      // When spell checking a word we need to figure out the start of the      // word and if it's badly spelled or not. @@ -1151,20 +1382,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    // Correct highlighting for cursor that can't be disabled.    // Avoids having to check this for each character. -  if (fromcol >= 0) { +  if (wlv.fromcol >= 0) {      if (noinvcur) { -      if ((colnr_T)fromcol == wp->w_virtcol) { +      if ((colnr_T)wlv.fromcol == wp->w_virtcol) {          // highlighting starts at cursor, let it start just after the          // cursor -        fromcol_prev = fromcol; -        fromcol = -1; -      } else if ((colnr_T)fromcol < wp->w_virtcol) { +        fromcol_prev = wlv.fromcol; +        wlv.fromcol = -1; +      } else if ((colnr_T)wlv.fromcol < wp->w_virtcol) {          // restart highlighting after the cursor          fromcol_prev = wp->w_virtcol;        }      } -    if (fromcol >= tocol) { -      fromcol = -1; +    if (wlv.fromcol >= wlv.tocol) { +      wlv.fromcol = -1;      }    } @@ -1176,15 +1407,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      ptr = line + v;  // "line" may have been updated    } -  int off = 0;  // Offset relative start of line -  int col = 0;  // Visual column on screen. -  if (wp->w_p_rl) { -    // Rightleft window: process the text in the normal direction, but put -    // it in linebuf_char[off] from right to left.  Start at the -    // rightmost column of the window. -    col = grid->cols - 1; -    off += col; -  } +  win_line_start(wp, &wlv, false);    // won't highlight after TERM_ATTRS_MAX columns    int term_attrs[TERM_ATTRS_MAX] = { 0 }; @@ -1197,13 +1420,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    if (*wp->w_p_stc != NUL) {      // Draw the 'statuscolumn' if option is set.      statuscol.draw = true; -    statuscol.sattrs = sattrs; +    statuscol.sattrs = wlv.sattrs;      statuscol.foldinfo = foldinfo;      statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin); -    statuscol.use_cul = use_cursor_line_sign(wp, lnum); +    statuscol.use_cul = use_cursor_line_highlight(wp, lnum);      statuscol.sign_cul_attr = statuscol.use_cul ? sign_cul_attr : 0; -    statuscol.num_attr = sign_num_attr ? sign_num_attr -                         : get_line_number_attr(wp, lnum, row, startrow, filler_lines); +    statuscol.num_attr = sign_num_attr ? sign_num_attr : get_line_number_attr(wp, &wlv);    }    int sign_idx = 0; @@ -1217,27 +1439,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      bool did_decrement_ptr = false;      // Skip this quickly when working on the text. -    if (draw_state != WL_LINE) { +    if (wlv.draw_state != WL_LINE) {        if (cul_screenline) { -        cul_attr = 0; -        line_attr = line_attr_save; -        line_attr_lowprio = line_attr_lowprio_save; +        wlv.cul_attr = 0; +        wlv.line_attr = line_attr_save; +        wlv.line_attr_lowprio = line_attr_lowprio_save;        } -      if (draw_state == WL_CMDLINE - 1 && n_extra == 0) { -        draw_state = WL_CMDLINE; +      if (wlv.draw_state == WL_CMDLINE - 1 && wlv.n_extra == 0) { +        wlv.draw_state = WL_CMDLINE;          if (cmdwin_type != 0 && wp == curwin) {            // Draw the cmdline character. -          n_extra = 1; -          c_extra = cmdwin_type; -          c_final = NUL; -          char_attr = win_hl_attr(wp, HLF_AT); +          wlv.n_extra = 1; +          wlv.c_extra = cmdwin_type; +          wlv.c_final = NUL; +          wlv.char_attr = win_hl_attr(wp, HLF_AT);          }        } -      if (draw_state == WL_FOLD - 1 && n_extra == 0) { -        if (filler_todo > 0) { -          int index = filler_todo - (filler_lines - n_virt_lines); +      if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) { +        if (wlv.filler_todo > 0) { +          int index = wlv.filler_todo - (wlv.filler_lines - wlv.n_virt_lines);            if (index > 0) {              virt_line_index = (int)kv_size(virt_lines) - index;              assert(virt_line_index >= 0); @@ -1246,327 +1468,165 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          }          if (!virt_line_offset) {            // Skip the column states if there is a "virt_left_col" line. -          draw_state = WL_BRI - 1; +          wlv.draw_state = WL_BRI - 1;          } else if (statuscol.draw) {            // Skip fold, sign and number states if 'statuscolumn' is set. -          draw_state = WL_STC - 1; +          wlv.draw_state = WL_STC - 1;          }        } -      if (draw_state == WL_FOLD - 1 && n_extra == 0) { -        int fdc = compute_foldcolumn(wp, 0); -        draw_state = WL_FOLD; -        if (fdc > 0) { -          // Draw the 'foldcolumn'.  Allocate a buffer, "extra" may -          // already be in use. -          xfree(p_extra_free); -          p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1); -          n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum); -          p_extra_free[n_extra] = NUL; -          p_extra = p_extra_free; -          c_extra = NUL; -          c_final = NUL; -          if (use_cursor_line_sign(wp, lnum)) { -            char_attr = win_hl_attr(wp, HLF_CLF); -          } else { -            char_attr = win_hl_attr(wp, HLF_FC); -          } -        } +      if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) { +        wlv.draw_state = WL_FOLD; +        handle_foldcolumn(wp, &wlv);        }        // sign column, this is hit until sign_idx reaches count -      if (draw_state == WL_SIGN - 1 && n_extra == 0) { -        draw_state = WL_SIGN; -        // Show the sign column when there are any signs in this buffer +      if (wlv.draw_state == WL_SIGN - 1 && wlv.n_extra == 0) { +        // Show the sign column when desired. +        wlv.draw_state = WL_SIGN;          if (wp->w_scwidth > 0) { -          get_sign_display_info(false, wp, lnum, sattrs, row, -                                startrow, filler_lines, filler_todo, -                                &c_extra, &c_final, extra, sizeof(extra), -                                &p_extra, &n_extra, &char_attr, sign_idx, -                                sign_cul_attr); +          get_sign_display_info(false, wp, &wlv, sign_idx, sign_cul_attr);            sign_idx++;            if (sign_idx < wp->w_scwidth) { -            draw_state = WL_SIGN - 1; +            wlv.draw_state = WL_SIGN - 1;            } else {              sign_idx = 0;            }          }        } -      if (draw_state == WL_NR - 1 && n_extra == 0) { -        draw_state = WL_NR; -        // Display the absolute or relative line number. After the -        // first fill with blanks when the 'n' flag isn't in 'cpo' -        if ((wp->w_p_nu || wp->w_p_rnu) -            && (row == startrow + filler_lines -                || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { -          // If 'signcolumn' is set to 'number' and a sign is present -          // in 'lnum', then display the sign instead of the line -          // number. -          if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { -            get_sign_display_info(true, wp, lnum, sattrs, row, -                                  startrow, filler_lines, filler_todo, -                                  &c_extra, &c_final, extra, sizeof(extra), -                                  &p_extra, &n_extra, &char_attr, sign_idx, -                                  sign_cul_attr); -          } else { -            // Draw the line number (empty space after wrapping). -            if (row == startrow + filler_lines) { -              get_line_number_str(wp, lnum, extra, sizeof(extra)); -              if (wp->w_skipcol > 0) { -                for (p_extra = extra; *p_extra == ' '; p_extra++) { -                  *p_extra = '-'; -                } -              } -              if (wp->w_p_rl) {                       // reverse line numbers -                // like rl_mirror(), but keep the space at the end -                char *p2 = skipwhite(extra); -                p2 = skiptowhite(p2) - 1; -                for (char *p1 = skipwhite(extra); p1 < p2; p1++, p2--) { -                  const char t = *p1; -                  *p1 = *p2; -                  *p2 = t; -                } -              } -              p_extra = extra; -              c_extra = NUL; -            } else { -              c_extra = ' '; -            } -            c_final = NUL; -            n_extra = number_width(wp) + 1; -            if (sign_num_attr > 0) { -              char_attr = sign_num_attr; -            } else { -              char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines); -            } -          } -        } +      if (wlv.draw_state == WL_NR - 1 && wlv.n_extra == 0) { +        // Show the line number, if desired. +        wlv.draw_state = WL_NR; +        handle_lnum_col(wp, &wlv, num_signs, sign_idx, sign_num_attr, sign_cul_attr);        } -      if (draw_state == WL_STC - 1 && n_extra == 0) { -        draw_state = WL_STC; +      if (wlv.draw_state == WL_STC - 1 && wlv.n_extra == 0) { +        wlv.draw_state = WL_STC;          // Draw the 'statuscolumn' if option is set.          if (statuscol.draw) {            if (statuscol.textp == NULL) { -            get_statuscol_str(wp, lnum, row - startrow - filler_lines, &statuscol); +            get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);              if (wp->w_redr_statuscol) {                break;              }            } -          get_statuscol_display_info(&statuscol, &draw_state, &char_attr, -                                     &n_extra, &c_extra, &c_final, &p_extra); +          get_statuscol_display_info(&statuscol, &wlv);          }        } -      if (draw_state == WL_STC && n_extra == 0) { -        win_col_offset = off; +      if (wlv.draw_state == WL_STC && wlv.n_extra == 0) { +        win_col_offset = wlv.off;        } -      if (wp->w_briopt_sbr && draw_state == WL_BRI - 1 -          && n_extra == 0 && *get_showbreak_value(wp) != NUL) { -        // draw indent after showbreak value -        draw_state = WL_BRI; -      } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) { -        // after the showbreak, draw the breakindent -        draw_state = WL_BRI - 1; +      // Check if 'breakindent' applies and show it. +      // May change wlv.draw_state to WL_BRI or WL_BRI - 1. +      if (wlv.n_extra == 0) { +        handle_breakindent(wp, &wlv);        } -      // draw 'breakindent': indent wrapped text accordingly -      if (draw_state == WL_BRI - 1 && n_extra == 0) { -        draw_state = WL_BRI; -        // if need_showbreak is set, breakindent also applies -        if (wp->w_p_bri && (row != startrow || need_showbreak) -            && filler_lines == 0) { -          char_attr = 0; - -          if (diff_hlf != (hlf_T)0) { -            char_attr = win_hl_attr(wp, (int)diff_hlf); -          } -          p_extra = NULL; -          c_extra = ' '; -          c_final = NUL; -          n_extra = -            get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false)); -          if (row == startrow) { -            n_extra -= win_col_off2(wp); -            if (n_extra < 0) { -              n_extra = 0; -            } -          } -          if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) { -            need_showbreak = false; -          } -          // Correct end of highlighted area for 'breakindent', -          // required wen 'linebreak' is also set. -          if (tocol == vcol) { -            tocol += n_extra; -          } -        } -      } - -      if (draw_state == WL_SBR - 1 && n_extra == 0) { -        draw_state = WL_SBR; -        if (filler_todo > filler_lines - n_virt_lines) { -          // TODO(bfredl): check this doesn't inhibit TUI-style -          //               clear-to-end-of-line. -          c_extra = ' '; -          c_final = NUL; -          if (wp->w_p_rl) { -            n_extra = col + 1; -          } else { -            n_extra = grid->cols - col; -          } -          char_attr = 0; -        } else if (filler_todo > 0) { -          // Draw "deleted" diff line(s) -          if (char2cells(wp->w_p_fcs_chars.diff) > 1) { -            c_extra = '-'; -            c_final = NUL; -          } else { -            c_extra = wp->w_p_fcs_chars.diff; -            c_final = NUL; -          } -          if (wp->w_p_rl) { -            n_extra = col + 1; -          } else { -            n_extra = grid->cols - col; -          } -          char_attr = win_hl_attr(wp, HLF_DED); -        } -        char *const sbr = get_showbreak_value(wp); -        if (*sbr != NUL && need_showbreak) { -          // Draw 'showbreak' at the start of each broken line. -          p_extra = sbr; -          c_extra = NUL; -          c_final = NUL; -          n_extra = (int)strlen(sbr); -          char_attr = win_hl_attr(wp, HLF_AT); -          if (wp->w_skipcol == 0 || !wp->w_p_wrap) { -            need_showbreak = false; -          } -          vcol_sbr = vcol + mb_charlen(sbr); -          // Correct end of highlighted area for 'showbreak', -          // required when 'linebreak' is also set. -          if (tocol == vcol) { -            tocol += n_extra; -          } -          // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. -          if (cul_attr) { -            char_attr = hl_combine_attr(cul_attr, char_attr); -          } -        } +      if (wlv.draw_state == WL_SBR - 1 && wlv.n_extra == 0) { +        wlv.draw_state = WL_SBR; +        handle_showbreak_and_filler(wp, &wlv);        } -      if (draw_state == WL_LINE - 1 && n_extra == 0) { +      if (wlv.draw_state == WL_LINE - 1 && wlv.n_extra == 0) {          sign_idx = 0; -        draw_state = WL_LINE; - -        if (has_decor && row == startrow + filler_lines) { +        wlv.draw_state = WL_LINE; +        if (has_decor && wlv.row == startrow + wlv.filler_lines) {            // hide virt_text on text hidden by 'nowrap' -          decor_redraw_col(wp->w_buffer, vcol, off, true, &decor_state); -        } - -        if (saved_n_extra) { -          // Continue item from end of wrapped line. -          n_extra = saved_n_extra; -          c_extra = saved_c_extra; -          c_final = saved_c_final; -          p_extra = saved_p_extra; -          char_attr = saved_char_attr; -        } else { -          char_attr = 0; +          decor_redraw_col(wp->w_buffer, wlv.vcol, wlv.off, true, &decor_state);          } +        win_line_continue(&wlv);  // use wlv.saved_ values        }      } -    if (cul_screenline && draw_state == WL_LINE -        && vcol >= left_curline_col -        && vcol < right_curline_col) { -      apply_cursorline_highlight(wp, lnum, &line_attr, &cul_attr, &line_attr_lowprio); +    if (cul_screenline && wlv.draw_state == WL_LINE +        && wlv.vcol >= left_curline_col +        && wlv.vcol < right_curline_col) { +      apply_cursorline_highlight(wp, &wlv);      }      // When still displaying '$' of change command, stop at cursor      if (((dollar_vcol >= 0            && wp == curwin            && lnum == wp->w_cursor.lnum -          && vcol >= (long)wp->w_virtcol) -         || (number_only && draw_state > WL_STC)) -        && filler_todo <= 0) { -      draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row); -      grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, bg_attr, false); +          && wlv.vcol >= (long)wp->w_virtcol) +         || (number_only && wlv.draw_state > WL_STC)) +        && wlv.filler_todo <= 0) { +      draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row); +      grid_put_linebuf(grid, wlv.row, 0, wlv.col, -grid->cols, wp->w_p_rl, wp, bg_attr, false);        // Pretend we have finished updating the window.  Except when        // 'cursorcolumn' is set.        if (wp->w_p_cuc) { -        row = wp->w_cline_row + wp->w_cline_height; +        wlv.row = wp->w_cline_row + wp->w_cline_height;        } else { -        row = grid->rows; +        wlv.row = grid->rows;        }        break;      } -    if (draw_state == WL_LINE +    if (wlv.draw_state == WL_LINE          && has_fold -        && col == win_col_offset -        && n_extra == 0 -        && row == startrow + filler_lines) { -      char_attr = win_hl_attr(wp, HLF_FL); +        && wlv.col == win_col_offset +        && wlv.n_extra == 0 +        && wlv.row == startrow + wlv.filler_lines) { +      wlv.char_attr = win_hl_attr(wp, HLF_FL);        linenr_T lnume = lnum + foldinfo.fi_lines - 1;        memset(buf_fold, ' ', FOLD_TEXT_LEN); -      p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); -      n_extra = (int)strlen(p_extra); +      wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); +      wlv.n_extra = (int)strlen(wlv.p_extra); -      if (p_extra != buf_fold) { -        xfree(p_extra_free); -        p_extra_free = p_extra; +      if (wlv.p_extra != buf_fold) { +        xfree(wlv.p_extra_free); +        wlv.p_extra_free = wlv.p_extra;        } -      c_extra = NUL; -      c_final = NUL; -      p_extra[n_extra] = NUL; +      wlv.c_extra = NUL; +      wlv.c_final = NUL; +      wlv.p_extra[wlv.n_extra] = NUL;      } -    if (draw_state == WL_LINE +    if (wlv.draw_state == WL_LINE          && has_fold -        && col < grid->cols -        && n_extra == 0 -        && row == startrow + filler_lines) { +        && wlv.col < grid->cols +        && wlv.n_extra == 0 +        && wlv.row == startrow + wlv.filler_lines) {        // fill rest of line with 'fold' -      c_extra = wp->w_p_fcs_chars.fold; -      c_final = NUL; +      wlv.c_extra = wp->w_p_fcs_chars.fold; +      wlv.c_final = NUL; -      n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col); +      wlv.n_extra = wp->w_p_rl ? (wlv.col + 1) : (grid->cols - wlv.col);      } -    if (draw_state == WL_LINE +    if (wlv.draw_state == WL_LINE          && has_fold -        && col >= grid->cols -        && n_extra != 0 -        && row == startrow + filler_lines) { +        && wlv.col >= grid->cols +        && wlv.n_extra != 0 +        && wlv.row == startrow + wlv.filler_lines) {        // Truncate the folding. -      n_extra = 0; +      wlv.n_extra = 0;      } -    if (draw_state == WL_LINE && (area_highlighting || has_spell)) { +    if (wlv.draw_state == WL_LINE && (area_highlighting || has_spell)) {        // handle Visual or match highlighting in this line -      if (vcol == fromcol -          || (vcol + 1 == fromcol && n_extra == 0 +      if (wlv.vcol == wlv.fromcol +          || (wlv.vcol + 1 == wlv.fromcol && wlv.n_extra == 0                && utf_ptr2cells(ptr) > 1)            || ((int)vcol_prev == fromcol_prev -              && vcol_prev < vcol               // not at margin -              && vcol < tocol)) { +              && vcol_prev < wlv.vcol               // not at margin +              && wlv.vcol < wlv.tocol)) {          area_attr = vi_attr;                    // start highlighting          if (area_highlighting) {            area_active = true;          } -      } else if (area_attr != 0 && (vcol == tocol -                                    || (noinvcur -                                        && vcol == wp->w_virtcol))) { +      } else if (area_attr != 0 && (wlv.vcol == wlv.tocol +                                    || (noinvcur && wlv.vcol == wp->w_virtcol))) {          area_attr = 0;                          // stop highlighting          area_active = false;        } -      if (!n_extra) { +      if (!wlv.n_extra) {          // Check for start/end of 'hlsearch' and other matches.          // After end, check for start/end of next match.          // When another match, have to check for start again. @@ -1583,22 +1643,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          }        } -      if (diff_hlf != (hlf_T)0) { -        if (diff_hlf == HLF_CHD && ptr - line >= change_start -            && n_extra == 0) { -          diff_hlf = HLF_TXD;                   // changed text +      if (wlv.diff_hlf != (hlf_T)0) { +        if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start +            && wlv.n_extra == 0) { +          wlv.diff_hlf = HLF_TXD;                   // changed text          } -        if (diff_hlf == HLF_TXD && ptr - line > change_end -            && n_extra == 0) { -          diff_hlf = HLF_CHD;                   // changed line +        if (wlv.diff_hlf == HLF_TXD && ptr - line > change_end +            && wlv.n_extra == 0) { +          wlv.diff_hlf = HLF_CHD;                   // changed line          } -        line_attr = win_hl_attr(wp, (int)diff_hlf); +        wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf);          // Overlay CursorLine onto diff-mode highlight. -        if (cul_attr) { -          line_attr = 0 != line_attr_lowprio  // Low-priority CursorLine -            ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr), +        if (wlv.cul_attr) { +          wlv.line_attr = 0 != wlv.line_attr_lowprio  // Low-priority CursorLine +            ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr),                                hl_get_underline()) -            : hl_combine_attr(line_attr, cul_attr); +            : hl_combine_attr(wlv.line_attr, wlv.cul_attr);          }        } @@ -1606,25 +1666,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        attr_pri = true;        if (area_attr != 0) { -        char_attr = hl_combine_attr(line_attr, area_attr); +        wlv.char_attr = hl_combine_attr(wlv.line_attr, area_attr);          if (!highlight_match) {            // let search highlight show in Visual area if possible -          char_attr = hl_combine_attr(search_attr, char_attr); +          wlv.char_attr = hl_combine_attr(search_attr, wlv.char_attr);          }        } else if (search_attr != 0) { -        char_attr = hl_combine_attr(line_attr, search_attr); -      } else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL) -                                    || vcol < fromcol || vcol_prev < fromcol_prev -                                    || vcol >= tocol)) { -        // Use line_attr when not in the Visual or 'incsearch' area +        wlv.char_attr = hl_combine_attr(wlv.line_attr, search_attr); +      } else if (wlv.line_attr != 0 +                 && ((wlv.fromcol == -10 && wlv.tocol == MAXCOL) +                     || wlv.vcol < wlv.fromcol +                     || vcol_prev < fromcol_prev +                     || wlv.vcol >= wlv.tocol)) { +        // Use wlv.line_attr when not in the Visual or 'incsearch' area          // (area_attr may be 0 when "noinvcur" is set). -        char_attr = line_attr; +        wlv.char_attr = wlv.line_attr;        } else {          attr_pri = false;          if (has_syntax) { -          char_attr = syntax_attr; +          wlv.char_attr = syntax_attr;          } else { -          char_attr = 0; +          wlv.char_attr = 0;          }        }      } @@ -1638,23 +1700,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past      // "p_extra[n_extra]".      // For the '$' of the 'list' option, n_extra == 1, p_extra == "". -    if (n_extra > 0) { -      if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) { -        c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra; +    if (wlv.n_extra > 0) { +      if (wlv.c_extra != NUL || (wlv.n_extra == 1 && wlv.c_final != NUL)) { +        c = (wlv.n_extra == 1 && wlv.c_final != NUL) ? wlv.c_final : wlv.c_extra;          mb_c = c;               // doesn't handle non-utf-8 multi-byte!          mb_utf8 = check_mb_utf8(&c, u8cc);        } else { -        assert(p_extra != NULL); -        c = (uint8_t)(*p_extra); +        assert(wlv.p_extra != NULL); +        c = (uint8_t)(*wlv.p_extra);          mb_c = c;          // If the UTF-8 character is more than one byte:          // Decode it into "mb_c". -        mb_l = utfc_ptr2len(p_extra); +        mb_l = utfc_ptr2len(wlv.p_extra);          mb_utf8 = false; -        if (mb_l > n_extra) { +        if (mb_l > wlv.n_extra) {            mb_l = 1;          } else if (mb_l > 1) { -          mb_c = utfc_ptr2char(p_extra, u8cc); +          mb_c = utfc_ptr2char(wlv.p_extra, u8cc);            mb_utf8 = true;            c = 0xc0;          } @@ -1663,7 +1725,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          }          // If a double-width char doesn't fit display a '>' in the last column. -        if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1)) +        if ((wp->w_p_rl ? (wlv.col <= 0) : (wlv.col >= grid->cols - 1))              && utf_char2cells(mb_c) == 2) {            c = '>';            mb_c = c; @@ -1671,32 +1733,29 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            (void)mb_l;            multi_attr = win_hl_attr(wp, HLF_AT); -          if (cul_attr) { -            multi_attr = 0 != line_attr_lowprio -              ? hl_combine_attr(cul_attr, multi_attr) -              : hl_combine_attr(multi_attr, cul_attr); +          if (wlv.cul_attr) { +            multi_attr = 0 != wlv.line_attr_lowprio +              ? hl_combine_attr(wlv.cul_attr, multi_attr) +              : hl_combine_attr(multi_attr, wlv.cul_attr);            }            // put the pointer back to output the double-width            // character at the start of the next line. -          n_extra++; -          p_extra--; +          wlv.n_extra++; +          wlv.p_extra--;          } else { -          n_extra -= mb_l - 1; -          p_extra += mb_l - 1; +          wlv.n_extra -= mb_l - 1; +          wlv.p_extra += mb_l - 1;          } -        p_extra++; +        wlv.p_extra++;        } -      n_extra--; +      wlv.n_extra--;      } else if (foldinfo.fi_lines > 0) {        // skip writing the buffer line itself        c = NUL; -      XFREE_CLEAR(p_extra_free);      } else {        int c0; -      XFREE_CLEAR(p_extra_free); -        // Get a character from the line itself.        c0 = c = (uint8_t)(*ptr);        mb_c = c; @@ -1729,22 +1788,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            || (mb_l > 1 && (!vim_isprintc(mb_c)))) {          // Illegal UTF-8 byte: display as <xx>.          // Non-BMP character : display as ? or fullwidth ?. -        transchar_hex(extra, mb_c); +        transchar_hex(wlv.extra, mb_c);          if (wp->w_p_rl) {  // reverse -          rl_mirror(extra); +          rl_mirror(wlv.extra);          } -        p_extra = extra; -        c = (uint8_t)(*p_extra); -        mb_c = mb_ptr2char_adv((const char **)&p_extra); +        wlv.p_extra = wlv.extra; +        c = (uint8_t)(*wlv.p_extra); +        mb_c = mb_ptr2char_adv((const char **)&wlv.p_extra);          mb_utf8 = (c >= 0x80); -        n_extra = (int)strlen(p_extra); -        c_extra = NUL; -        c_final = NUL; +        wlv.n_extra = (int)strlen(wlv.p_extra); +        wlv.c_extra = NUL; +        wlv.c_final = NUL;          if (area_attr == 0 && search_attr == 0) { -          n_attr = n_extra + 1; -          extra_attr = win_hl_attr(wp, HLF_8); -          saved_attr2 = char_attr;               // save current attr +          n_attr = wlv.n_extra + 1; +          wlv.extra_attr = win_hl_attr(wp, HLF_8); +          saved_attr2 = wlv.char_attr;               // save current attr          }        } else if (mb_l == 0) {        // at the NUL at end-of-line          mb_l = 1; @@ -1774,8 +1833,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        // If a double-width char doesn't fit display a '>' in the        // last column; the character is displayed at the start of the        // next line. -      if ((wp->w_p_rl ? (col <= 0) : -           (col >= grid->cols - 1)) +      if ((wp->w_p_rl ? (wlv.col <= 0) : (wlv.col >= grid->cols - 1))            && utf_char2cells(mb_c) == 2) {          c = '>';          mb_c = c; @@ -1792,15 +1850,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        // If a double-width char doesn't fit at the left side display a '<' in        // the first column.  Don't do this for unprintable characters. -      if (n_skip > 0 && mb_l > 1 && n_extra == 0) { -        n_extra = 1; -        c_extra = MB_FILLER_CHAR; -        c_final = NUL; +      if (n_skip > 0 && mb_l > 1 && wlv.n_extra == 0) { +        wlv.n_extra = 1; +        wlv.c_extra = MB_FILLER_CHAR; +        wlv.c_final = NUL;          c = ' ';          if (area_attr == 0 && search_attr == 0) { -          n_attr = n_extra + 1; -          extra_attr = win_hl_attr(wp, HLF_AT); -          saved_attr2 = char_attr;             // save current attr +          n_attr = wlv.n_extra + 1; +          wlv.extra_attr = win_hl_attr(wp, HLF_AT); +          saved_attr2 = wlv.char_attr;             // save current attr          }          mb_c = c;          mb_utf8 = false; @@ -1841,15 +1899,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            ptr = line + v;            if (!attr_pri) { -            if (cul_attr) { -              char_attr = 0 != line_attr_lowprio -                ? hl_combine_attr(cul_attr, syntax_attr) -                : hl_combine_attr(syntax_attr, cul_attr); +            if (wlv.cul_attr) { +              wlv.char_attr = 0 != wlv.line_attr_lowprio +                ? hl_combine_attr(wlv.cul_attr, syntax_attr) +                : hl_combine_attr(syntax_attr, wlv.cul_attr);              } else { -              char_attr = syntax_attr; +              wlv.char_attr = syntax_attr;              }            } else { -            char_attr = hl_combine_attr(syntax_attr, char_attr); +            wlv.char_attr = hl_combine_attr(syntax_attr, wlv.char_attr);            }            // no concealing past the end of the line, it interferes            // with line highlighting. @@ -1859,19 +1917,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,              syntax_flags = get_syntax_info(&syntax_seqnr);            }          } else if (!attr_pri) { -          char_attr = 0; +          wlv.char_attr = 0;          }          if (has_decor && v > 0) {            bool selected = (area_active || (area_highlighting && noinvcur -                                           && vcol == wp->w_virtcol)); -          int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off, +                                           && wlv.vcol == wp->w_virtcol)); +          int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, wlv.off,                                                selected, &decor_state);            if (extmark_attr != 0) {              if (!attr_pri) { -              char_attr = hl_combine_attr(char_attr, extmark_attr); +              wlv.char_attr = hl_combine_attr(wlv.char_attr, extmark_attr);              } else { -              char_attr = hl_combine_attr(extmark_attr, char_attr); +              wlv.char_attr = hl_combine_attr(extmark_attr, wlv.char_attr);              }            } @@ -1891,7 +1949,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          if (has_spell && v >= word_end && v > cur_checked_col) {            spell_attr = 0;            if (!attr_pri) { -            char_attr = hl_combine_attr(char_attr, syntax_attr); +            wlv.char_attr = hl_combine_attr(wlv.char_attr, syntax_attr);            }            if (c != 0 && ((!has_syntax && !no_plain_buffer) || can_spell)) {              char *prev_ptr; @@ -1956,14 +2014,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          }          if (spell_attr != 0) {            if (!attr_pri) { -            char_attr = hl_combine_attr(char_attr, spell_attr); +            wlv.char_attr = hl_combine_attr(wlv.char_attr, spell_attr);            } else { -            char_attr = hl_combine_attr(spell_attr, char_attr); +            wlv.char_attr = hl_combine_attr(spell_attr, wlv.char_attr);            }          }          if (wp->w_buffer->terminal) { -          char_attr = hl_combine_attr(term_attrs[vcol], char_attr); +          wlv.char_attr = hl_combine_attr(term_attrs[wlv.vcol], wlv.char_attr);          }          // Found last space before word: check for line break. @@ -1973,15 +2031,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            char *p = ptr - (mb_off + 1);            chartabsize_T cts; -          init_chartabsize_arg(&cts, wp, lnum, vcol, line, p); -          n_extra = win_lbr_chartabsize(&cts, NULL) - 1; +          init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p); +          wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1;            // We have just drawn the showbreak value, no need to add            // space for it again. -          if (vcol == vcol_sbr) { -            n_extra -= mb_charlen(get_showbreak_value(wp)); -            if (n_extra < 0) { -              n_extra = 0; +          if (wlv.vcol == wlv.vcol_sbr) { +            wlv.n_extra -= mb_charlen(get_showbreak_value(wp)); +            if (wlv.n_extra < 0) { +              wlv.n_extra = 0;              }            }            if (on_last_col && c != TAB) { @@ -1991,12 +2049,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,              search_attr = 0;            } -          if (c == TAB && n_extra + col > grid->cols) { -            n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, -                                      wp->w_buffer->b_p_vts_array) - 1; +          if (c == TAB && wlv.n_extra + wlv.col > grid->cols) { +            wlv.n_extra = tabstop_padding(wlv.vcol, wp->w_buffer->b_p_ts, +                                          wp->w_buffer->b_p_vts_array) - 1;            } -          c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; -          c_final = NUL; +          wlv.c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; +          wlv.c_final = NUL;            if (ascii_iswhite(c)) {              if (c == TAB) {                // See "Tab alignment" below. @@ -2038,8 +2096,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,              c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;            }            n_attr = 1; -          extra_attr = win_hl_attr(wp, HLF_0); -          saved_attr2 = char_attr;  // save current attr +          wlv.extra_attr = win_hl_attr(wp, HLF_0); +          saved_attr2 = wlv.char_attr;  // save current attr            mb_c = c;            mb_utf8 = check_mb_utf8(&c, u8cc);          } @@ -2061,8 +2119,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            }            n_attr = 1; -          extra_attr = win_hl_attr(wp, HLF_0); -          saved_attr2 = char_attr;  // save current attr +          wlv.extra_attr = win_hl_attr(wp, HLF_0); +          saved_attr2 = wlv.char_attr;  // save current attr            mb_c = c;            mb_utf8 = check_mb_utf8(&c, u8cc);          } @@ -2074,13 +2132,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          // turn it into something else on the way to putting it on the screen.          if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {            int tab_len = 0; -          long vcol_adjusted = vcol;  // removed showbreak length +          long vcol_adjusted = wlv.vcol;  // removed showbreak length            char *const sbr = get_showbreak_value(wp);            // Only adjust the tab_len, when at the first column after the            // showbreak value was drawn. -          if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) { -            vcol_adjusted = vcol - mb_charlen(sbr); +          if (*sbr != NUL && wlv.vcol == wlv.vcol_sbr && wp->w_p_wrap) { +            vcol_adjusted = wlv.vcol - mb_charlen(sbr);            }            // tab amount depends on current column            tab_len = tabstop_padding((colnr_T)vcol_adjusted, @@ -2088,62 +2146,65 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,                                      wp->w_buffer->b_p_vts_array) - 1;            if (!wp->w_p_lbr || !wp->w_p_list) { -            n_extra = tab_len; +            wlv.n_extra = tab_len;            } else {              char *p; -            int saved_nextra = n_extra; +            int saved_nextra = wlv.n_extra; -            if (vcol_off > 0) { +            if (wlv.vcol_off > 0) {                // there are characters to conceal -              tab_len += vcol_off; +              tab_len += wlv.vcol_off;              }              // boguscols before FIX_FOR_BOGUSCOLS macro from above.              if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0 -                && n_extra > tab_len) { -              tab_len += n_extra - tab_len; +                && wlv.n_extra > tab_len) { +              tab_len += wlv.n_extra - tab_len;              } -            // If n_extra > 0, it gives the number of chars -            // to use for a tab, else we need to calculate the width -            // for a tab. -            int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2)); -            if (wp->w_p_lcs_chars.tab3) { -              len += utf_char2len(wp->w_p_lcs_chars.tab3); -            } -            if (n_extra > 0) { -              len += n_extra - tab_len; -            } -            c = wp->w_p_lcs_chars.tab1; -            p = xmalloc((size_t)len + 1); -            memset(p, ' ', (size_t)len); -            p[len] = NUL; -            xfree(p_extra_free); -            p_extra_free = p; -            for (int i = 0; i < tab_len; i++) { -              if (*p == NUL) { -                tab_len = i; -                break; +            if (tab_len > 0) { +              // If wlv.n_extra > 0, it gives the number of chars +              // to use for a tab, else we need to calculate the +              // width for a tab. +              int tab2_len = utf_char2len(wp->w_p_lcs_chars.tab2); +              int len = tab_len * tab2_len; +              if (wp->w_p_lcs_chars.tab3) { +                len += utf_char2len(wp->w_p_lcs_chars.tab3) - tab2_len;                } -              int lcs = wp->w_p_lcs_chars.tab2; +              if (wlv.n_extra > 0) { +                len += wlv.n_extra - tab_len; +              } +              c = wp->w_p_lcs_chars.tab1; +              p = xmalloc((size_t)len + 1); +              memset(p, ' ', (size_t)len); +              p[len] = NUL; +              xfree(wlv.p_extra_free); +              wlv.p_extra_free = p; +              for (int i = 0; i < tab_len; i++) { +                if (*p == NUL) { +                  tab_len = i; +                  break; +                } +                int lcs = wp->w_p_lcs_chars.tab2; -              // if tab3 is given, use it for the last char -              if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { -                lcs = wp->w_p_lcs_chars.tab3; +                // if tab3 is given, use it for the last char +                if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { +                  lcs = wp->w_p_lcs_chars.tab3; +                } +                p += utf_char2bytes(lcs, p); +                wlv.n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);                } -              p += utf_char2bytes(lcs, p); -              n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); -            } -            p_extra = p_extra_free; +              wlv.p_extra = wlv.p_extra_free; -            // n_extra will be increased by FIX_FOX_BOGUSCOLS -            // macro below, so need to adjust for that here -            if (vcol_off > 0) { -              n_extra -= vcol_off; +              // n_extra will be increased by FIX_FOX_BOGUSCOLS +              // macro below, so need to adjust for that here +              if (wlv.vcol_off > 0) { +                wlv.n_extra -= wlv.vcol_off; +              }              }            }            { -            int vc_saved = vcol_off; +            int vc_saved = wlv.vcol_off;              // Tab alignment should be identical regardless of              // 'conceallevel' value. So tab compensates of all @@ -2156,7 +2217,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,              // Make sure, the highlighting for the tab char will be              // correctly set further below (effectively reverts the              // FIX_FOR_BOGSUCOLS macro). -            if (n_extra == tab_len + vc_saved && wp->w_p_list +            if (wlv.n_extra == tab_len + vc_saved && wp->w_p_list                  && wp->w_p_lcs_chars.tab1) {                tab_len += vc_saved;              } @@ -2164,50 +2225,50 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            mb_utf8 = false;  // don't draw as UTF-8            if (wp->w_p_list) { -            c = (n_extra == 0 && wp->w_p_lcs_chars.tab3) +            c = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)                   ? wp->w_p_lcs_chars.tab3                   : wp->w_p_lcs_chars.tab1; -            if (wp->w_p_lbr) { -              c_extra = NUL;  // using p_extra from above +            if (wp->w_p_lbr && wlv.p_extra != NULL && *wlv.p_extra != NUL) { +              wlv.c_extra = NUL;  // using p_extra from above              } else { -              c_extra = wp->w_p_lcs_chars.tab2; +              wlv.c_extra = wp->w_p_lcs_chars.tab2;              } -            c_final = wp->w_p_lcs_chars.tab3; +            wlv.c_final = wp->w_p_lcs_chars.tab3;              n_attr = tab_len + 1; -            extra_attr = win_hl_attr(wp, HLF_0); -            saved_attr2 = char_attr;  // save current attr +            wlv.extra_attr = win_hl_attr(wp, HLF_0); +            saved_attr2 = wlv.char_attr;  // save current attr              mb_c = c;              mb_utf8 = check_mb_utf8(&c, u8cc);            } else { -            c_final = NUL; -            c_extra = ' '; +            wlv.c_final = NUL; +            wlv.c_extra = ' ';              c = ' ';            }          } else if (c == NUL                     && (wp->w_p_list -                       || ((fromcol >= 0 || fromcol_prev >= 0) -                           && tocol > vcol +                       || ((wlv.fromcol >= 0 || fromcol_prev >= 0) +                           && wlv.tocol > wlv.vcol                             && VIsual_mode != Ctrl_V -                           && (wp->w_p_rl ? (col >= 0) : (col < grid->cols)) +                           && (wp->w_p_rl ? (wlv.col >= 0) : (wlv.col < grid->cols))                             && !(noinvcur                                  && lnum == wp->w_cursor.lnum -                                && vcol == wp->w_virtcol))) +                                && wlv.vcol == wp->w_virtcol)))                     && lcs_eol_one > 0) {            // Display a '$' after the line or highlight an extra            // character if the line break is included.            // For a diff line the highlighting continues after the "$". -          if (diff_hlf == (hlf_T)0 -              && line_attr == 0 -              && line_attr_lowprio == 0) { +          if (wlv.diff_hlf == (hlf_T)0 +              && wlv.line_attr == 0 +              && wlv.line_attr_lowprio == 0) {              // In virtualedit, visual selections may extend beyond end of line              if (area_highlighting && virtual_active() -                && tocol != MAXCOL && vcol < tocol) { -              n_extra = 0; +                && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol) { +              wlv.n_extra = 0;              } else { -              p_extra = at_end_str; -              n_extra = 1; -              c_extra = NUL; -              c_final = NUL; +              wlv.p_extra = at_end_str; +              wlv.n_extra = 1; +              wlv.c_extra = NUL; +              wlv.c_final = NUL;              }            }            if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { @@ -2217,46 +2278,46 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            }            lcs_eol_one = -1;            ptr--;  // put it back at the NUL -          extra_attr = win_hl_attr(wp, HLF_AT); +          wlv.extra_attr = win_hl_attr(wp, HLF_AT);            n_attr = 1;            mb_c = c;            mb_utf8 = check_mb_utf8(&c, u8cc);          } else if (c != NUL) { -          p_extra = (char *)transchar_buf(wp->w_buffer, c); -          if (n_extra == 0) { -            n_extra = byte2cells(c) - 1; +          wlv.p_extra = (char *)transchar_buf(wp->w_buffer, c); +          if (wlv.n_extra == 0) { +            wlv.n_extra = byte2cells(c) - 1;            }            if ((dy_flags & DY_UHEX) && wp->w_p_rl) { -            rl_mirror(p_extra);                 // reverse "<12>" +            rl_mirror(wlv.p_extra);                 // reverse "<12>"            } -          c_extra = NUL; -          c_final = NUL; +          wlv.c_extra = NUL; +          wlv.c_final = NUL;            if (wp->w_p_lbr) {              char *p; -            c = (uint8_t)(*p_extra); -            p = xmalloc((size_t)n_extra + 1); -            memset(p, ' ', (size_t)n_extra); +            c = (uint8_t)(*wlv.p_extra); +            p = xmalloc((size_t)wlv.n_extra + 1); +            memset(p, ' ', (size_t)wlv.n_extra);              strncpy(p,  // NOLINT(runtime/printf) -                    p_extra + 1, -                    (size_t)strlen(p_extra) - 1); -            p[n_extra] = NUL; -            xfree(p_extra_free); -            p_extra_free = p_extra = p; +                    wlv.p_extra + 1, +                    (size_t)strlen(wlv.p_extra) - 1); +            p[wlv.n_extra] = NUL; +            xfree(wlv.p_extra_free); +            wlv.p_extra_free = wlv.p_extra = p;            } else { -            n_extra = byte2cells(c) - 1; -            c = (uint8_t)(*p_extra++); +            wlv.n_extra = byte2cells(c) - 1; +            c = (uint8_t)(*wlv.p_extra++);            } -          n_attr = n_extra + 1; -          extra_attr = win_hl_attr(wp, HLF_8); -          saved_attr2 = char_attr;  // save current attr +          n_attr = wlv.n_extra + 1; +          wlv.extra_attr = win_hl_attr(wp, HLF_8); +          saved_attr2 = wlv.char_attr;  // save current attr            mb_utf8 = false;   // don't draw as UTF-8          } else if (VIsual_active                     && (VIsual_mode == Ctrl_V || VIsual_mode == 'v')                     && virtual_active() -                   && tocol != MAXCOL -                   && vcol < tocol -                   && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) { +                   && wlv.tocol != MAXCOL +                   && wlv.vcol < wlv.tocol +                   && (wp->w_p_rl ? (wlv.col >= 0) : (wlv.col < grid->cols))) {            c = ' ';            ptr--;  // put it back at the NUL          } @@ -2266,7 +2327,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp))            && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)            && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { -        char_attr = conceal_attr; +        wlv.char_attr = conceal_attr;          if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)               || has_match_conc > 1 || decor_conceal > 1)              && (syn_get_sub_char() != NUL @@ -2281,7 +2342,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            } else if (decor_conceal && decor_state.conceal_char) {              c = decor_state.conceal_char;              if (decor_state.conceal_attr) { -              char_attr = decor_state.conceal_attr; +              wlv.char_attr = decor_state.conceal_attr;              }            } else if (syn_get_sub_char() != NUL) {              c = syn_get_sub_char(); @@ -2293,20 +2354,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            prev_syntax_id = syntax_seqnr; -          if (n_extra > 0) { -            vcol_off += n_extra; +          if (wlv.n_extra > 0) { +            wlv.vcol_off += wlv.n_extra;            } -          vcol += n_extra; -          if (wp->w_p_wrap && n_extra > 0) { +          wlv.vcol += wlv.n_extra; +          if (wp->w_p_wrap && wlv.n_extra > 0) {              if (wp->w_p_rl) { -              col -= n_extra; -              boguscols -= n_extra; +              wlv.col -= wlv.n_extra; +              wlv.boguscols -= wlv.n_extra;              } else { -              boguscols += n_extra; -              col += n_extra; +              wlv.boguscols += wlv.n_extra; +              wlv.col += wlv.n_extra;              }            } -          n_extra = 0; +          wlv.n_extra = 0;            n_attr = 0;          } else if (n_skip == 0) {            is_concealing = true; @@ -2327,23 +2388,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      // In the cursor line and we may be concealing characters: correct      // the cursor column when we reach its position. -    if (!did_wcol && draw_state == WL_LINE +    if (!did_wcol && wlv.draw_state == WL_LINE          && wp == curwin && lnum == wp->w_cursor.lnum          && conceal_cursor_line(wp) -        && (int)wp->w_virtcol <= vcol + n_skip) { +        && (int)wp->w_virtcol <= wlv.vcol + n_skip) {        if (wp->w_p_rl) { -        wp->w_wcol = grid->cols - col + boguscols - 1; +        wp->w_wcol = grid->cols - wlv.col + wlv.boguscols - 1;        } else { -        wp->w_wcol = col - boguscols; +        wp->w_wcol = wlv.col - wlv.boguscols;        } -      wp->w_wrow = row; +      wp->w_wrow = wlv.row;        did_wcol = true;        wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;      }      // Don't override visual selection highlighting. -    if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) { -      char_attr = hl_combine_attr(char_attr, extra_attr); +    if (n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { +      wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr);      }      // Handle the case where we are in column 0 but not on the first @@ -2351,25 +2412,25 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      // special character (via 'listchars' option "precedes:<char>".      if (lcs_prec_todo != NUL          && wp->w_p_list -        && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0) -        && filler_todo <= 0 -        && draw_state > WL_STC +        && (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0) +        && wlv.filler_todo <= 0 +        && wlv.draw_state > WL_STC          && c != NUL) {        c = wp->w_p_lcs_chars.prec;        lcs_prec_todo = NUL;        if (utf_char2cells(mb_c) > 1) {          // Double-width character being overwritten by the "precedes"          // character, need to fill up half the character. -        c_extra = MB_FILLER_CHAR; -        c_final = NUL; -        n_extra = 1; +        wlv.c_extra = MB_FILLER_CHAR; +        wlv.c_final = NUL; +        wlv.n_extra = 1;          n_attr = 2; -        extra_attr = win_hl_attr(wp, HLF_AT); +        wlv.extra_attr = win_hl_attr(wp, HLF_AT);        }        mb_c = c;        mb_utf8 = check_mb_utf8(&c, u8cc); -      saved_attr3 = char_attr;  // save current attr -      char_attr = win_hl_attr(wp, HLF_AT);  // overwriting char_attr +      saved_attr3 = wlv.char_attr;  // save current attr +      wlv.char_attr = win_hl_attr(wp, HLF_AT);  // overwriting char_attr        n_attr3 = 1;      } @@ -2385,7 +2446,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        // char on the screen, just overwrite that one (tricky!)  Not        // needed when a '$' was displayed for 'list'.        if (wp->w_p_lcs_chars.eol == lcs_eol_one -          && ((area_attr != 0 && vcol == fromcol +          && ((area_attr != 0 && wlv.vcol == wlv.fromcol                 && (VIsual_mode != Ctrl_V                     || lnum == VIsual.lnum                     || lnum == curwin->w_cursor.lnum)) @@ -2394,22 +2455,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          int n = 0;          if (wp->w_p_rl) { -          if (col < 0) { +          if (wlv.col < 0) {              n = 1;            }          } else { -          if (col >= grid->cols) { +          if (wlv.col >= grid->cols) {              n = -1;            }          }          if (n != 0) {            // At the window boundary, highlight the last character            // instead (better than nothing). -          off += n; -          col += n; +          wlv.off += n; +          wlv.col += n;          } else {            // Add a blank character to highlight. -          schar_from_ascii(linebuf_char[off], ' '); +          schar_from_ascii(linebuf_char[wlv.off], ' ');          }          if (area_attr == 0 && !has_fold) {            // Use attributes from match with highest priority among @@ -2417,24 +2478,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,            get_search_match_hl(wp,                                &screen_search_hl,                                (long)(ptr - line),  // NOLINT(google-readability-casting) -                              &char_attr); +                              &wlv.char_attr);          } -        int eol_attr = char_attr; -        if (cul_attr) { -          eol_attr = hl_combine_attr(cul_attr, eol_attr); +        int eol_attr = wlv.char_attr; +        if (wlv.cul_attr) { +          eol_attr = hl_combine_attr(wlv.cul_attr, eol_attr);          } -        linebuf_attr[off] = eol_attr; +        linebuf_attr[wlv.off] = eol_attr;          if (wp->w_p_rl) { -          col--; -          off--; +          wlv.col--; +          wlv.off--;          } else { -          col++; -          off++; +          wlv.col++; +          wlv.off++;          } -        vcol++; +        wlv.vcol++;          eol_hl_off = 1;        } +    } + +    // At end of the text line. +    if (c == NUL) {        // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.        if (wp->w_p_wrap) {          v = wp->w_skipcol; @@ -2443,13 +2508,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        }        // check if line ends before left margin -      if (vcol < v + col - win_col_off(wp)) { -        vcol = (colnr_T)v + col - win_col_off(wp); +      if (wlv.vcol < v + wlv.col - win_col_off(wp)) { +        wlv.vcol = (colnr_T)v + wlv.col - win_col_off(wp);        }        // Get rid of the boguscols now, we want to draw until the right        // edge for 'cursorcolumn'. -      col -= boguscols; -      // boguscols = 0;  // Disabled because value never read after this +      wlv.col -= wlv.boguscols; +      wlv.boguscols = 0;        if (draw_color_col) {          draw_color_col = advance_color_col(VCOL_HLC, &color_cols); @@ -2462,17 +2527,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,                        ? 1 : 0);        if (has_decor) { -        has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr, -                                        col + eol_skip); +        has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &wlv.line_attr, +                                        wlv.col + eol_skip);        }        if (((wp->w_p_cuc              && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off              && (int)wp->w_virtcol < -            (long)grid->cols * (row - startrow + 1) + v +            (long)grid->cols * (wlv.row - startrow + 1) + v              && lnum != wp->w_cursor.lnum) -           || draw_color_col || line_attr_lowprio || line_attr -           || diff_hlf != (hlf_T)0 || has_virttext)) { +           || draw_color_col || wlv.line_attr_lowprio || wlv.line_attr +           || wlv.diff_hlf != (hlf_T)0 || has_virttext)) {          int rightmost_vcol = 0;          if (wp->w_p_cuc) { @@ -2492,23 +2557,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          int mc_attr = win_hl_attr(wp, HLF_MC);          int diff_attr = 0; -        if (diff_hlf == HLF_TXD) { -          diff_hlf = HLF_CHD; +        if (wlv.diff_hlf == HLF_TXD) { +          wlv.diff_hlf = HLF_CHD;          } -        if (diff_hlf != 0) { -          diff_attr = win_hl_attr(wp, (int)diff_hlf); +        if (wlv.diff_hlf != 0) { +          diff_attr = win_hl_attr(wp, (int)wlv.diff_hlf);          } -        int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr); -        if (base_attr || line_attr || has_virttext) { +        int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr); +        if (base_attr || wlv.line_attr || has_virttext) {            rightmost_vcol = INT_MAX;          }          int col_stride = wp->w_p_rl ? -1 : 1; -        while (wp->w_p_rl ? col >= 0 : col < grid->cols) { -          schar_from_ascii(linebuf_char[off], ' '); -          col += col_stride; +        while (wp->w_p_rl ? wlv.col >= 0 : wlv.col < grid->cols) { +          schar_from_ascii(linebuf_char[wlv.off], ' '); +          wlv.col += col_stride;            if (draw_color_col) {              draw_color_col = advance_color_col(VCOL_HLC, &color_cols);            } @@ -2521,16 +2586,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,              col_attr = mc_attr;            } -          col_attr = hl_combine_attr(col_attr, line_attr); +          col_attr = hl_combine_attr(col_attr, wlv.line_attr); -          linebuf_attr[off] = col_attr; -          off += col_stride; +          linebuf_attr[wlv.off] = col_attr; +          wlv.off += col_stride;            if (VCOL_HLC >= rightmost_vcol) {              break;            } -          vcol += 1; +          wlv.vcol += 1;          }        } @@ -2539,24 +2604,24 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          // terminal buffers may need to highlight beyond the end of the          // logical line          int n = wp->w_p_rl ? -1 : 1; -        while (col >= 0 && col < grid->cols) { -          schar_from_ascii(linebuf_char[off], ' '); -          linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol]; -          off += n; -          vcol += n; -          col += n; +        while (wlv.col >= 0 && wlv.col < grid->cols) { +          schar_from_ascii(linebuf_char[wlv.off], ' '); +          linebuf_attr[wlv.off] = wlv.vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[wlv.vcol]; +          wlv.off += n; +          wlv.vcol += n; +          wlv.col += n;          }        } -      draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row); -      grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp, bg_attr, false); -      row++; +      draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row); +      grid_put_linebuf(grid, wlv.row, 0, wlv.col, grid->cols, wp->w_p_rl, wp, bg_attr, false); +      wlv.row++;        // Update w_cline_height and w_cline_folded if the cursor line was        // updated (saves a call to plines_win() later).        if (wp == curwin && lnum == curwin->w_cursor.lnum) {          curwin->w_cline_row = startrow; -        curwin->w_cline_height = row - startrow; +        curwin->w_cline_height = wlv.row - startrow;          curwin->w_cline_folded = foldinfo.fi_lines > 0;          curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);          conceal_cursor_used = conceal_cursor_line(curwin); @@ -2567,17 +2632,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      // Show "extends" character from 'listchars' if beyond the line end and      // 'list' is set.      if (wp->w_p_lcs_chars.ext != NUL -        && draw_state == WL_LINE +        && wlv.draw_state == WL_LINE          && wp->w_p_list          && !wp->w_p_wrap -        && filler_todo <= 0 -        && (wp->w_p_rl ? col == 0 : col == grid->cols - 1) +        && wlv.filler_todo <= 0 +        && (wp->w_p_rl ? wlv.col == 0 : wlv.col == grid->cols - 1)          && !has_fold          && (*ptr != NUL              || lcs_eol_one > 0 -            || (n_extra && (c_extra != NUL || *p_extra != NUL)))) { +            || (wlv.n_extra && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) {        c = wp->w_p_lcs_chars.ext; -      char_attr = win_hl_attr(wp, HLF_AT); +      wlv.char_attr = win_hl_attr(wp, HLF_AT);        mb_c = c;        mb_utf8 = check_mb_utf8(&c, u8cc);      } @@ -2594,84 +2659,84 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,      // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'      // options are set      vcol_save_attr = -1; -    if ((draw_state == WL_LINE -         || draw_state == WL_BRI -         || draw_state == WL_SBR) +    if ((wlv.draw_state == WL_LINE +         || wlv.draw_state == WL_BRI +         || wlv.draw_state == WL_SBR)          && !lnum_in_visual_area          && search_attr == 0          && area_attr == 0 -        && filler_todo <= 0) { +        && wlv.filler_todo <= 0) {        if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol            && lnum != wp->w_cursor.lnum) { -        vcol_save_attr = char_attr; -        char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr); +        vcol_save_attr = wlv.char_attr; +        wlv.char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), wlv.char_attr);        } else if (draw_color_col && VCOL_HLC == *color_cols) { -        vcol_save_attr = char_attr; -        char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr); +        vcol_save_attr = wlv.char_attr; +        wlv.char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), wlv.char_attr);        }      }      // Apply lowest-priority line attr now, so everything can override it. -    if (draw_state == WL_LINE) { -      char_attr = hl_combine_attr(line_attr_lowprio, char_attr); +    if (wlv.draw_state == WL_LINE) { +      wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr);      }      // Store character to be displayed.      // Skip characters that are left of the screen for 'nowrap'. -    vcol_prev = vcol; -    if (draw_state < WL_LINE || n_skip <= 0) { +    vcol_prev = wlv.vcol; +    if (wlv.draw_state < WL_LINE || n_skip <= 0) {        //        // Store the character.        //        if (wp->w_p_rl && utf_char2cells(mb_c) > 1) {          // A double-wide character is: put first half in left cell. -        off--; -        col--; +        wlv.off--; +        wlv.col--;        }        if (mb_utf8) { -        schar_from_cc(linebuf_char[off], mb_c, u8cc); +        schar_from_cc(linebuf_char[wlv.off], mb_c, u8cc);        } else { -        schar_from_ascii(linebuf_char[off], (char)c); +        schar_from_ascii(linebuf_char[wlv.off], (char)c);        }        if (multi_attr) { -        linebuf_attr[off] = multi_attr; +        linebuf_attr[wlv.off] = multi_attr;          multi_attr = 0;        } else { -        linebuf_attr[off] = char_attr; +        linebuf_attr[wlv.off] = wlv.char_attr;        }        if (utf_char2cells(mb_c) > 1) {          // Need to fill two screen columns. -        off++; -        col++; +        wlv.off++; +        wlv.col++;          // UTF-8: Put a 0 in the second screen char. -        linebuf_char[off][0] = 0; -        if (draw_state > WL_STC && filler_todo <= 0) { -          vcol++; +        linebuf_char[wlv.off][0] = 0; +        if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) { +          wlv.vcol++;          } -        // When "tocol" is halfway through a character, set it to the end of -        // the character, otherwise highlighting won't stop. -        if (tocol == vcol) { -          tocol++; +        // When "wlv.tocol" is halfway through a character, set it to the end +        // of the character, otherwise highlighting won't stop. +        if (wlv.tocol == wlv.vcol) { +          wlv.tocol++;          }          if (wp->w_p_rl) {            // now it's time to backup one cell -          off--; -          col--; +          wlv.off--; +          wlv.col--;          }        }        if (wp->w_p_rl) { -        off--; -        col--; +        wlv.off--; +        wlv.col--;        } else { -        off++; -        col++; +        wlv.off++; +        wlv.col++;        }      } else if (wp->w_p_cole > 0 && is_concealing) {        n_skip--; -      vcol_off++; -      if (n_extra > 0) { -        vcol_off += n_extra; +      wlv.vcol_off++; +      if (wlv.n_extra > 0) { +        wlv.vcol_off += wlv.n_extra;        }        if (wp->w_p_wrap) {          // Special voodoo required if 'wrap' is on. @@ -2681,45 +2746,45 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          // take up the same screen space when parts are concealed,          // so that cursor line computations aren't messed up.          // -        // To avoid the fictitious advance of 'col' causing +        // To avoid the fictitious advance of 'wlv.col' causing          // trailing junk to be written out of the screen line          // we are building, 'boguscols' keeps track of the number          // of bad columns we have advanced. -        if (n_extra > 0) { -          vcol += n_extra; +        if (wlv.n_extra > 0) { +          wlv.vcol += wlv.n_extra;            if (wp->w_p_rl) { -            col -= n_extra; -            boguscols -= n_extra; +            wlv.col -= wlv.n_extra; +            wlv.boguscols -= wlv.n_extra;            } else { -            col += n_extra; -            boguscols += n_extra; +            wlv.col += wlv.n_extra; +            wlv.boguscols += wlv.n_extra;            } -          n_extra = 0; +          wlv.n_extra = 0;            n_attr = 0;          }          if (utf_char2cells(mb_c) > 1) {            // Need to fill two screen columns.            if (wp->w_p_rl) { -            boguscols--; -            col--; +            wlv.boguscols--; +            wlv.col--;            } else { -            boguscols++; -            col++; +            wlv.boguscols++; +            wlv.col++;            }          }          if (wp->w_p_rl) { -          boguscols--; -          col--; +          wlv.boguscols--; +          wlv.col--;          } else { -          boguscols++; -          col++; +          wlv.boguscols++; +          wlv.col++;          }        } else { -        if (n_extra > 0) { -          vcol += n_extra; -          n_extra = 0; +        if (wlv.n_extra > 0) { +          wlv.vcol += wlv.n_extra; +          wlv.n_extra = 0;            n_attr = 0;          }        } @@ -2727,58 +2792,58 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,        n_skip--;      } -    // Only advance the "vcol" when after the 'number' or 'relativenumber' -    // column. -    if (draw_state > WL_STC -        && filler_todo <= 0) { -      vcol++; +    // Only advance the "wlv.vcol" when after the 'number' or +    // 'relativenumber' column. +    if (wlv.draw_state > WL_STC +        && wlv.filler_todo <= 0) { +      wlv.vcol++;      }      if (vcol_save_attr >= 0) { -      char_attr = vcol_save_attr; +      wlv.char_attr = vcol_save_attr;      }      // restore attributes after "predeces" in 'listchars' -    if (draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) { -      char_attr = saved_attr3; +    if (wlv.draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) { +      wlv.char_attr = saved_attr3;      }      // restore attributes after last 'listchars' or 'number' char -    if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) { -      char_attr = saved_attr2; +    if (n_attr > 0 && wlv.draw_state == WL_LINE && --n_attr == 0) { +      wlv.char_attr = saved_attr2;      }      // At end of screen line and there is more to come: Display the line      // so far.  If there is no more to display it is caught above. -    if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols)) +    if ((wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols))          && (!has_fold || virt_line_offset >= 0) -        && (draw_state != WL_LINE +        && (wlv.draw_state != WL_LINE              || *ptr != NUL -            || filler_todo > 0 +            || wlv.filler_todo > 0              || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL -                && p_extra != at_end_str) -            || (n_extra != 0 -                && (c_extra != NUL || *p_extra != NUL)))) { +                && wlv.p_extra != at_end_str) +            || (wlv.n_extra != 0 +                && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) {        bool wrap = wp->w_p_wrap       // Wrapping enabled. -                  && filler_todo <= 0          // Not drawing diff filler lines. +                  && wlv.filler_todo <= 0          // Not drawing diff filler lines.                    && lcs_eol_one != -1         // Haven't printed the lcs_eol character. -                  && row != endrow - 1         // Not the last line being displayed. +                  && wlv.row != endrow - 1     // Not the last line being displayed.                    && (grid->cols == Columns  // Window spans the width of the screen,                        || ui_has(kUIMultigrid))  // or has dedicated grid.                    && !wp->w_p_rl;              // Not right-to-left. -      int draw_col = col - boguscols; +      int draw_col = wlv.col - wlv.boguscols;        if (virt_line_offset >= 0) {          draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line,                              kHlModeReplace, grid->cols, 0);        } else { -        draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row); +        draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, wlv.row);        } -      grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap); +      grid_put_linebuf(grid, wlv.row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap);        if (wrap) {          ScreenGrid *current_grid = grid; -        int current_row = row, dummy_col = 0;  // dummy_col unused +        int current_row = wlv.row, dummy_col = 0;  // dummy_col unused          grid_adjust(¤t_grid, ¤t_row, &dummy_col);          // Force a redraw of the first column of the next line. @@ -2788,49 +2853,37 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          current_grid->line_wraps[current_row] = true;        } -      boguscols = 0; -      row++; +      wlv.boguscols = 0; +      wlv.row++;        // When not wrapping and finished diff lines, or when displayed        // '$' and highlighting until last column, break here. -      if ((!wp->w_p_wrap && filler_todo <= 0) || lcs_eol_one == -1) { +      if ((!wp->w_p_wrap && wlv.filler_todo <= 0) || lcs_eol_one == -1) {          break;        }        // When the window is too narrow draw all "@" lines. -      if (draw_state != WL_LINE && filler_todo <= 0) { -        win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT); -        set_empty_rows(wp, row); -        row = endrow; +      if (wlv.draw_state != WL_LINE && wlv.filler_todo <= 0) { +        win_draw_end(wp, '@', ' ', true, wlv.row, wp->w_grid.rows, HLF_AT); +        set_empty_rows(wp, wlv.row); +        wlv.row = endrow;        }        // When line got too long for screen break here. -      if (row == endrow) { -        row++; +      if (wlv.row == endrow) { +        wlv.row++;          break;        } -      col = 0; -      off = 0; -      if (wp->w_p_rl) { -        col = grid->cols - 1;  // col is not used if breaking! -        off += col; -      } - -      // reset the drawing state for the start of a wrapped line -      draw_state = WL_START; -      saved_n_extra = n_extra; -      saved_p_extra = p_extra; -      saved_c_extra = c_extra; -      saved_c_final = c_final; -      saved_char_attr = char_attr; -      n_extra = 0; +      win_line_start(wp, &wlv, true); +        lcs_prec_todo = wp->w_p_lcs_chars.prec; -      if (filler_todo <= 0) { -        need_showbreak = true; +      if (wlv.filler_todo <= 0) { +        wlv.need_showbreak = true;        }        if (statuscol.draw) { -        if (row == startrow + filler_lines + 1 || row == startrow + filler_lines) { +        if (wlv.row == startrow + wlv.filler_lines + 1 +            || wlv.row == startrow + wlv.filler_lines) {            // Re-evaluate 'statuscolumn' for the first wrapped row and non filler line            statuscol.textp = NULL;          } else if (statuscol.textp) { @@ -2840,11 +2893,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,          }  // Fall back to default columns if the 'n' flag isn't in 'cpo'          statuscol.draw = vim_strchr(p_cpo, CPO_NUMCOL) == NULL;        } -      filler_todo--; +      wlv.filler_todo--;        virt_line_offset = -1;        // When the filler lines are actually below the last line of the        // file, don't draw the line itself, break here. -      if (filler_todo == 0 && (wp->w_botfill || end_fill)) { +      if (wlv.filler_todo == 0 && (wp->w_botfill || end_fill)) {          break;        }      } @@ -2857,6 +2910,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,    }    kv_destroy(virt_lines); -  xfree(p_extra_free); -  return row; +  xfree(wlv.p_extra_free); +  return wlv.row;  } diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 6de3b0a9d0..8928979455 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -2096,10 +2096,10 @@ bool ins_compl_prep(int c)      edit_submode_extra = NULL;    } -  // Ignore end of Select mode mapping and mouse scroll buttons. +  // Ignore end of Select mode mapping and mouse scroll/movement.    if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP -      || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT -      || c == K_COMMAND || c == K_LUA) { +      || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_MOUSEMOVE +      || c == K_EVENT || c == K_COMMAND || c == K_LUA) {      return retval;    } @@ -3043,8 +3043,8 @@ static void get_next_spell_completion(linenr_T lnum)  /// @param cur_match_pos  current match position  /// @param match_len  /// @param cont_s_ipos    next ^X<> will set initial_pos -static char *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len, -                                            bool *cont_s_ipos) +static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len, +                                             bool *cont_s_ipos)  {    *match_len = 0;    char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col; @@ -3206,8 +3206,8 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_        continue;      }      int len; -    char *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos, -                                               &len, &cont_s_ipos); +    char *ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos, +                                                &len, &cont_s_ipos);      if (ptr == NULL) {        continue;      } diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 74f043fe03..68e572911c 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -95,7 +95,6 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)        COMMENT "Checking ${name}.po"        VERBATIM        DEPENDS ${poFile}) -    set_target_properties(check-po-${name} PROPERTIES FOLDER po/check)    endmacro()    macro(BuildPoIconvGenericWithCharset @@ -182,9 +181,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)      BuildMo(${LANGUAGE})    endforeach() -  set_target_properties(${UPDATE_PO_TARGETS} PROPERTIES FOLDER po/update)    add_custom_target(translations ALL DEPENDS ${LANGUAGE_MO_FILES})    add_custom_target(update-po DEPENDS ${UPDATE_PO_TARGETS}) -  set_target_properties(translations update-po PROPERTIES FOLDER po)  endif() diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 2e377aa434..b61c9a570d 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -1075,4 +1075,22 @@ func Test_breakindent_column()    bwipeout!  endfunc +func Test_linebreak_list() +  " This was setting wlv.c_extra to NUL while wlv.p_extra is NULL +  filetype plugin on +  syntax enable +  edit! $VIMRUNTIME/doc/index.txt +  /v_P + +  setlocal list +  setlocal listchars=tab:>- +  setlocal linebreak +  setlocal nowrap +  setlocal filetype=help +  redraw! + +  bwipe! +endfunc + +  " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 148f8b6d42..eb00cb98ca 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -80,6 +80,7 @@ let s:filename_checks = {      \ 'awk': ['file.awk', 'file.gawk'],      \ 'b': ['file.mch', 'file.ref', 'file.imp'],      \ 'basic': ['file.bas', 'file.bi', 'file.bm'], +    \ 'bass': ['file.bass'],      \ 'bc': ['file.bc'],      \ 'bdf': ['file.bdf'],      \ 'beancount': ['file.beancount'], diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index ec1379a378..af0856331d 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -375,6 +375,54 @@ func Test_completefunc_info()    set completefunc&  endfunc +" Test that mouse scrolling/movement should not interrupt completion. +func Test_mouse_scroll_move_during_completion() +  new +  com! -buffer TestCommand1 echo 'TestCommand1' +  com! -buffer TestCommand2 echo 'TestCommand2' +  call setline(1, ['', '', '', '', '']) +  call cursor(5, 1) + +  " Without completion menu scrolling can move text. +  set completeopt-=menu wrap +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelDown>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_notequal(1, winsaveview().topline) +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelUp>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_equal(1, winsaveview().topline) +  set nowrap +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelRight>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_notequal(0, winsaveview().leftcol) +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelLeft>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_equal(0, winsaveview().leftcol) +  call feedkeys("ccT\<C-X>\<C-V>\<MouseMove>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) + +  " With completion menu scrolling cannot move text. +  set completeopt+=menu wrap +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelDown>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_equal(1, winsaveview().topline) +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelUp>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_equal(1, winsaveview().topline) +  set nowrap +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelRight>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_equal(0, winsaveview().leftcol) +  call feedkeys("ccT\<C-X>\<C-V>\<ScrollWheelLeft>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) +  call assert_equal(0, winsaveview().leftcol) +  call feedkeys("ccT\<C-X>\<C-V>\<MouseMove>\<C-V>", 'tx') +  call assert_equal('TestCommand2', getline('.')) + +  bwipe! +  set completeopt& wrap& +endfunc +  " Check that when using feedkeys() typeahead does not interrupt searching for  " completions.  func Test_compl_feedkeys() diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim index 1cbdba5d76..a746779e73 100644 --- a/src/nvim/testdir/test_listlbr.vim +++ b/src/nvim/testdir/test_listlbr.vim @@ -73,6 +73,30 @@ func Test_linebreak_with_nolist()    call s:close_windows()  endfunc +func Test_linebreak_with_list_and_number() +  call s:test_windows('setl list listchars+=tab:>-') +  call setline(1, ["abcdefg\thijklmnopqrstu", "v"]) +  let lines = s:screen_lines([1, 4], winwidth(0)) +  let expect_nonumber = [ +\ "abcdefg>------------", +\ "hijklmnopqrstu$     ", +\ "v$                  ", +\ "~                   ", +\ ] +  call s:compare_lines(expect_nonumber, lines) + +  setl number +  let lines = s:screen_lines([1, 4], winwidth(0)) +  let expect_number = [ +\ "  1 abcdefg>--------", +\ "    hijklmnopqrstu$ ", +\ "  2 v$              ", +\ "~                   ", +\ ] +  call s:compare_lines(expect_number, lines) +  call s:close_windows() +endfunc +  func Test_should_break()    call s:test_windows('setl sbr=+ nolist')    call setline(1, "1\t" . repeat('a', winwidth(0)-2)) | 
