Compare commits

...

3 Commits

Author SHA1 Message Date
glepnir cd240d085b
Merge 20a5ba6796 into c18d7941ef 2024-05-09 12:08:54 -04:00
dundargoc c18d7941ef build: allow sccache as compiler cache
Also enable caching for dependencies.

Closes https://github.com/neovim/neovim/issues/28670
2024-05-09 16:39:45 +02:00
glepnir 20a5ba6796 feat(float): add previewpopup option like vim 2024-05-06 17:00:07 +08:00
21 changed files with 646 additions and 43 deletions

View File

@ -50,11 +50,6 @@ file(GLOB DOCFILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/runtime/doc/*.txt)
set_directory_properties(PROPERTIES
EP_PREFIX "${DEPS_BUILD_DIR}")
find_program(CCACHE_PRG ccache)
if(CCACHE_PRG)
set(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env CCACHE_SLOPPINESS=pch_defines,time_macros ${CCACHE_PRG})
endif()
if(NOT CI_BUILD)
set(CMAKE_INSTALL_MESSAGE NEVER)
endif()

View File

@ -23,6 +23,12 @@ if(POLICY CMP0092)
list(APPEND DEPS_CMAKE_ARGS -D CMAKE_POLICY_DEFAULT_CMP0092=NEW)
endif()
find_program(CACHE_PRG NAMES ccache sccache)
if(CACHE_PRG)
set(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env CCACHE_SLOPPINESS=pch_defines,time_macros ${CACHE_PRG})
list(APPEND DEPS_CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER})
endif()
# MAKE_PRG
if(UNIX)
find_program(MAKE_PRG NAMES gmake make)
@ -58,7 +64,8 @@ function(get_externalproject_options name DEPS_IGNORE_SHA)
set(EXTERNALPROJECT_OPTIONS
DOWNLOAD_NO_PROGRESS TRUE
EXTERNALPROJECT_OPTIONS URL ${${name_allcaps}_URL})
EXTERNALPROJECT_OPTIONS URL ${${name_allcaps}_URL}
CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
if(NOT ${DEPS_IGNORE_SHA})
list(APPEND EXTERNALPROJECT_OPTIONS URL_HASH SHA256=${${name_allcaps}_SHA256})

View File

@ -386,6 +386,10 @@ The following new APIs and features were added.
using the OSC 8 control sequence, enabling clickable text in supporting
terminals.
• |extmarks| can be associated with a URL and URLs are included as a new
highlight attribute. The TUI will display URLs using the OSC 8 control
sequence, enabling clickable text in supporting terminals.
• Added |nvim_tabpage_set_win()| to set the current window of a tabpage.
• Clicking on a tabpage in the tabline with the middle mouse button closes it.
@ -399,6 +403,8 @@ The following new APIs and features were added.
• |vim.fs.root()| finds project root directories from a list of "root
markers".
• Added 'previewpopup' option.
==============================================================================
CHANGED FEATURES *news-changed*

View File

@ -4609,6 +4609,14 @@ A jump table for the options with a short description can be found at |Q_op|.
Default height for a preview window. Used for |:ptag| and associated
commands. Used for |CTRL-W_}| when no count is given.
*'previewpopup'* *'pvp'*
'previewpopup' 'pvp' string (default "")
global
When not empty a floating window is used for commands that would open
a preview window. See |preview-popup|.
Not used for the insert completion info, add "popup" to
'completeopt' for that.
*'previewwindow'* *'pvw'* *'nopreviewwindow'* *'nopvw'* *E590*
'previewwindow' 'pvw' boolean (default off)
local to window |local-noglobal|

View File

@ -590,7 +590,6 @@ These legacy Vim features are not yet implemented:
- *:gui*
- *:gvim*
- *'completepopup'*
- *'previewpopup'*
==============================================================================
Removed legacy features *nvim-removed*

View File

@ -922,6 +922,26 @@ set in the preview window to be able to recognize it. The 'winfixheight'
option is set to have it keep the same height when opening/closing other
windows.
*preview-popup*
Alternatively, a floating window can be used by setting the 'previewpopup'
option. When set, it overrules the 'previewwindow' and 'previewheight'
settings. The option is a comma-separated list of values:
height maximum height of the popup
width maximum width of the popup
border string values are one of none, single, double, rounded,
solid, shadow.
Example: >vim
:set previewpopup=border:double,height:10,width:60
A few peculiarities:
- If the file is in a buffer already, it will be re-used. This will allow for
editing the file while it's visible in the popup window.
- No ATTENTION dialog will be used, since you can't edit the file in the popup
window. However, if you later open the same buffer in a normal window, you
may not notice it's edited elsewhere. And when then using ":edit" to
trigger the ATTENTION and responding "A" for Abort, the preview window will
become empty.
*:pta* *:ptag*
:pta[g][!] [tagname]
Does ":tag[!] [tagname]" and shows the found tag in a

View File

@ -4801,6 +4801,17 @@ vim.o.pvh = vim.o.previewheight
vim.go.previewheight = vim.o.previewheight
vim.go.pvh = vim.go.previewheight
--- When not empty a floating window is used for commands that would open
--- a preview window. See `preview-popup`.
--- Not used for the insert completion info, add "popup" to
--- 'completeopt' for that.
---
--- @type string
vim.o.previewpopup = ""
vim.o.pvp = vim.o.previewpopup
vim.go.previewpopup = vim.o.previewpopup
vim.go.pvp = vim.go.previewpopup
--- Identifies the preview window. Only one window can have this option
--- set. It's normally not set directly, but by using one of the commands
--- `:ptag`, `:pedit`, etc.

View File

@ -835,8 +835,8 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
return true;
}
static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig,
Error *err)
void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig,
Error *err)
{
if (bordertext.type != kObjectTypeString && bordertext.type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "title/footer must be string or array");
@ -893,8 +893,8 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
*is_present = true;
}
static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
WinConfig *fconfig, Error *err)
bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type, WinConfig *fconfig,
Error *err)
{
AlignTextPos *align;
switch (bordertext_type) {
@ -933,7 +933,7 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex
return true;
}
static void parse_border_style(Object style, WinConfig *fconfig, Error *err)
void parse_border_style(Object style, WinConfig *fconfig, Error *err)
{
struct {
const char *name;

View File

@ -4,6 +4,7 @@
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/buffer_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/win_config.h.generated.h"

View File

@ -904,6 +904,11 @@ typedef enum {
kFloatRelativeMouse = 3,
} FloatRelative;
typedef enum {
kFloatInfo = 0,
kFloatPreview = 1,
} FloatType;
/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c)
typedef enum {
kWinSplitLeft = 0,
@ -1295,7 +1300,7 @@ struct window_S {
ScreenGrid w_grid_alloc; // the grid specific to the window
bool w_pos_changed; // true if window position changed
bool w_floating; ///< whether the window is floating
bool w_float_is_info; // the floating window is info float
FloatType w_float_is; // the floating window is info float
WinConfig w_config;
// w_fraction is the fractional row of the cursor within the window, from

View File

@ -98,6 +98,7 @@
#include "nvim/undo.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
#include "nvim/winfloat.h"
/// Case matching style to use for :substitute
typedef enum {
@ -2617,6 +2618,10 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
changed_line_abv_curs();
maketitle();
if (curwin->w_floating && curwin->w_p_pvw) {
win_float_set_title(curwin, false);
win_float_adjust_position(curwin);
}
}
// Tell the diff stuff that this buffer is new and/or needs updating.
@ -4551,24 +4556,40 @@ void free_old_sub(void)
/// @param undo_sync sync undo when leaving the window
///
/// @return true when it was created.
bool prepare_tagpreview(bool undo_sync)
bool prepare_tagpreview(bool undo_sync, bool use_float)
{
if (curwin->w_p_pvw) {
return false;
}
// If there is already a preview window open, use that one.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_pvw) {
if (use_float) {
win_T *wp = win_float_find_preview(false);
if (wp) {
win_enter(wp, undo_sync);
return false;
} else {
wp = win_float_create(false, true, kFloatPreview);
if (!wp) {
return false;
}
win_enter(wp, undo_sync);
return true;
}
} else {
// If there is already a preview window open, use that one.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_pvw) {
win_enter(wp, undo_sync);
return false;
}
}
}
// There is no preview window open yet. Create one.
if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
== FAIL) {
return false;
if (!use_float) {
// There is no preview window open yet. Create one.
if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
== FAIL) {
return false;
}
}
curwin->w_p_pvw = true;
curwin->w_p_wfh = true;

View File

@ -4834,6 +4834,10 @@ static void ex_pclose(exarg_T *eap)
break;
}
}
if (*p_pvp != NUL) {
win_float_close(kFloatPreview);
}
}
/// Close window "win" and take care of handling closing the last window for a
@ -6790,7 +6794,7 @@ static void ex_pedit(exarg_T *eap)
// Open the preview window or popup and make it the current window.
g_do_tagpreview = (int)p_pvh;
prepare_tagpreview(true);
prepare_tagpreview(true, *p_pvp != NUL);
// Edit the file.
do_exedit(eap, NULL);
@ -7565,7 +7569,7 @@ static void ex_terminal(exarg_T *eap)
/// ":fclose"
static void ex_fclose(exarg_T *eap)
{
win_float_remove(eap->forceit, eap->line1);
win_float_remove_by_zindex(eap->forceit, eap->line1);
}
void verify_command(char *cmd)

View File

@ -2821,7 +2821,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
}
if (ret == OK && (what_flag & CI_WHAT_SELECTED)) {
ret = tv_dict_add_nr(retdict, S_LEN("selected"), selected_idx);
win_T *wp = win_float_find_preview();
win_T *wp = win_float_find_preview(true);
if (wp != NULL) {
tv_dict_add_nr(retdict, S_LEN("preview_winid"), wp->handle);
tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle);

View File

@ -596,6 +596,7 @@ EXTERN unsigned rdb_flags;
EXTERN OptInt p_rdt; ///< 'redrawtime'
EXTERN OptInt p_re; ///< 'regexpengine'
EXTERN OptInt p_report; ///< 'report'
EXTERN char *p_pvp; ///< 'previewpopup'
EXTERN OptInt p_pvh; ///< 'previewheight'
EXTERN int p_ari; ///< 'allowrevins'
EXTERN int p_ri; ///< 'revins'

View File

@ -6034,6 +6034,24 @@ return {
type = 'number',
varname = 'p_pvh',
},
{
abbreviation = 'pvp',
cb = 'did_set_previewpopup',
expand_cb = 'expand_set_popupoption',
defaults = { if_true = '' },
desc = [=[
When not empty a floating window is used for commands that would open
a preview window. See |preview-popup|.
Not used for the insert completion info, add "popup" to
'completeopt' for that.
]=],
full_name = 'previewpopup',
list = 'commacolon',
scope = { 'global' },
short_desc = N_('use a flaoting window for preview'),
type = 'string',
varname = 'p_pvp',
},
{
abbreviation = 'pvw',
cb = 'did_set_previewwindow',

View File

@ -47,6 +47,7 @@
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "optionstr.c.generated.h"
@ -142,6 +143,11 @@ static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelt
"flush", NULL };
static char *(p_sloc_values[]) = { "last", "statusline", "tabline", NULL };
// Note: Keep this in sync with parse_float_option()
static char *(p_popup_option_values[]) = { "height:", "width:", "border:", NULL };
static char *(p_popup_option_border_values[]) = { "single", "double", "none", "rounded", "solid",
"shadow", NULL };
/// All possible flags for 'shm'.
/// the literal chars before 0 are removed flags. these are safely ignored
static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES,
@ -2881,3 +2887,41 @@ const char *check_chars_options(void)
}
return NULL;
}
const char *did_set_previewpopup(optset_T *args)
{
win_T *wp = win_float_find_preview(kFloatPreview);
WinConfig fconfig = wp ? wp->w_config : WIN_CONFIG_INIT;
if (!parse_float_option(&fconfig)) {
return e_invarg;
}
if (wp) {
win_config_float(wp, fconfig);
}
return NULL;
}
int expand_set_popupoption(optexpand_T *args, int *numMatches, char ***matches)
{
expand_T *xp = args->oe_xp;
if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern - 1) == ':') {
int arg_len = (int)(xp->xp_pattern - args->oe_set_arg);
// match border:
if (arg_len >= 7 && strncmp(xp->xp_pattern - 7, "border:", 7) == 0) {
return expand_set_opt_string(args, p_popup_option_border_values,
ARRAY_SIZE(p_popup_option_border_values) - 1, numMatches,
matches);
}
return FAIL;
}
return expand_set_opt_string(args,
p_popup_option_values,
ARRAY_SIZE(p_popup_option_values) - 1,
numMatches,
matches);
}

View File

@ -745,9 +745,9 @@ win_T *pum_set_info(int selected, char *info)
block_autocmds();
RedrawingDisabled++;
no_u_sync++;
win_T *wp = win_float_find_preview();
win_T *wp = win_float_find_preview(kFloatInfo);
if (wp == NULL) {
wp = win_float_create(false, true);
wp = win_float_create(false, true, kFloatInfo);
if (!wp) {
return NULL;
}
@ -797,7 +797,7 @@ static bool pum_set_selected(int n, int repeat)
bool use_float = strstr(p_cot, "popup") != NULL;
// when new leader add and info window is shown and no selected we still
// need use the first index item to update the info float window position.
bool force_select = use_float && pum_selected < 0 && win_float_find_preview();
bool force_select = use_float && pum_selected < 0 && win_float_find_preview(kFloatInfo);
if (force_select) {
pum_selected = 0;
}
@ -881,13 +881,13 @@ static bool pum_set_selected(int n, int repeat)
no_u_sync++;
if (!use_float) {
resized = prepare_tagpreview(false);
resized = prepare_tagpreview(false, false);
} else {
win_T *wp = win_float_find_preview();
win_T *wp = win_float_find_preview(kFloatInfo);
if (wp) {
win_enter(wp, false);
} else {
wp = win_float_create(true, true);
wp = win_float_create(true, true, kFloatInfo);
if (wp) {
resized = true;
}
@ -898,7 +898,7 @@ static bool pum_set_selected(int n, int repeat)
RedrawingDisabled--;
g_do_tagpreview = 0;
if (curwin->w_p_pvw || curwin->w_float_is_info) {
if (curwin->w_p_pvw || (curwin->w_float_is == kFloatInfo)) {
int res = OK;
if (!resized
&& (curbuf->b_nwindows == 1)
@ -1054,9 +1054,8 @@ void pum_check_clear(void)
}
pum_is_drawn = false;
pum_external = false;
win_T *wp = win_float_find_preview();
if (wp != NULL) {
win_close(wp, false, false);
if (strstr(p_cot, "popup") != NULL) {
win_float_close(kFloatInfo);
}
}
}

View File

@ -4012,7 +4012,7 @@ search_line:
// ":psearch" uses the preview window
if (l_g_do_tagpreview != 0) {
curwin_save = curwin;
prepare_tagpreview(true);
prepare_tagpreview(true, *p_pvp != NUL);
}
if (action == ACTION_SPLIT) {
if (win_split(0, 0) == FAIL) {

View File

@ -2868,7 +2868,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
// Make the preview window the current window.
// Open a preview window when needed.
prepare_tagpreview(true);
prepare_tagpreview(true, *p_pvp != NUL);
}
}

View File

@ -7,20 +7,32 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/decoration.h"
#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/plines.h"
#include "nvim/pos_defs.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
@ -271,7 +283,7 @@ static int float_zindex_cmp(const void *a, const void *b)
return za == zb ? 0 : za < zb ? 1 : -1;
}
void win_float_remove(bool bang, int count)
void win_float_remove_by_zindex(bool bang, int count)
{
kvec_t(win_T *) float_win_arr = KV_INITIAL_VALUE;
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
@ -330,10 +342,123 @@ bool win_float_valid(const win_T *win)
return false;
}
win_T *win_float_find_preview(void)
/// Parses the 'border' style configuration and updates WinConfig.
///
/// @param fconfig Configuration storage.
/// @param dup_val Value text to parse.
/// @param len Length of the text.
/// @param err Pointer to the Error structure for error handling.
///
/// @return true if parsing is successful, otherwise false.
static bool parse_opt_border(WinConfig *config, char *dup_val, size_t len, Error *err)
{
Object style = CSTR_AS_OBJ(dup_val);
parse_border_style(style, config, err);
api_free_object(style);
if (ERROR_SET(err)) {
return false;
}
int border_attr = syn_name2attr("FloatBorder");
for (int i = 0; i < 8; i++) {
config->border_attr[i] = config->border_hl_ids[i]
? hl_get_ui_attr(0, HLF_BORDER, config->border_hl_ids[i], false)
: border_attr;
}
return true;
}
/// Parses numeric keys for 'height' and 'width' options and updates WinConfig.
///
/// @param fconfig Configuration storage.
/// @param dig Digits representing the numeric value.
/// @param len Length of the digits.
/// @param err Pointer to the Error structure for error handling.
///
/// @return true if parsing is successful, otherwise false.
static bool parse_opt_dig_key(WinConfig *config, char *dig, size_t len, Error *err)
{
int val = getdigits_int(&dig, false, 0);
if (len == 6) {
config->width = val;
} else {
config->height = val;
}
return true;
}
/// Parses options for configuring floating windows for completion popups or preview popups.
/// Supports setting border style, title, title position, footer, footer position, height, and width.
/// Only processes height and width options if `preview` is true.
///
/// @param fconfig The floating window configuration to modify.
/// @param preview Indicates if the configuration is for a preview popup.
///
/// @return True if options are successfully parsed, otherwise false.
bool parse_float_option(WinConfig *config)
{
char *p = p_pvp;
Error err = ERROR_INIT;
struct {
char *key;
bool (*parser_func)(WinConfig *, char *, size_t, Error *);
} parsers[] = {
{ "border:", parse_opt_border },
{ "height:", parse_opt_dig_key },
{ "width:", parse_opt_dig_key },
{ NULL, NULL },
};
for (; *p != NUL; p += (*p == ',' ? 1 : 0)) {
char *s = p;
char *e = strchr(p, ':');
if (e == NULL || e[1] == NUL) {
return false;
}
p = strchr(e, ',');
if (p == NULL) {
p = e + strlen(e);
}
bool parsed = false;
for (size_t i = 0; parsers[i].key; i++) {
size_t len = strlen(parsers[i].key);
if (strncmp(s, parsers[i].key, len) == 0) {
// when is width or height use e + 1
char *val = s[0] == 'w' || s[0] == 'h' ? e + 1 : NULL;
if (!val) {
val = xmemdupz(s + len, (p ? (size_t)(p - s) - len : (size_t)(s - len)));
if (!val) {
return false;
}
}
if (!parsers[i].parser_func(config, val, len, &err)) {
return false;
}
parsed = true;
break;
}
}
if (!parsed) {
return false;
}
}
return true;
}
/// Searches for a floating window matching given criteria.
///
/// @param find_info Search for info window if true, else preview window.
///
/// @return A pointer to the a floating window structure.
win_T *win_float_find_preview(FloatType float_type)
{
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
if (wp->w_float_is_info) {
if (wp->w_float_is == float_type) {
return wp;
}
}
@ -364,7 +489,7 @@ win_T *win_float_find_altwin(const win_T *win, const tabpage_T *tp)
/// @param[in] bool create a new buffer for window.
///
/// @return win_T
win_T *win_float_create(bool enter, bool new_buf)
win_T *win_float_create(bool enter, bool new_buf, FloatType float_type)
{
WinConfig config = WIN_CONFIG_INIT;
config.col = curwin->w_wcol;
@ -375,8 +500,11 @@ win_T *win_float_create(bool enter, bool new_buf)
config.noautocmd = true;
config.hide = true;
config.style = kWinStyleMinimal;
if (float_type == kFloatPreview && !parse_float_option(&config)) {
emsg(_(e_invarg));
return NULL;
}
Error err = ERROR_INIT;
block_autocmds();
win_T *wp = win_new_float(NULL, false, config, &err);
if (!wp) {
@ -400,9 +528,144 @@ win_T *win_float_create(bool enter, bool new_buf)
}
unblock_autocmds();
wp->w_p_diff = false;
wp->w_float_is_info = true;
wp->w_float_is = float_type;
if (wp->w_float_is == kFloatPreview) {
wp->w_p_pvw = true;
wp->w_p_wrap = true;
wp->w_p_so = 0;
}
if (enter) {
win_enter(wp, false);
}
return wp;
}
/// Closes a specified floating window used for previews or popups.
/// Searches for and closes a floating window based on given criteria.
///
/// @param find_info Flag to determine search criteria for the floating window.
///
/// @return True if the window is successfully closed, otherwise false.
bool win_float_close(FloatType float_type)
{
win_T *wp = win_float_find_preview(float_type);
return wp && win_close(wp, false, false) != FAIL;
}
/// Set bufname as title for a floating window.
/// Title position is center.
///
/// @param wp A pointer of win_T
/// @param redraw bool
/// @return
void win_float_set_title(win_T *wp, bool redraw)
{
if (!wp->w_floating || !wp->w_config.border) {
return;
}
if (wp->w_config.title) {
clear_virttext(&wp->w_config.title_chunks);
}
int title_id = syn_check_group(S_LEN("FloatTitle"));
wp->w_config.title = true;
wp->w_config.title_pos = kAlignCenter;
wp->w_config.title_width = (int)mb_string2cells(wp->w_buffer->b_fname);
kv_push(*(&wp->w_config.title_chunks), ((VirtTextChunk){ .text = xstrdup(wp->w_buffer->b_fname),
.hl_id = title_id }));
if (redraw) {
win_config_float(wp, wp->w_config);
}
}
/// adjust a preview floating window postion to fit screen and buffer in wp.
///
/// @param wp A pointer of win_T
/// @return
void win_float_adjust_position(win_T *wp)
{
if (!wp->w_floating) {
return;
}
int border_extra = wp->w_config.border ? 2 : 0;
int right_extra = Columns - curwin->w_wincol - curwin->w_wcol - border_extra;
int left_extra = curwin->w_wincol + curwin->w_wcol - border_extra;
int below_height = Rows - curwin->w_winrow - curwin->w_wrow - border_extra;
int above_height = curwin->w_winrow + curwin->w_wrow - border_extra;
// fit screen
bool west = false;
if (wp->w_config.width < right_extra) { // placed in right
west = true;
} else if (wp->w_config.width < left_extra) { // placed in left
west = false;
} else { // eighter width not enough to placed the preview window use the largest onw.
if (right_extra > left_extra) {
west = true;
wp->w_config.width = right_extra;
} else {
wp->w_config.width = left_extra;
}
}
if (wp->w_config.height < below_height) { // below is enough to placed preview window
wp->w_config.anchor = west ? 0 : kFloatAnchorEast; // NW or NE
} else if (wp->w_config.height < above_height) {
// SW or SE
wp->w_config.anchor = west ? kFloatAnchorSouth : kFloatAnchorSouth | kFloatAnchorEast;
} else { // either height value smaller than max height use the largest one
if (below_height > above_height) {
wp->w_config.height = below_height;
wp->w_config.anchor = west ? 0 : kFloatAnchorEast; // NW or NE
} else {
wp->w_config.height = above_height;
// SW or SE
wp->w_config.anchor = west ? kFloatAnchorSouth : kFloatAnchorSouth | kFloatAnchorEast;
}
}
if (wp->w_topline < 1) {
wp->w_topline = 1;
} else if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count) {
wp->w_topline = wp->w_buffer->b_ml.ml_line_count;
}
// fit buffer preview window it's wrap always
if (wp->w_p_wrap) {
// actually height of preview window
int actual_height = 0;
// max width of lines in preview window
int max_width = 0;
int lnum = wp->w_topline;
int height = wp->w_config.height;
while (height) {
actual_height += plines_win(wp, lnum, false);
int len = linetabsize(wp, lnum);
if (len > max_width) {
max_width = len;
}
lnum++;
if (lnum > wp->w_buffer->b_ml.ml_line_count) {
break;
}
height--;
}
if (actual_height > 0) {
wp->w_config.height = MIN(wp->w_config.height, actual_height);
}
if (max_width > 0) {
wp->w_config.width = MIN(wp->w_config.width, max_width);
}
}
if ((wp->w_config.anchor & kFloatAnchorSouth) == 0) {
wp->w_config.row += 1;
wp->w_config.col += 1;
}
wp->w_config.hide = false;
win_config_float(wp, wp->w_config);
}

View File

@ -9316,6 +9316,207 @@ describe('float window', function()
]]
})
end
end)
it('#previewpopup option', function()
command('call writefile(["bar"], "foo", "a")')
finally(function()
os.remove('foo')
end)
command('set previewpopup=height:2,width:5,border:single | pedit foo')
if multigrid then
screen:expect({grid = [[
## grid 1
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
^ |
{0:~ }|*5
## grid 3
|
## grid 4
{5:}{11:foo}{5:}|
{5:}{1:bar}{5:}|
{5:}|
]], float_pos={
[4] = {1001, "NW", 1, 1, 1, false, 50};
}, win_viewport={
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
[4] = {win = 1001, topline = 0, botline = 1, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
}, win_viewport_margins={
[2] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
win = 1000
},
[4] = {
bottom = 1,
left = 1,
right = 1,
top = 1,
win = 1001
}
}
})
else
screen:expect({
grid = [[
^ |
{0:~}{5:}{11:foo}{5:}{0: }|
{0:~}{5:}{1:bar}{5:}{0: }|
{0:~}{5:}{0: }|
{0:~ }|*2
|
]]
})
end
--refconfig it by using set
command('set previewpopup=height:2,width:5,border:double')
if multigrid then
screen:expect({
grid = [[
## grid 1
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
^ |
{0:~ }|*5
## grid 3
|
## grid 4
{5:}{11:foo}{5:}|
{5:}{1:bar }{5:}|
{5:}{1: }{5:}|
{5:}|
]], float_pos={
[4] = {1001, "NW", 1, 1, 1, false, 50};
}, win_viewport={
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
[4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
}, win_viewport_margins={
[2] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
win = 1000
},
[4] = {
bottom = 1,
left = 1,
right = 1,
top = 1,
win = 1001
}
}
})
else
screen:expect({
grid = [[
^ |
{0:~}{5:}{11:foo}{5:}{0: }|
{0:~}{5:}{1:bar }{5:}{0: }|
{0:~}{5:}{1: }{5:}{0: }|
{0:~}{5:}{0: }|
{0:~ }|
|
]]
})
end
--can close by pclose command
command('pclose')
if multigrid then
screen:expect({
grid = [[
## grid 1
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
^ |
{0:~ }|*5
## grid 3
|
]], win_viewport={
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
}, win_viewport_margins={
[2] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
win = 1000
}
}
})
else
screen:expect({
grid = [[
^ |
{0:~ }|*5
|
]]
})
end
--when open an empty buffer/file will use max width and height
command('set previewpopup=height:2,width:7,border:double | pedit unexist')
if multigrid then
screen:expect({
grid = [[
## grid 1
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
^ |
{0:~ }|*5
## grid 3
|
## grid 5
{5:}{11:unexist}{5:}|
{5:}{1: }{5:}|
{5:}|
]], float_pos={
[5] = {1002, "NW", 1, 1, 1, false, 50};
}, win_viewport={
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
[5] = {win = 1002, topline = 0, botline = 1, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
}, win_viewport_margins={
[2] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
win = 1000
},
[5] = {
bottom = 1,
left = 1,
right = 1,
top = 1,
win = 1002
}
}
})
else
screen:expect({
grid = [[
^ |
{0:~}{5:}{11:unexist}{5:}{0: }|
{0:~}{5:}{1: }{5:}{0: }|
{0:~}{5:}{0: }|
{0:~ }|*2
|
]]
})
end
end)
it('invalid argument in previewpopup', function ()
local err = pcall_err(n.exec_capture, 'set previewpopup=height:10,border:nonexist')
eq('nvim_exec2(): Vim(set):E474: Invalid argument: previewpopup=height:10,border:nonexist', err)
end)
end