// Low-level functions to manipulate individual character cells on the
// screen grid.
//
// Most of the routines in this file perform screen (grid) manipulations. The
// given operation is performed physically on the screen. The corresponding
// change is also made to the internal screen image. In this way, the editor
// anticipates the effect of editing changes on the appearance of the screen.
// That way, when we call update_screen() a complete redraw isn't usually
// necessary. Another advantage is that we can keep adding code to anticipate
// screen changes, and in the meantime, everything still works.
//
// The grid_*() functions write to the screen and handle updating grid->lines[].
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "nvim/api/private/defs.h"
#include "nvim/arabic.h"
#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/decoration.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/log.h"
#include "nvim/map_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_defs.h"
#include "grid.c.generated.h"
// temporary buffer for rendering a single screenline, so it can be
// compared with previous contents to calculate smallest delta.
// Per-cell attributes
static size_t linebuf_size = 0;
// Used to cache glyphs which doesn't fit an a sizeof(schar_T) length UTF-8 string.
// Then it instead stores an index into glyph_cache.keys[] which is a flat char array.
// The hash part is used by schar_from_buf() to quickly lookup glyphs which already
// has been interned. schar_get() should used to convert a schar_T value
// back to a string buffer.
//
// The maximum byte size of a glyph is MAX_SCHAR_SIZE (including the final NUL).
static Set(glyph) glyph_cache = SET_INIT;
/// Determine if dedicated window grid should be used or the default_grid
///
/// If UI did not request multigrid support, draw all windows on the
/// default_grid.
///
/// NB: this function can only been used with window grids in a context where
/// win_grid_alloc already has been called!
///
/// If the default_grid is used, adjust window relative positions to global
/// screen positions.
ScreenGrid *grid_adjust(GridView *grid, int *row_off, int *col_off)
{
*row_off += grid->row_offset;
*col_off += grid->col_offset;
return grid->target;
}
schar_T schar_from_str(const char *str)
{
if (str == NULL) {
return 0;
}
return schar_from_buf(str, strlen(str));
}
/// @param buf need not be NUL terminated, but may not contain embedded NULs.
///
/// caller must ensure len < MAX_SCHAR_SIZE (not =, as NUL needs a byte)
schar_T schar_from_buf(const char *buf, size_t len)
{
assert(len < MAX_SCHAR_SIZE);
if (len <= 4) {
schar_T sc = 0;
memcpy((char *)&sc, buf, len);
return sc;
} else {
String str = { .data = (char *)buf, .size = len };
MHPutStatus status;
uint32_t idx = set_put_idx(glyph, &glyph_cache, str, &status);
assert(idx < 0xFFFFFF);
#ifdef ORDER_BIG_ENDIAN
return idx + ((uint32_t)0xFF << 24);
#else
return 0xFF + (idx << 8);
#endif
}
}
/// Check if cache is full, and if it is, clear it.
///
/// This should normally only be called in update_screen()
///
/// @return true if cache was clered, and all your screen buffers now are hosed
/// and you need to use UPD_CLEAR
bool schar_cache_clear_if_full(void)
{
// note: critical max is really (1<<24)-1. This gives us some marginal
// until next time update_screen() is called
if (glyph_cache.h.n_keys > (1<<21)) {
schar_cache_clear();
return true;
}
return false;
}
void schar_cache_clear(void)
{
decor_check_invalid_glyphs();
set_clear(glyph, &glyph_cache);
// for char options we have stored the original strings. Regenerate
// the parsed schar_T values with the new clean cache.
// This must not return an error as cell widths have not changed.
if (check_chars_options()) {
abort();
}
}
bool schar_high(schar_T sc)
{
#ifdef ORDER_BIG_ENDIAN
return ((sc & 0xFF000000) == 0xFF000000);
#else
return ((sc & 0xFF) == 0xFF);
#endif
}
#ifdef ORDER_BIG_ENDIAN
# define schar_idx(sc) (sc & (0x00FFFFFF))
#else
# define schar_idx(sc) (sc >> 8)
#endif
/// sets final NUL
size_t schar_get(char *buf_out, schar_T sc)
{
size_t len = schar_get_adv(&buf_out, sc);
*buf_out = NUL;
return len;
}
/// advance buf_out. do NOT set final NUL
size_t schar_get_adv(char **buf_out, schar_T sc)
{
size_t len;
if (schar_high(sc)) {
uint32_t idx = schar_idx(sc);
assert(idx < glyph_cache.h.n_keys);
len = strlen(&glyph_cache.keys[idx]);
memcpy(*buf_out, &glyph_cache.keys[idx], len);
} else {
len = strnlen((char *)&sc, 4);
memcpy(*buf_out, (char *)&sc, len);
}
*buf_out += len;
return len;
}
size_t schar_len(schar_T sc)
{
if (schar_high(sc)) {
uint32_t idx = schar_idx(sc);
assert(idx < glyph_cache.h.n_keys);
return strlen(&glyph_cache.keys[idx]);
} else {
return strnlen((char *)&sc, 4);
}
}
int schar_cells(schar_T sc)
{
// hot path
#ifdef ORDER_BIG_ENDIAN
if (!(sc & 0x80FFFFFF)) {
return 1;
}
#else
if (sc < 0x80) {
return 1;
}
#endif
char sc_buf[MAX_SCHAR_SIZE];
schar_get(sc_buf, sc);
return ptr2cells
|