mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	UI: implement 'pumblend' option for semi-transparent popupmenu
Why? - Because we can. - Because the TUI is just another GUI™ - Because it looks kinda nice, and provides useful context like 1 out of 100 times Complies with "don't pay for what you don't use". Some crashes for resizing were unfolded, add tests for those.
This commit is contained in:
		| @@ -4486,6 +4486,16 @@ A jump table for the options with a short description can be found at |Q_op|. | ||||
| 	Insert mode completion.  When zero as much space as available is used. | ||||
| 	|ins-completion-menu|. | ||||
|  | ||||
| 						*'pumblend'* *'pb'* | ||||
| 'pumblend' 'pb'		number	(default 0) | ||||
| 			global | ||||
| 	Enables semi-transparency for the completion popupmenu. Valid values | ||||
| 	are in the range from 0 for fully opaque popupmenu (disabled) to 100 | ||||
| 	for fully transparent background. Lower values 0-30 are typically most | ||||
| 	useful. | ||||
|  | ||||
| 	UI-dependent. Supported by TUI with 'termguicolors' enabled. | ||||
|  | ||||
| 						*'pyxversion'* *'pyx'* | ||||
| 'pyxversion' 'pyx'	number	(default depends on the build) | ||||
| 			global | ||||
|   | ||||
| @@ -189,6 +189,7 @@ Options: | ||||
|               for |hl-EndOfBuffer| marker | ||||
|   'inccommand' shows interactive results for |:substitute|-like commands | ||||
|   'listchars' local to window | ||||
|   'pumblend' semi-transparent popupmenu | ||||
|   'scrollback' | ||||
|   'statusline' supports unlimited alignment sections | ||||
|   'tabline' %@Func@foo%X can call any function on mouse-click | ||||
|   | ||||
| @@ -357,10 +357,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, | ||||
|                                          Integer cterm_fg, Integer cterm_bg) | ||||
| { | ||||
|   if (!ui->ui_ext[kUITermColors]) { | ||||
|     bool dark = (*p_bg == 'd'); | ||||
|     rgb_fg = rgb_fg != -1 ? rgb_fg : (dark ? 0xFFFFFF : 0x000000); | ||||
|     rgb_bg = rgb_bg != -1 ? rgb_bg : (dark ? 0x000000 : 0xFFFFFF); | ||||
|     rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; | ||||
|     HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); | ||||
|   } | ||||
|   Array args = ARRAY_DICT_INIT; | ||||
|   ADD(args, INTEGER_OBJ(rgb_fg)); | ||||
|   | ||||
| @@ -23,11 +23,15 @@ static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE; | ||||
|  | ||||
| static Map(HlEntry, int) *attr_entry_ids; | ||||
| static Map(int, int) *combine_attr_entries; | ||||
| static Map(int, int) *blend_attr_entries; | ||||
| static Map(int, int) *blendthrough_attr_entries; | ||||
|  | ||||
| void highlight_init(void) | ||||
| { | ||||
|   attr_entry_ids = map_new(HlEntry, int)(); | ||||
|   combine_attr_entries = map_new(int, int)(); | ||||
|   blend_attr_entries = map_new(int, int)(); | ||||
|   blendthrough_attr_entries = map_new(int, int)(); | ||||
|  | ||||
|   // index 0 is no attribute, add dummy entry: | ||||
|   kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown, | ||||
| @@ -213,6 +217,8 @@ void clear_hl_tables(bool reinit) | ||||
|     kv_size(attr_entries) = 1; | ||||
|     map_clear(HlEntry, int)(attr_entry_ids); | ||||
|     map_clear(int, int)(combine_attr_entries); | ||||
|     map_clear(int, int)(blend_attr_entries); | ||||
|     map_clear(int, int)(blendthrough_attr_entries); | ||||
|     highlight_attr_set_all(); | ||||
|     highlight_changed(); | ||||
|     screen_invalidate_highlights(); | ||||
| @@ -220,15 +226,22 @@ void clear_hl_tables(bool reinit) | ||||
|     kv_destroy(attr_entries); | ||||
|     map_free(HlEntry, int)(attr_entry_ids); | ||||
|     map_free(int, int)(combine_attr_entries); | ||||
|     map_free(int, int)(blend_attr_entries); | ||||
|     map_free(int, int)(blendthrough_attr_entries); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void hl_invalidate_blends(void) | ||||
| { | ||||
|   map_clear(int, int)(blend_attr_entries); | ||||
|   map_clear(int, int)(blendthrough_attr_entries); | ||||
| } | ||||
|  | ||||
| // Combine special attributes (e.g., for spelling) with other attributes | ||||
| // (e.g., for syntax highlighting). | ||||
| // "prim_attr" overrules "char_attr". | ||||
| // This creates a new group when required. | ||||
| // Since we expect there to be few spelling mistakes we don't cache the | ||||
| // result. | ||||
| // Since we expect there to be a lot of spelling mistakes we cache the result. | ||||
| // Return the resulting attributes. | ||||
| int hl_combine_attr(int char_attr, int prim_attr) | ||||
| { | ||||
| @@ -283,6 +296,85 @@ int hl_combine_attr(int char_attr, int prim_attr) | ||||
|   return id; | ||||
| } | ||||
|  | ||||
| /// Get the used rgb colors for an attr group. | ||||
| /// | ||||
| /// If colors are unset, use builtin default colors. Never returns -1 | ||||
| /// Cterm colors are unchanged. | ||||
| static HlAttrs get_colors_force(int attr) | ||||
| { | ||||
|   HlAttrs attrs = syn_attr2entry(attr); | ||||
|   if (attrs.rgb_bg_color == -1) { | ||||
|     attrs.rgb_bg_color = normal_bg; | ||||
|   } | ||||
|   if (attrs.rgb_fg_color == -1) { | ||||
|     attrs.rgb_fg_color = normal_fg; | ||||
|   } | ||||
|   if (attrs.rgb_sp_color == -1) { | ||||
|     attrs.rgb_sp_color = normal_sp; | ||||
|   } | ||||
|   HL_SET_DEFAULT_COLORS(attrs.rgb_fg_color, attrs.rgb_bg_color, | ||||
|                         attrs.rgb_sp_color); | ||||
|   return attrs; | ||||
| } | ||||
|  | ||||
| /// Blend overlay attributes (for popupmenu) with other attributes | ||||
| /// | ||||
| /// This creates a new group when required. | ||||
| /// This will be called on a per-cell basis when in use, so cache the result. | ||||
| /// @return the resulting attributes. | ||||
| int hl_blend_attrs(int back_attr, int front_attr, bool through) | ||||
| { | ||||
|   int combine_tag = (back_attr << 16) + front_attr; | ||||
|   Map(int, int) *map = through ? blendthrough_attr_entries : blend_attr_entries; | ||||
|   int id = map_get(int, int)(map, combine_tag); | ||||
|   if (id > 0) { | ||||
|     return id; | ||||
|   } | ||||
|  | ||||
|   HlAttrs battrs = get_colors_force(back_attr); | ||||
|   HlAttrs fattrs = get_colors_force(front_attr); | ||||
|   HlAttrs cattrs; | ||||
|   if (through) { | ||||
|     cattrs = battrs; | ||||
|     cattrs.rgb_fg_color = rgb_blend((int)p_pb, battrs.rgb_fg_color, | ||||
|                                     fattrs.rgb_bg_color); | ||||
|     cattrs.cterm_bg_color = fattrs.cterm_bg_color; | ||||
|     cattrs.cterm_fg_color = fattrs.cterm_bg_color; | ||||
|   } else { | ||||
|     cattrs = fattrs; | ||||
|     if (p_pb >= 50) { | ||||
|       cattrs.rgb_ae_attr |= battrs.rgb_ae_attr; | ||||
|     } | ||||
|     cattrs.rgb_fg_color = rgb_blend((int)p_pb/2, battrs.rgb_fg_color, | ||||
|                                     fattrs.rgb_fg_color); | ||||
|   } | ||||
|   cattrs.rgb_bg_color = rgb_blend((int)p_pb, battrs.rgb_bg_color, | ||||
|                                   fattrs.rgb_bg_color); | ||||
|  | ||||
|   HlKind kind = through ? kHlBlendThrough : kHlBlend; | ||||
|   id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind, | ||||
|                                  .id1 = back_attr, .id2 = front_attr }); | ||||
|   if (id > 0) { | ||||
|     map_put(int, int)(map, combine_tag, id); | ||||
|   } | ||||
|   return id; | ||||
| } | ||||
|  | ||||
| static int rgb_blend(int ratio, int rgb1, int rgb2) | ||||
| { | ||||
|   int a = ratio, b = 100-ratio; | ||||
|   int r1 = (rgb1 & 0xFF0000) >> 16; | ||||
|   int g1 = (rgb1 & 0x00FF00) >> 8; | ||||
|   int b1 = (rgb1 & 0x0000FF) >> 0; | ||||
|   int r2 = (rgb2 & 0xFF0000) >> 16; | ||||
|   int g2 = (rgb2 & 0x00FF00) >> 8; | ||||
|   int b2 = (rgb2 & 0x0000FF) >> 0; | ||||
|   int mr = (a * r1 + b * r2)/100; | ||||
|   int mg = (a * g1 + b * g2)/100; | ||||
|   int mb = (a * b1 + b * b2)/100; | ||||
|   return (mr << 16) + (mg << 8) + mb; | ||||
| } | ||||
|  | ||||
| /// Get highlight attributes for a attribute code | ||||
| HlAttrs syn_attr2entry(int attr) | ||||
| { | ||||
| @@ -406,6 +498,8 @@ static void hl_inspect_impl(Array *arr, int attr) | ||||
|       break; | ||||
|  | ||||
|     case kHlCombine: | ||||
|     case kHlBlend: | ||||
|     case kHlBlendThrough: | ||||
|       // attribute combination is associative, so flatten to an array | ||||
|       hl_inspect_impl(arr, e.id1); | ||||
|       hl_inspect_impl(arr, e.id2); | ||||
|   | ||||
| @@ -10,4 +10,12 @@ | ||||
| # include "highlight.h.generated.h" | ||||
| #endif | ||||
|  | ||||
| # define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \ | ||||
|   do { \ | ||||
|     bool dark_ = (*p_bg == 'd'); \ | ||||
|     rgb_fg = rgb_fg != -1 ? rgb_fg : (dark_ ? 0xFFFFFF : 0x000000); \ | ||||
|     rgb_bg = rgb_bg != -1 ? rgb_bg : (dark_ ? 0x000000 : 0xFFFFFF); \ | ||||
|     rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; \ | ||||
|   } while (0); | ||||
|  | ||||
| #endif  // NVIM_HIGHLIGHT_H | ||||
|   | ||||
| @@ -160,6 +160,8 @@ typedef enum { | ||||
|   kHlSyntax, | ||||
|   kHlTerminal, | ||||
|   kHlCombine, | ||||
|   kHlBlend, | ||||
|   kHlBlendThrough, | ||||
| } HlKind; | ||||
|  | ||||
| typedef struct { | ||||
|   | ||||
| @@ -50,6 +50,7 @@ | ||||
| #include "nvim/fold.h" | ||||
| #include "nvim/getchar.h" | ||||
| #include "nvim/hardcopy.h" | ||||
| #include "nvim/highlight.h" | ||||
| #include "nvim/indent_c.h" | ||||
| #include "nvim/mbyte.h" | ||||
| #include "nvim/memfile.h" | ||||
| @@ -65,6 +66,7 @@ | ||||
| #include "nvim/normal.h" | ||||
| #include "nvim/os_unix.h" | ||||
| #include "nvim/path.h" | ||||
| #include "nvim/popupmnu.h" | ||||
| #include "nvim/regexp.h" | ||||
| #include "nvim/screen.h" | ||||
| #include "nvim/spell.h" | ||||
| @@ -4338,6 +4340,14 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, | ||||
|     if (p_uc && !old_value) { | ||||
|       ml_open_files(); | ||||
|     } | ||||
|   } else if (pp == &p_pb) { | ||||
|     p_pb = MAX(MIN(p_pb, 100), 0); | ||||
|     if (old_value != 0) { | ||||
|       hl_invalidate_blends(); | ||||
|     } | ||||
|     if (pum_drawn()) { | ||||
|       pum_recompose(); | ||||
|     } | ||||
|   } else if (pp == &p_pyx) { | ||||
|     if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) { | ||||
|       errmsg = e_invarg; | ||||
|   | ||||
| @@ -375,6 +375,7 @@ EXTERN int p_confirm;           // 'confirm' | ||||
| EXTERN int p_cp;                // 'compatible' | ||||
| EXTERN char_u   *p_cot;         // 'completeopt' | ||||
| EXTERN long p_ph;               // 'pumheight' | ||||
| EXTERN long p_pb;               // 'pumblend' | ||||
| EXTERN char_u   *p_cpo;         // 'cpoptions' | ||||
| EXTERN char_u   *p_csprg;       // 'cscopeprg' | ||||
| EXTERN int p_csre;              // 'cscoperelative' | ||||
|   | ||||
| @@ -1805,6 +1805,14 @@ return { | ||||
|       varname='p_ph', | ||||
|       defaults={if_true={vi=0}} | ||||
|     }, | ||||
|     { | ||||
|       full_name='pumblend', abbreviation='pb', | ||||
|       type='number', scope={'global'}, | ||||
|       vi_def=true, | ||||
|       redraw={'ui_option'}, | ||||
|       varname='p_pb', | ||||
|       defaults={if_true={vi=0}} | ||||
|     }, | ||||
|     { | ||||
|       full_name='pyxversion', abbreviation='pyx', | ||||
|       type='number', scope={'global'}, | ||||
|   | ||||
| @@ -44,6 +44,7 @@ static int pum_col;                 // left column of pum | ||||
| static bool pum_is_visible = false; | ||||
| static bool pum_is_drawn = false; | ||||
| static bool pum_external = false; | ||||
| static bool pum_invalid = false;  // the screen was just cleared | ||||
|  | ||||
| static ScreenGrid pum_grid = SCREEN_GRID_INIT; | ||||
|  | ||||
| @@ -360,12 +361,14 @@ void pum_redraw(void) | ||||
|   grid_assign_handle(&pum_grid); | ||||
|   bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off, | ||||
|                                 pum_height, grid_width); | ||||
|   bool invalid_grid = moved || pum_invalid; | ||||
|   pum_invalid = false; | ||||
|  | ||||
|   if (!pum_grid.chars | ||||
|       || pum_grid.Rows != pum_height || pum_grid.Columns != grid_width) { | ||||
|     grid_alloc(&pum_grid, pum_height, grid_width, !moved, false); | ||||
|     grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false); | ||||
|     ui_call_grid_resize(pum_grid.handle, pum_grid.Columns, pum_grid.Rows); | ||||
|   } else if (moved) { | ||||
|   } else if (invalid_grid) { | ||||
|     grid_invalidate(&pum_grid); | ||||
|   } | ||||
|  | ||||
| @@ -806,6 +809,17 @@ bool pum_drawn(void) | ||||
|   return pum_visible() && !pum_external; | ||||
| } | ||||
|  | ||||
| /// Screen was cleared, need to redraw next time | ||||
| void pum_invalidate(void) | ||||
| { | ||||
|   pum_invalid = true; | ||||
| } | ||||
|  | ||||
| void pum_recompose(void) | ||||
| { | ||||
|   ui_comp_compose_grid(&pum_grid); | ||||
| } | ||||
|  | ||||
| /// Gets the height of the menu. | ||||
| /// | ||||
| /// @return the height of the popup menu, the number of entries visible. | ||||
|   | ||||
| @@ -154,7 +154,7 @@ static bool highlights_invalid = false; | ||||
|  | ||||
| static bool conceal_cursor_used = false; | ||||
|  | ||||
| static bool floats_invalid = false; | ||||
| static bool redraw_popupmenu = false; | ||||
|  | ||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||
| # include "screen.c.generated.h" | ||||
| @@ -466,13 +466,13 @@ void update_screen(int type) | ||||
|  | ||||
|   end_search_hl(); | ||||
|   // May need to redraw the popup menu. | ||||
|   if (pum_drawn() && floats_invalid) { | ||||
|   if (pum_drawn() && redraw_popupmenu) { | ||||
|     pum_redraw(); | ||||
|   } | ||||
|  | ||||
|   send_grid_resize = false; | ||||
|   highlights_invalid = false; | ||||
|   floats_invalid = false; | ||||
|   redraw_popupmenu = false; | ||||
|  | ||||
|   /* Reset b_mod_set flags.  Going through all windows is probably faster | ||||
|    * than going through all buffers (there could be many buffers). */ | ||||
| @@ -6185,7 +6185,6 @@ static void screenclear2(void) | ||||
|     default_grid.line_wraps[i] = false; | ||||
|   } | ||||
|  | ||||
|   floats_invalid = true; | ||||
|   ui_call_grid_clear(1);  // clear the display | ||||
|   clear_cmdline = false; | ||||
|   mode_displayed = false; | ||||
| @@ -6193,6 +6192,8 @@ static void screenclear2(void) | ||||
|   redraw_all_later(NOT_VALID); | ||||
|   redraw_cmdline = true; | ||||
|   redraw_tabline = true; | ||||
|   redraw_popupmenu = true; | ||||
|   pum_invalidate(); | ||||
|   if (must_redraw == CLEAR) { | ||||
|     must_redraw = NOT_VALID;  // no need to clear again | ||||
|   } | ||||
| @@ -7196,6 +7197,10 @@ void screen_resize(int width, int height) | ||||
|       } else { | ||||
|         update_topline(); | ||||
|         if (pum_drawn()) { | ||||
|           // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first. | ||||
|           // For now make sure the nested update_screen(0) won't redraw the | ||||
|           // pum at the old position. Try to untangle this later. | ||||
|           redraw_popupmenu = false; | ||||
|           ins_compl_show_pum(); | ||||
|         } | ||||
|         update_screen(NOT_VALID); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "nvim/ascii.h" | ||||
| #include "nvim/vim.h" | ||||
| #include "nvim/ui.h" | ||||
| #include "nvim/highlight.h" | ||||
| #include "nvim/memory.h" | ||||
| #include "nvim/ui_compositor.h" | ||||
| #include "nvim/ugrid.h" | ||||
| @@ -169,11 +170,9 @@ void ui_comp_remove_grid(ScreenGrid *grid) | ||||
|   (void)kv_pop(layers); | ||||
|   grid->comp_index = 0; | ||||
|  | ||||
|   if (ui_comp_should_draw()) { | ||||
|     // inefficent: only draw up to grid->comp_index | ||||
|     compose_area(grid->comp_row, grid->comp_row+grid->Rows, | ||||
|                  grid->comp_col, grid->comp_col+grid->Columns); | ||||
|   } | ||||
|   // recompose the area under the grid | ||||
|   // inefficent when being overlapped: only draw up to grid->comp_index | ||||
|   ui_comp_compose_grid(grid); | ||||
| } | ||||
|  | ||||
| bool ui_comp_set_grid(handle_T handle) | ||||
| @@ -229,6 +228,10 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, | ||||
|  | ||||
|   int col = (int)startcol; | ||||
|   ScreenGrid *grid = NULL; | ||||
|   schar_T *bg_line = &default_grid.chars[default_grid.line_offset[row] | ||||
|                                          +(size_t)startcol]; | ||||
|   sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row] | ||||
|                                           +(size_t)startcol]; | ||||
|  | ||||
|   while (col < endcol) { | ||||
|     int until = 0; | ||||
| @@ -256,6 +259,16 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, | ||||
|     memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf)); | ||||
|     memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf)); | ||||
|  | ||||
|     if (grid != &default_grid && p_pb) { | ||||
|       for (int i = col-(int)startcol; i < until-startcol; i++) { | ||||
|         bool thru = strequal((char *)linebuf[i], " "); | ||||
|         attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], thru); | ||||
|         if (thru) { | ||||
|           memcpy(linebuf[i], bg_line[i], sizeof(linebuf[i])); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Tricky: if overlap caused a doublewidth char to get cut-off, must | ||||
|     // replace the visible half with a space. | ||||
|     if (linebuf[col-startcol][0] == NUL) { | ||||
| @@ -272,6 +285,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, | ||||
|         skip = 0; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     col = until; | ||||
|   } | ||||
|   assert(endcol <= chk_width); | ||||
| @@ -293,11 +307,25 @@ static void compose_area(Integer startrow, Integer endrow, | ||||
| { | ||||
|   endrow = MIN(endrow, default_grid.Rows); | ||||
|   endcol = MIN(endcol, default_grid.Columns); | ||||
|   if (endcol <= startcol) { | ||||
|     return; | ||||
|   } | ||||
|   for (int r = (int)startrow; r < endrow; r++) { | ||||
|     compose_line(r, startcol, endcol, kLineFlagInvalid); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// compose the area under the grid. | ||||
| /// | ||||
| /// This is needed when some option affecting composition is changed, | ||||
| /// such as 'pumblend' for popupmenu grid. | ||||
| void ui_comp_compose_grid(ScreenGrid *grid) | ||||
| { | ||||
|   if (ui_comp_should_draw()) { | ||||
|     compose_area(grid->comp_row, grid->comp_row+grid->Rows, | ||||
|                  grid->comp_col, grid->comp_col+grid->Columns); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, | ||||
|                              Integer startcol, Integer endcol, | ||||
| @@ -316,8 +344,10 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, | ||||
|   if (curgrid != &default_grid) { | ||||
|     flags = flags & ~kLineFlagWrap; | ||||
|   } | ||||
|   assert(row < default_grid.Rows); | ||||
|   assert(clearcol <= default_grid.Columns); | ||||
|   if (flags & kLineFlagInvalid || kv_size(layers) > curgrid->comp_index+1) { | ||||
|   if (flags & kLineFlagInvalid | ||||
|       || kv_size(layers) > (p_pb ? 1 : curgrid->comp_index+1)) { | ||||
|     compose_line(row, startcol, clearcol, flags); | ||||
|   } else { | ||||
|     ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr, | ||||
|   | ||||
| @@ -18,6 +18,7 @@ describe('ui receives option updates', function() | ||||
|       guifontset='', | ||||
|       guifontwide='', | ||||
|       linespace=0, | ||||
|       pumblend=0, | ||||
|       showtabline=1, | ||||
|       termguicolors=false, | ||||
|       ext_cmdline=false, | ||||
|   | ||||
| @@ -999,9 +999,52 @@ describe('builtin popupmenu', function() | ||||
|       {1:~                                                 }| | ||||
|       {2:-- INSERT --}                                      | | ||||
|     ]]) | ||||
|  | ||||
|     screen:try_resize(25,10) | ||||
|     screen:expect([[ | ||||
|       some long prefix before  | | ||||
|       the text^                 | | ||||
|       {1:~  }{n: word           }{1:      }| | ||||
|       {1:~  }{n: choice         }{1:      }| | ||||
|       {1:~  }{s: text           }{1:      }| | ||||
|       {1:~  }{n: thing          }{1:      }| | ||||
|       {1:~                        }| | ||||
|       {1:~                        }| | ||||
|       {1:~                        }| | ||||
|       {2:-- INSERT --}             | | ||||
|     ]]) | ||||
|  | ||||
|     screen:try_resize(12,5) | ||||
|     screen:expect([[ | ||||
|       some long   | | ||||
|       prefix      | | ||||
|       bef{n: word  }  | | ||||
|       tex{n: }^        | | ||||
|       {2:-- }{s: text   } | | ||||
|     ]]) | ||||
|  | ||||
|     -- can't draw the pum, but check we don't crash | ||||
|     screen:try_resize(12,2) | ||||
|     screen:expect([[ | ||||
|       text^        | | ||||
|       {2:-- INSERT -} | | ||||
|     ]]) | ||||
|  | ||||
|     -- but state is preserved, pum reappears | ||||
|     screen:try_resize(20,8) | ||||
|     screen:expect([[ | ||||
|       some long prefix    | | ||||
|       before the text^     | | ||||
|       {1:~         }{n: word     }| | ||||
|       {1:~         }{n: choice   }| | ||||
|       {1:~         }{s: text     }| | ||||
|       {1:~         }{n: thing    }| | ||||
|       {1:~                   }| | ||||
|       {2:-- INSERT --}        | | ||||
|     ]]) | ||||
|   end) | ||||
|  | ||||
|   it('can be moved due to wrap or resize', function() | ||||
|   it('works with rightleft window', function() | ||||
|     command("set rl") | ||||
|     feed('isome rightleft ') | ||||
|     screen:expect([[ | ||||
| @@ -1230,4 +1273,176 @@ describe('builtin popupmenu', function() | ||||
|     ]]) | ||||
|   end) | ||||
|  | ||||
|   it('works with pumblend', function() | ||||
|     screen:try_resize(60,14) | ||||
|     screen:set_default_attr_ids({ | ||||
|       [1] = {background = Screen.colors.Yellow}, | ||||
|       [2] = {bold = true, reverse = true}, | ||||
|       [3] = {bold = true, foreground = Screen.colors.Brown}, | ||||
|       [4] = {foreground = Screen.colors.Blue1}, | ||||
|       [5] = {reverse = true}, | ||||
|       [6] = {background = Screen.colors.Gray55, foreground = Screen.colors.Grey45, special = Screen.colors.Red}, | ||||
|       [7] = {background = Screen.colors.Gray55, foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [8] = {background = tonumber('0x191919'), foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [9] = {background = tonumber('0xffc1ff'), foreground = tonumber('0xe5a8e5'), special = Screen.colors.Red}, | ||||
|       [10] = {background = tonumber('0xffc1ff'), foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [11] = {foreground = tonumber('0xe5a8e5'), special = Screen.colors.Red, background = tonumber('0xffc1ff'), reverse = true, bold = true}, | ||||
|       [12] = {foreground = Screen.colors.Grey45, special = Screen.colors.Red, background = Screen.colors.Gray55, reverse = true, bold = true}, | ||||
|       [13] = {background = tonumber('0xffc1e5'), foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [14] = {background = tonumber('0xffc1e5'), foreground = tonumber('0xe5a8e5'), special = Screen.colors.Red}, | ||||
|       [15] = {background = tonumber('0xffc1ff'), foreground = tonumber('0x080202'), special = Screen.colors.Red}, | ||||
|       [16] = {special = Screen.colors.Red, background = tonumber('0xffc1ff'), bold = true, foreground = tonumber('0xf6ace9')}, | ||||
|       [17] = {background = tonumber('0xffc1ff'), foreground = tonumber('0xe5a8ff'), special = Screen.colors.Red}, | ||||
|       [18] = {special = Screen.colors.Red, background = tonumber('0xffc1ff'), reverse = true, foreground = tonumber('0xe5a8e5')}, | ||||
|       [19] = {special = Screen.colors.Red, background = Screen.colors.Gray55, reverse = true, foreground = Screen.colors.Grey45}, | ||||
|       [20] = {bold = true}, | ||||
|       [21] = {bold = true, foreground = Screen.colors.SeaGreen4}, | ||||
|       [22] = {background = Screen.colors.WebGray}, | ||||
|       [23] = {background = Screen.colors.Grey0}, | ||||
|       [24] = {background = Screen.colors.LightMagenta}, | ||||
|       [25] = {background = Screen.colors.Gray75, foreground = Screen.colors.Grey25, special = Screen.colors.Red}, | ||||
|       [26] = {background = Screen.colors.Gray75, foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [27] = {background = Screen.colors.Gray50, foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [28] = {background = tonumber('0xffddff'), foreground = tonumber('0x7f5d7f'), special = Screen.colors.Red}, | ||||
|       [29] = {background = tonumber('0xffddff'), foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [30] = {foreground = tonumber('0x7f5d7f'), special = Screen.colors.Red, background = tonumber('0xffddff'), reverse = true, bold = true}, | ||||
|       [31] = {foreground = Screen.colors.Grey0, special = Screen.colors.Red, background = tonumber('0xffddff'), reverse = true, bold = true}, | ||||
|       [32] = {foreground = Screen.colors.Grey25, special = Screen.colors.Red, background = Screen.colors.Gray75, reverse = true, bold = true}, | ||||
|       [33] = {background = tonumber('0xffdd7f'), foreground = Screen.colors.Grey0, special = Screen.colors.Red}, | ||||
|       [34] = {background = tonumber('0xffdd7f'), foreground = tonumber('0x7f5d7f'), special = Screen.colors.Red}, | ||||
|       [35] = {special = Screen.colors.Red, background = tonumber('0xffddff'), bold = true, foreground = tonumber('0x290a0a')}, | ||||
|       [36] = {special = Screen.colors.Red, background = tonumber('0xffddff'), bold = true, foreground = tonumber('0xd27294')}, | ||||
|       [37] = {background = tonumber('0xffddff'), foreground = tonumber('0x7f5dff'), special = Screen.colors.Red}, | ||||
|       [38] = {special = Screen.colors.Red, background = tonumber('0xffddff'), reverse = true, foreground = tonumber('0x7f5d7f')}, | ||||
|       [39] = {special = Screen.colors.Red, background = tonumber('0xffddff'), reverse = true, foreground = Screen.colors.Grey0}, | ||||
|       [40] = {special = Screen.colors.Red, background = Screen.colors.Gray75, reverse = true, foreground = Screen.colors.Grey25}, | ||||
|       [41] = {background = tonumber('0xffddff'), foreground = tonumber('0x00003f'), special = Screen.colors.Red}, | ||||
|     }) | ||||
|     command('syntax on') | ||||
|     command('set mouse=a') | ||||
|     command('set pumblend=10') | ||||
|     insert([[ | ||||
|       Lorem ipsum dolor sit amet, consectetur | ||||
|       adipisicing elit, sed do eiusmod tempor | ||||
|       incididunt ut labore et dolore magna aliqua. | ||||
|       Ut enim ad minim veniam, quis nostrud | ||||
|       exercitation ullamco laboris nisi ut aliquip ex | ||||
|       ea commodo consequat. Duis aute irure dolor in | ||||
|       reprehenderit in voluptate velit esse cillum | ||||
|       dolore eu fugiat nulla pariatur. Excepteur sint | ||||
|       occaecat cupidatat non proident, sunt in culpa | ||||
|       qui officia deserunt mollit anim id est | ||||
|       laborum.]]) | ||||
|     command('match Statement /el/') | ||||
|     command('2match Comment /ut/') | ||||
|     command('1') | ||||
|     command('split') | ||||
|     command('/ol') | ||||
|     screen:expect([[ | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing elit, sed do eiusmod tempor                     | | ||||
|       ^incididunt ut labore et d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim ad minim veniam, quis nostrud                       | | ||||
|       exercitation ullamco laboris nisi ut aliquip ex             | | ||||
|       ea commodo consequat. Duis aute irure d{1:ol}or in              | | ||||
|       {2:[No Name] [+]                                               }| | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing {3:el}it, sed do eiusmod tempor                     | | ||||
|       incididunt {4:ut} labore et d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim ad minim veniam, quis nostrud                       | | ||||
|       exercitation ullamco laboris nisi {4:ut} aliquip ex             | | ||||
|       {5:[No Name] [+]                                               }| | ||||
|                                                                   | | ||||
|     ]]) | ||||
|  | ||||
|     feed('Obla bla <c-x><c-n>') | ||||
|     screen:expect([[ | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing elit, sed do eiusmod tempor                     | | ||||
|       bla bla incididunt^                                          | | ||||
|       incidid{6:u}{7:incididunt}{6:re et}{8: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{9: }{10:ut}{9: minim veniam}{6:,} quis nostrud                       | | ||||
|       exercit{9:a}{10:labore}{9:llamco la}{6:b}oris nisi ut aliquip ex             | | ||||
|       {2:[No Nam}{11:e}{10:et}{11:[+]          }{12: }{2:                                    }| | ||||
|       Lorem i{9:p}{10:dolor}{13:e}{14:l}{9:or sit a}{6:m}et, consectetur                     | | ||||
|       adipisi{9:c}{10:magn}{15:a}{16:l}{9:it, sed d}{6:o} eiusmod tempor                     | | ||||
|       bla bla{9: }{10:aliqua}{9:dunt     }{6: }                                    | | ||||
|       incidid{9:u}{10:Ut}{9: }{17:ut}{9: labore et}{6: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{9: }{10:enim}{9:inim veniam}{6:,} quis nostrud                       | | ||||
|       {5:[No Nam}{18:e}{10:ad}{18:[+]          }{19: }{5:                                    }| | ||||
|       {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65}            | | ||||
|     ]]) | ||||
|  | ||||
|     command('set pumblend=0') | ||||
|     screen:expect([[ | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing elit, sed do eiusmod tempor                     | | ||||
|       bla bla incididunt^                                          | | ||||
|       incidid{22: incididunt     }{23: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{24: ut             }{22: } quis nostrud                       | | ||||
|       exercit{24: labore         }{22: }oris nisi ut aliquip ex             | | ||||
|       {2:[No Nam}{24: et             }{22: }{2:                                    }| | ||||
|       Lorem i{24: dolore         }{22: }et, consectetur                     | | ||||
|       adipisi{24: magna          }{22: } eiusmod tempor                     | | ||||
|       bla bla{24: aliqua         }{22: }                                    | | ||||
|       incidid{24: Ut             }{22: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{24: enim           }{22: } quis nostrud                       | | ||||
|       {5:[No Nam}{24: ad             }{22: }{5:                                    }| | ||||
|       {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65}            | | ||||
|     ]]) | ||||
|  | ||||
|     command('set pumblend=50') | ||||
|     screen:expect([[ | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing elit, sed do eiusmod tempor                     | | ||||
|       bla bla incididunt^                                          | | ||||
|       incidid{25:u}{26:incididunt}{25:re et}{27: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{28: }{29:ut}{28: minim veniam}{25:,} quis nostrud                       | | ||||
|       exercit{28:a}{29:labore}{28:llamco la}{25:b}oris nisi ut aliquip ex             | | ||||
|       {2:[No Nam}{30:e}{31:et}{30:[+]          }{32: }{2:                                    }| | ||||
|       Lorem i{28:p}{29:dolor}{33:e}{34:l}{28:or sit a}{25:m}et, consectetur                     | | ||||
|       adipisi{28:c}{29:magn}{35:a}{36:l}{28:it, sed d}{25:o} eiusmod tempor                     | | ||||
|       bla bla{28: }{29:aliqua}{28:dunt     }{25: }                                    | | ||||
|       incidid{28:u}{29:Ut}{28: }{37:ut}{28: labore et}{25: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{28: }{29:enim}{28:inim veniam}{25:,} quis nostrud                       | | ||||
|       {5:[No Nam}{38:e}{39:ad}{38:[+]          }{40: }{5:                                    }| | ||||
|       {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65}            | | ||||
|     ]]) | ||||
|  | ||||
|     meths.input_mouse('wheel', 'down', '', 0, 9, 40) | ||||
|     screen:expect([[ | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing elit, sed do eiusmod tempor                     | | ||||
|       bla bla incididunt^                                          | | ||||
|       incidid{25:u}{26:incididunt}{25:re et}{27: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{28: }{29:ut}{28: minim veniam}{25:,} quis nostrud                       | | ||||
|       exercit{28:a}{29:labore}{28:llamco la}{25:b}oris nisi ut aliquip ex             | | ||||
|       {2:[No Nam}{30:e}{31:et}{30:[+]          }{32: }{2:                                    }| | ||||
|       incidid{28:u}{29:dol}{41:or}{29:e}{28:labore et}{25: }d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim{28: }{29:magna}{28:nim veniam}{25:,} quis nostrud                       | | ||||
|       exercit{28:a}{29:aliqua}{28:llamco la}{25:b}oris nisi {4:ut} aliquip ex             | | ||||
|       ea comm{28:o}{29:Ut}{28: consequat. D}{25:u}is a{4:ut}e irure d{1:ol}or in              | | ||||
|       reprehe{28:n}{29:enim}{28:t in v}{34:ol}{28:upt}{25:a}te v{3:el}it esse cillum                | | ||||
|       {5:[No Nam}{38:e}{39:ad}{38:[+]          }{40: }{5:                                    }| | ||||
|       {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65}            | | ||||
|     ]]) | ||||
|  | ||||
|     feed('<c-e>') | ||||
|     screen:expect([[ | ||||
|       Lorem ipsum d{1:ol}or sit amet, consectetur                     | | ||||
|       adipisicing elit, sed do eiusmod tempor                     | | ||||
|       bla bla ^                                                    | | ||||
|       incididunt ut labore et d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim ad minim veniam, quis nostrud                       | | ||||
|       exercitation ullamco laboris nisi ut aliquip ex             | | ||||
|       {2:[No Name] [+]                                               }| | ||||
|       incididunt {4:ut} labore et d{1:ol}ore magna aliqua.                | | ||||
|       Ut enim ad minim veniam, quis nostrud                       | | ||||
|       exercitation ullamco laboris nisi {4:ut} aliquip ex             | | ||||
|       ea commodo consequat. Duis a{4:ut}e irure d{1:ol}or in              | | ||||
|       reprehenderit in v{1:ol}uptate v{3:el}it esse cillum                | | ||||
|       {5:[No Name] [+]                                               }| | ||||
|       {20:-- INSERT --}                                                | | ||||
|     ]]) | ||||
|   end) | ||||
| end) | ||||
|   | ||||
| @@ -1303,6 +1303,8 @@ function Screen:_pprint_attrs(attrs) | ||||
|       if f == "foreground" or f == "background" or f == "special" then | ||||
|         if Screen.colornames[v] ~= nil then | ||||
|           desc = "Screen.colors."..Screen.colornames[v] | ||||
|         else | ||||
|           desc = string.format("tonumber('0x%06x')",v) | ||||
|         end | ||||
|       end | ||||
|       table.insert(items, f.." = "..desc) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse