Repository: neovim/neovim
File: src/nvim/popupmenu.c (C)

Code before:

/// Set the informational text in the preview buffer when the completion
/// item does not include a dedicated preview or popup window.
///
/// @param[in]  buf        Buffer where the text will be set.
/// @param[in]  info       Informational text to display in the preview buffer.
/// @param[in]  lnum       Where to start the text. Incremented for each added line.
/// @param[out] max_width  Maximum width of the displayed text.
static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width)
{
  Error err = ERROR_INIT;
  Arena arena = ARENA_EMPTY;
  Array replacement = ARRAY_DICT_INIT;
  buf->b_p_ma = true;

  // Iterate through the string line by line by temporarily replacing newlines with NUL
  for (char *curr = info, *next; curr; curr = next ? next + 1 : NULL) {
    if ((next = strchr(curr, '\n'))) {
      *next = NUL;  // Temporarily replace the newline with a string terminator
    }
    // Only skip if this is an empty line AND it's the last line
    if (*curr == '\0' && !next) {
      break;
    }

    *max_width = MAX(*max_width, linetabsize_str(curr));
    ADD(replacement, STRING_OBJ(cstr_to_string(curr)));
    (*lnum)++;

    if (next) {
      *next = '\n';
    }
  }

  int original_textlock = textlock;
  if (textlock > 0) {
    textlock = 0;
  }
  nvim_buf_set_lines(0, buf->handle, 0, -1, false, replacement, &arena, &err);
  textlock = original_textlock;
  if (ERROR_SET(&err)) {
    emsg(err.msg);
    api_clear_error(&err);
  }
  arena_mem_free(arena_finish(&arena));
  api_free_array(replacement);
  buf->b_p_ma = false;
}

/// adjust floating info preview window position
static bool pum_adjust_info_position(win_T *wp, int width)

Review comment (bug):
This'll use the tab width from `curwin`, which may be the wrong window.

E.g: the size of the preview window is wrong here: (add a test case for this)
```vim
set completeopt+=popup,menuone nolist
setlocal tabstop=1
startinsert
autocmd ModeChanged *:i ++once call complete(1, [#{word: 'a'}])
      \| call nvim__complete_set(0, #{info: "\tfloob\tfloob"})
```

I'm not an expert with the plines stuff, but maybe this'll work?

```diff
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index d3dd3e3104..57452196b6 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -910,15 +910,16 @@ void pum_redraw(void)
 /// Set the informational text in the preview buffer when the completion
 /// item does not include a dedicated preview or popup window.
 ///
-/// @param[in]  buf        Buffer where the text will be set.
+/// @param[in]  win        Window containing buffer where the text will be set.
 /// @param[in]  info       Informational text to display in the preview buffer.
 /// @param[in]  lnum       Where to start the text. Incremented for each added line.
 /// @param[out] max_width  Maximum width of the displayed text.
-static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width)
+static void pum_preview_set_text(win_T *win, char *info, linenr_T *lnum, int *max_width)
 {
   Error err = ERROR_INIT;
   Arena arena = ARENA_EMPTY;
   Array replacement = ARRAY_DICT_INIT;
+  buf_T *buf = win->w_buffer;
   buf->b_p_ma = true;
 
   // Iterate through the string line by line by temporarily replacing newlines with NUL
@@ -931,7 +932,7 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma
       break;
     }
 
-    *max_width = MAX(*max_width, linetabsize_str(curr));
+    *max_width = MAX(*max_width, win_linetabsize(win, 0, curr, MAXCOL));
     ADD(replacement, STRING_OBJ(cstr_to_string(curr)));
     (*lnum)++;
 
@@ -941,9 +942,7 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma
   }
 
   int original_textlock = textlock;
-  if (textlock > 0) {
-    textlock = 0;
-  }
+  textlock = 0;
   nvim_buf_set_lines(0, buf->handle, 0, -1, false, replacement, &arena, &err);
   textlock = original_textlock;
   if (ERROR_SET(&err)) {
@@ -1019,7 +1018,7 @@ win_T *pum_set_info(int selected, char *info)
   }
   linenr_T lnum = 0;
   int max_info_width = 0;
-  pum_preview_set_text(wp->w_buffer, info, &lnum, &max_info_width);
+  pum_preview_set_text(wp, info, &lnum, &max_info_width);
   no_u_sync--;
   RedrawingDisabled--;
   redraw_later(wp, UPD_NOT_VALID);
@@ -1178,7 +1177,7 @@ static bool pum_set_selected(int n, int repeat)
         if (res == OK) {
           linenr_T lnum = 0;
           int max_info_width = 0;
-          pum_preview_set_text(curbuf, pum_array[pum_selected].pum_info, &lnum, &max_info_width);
+          pum_preview_set_text(curwin, pum_array[pum_selected].pum_info, &lnum, &max_info_width);
           // Increase the height of the preview window to show the
           // text, but no more than 'previewheight' lines.
           if (repeat == 0 && !use_float) {
```

Code after:

/// Set the informational text in the preview buffer when the completion
/// item does not include a dedicated preview or popup window.
///
/// @param[in]  win        Window containing buffer where the text will be set.
/// @param[in]  info       Informational text to display in the preview buffer.
/// @param[in]  lnum       Where to start the text. Incremented for each added line.
/// @param[out] max_width  Maximum width of the displayed text.
static void pum_preview_set_text(win_T *win, char *info, linenr_T *lnum, int *max_width)
{
  Error err = ERROR_INIT;
  Arena arena = ARENA_EMPTY;
  Array replacement = ARRAY_DICT_INIT;
  buf_T *buf = win->w_buffer;
  buf->b_p_ma = true;

  // Iterate through the string line by line by temporarily replacing newlines with NUL
  for (char *curr = info, *next; curr; curr = next ? next + 1 : NULL) {
    if ((next = strchr(curr, '\n'))) {
      *next = NUL;  // Temporarily replace the newline with a string terminator
    }
    // Only skip if this is an empty line AND it's the last line
    if (*curr == '\0' && !next) {
      break;
    }

    *max_width = MAX(*max_width, win_linetabsize(win, 0, curr, MAXCOL));
    ADD(replacement, STRING_OBJ(cstr_to_string(curr)));
    (*lnum)++;

    if (next) {
      *next = '\n';
    }
  }

  int original_textlock = textlock;
  textlock = 0;
  nvim_buf_set_lines(0, buf->handle, 0, -1, false, replacement, &arena, &err);
  textlock = original_textlock;
  if (ERROR_SET(&err)) {
    emsg(err.msg);
    api_clear_error(&err);
  }
  arena_mem_free(arena_finish(&arena));
  api_free_array(replacement);
  buf->b_p_ma = false;
}

/// adjust floating info preview window position
static bool pum_adjust_info_position(win_T *wp, int width)
{