mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 22:18:33 +00:00
Merge #7234 'built-in expression parser'
This commit is contained in:
@@ -86,6 +86,8 @@ foreach(subdir
|
||||
event
|
||||
eval
|
||||
lua
|
||||
viml
|
||||
viml/parser
|
||||
)
|
||||
if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
|
||||
continue()
|
||||
|
@@ -33,6 +33,8 @@
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/viml/parser/expressions.h"
|
||||
#include "nvim/viml/parser/parser.h"
|
||||
|
||||
#define LINE_BUFFER_SIZE 4096
|
||||
|
||||
@@ -889,6 +891,458 @@ theend:
|
||||
return rv;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ExprASTNode **node_p;
|
||||
Object *ret_node_p;
|
||||
} ExprASTConvStackItem;
|
||||
|
||||
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
|
||||
|
||||
/// Parse a VimL expression
|
||||
///
|
||||
/// @param[in] expr Expression to parse. Is always treated as a single line.
|
||||
/// @param[in] flags Flags:
|
||||
///
|
||||
/// - "m" if multiple expressions in a row are allowed (only
|
||||
/// the first one will be parsed),
|
||||
/// - "E" if EOC tokens are not allowed (determines whether
|
||||
/// they will stop parsing process or be recognized as an
|
||||
/// operator/space, though also yielding an error).
|
||||
/// - "l" when needing to start parsing with lvalues for
|
||||
/// ":let" or ":for".
|
||||
///
|
||||
/// Common flag sets:
|
||||
/// - "m" to parse like for ":echo".
|
||||
/// - "E" to parse like for "<C-r>=".
|
||||
/// - empty string for ":call".
|
||||
/// - "lm" to parse for ":let".
|
||||
/// @param[in] highlight If true, return value will also include "highlight"
|
||||
/// key containing array of 4-tuples (arrays) (Integer,
|
||||
/// Integer, Integer, String), where first three numbers
|
||||
/// define the highlighted region and represent line,
|
||||
/// starting column and ending column (latter exclusive:
|
||||
/// one should highlight region [start_col, end_col)).
|
||||
///
|
||||
/// @return AST: top-level dictionary holds keys
|
||||
///
|
||||
/// "error": Dictionary with error, present only if parser saw some
|
||||
/// error. Contains the following keys:
|
||||
///
|
||||
/// "message": String, error message in printf format, translated.
|
||||
/// Must contain exactly one "%.*s".
|
||||
/// "arg": String, error message argument.
|
||||
///
|
||||
/// "len": Amount of bytes successfully parsed. With flags equal to ""
|
||||
/// that should be equal to the length of expr string.
|
||||
///
|
||||
/// @note: “Sucessfully parsed” here means “participated in AST
|
||||
/// creation”, not “till the first error”.
|
||||
///
|
||||
/// "ast": AST, either nil or a dictionary with these keys:
|
||||
///
|
||||
/// "type": node type, one of the value names from ExprASTNodeType
|
||||
/// stringified without "kExprNode" prefix.
|
||||
/// "start": a pair [line, column] describing where node is “started”
|
||||
/// where "line" is always 0 (will not be 0 if you will be
|
||||
/// using nvim_parse_viml() on e.g. ":let", but that is not
|
||||
/// present yet). Both elements are Integers.
|
||||
/// "len": “length” of the node. This and "start" are there for
|
||||
/// debugging purposes primary (debugging parser and providing
|
||||
/// debug information).
|
||||
/// "children": a list of nodes described in top/"ast". There always
|
||||
/// is zero, one or two children, key will not be present
|
||||
/// if node has no children. Maximum number of children
|
||||
/// may be found in node_maxchildren array.
|
||||
///
|
||||
/// Local values (present only for certain nodes):
|
||||
///
|
||||
/// "scope": a single Integer, specifies scope for "Option" and
|
||||
/// "PlainIdentifier" nodes. For "Option" it is one of
|
||||
/// ExprOptScope values, for "PlainIdentifier" it is one of
|
||||
/// ExprVarScope values.
|
||||
/// "ident": identifier (without scope, if any), present for "Option",
|
||||
/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
|
||||
/// "name": Integer, register name (one character) or -1. Only present
|
||||
/// for "Register" nodes.
|
||||
/// "cmp_type": String, comparison type, one of the value names from
|
||||
/// ExprComparisonType, stringified without "kExprCmp"
|
||||
/// prefix. Only present for "Comparison" nodes.
|
||||
/// "ccs_strategy": String, case comparison strategy, one of the
|
||||
/// value names from ExprCaseCompareStrategy,
|
||||
/// stringified without "kCCStrategy" prefix. Only
|
||||
/// present for "Comparison" nodes.
|
||||
/// "augmentation": String, augmentation type for "Assignment" nodes.
|
||||
/// Is either an empty string, "Add", "Subtract" or
|
||||
/// "Concat" for "=", "+=", "-=" or ".=" respectively.
|
||||
/// "invert": Boolean, true if result of comparison needs to be
|
||||
/// inverted. Only present for "Comparison" nodes.
|
||||
/// "ivalue": Integer, integer value for "Integer" nodes.
|
||||
/// "fvalue": Float, floating-point value for "Float" nodes.
|
||||
/// "svalue": String, value for "SingleQuotedString" and
|
||||
/// "DoubleQuotedString" nodes.
|
||||
Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
|
||||
Error *err)
|
||||
FUNC_API_SINCE(4) FUNC_API_ASYNC
|
||||
{
|
||||
int pflags = 0;
|
||||
for (size_t i = 0 ; i < flags.size ; i++) {
|
||||
switch (flags.data[i]) {
|
||||
case 'm': { pflags |= kExprFlagsMulti; break; }
|
||||
case 'E': { pflags |= kExprFlagsDisallowEOC; break; }
|
||||
case 'l': { pflags |= kExprFlagsParseLet; break; }
|
||||
case NUL: {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
|
||||
(unsigned)flags.data[i]);
|
||||
return (Dictionary)ARRAY_DICT_INIT;
|
||||
}
|
||||
default: {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)",
|
||||
flags.data[i], (unsigned)flags.data[i]);
|
||||
return (Dictionary)ARRAY_DICT_INIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParserLine plines[] = {
|
||||
{
|
||||
.data = expr.data,
|
||||
.size = expr.size,
|
||||
.allocated = false,
|
||||
},
|
||||
{ NULL, 0, false },
|
||||
};
|
||||
ParserLine *plines_p = plines;
|
||||
ParserHighlight colors;
|
||||
kvi_init(colors);
|
||||
ParserHighlight *const colors_p = (highlight ? &colors : NULL);
|
||||
ParserState pstate;
|
||||
viml_parser_init(
|
||||
&pstate, parser_simple_get_line, &plines_p, colors_p);
|
||||
ExprAST east = viml_pexpr_parse(&pstate, pflags);
|
||||
|
||||
const size_t ret_size = (
|
||||
2 // "ast", "len"
|
||||
+ (size_t)(east.err.msg != NULL) // "error"
|
||||
+ (size_t)highlight // "highlight"
|
||||
+ 0);
|
||||
Dictionary ret = {
|
||||
.items = xmalloc(ret_size * sizeof(ret.items[0])),
|
||||
.size = 0,
|
||||
.capacity = ret_size,
|
||||
};
|
||||
ret.items[ret.size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ast"),
|
||||
.value = NIL,
|
||||
};
|
||||
ret.items[ret.size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("len"),
|
||||
.value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
|
||||
? plines[0].size
|
||||
: pstate.pos.col)),
|
||||
};
|
||||
if (east.err.msg != NULL) {
|
||||
Dictionary err_dict = {
|
||||
.items = xmalloc(2 * sizeof(err_dict.items[0])),
|
||||
.size = 2,
|
||||
.capacity = 2,
|
||||
};
|
||||
err_dict.items[0] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("message"),
|
||||
.value = STRING_OBJ(cstr_to_string(east.err.msg)),
|
||||
};
|
||||
if (east.err.arg == NULL) {
|
||||
err_dict.items[1] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("arg"),
|
||||
.value = STRING_OBJ(STRING_INIT),
|
||||
};
|
||||
} else {
|
||||
err_dict.items[1] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("arg"),
|
||||
.value = STRING_OBJ(((String) {
|
||||
.data = xmemdupz(east.err.arg, (size_t)east.err.arg_len),
|
||||
.size = (size_t)east.err.arg_len,
|
||||
})),
|
||||
};
|
||||
}
|
||||
ret.items[ret.size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("error"),
|
||||
.value = DICTIONARY_OBJ(err_dict),
|
||||
};
|
||||
}
|
||||
if (highlight) {
|
||||
Array hl = (Array) {
|
||||
.items = xmalloc(kv_size(colors) * sizeof(hl.items[0])),
|
||||
.capacity = kv_size(colors),
|
||||
.size = kv_size(colors),
|
||||
};
|
||||
for (size_t i = 0 ; i < kv_size(colors) ; i++) {
|
||||
const ParserHighlightChunk chunk = kv_A(colors, i);
|
||||
Array chunk_arr = (Array) {
|
||||
.items = xmalloc(4 * sizeof(chunk_arr.items[0])),
|
||||
.capacity = 4,
|
||||
.size = 4,
|
||||
};
|
||||
chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line);
|
||||
chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col);
|
||||
chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col);
|
||||
chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group));
|
||||
hl.items[i] = ARRAY_OBJ(chunk_arr);
|
||||
}
|
||||
ret.items[ret.size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("highlight"),
|
||||
.value = ARRAY_OBJ(hl),
|
||||
};
|
||||
}
|
||||
kvi_destroy(colors);
|
||||
|
||||
// Walk over the AST, freeing nodes in process.
|
||||
ExprASTConvStack ast_conv_stack;
|
||||
kvi_init(ast_conv_stack);
|
||||
kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
|
||||
.node_p = &east.root,
|
||||
.ret_node_p = &ret.items[0].value,
|
||||
}));
|
||||
while (kv_size(ast_conv_stack)) {
|
||||
ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
|
||||
ExprASTNode *const node = *cur_item.node_p;
|
||||
if (node == NULL) {
|
||||
assert(kv_size(ast_conv_stack) == 1);
|
||||
kv_drop(ast_conv_stack, 1);
|
||||
} else {
|
||||
if (cur_item.ret_node_p->type == kObjectTypeNil) {
|
||||
const size_t ret_node_items_size = (size_t)(
|
||||
3 // "type", "start" and "len"
|
||||
+ (node->children != NULL) // "children"
|
||||
+ (node->type == kExprNodeOption
|
||||
|| node->type == kExprNodePlainIdentifier) // "scope"
|
||||
+ (node->type == kExprNodeOption
|
||||
|| node->type == kExprNodePlainIdentifier
|
||||
|| node->type == kExprNodePlainKey
|
||||
|| node->type == kExprNodeEnvironment) // "ident"
|
||||
+ (node->type == kExprNodeRegister) // "name"
|
||||
+ (3 // "cmp_type", "ccs_strategy", "invert"
|
||||
* (node->type == kExprNodeComparison))
|
||||
+ (node->type == kExprNodeInteger) // "ivalue"
|
||||
+ (node->type == kExprNodeFloat) // "fvalue"
|
||||
+ (node->type == kExprNodeDoubleQuotedString
|
||||
|| node->type == kExprNodeSingleQuotedString) // "svalue"
|
||||
+ (node->type == kExprNodeAssignment) // "augmentation"
|
||||
+ 0);
|
||||
Dictionary ret_node = {
|
||||
.items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])),
|
||||
.capacity = ret_node_items_size,
|
||||
.size = 0,
|
||||
};
|
||||
*cur_item.ret_node_p = DICTIONARY_OBJ(ret_node);
|
||||
}
|
||||
Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary;
|
||||
if (node->children != NULL) {
|
||||
const size_t num_children = 1 + (node->children->next != NULL);
|
||||
Array children_array = {
|
||||
.items = xmalloc(num_children * sizeof(children_array.items[0])),
|
||||
.capacity = num_children,
|
||||
.size = num_children,
|
||||
};
|
||||
for (size_t i = 0; i < num_children; i++) {
|
||||
children_array.items[i] = NIL;
|
||||
}
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("children"),
|
||||
.value = ARRAY_OBJ(children_array),
|
||||
};
|
||||
kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
|
||||
.node_p = &node->children,
|
||||
.ret_node_p = &children_array.items[0],
|
||||
}));
|
||||
} else if (node->next != NULL) {
|
||||
kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
|
||||
.node_p = &node->next,
|
||||
.ret_node_p = cur_item.ret_node_p + 1,
|
||||
}));
|
||||
} else if (node != NULL) {
|
||||
kv_drop(ast_conv_stack, 1);
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("type"),
|
||||
.value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])),
|
||||
};
|
||||
Array start_array = {
|
||||
.items = xmalloc(2 * sizeof(start_array.items[0])),
|
||||
.capacity = 2,
|
||||
.size = 2,
|
||||
};
|
||||
start_array.items[0] = INTEGER_OBJ((Integer)node->start.line);
|
||||
start_array.items[1] = INTEGER_OBJ((Integer)node->start.col);
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("start"),
|
||||
.value = ARRAY_OBJ(start_array),
|
||||
};
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("len"),
|
||||
.value = INTEGER_OBJ((Integer)node->len),
|
||||
};
|
||||
switch (node->type) {
|
||||
case kExprNodeDoubleQuotedString:
|
||||
case kExprNodeSingleQuotedString: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("svalue"),
|
||||
.value = STRING_OBJ(((String) {
|
||||
.data = node->data.str.value,
|
||||
.size = node->data.str.size,
|
||||
})),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeOption: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("scope"),
|
||||
.value = INTEGER_OBJ(node->data.opt.scope),
|
||||
};
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ident"),
|
||||
.value = STRING_OBJ(((String) {
|
||||
.data = xmemdupz(node->data.opt.ident,
|
||||
node->data.opt.ident_len),
|
||||
.size = node->data.opt.ident_len,
|
||||
})),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodePlainIdentifier: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("scope"),
|
||||
.value = INTEGER_OBJ(node->data.var.scope),
|
||||
};
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ident"),
|
||||
.value = STRING_OBJ(((String) {
|
||||
.data = xmemdupz(node->data.var.ident,
|
||||
node->data.var.ident_len),
|
||||
.size = node->data.var.ident_len,
|
||||
})),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodePlainKey: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ident"),
|
||||
.value = STRING_OBJ(((String) {
|
||||
.data = xmemdupz(node->data.var.ident,
|
||||
node->data.var.ident_len),
|
||||
.size = node->data.var.ident_len,
|
||||
})),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeEnvironment: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ident"),
|
||||
.value = STRING_OBJ(((String) {
|
||||
.data = xmemdupz(node->data.env.ident,
|
||||
node->data.env.ident_len),
|
||||
.size = node->data.env.ident_len,
|
||||
})),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeRegister: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("name"),
|
||||
.value = INTEGER_OBJ(node->data.reg.name),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeComparison: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("cmp_type"),
|
||||
.value = STRING_OBJ(cstr_to_string(
|
||||
eltkn_cmp_type_tab[node->data.cmp.type])),
|
||||
};
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ccs_strategy"),
|
||||
.value = STRING_OBJ(cstr_to_string(
|
||||
ccs_tab[node->data.cmp.ccs])),
|
||||
};
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("invert"),
|
||||
.value = BOOLEAN_OBJ(node->data.cmp.inv),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeFloat: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("fvalue"),
|
||||
.value = FLOAT_OBJ(node->data.flt.value),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeInteger: {
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("ivalue"),
|
||||
.value = INTEGER_OBJ((Integer)(
|
||||
node->data.num.value > API_INTEGER_MAX
|
||||
? API_INTEGER_MAX
|
||||
: (Integer)node->data.num.value)),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeAssignment: {
|
||||
const ExprAssignmentType asgn_type = node->data.ass.type;
|
||||
ret_node->items[ret_node->size++] = (KeyValuePair) {
|
||||
.key = STATIC_CSTR_TO_STRING("augmentation"),
|
||||
.value = STRING_OBJ(
|
||||
asgn_type == kExprAsgnPlain
|
||||
? (String)STRING_INIT
|
||||
: cstr_to_string(expr_asgn_type_tab[asgn_type])),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case kExprNodeMissing:
|
||||
case kExprNodeOpMissing:
|
||||
case kExprNodeTernary:
|
||||
case kExprNodeTernaryValue:
|
||||
case kExprNodeSubscript:
|
||||
case kExprNodeListLiteral:
|
||||
case kExprNodeUnaryPlus:
|
||||
case kExprNodeBinaryPlus:
|
||||
case kExprNodeNested:
|
||||
case kExprNodeCall:
|
||||
case kExprNodeComplexIdentifier:
|
||||
case kExprNodeUnknownFigure:
|
||||
case kExprNodeLambda:
|
||||
case kExprNodeDictLiteral:
|
||||
case kExprNodeCurlyBracesIdentifier:
|
||||
case kExprNodeComma:
|
||||
case kExprNodeColon:
|
||||
case kExprNodeArrow:
|
||||
case kExprNodeConcat:
|
||||
case kExprNodeConcatOrSubscript:
|
||||
case kExprNodeOr:
|
||||
case kExprNodeAnd:
|
||||
case kExprNodeUnaryMinus:
|
||||
case kExprNodeBinaryMinus:
|
||||
case kExprNodeNot:
|
||||
case kExprNodeMultiplication:
|
||||
case kExprNodeDivision:
|
||||
case kExprNodeMod: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(cur_item.ret_node_p->data.dictionary.size
|
||||
== cur_item.ret_node_p->data.dictionary.capacity);
|
||||
xfree(*cur_item.node_p);
|
||||
*cur_item.node_p = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
kvi_destroy(ast_conv_stack);
|
||||
|
||||
assert(ret.size == ret.capacity);
|
||||
// Should be a no-op actually, leaving it in case non-nodes will need to be
|
||||
// freed later.
|
||||
viml_pexpr_free_ast(east);
|
||||
viml_parser_destroy(&pstate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/// Writes a message to vim output or error buffer. The string is split
|
||||
/// and flushed after each newline. Incomplete lines are kept for writing
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nvim/macros.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
|
||||
@@ -98,6 +99,10 @@ static inline bool ascii_isxdigit(int)
|
||||
REAL_FATTR_CONST
|
||||
REAL_FATTR_ALWAYS_INLINE;
|
||||
|
||||
static inline bool ascii_isident(int)
|
||||
REAL_FATTR_CONST
|
||||
REAL_FATTR_ALWAYS_INLINE;
|
||||
|
||||
static inline bool ascii_isbdigit(int)
|
||||
REAL_FATTR_CONST
|
||||
REAL_FATTR_ALWAYS_INLINE;
|
||||
@@ -138,6 +143,14 @@ static inline bool ascii_isxdigit(int c)
|
||||
|| (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
/// Checks if `c` is an “identifier” character
|
||||
///
|
||||
/// That is, whether it is alphanumeric character or underscore.
|
||||
static inline bool ascii_isident(const int c)
|
||||
{
|
||||
return ASCII_ISALNUM(c) || c == '_';
|
||||
}
|
||||
|
||||
/// Checks if `c` is a binary digit, that is, 0-1.
|
||||
///
|
||||
/// @see {ascii_isdigit}
|
||||
|
@@ -1611,141 +1611,145 @@ bool vim_isblankline(char_u *lbuf)
|
||||
/// If maxlen > 0, check at a maximum maxlen chars.
|
||||
///
|
||||
/// @param start
|
||||
/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
|
||||
/// '0' = octal, 'b' or 'B' is bin
|
||||
/// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is
|
||||
/// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using
|
||||
/// STR2NR_FORCE is always zero.
|
||||
/// @param len Returns the detected length of number.
|
||||
/// @param what Recognizes what number passed.
|
||||
/// @param what Recognizes what number passed, @see ChStr2NrFlags.
|
||||
/// @param nptr Returns the signed result.
|
||||
/// @param unptr Returns the unsigned result.
|
||||
/// @param maxlen Max length of string to check.
|
||||
void vim_str2nr(const char_u *const start, int *const prep, int *const len,
|
||||
const int what, varnumber_T *const nptr,
|
||||
uvarnumber_T *const unptr, const int maxlen)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
const char_u *ptr = start;
|
||||
const char *ptr = (const char *)start;
|
||||
#define STRING_ENDED(ptr) \
|
||||
(!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
|
||||
int pre = 0; // default is decimal
|
||||
bool negative = false;
|
||||
const bool negative = (ptr[0] == '-');
|
||||
uvarnumber_T un = 0;
|
||||
|
||||
if (ptr[0] == '-') {
|
||||
negative = true;
|
||||
if (negative) {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
// Recognize hex, octal and bin.
|
||||
if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')
|
||||
&& (maxlen == 0 || maxlen > 1)) {
|
||||
pre = ptr[1];
|
||||
|
||||
if ((what & STR2NR_HEX)
|
||||
&& ((pre == 'X') || (pre == 'x'))
|
||||
&& ascii_isxdigit(ptr[2])
|
||||
&& (maxlen == 0 || maxlen > 2)) {
|
||||
// hexadecimal
|
||||
ptr += 2;
|
||||
} else if ((what & STR2NR_BIN)
|
||||
&& ((pre == 'B') || (pre == 'b'))
|
||||
&& ascii_isbdigit(ptr[2])
|
||||
&& (maxlen == 0 || maxlen > 2)) {
|
||||
// binary
|
||||
ptr += 2;
|
||||
} else {
|
||||
// decimal or octal, default is decimal
|
||||
pre = 0;
|
||||
|
||||
if (what & STR2NR_OCT) {
|
||||
// Don't interpret "0", "08" or "0129" as octal.
|
||||
for (int n = 1; ascii_isdigit(ptr[n]); ++n) {
|
||||
if (ptr[n] > '7') {
|
||||
// can't be octal
|
||||
pre = 0;
|
||||
break;
|
||||
}
|
||||
if (ptr[n] >= '0') {
|
||||
// assume octal
|
||||
pre = '0';
|
||||
}
|
||||
if (n == maxlen) {
|
||||
break;
|
||||
}
|
||||
if (what & STR2NR_FORCE) {
|
||||
// When forcing main consideration is skipping the prefix. Octal and decimal
|
||||
// numbers have no prefixes to skip. pre is not set.
|
||||
switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) {
|
||||
case STR2NR_HEX: {
|
||||
if (!STRING_ENDED(ptr + 2)
|
||||
&& ptr[0] == '0'
|
||||
&& (ptr[1] == 'x' || ptr[1] == 'X')
|
||||
&& ascii_isxdigit(ptr[2])) {
|
||||
ptr += 2;
|
||||
}
|
||||
goto vim_str2nr_hex;
|
||||
}
|
||||
case STR2NR_BIN: {
|
||||
if (!STRING_ENDED(ptr + 2)
|
||||
&& ptr[0] == '0'
|
||||
&& (ptr[1] == 'b' || ptr[1] == 'B')
|
||||
&& ascii_isbdigit(ptr[2])) {
|
||||
ptr += 2;
|
||||
}
|
||||
goto vim_str2nr_bin;
|
||||
}
|
||||
case STR2NR_OCT: {
|
||||
goto vim_str2nr_oct;
|
||||
}
|
||||
case 0: {
|
||||
goto vim_str2nr_dec;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
|
||||
&& !STRING_ENDED(ptr + 1)
|
||||
&& ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
|
||||
pre = ptr[1];
|
||||
// Detect hexadecimal: 0x or 0X follwed by hex digit
|
||||
if ((what & STR2NR_HEX)
|
||||
&& !STRING_ENDED(ptr + 2)
|
||||
&& (pre == 'X' || pre == 'x')
|
||||
&& ascii_isxdigit(ptr[2])) {
|
||||
ptr += 2;
|
||||
goto vim_str2nr_hex;
|
||||
}
|
||||
// Detect binary: 0b or 0B follwed by 0 or 1
|
||||
if ((what & STR2NR_BIN)
|
||||
&& !STRING_ENDED(ptr + 2)
|
||||
&& (pre == 'B' || pre == 'b')
|
||||
&& ascii_isbdigit(ptr[2])) {
|
||||
ptr += 2;
|
||||
goto vim_str2nr_bin;
|
||||
}
|
||||
// Detect octal number: zero followed by octal digits without '8' or '9'
|
||||
pre = 0;
|
||||
if (!(what & STR2NR_OCT)
|
||||
|| !('0' <= ptr[1] && ptr[1] <= '7')) {
|
||||
goto vim_str2nr_dec;
|
||||
}
|
||||
for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
|
||||
if (ptr[i] > '7') {
|
||||
goto vim_str2nr_dec;
|
||||
}
|
||||
}
|
||||
pre = '0';
|
||||
goto vim_str2nr_oct;
|
||||
} else {
|
||||
goto vim_str2nr_dec;
|
||||
}
|
||||
|
||||
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
|
||||
int n = 1;
|
||||
if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) {
|
||||
// bin
|
||||
if (pre != 0) {
|
||||
n += 2; // skip over "0b"
|
||||
assert(false); // Should’ve used goto earlier.
|
||||
#define PARSE_NUMBER(base, cond, conv) \
|
||||
do { \
|
||||
while (!STRING_ENDED(ptr) && (cond)) { \
|
||||
/* avoid ubsan error for overflow */ \
|
||||
if (un < UVARNUMBER_MAX / base) { \
|
||||
un = base * un + (uvarnumber_T)(conv); \
|
||||
} else { \
|
||||
un = UVARNUMBER_MAX; \
|
||||
} \
|
||||
ptr++; \
|
||||
} \
|
||||
} while (0)
|
||||
switch (pre) {
|
||||
case 'b':
|
||||
case 'B': {
|
||||
vim_str2nr_bin:
|
||||
PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
|
||||
break;
|
||||
}
|
||||
while ('0' <= *ptr && *ptr <= '1') {
|
||||
// avoid ubsan error for overflow
|
||||
if (un < UVARNUMBER_MAX / 2) {
|
||||
un = 2 * un + (uvarnumber_T)(*ptr - '0');
|
||||
} else {
|
||||
un = UVARNUMBER_MAX;
|
||||
}
|
||||
ptr++;
|
||||
if (n++ == maxlen) {
|
||||
break;
|
||||
}
|
||||
case '0': {
|
||||
vim_str2nr_oct:
|
||||
PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
|
||||
break;
|
||||
}
|
||||
} else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) {
|
||||
// octal
|
||||
while ('0' <= *ptr && *ptr <= '7') {
|
||||
// avoid ubsan error for overflow
|
||||
if (un < UVARNUMBER_MAX / 8) {
|
||||
un = 8 * un + (uvarnumber_T)(*ptr - '0');
|
||||
} else {
|
||||
un = UVARNUMBER_MAX;
|
||||
}
|
||||
ptr++;
|
||||
if (n++ == maxlen) {
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
vim_str2nr_dec:
|
||||
PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
|
||||
break;
|
||||
}
|
||||
} else if ((pre == 'X') || (pre == 'x')
|
||||
|| what == STR2NR_HEX + STR2NR_FORCE) {
|
||||
// hex
|
||||
if (pre != 0) {
|
||||
n += 2; // skip over "0x"
|
||||
}
|
||||
while (ascii_isxdigit(*ptr)) {
|
||||
// avoid ubsan error for overflow
|
||||
if (un < UVARNUMBER_MAX / 16) {
|
||||
un = 16 * un + (uvarnumber_T)hex2nr(*ptr);
|
||||
} else {
|
||||
un = UVARNUMBER_MAX;
|
||||
}
|
||||
ptr++;
|
||||
if (n++ == maxlen) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// decimal
|
||||
while (ascii_isdigit(*ptr)) {
|
||||
// avoid ubsan error for overflow
|
||||
if (un < UVARNUMBER_MAX / 10) {
|
||||
un = 10 * un + (uvarnumber_T)(*ptr - '0');
|
||||
} else {
|
||||
un = UVARNUMBER_MAX;
|
||||
}
|
||||
ptr++;
|
||||
if (n++ == maxlen) {
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'X': {
|
||||
vim_str2nr_hex:
|
||||
PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef PARSE_NUMBER
|
||||
|
||||
if (prep != NULL) {
|
||||
*prep = pre;
|
||||
}
|
||||
|
||||
if (len != NULL) {
|
||||
*len = (int)(ptr - start);
|
||||
*len = (int)(ptr - (const char *)start);
|
||||
}
|
||||
|
||||
if (nptr != NULL) {
|
||||
@@ -1767,6 +1771,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
|
||||
if (unptr != NULL) {
|
||||
*unptr = un;
|
||||
}
|
||||
#undef STRING_ENDED
|
||||
}
|
||||
|
||||
/// Return the value of a single hex character.
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include "nvim/types.h"
|
||||
#include "nvim/pos.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
|
||||
/// Return the folded-case equivalent of the given character
|
||||
///
|
||||
@@ -15,6 +16,21 @@
|
||||
?((int)(uint8_t)(c)) \
|
||||
:((int)(c)))
|
||||
|
||||
/// Flags for vim_str2nr()
|
||||
typedef enum {
|
||||
STR2NR_DEC = 0,
|
||||
STR2NR_BIN = (1 << 0), ///< Allow binary numbers.
|
||||
STR2NR_OCT = (1 << 1), ///< Allow octal numbers.
|
||||
STR2NR_HEX = (1 << 2), ///< Allow hexadecimal numbers.
|
||||
/// Force one of the above variants.
|
||||
///
|
||||
/// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero
|
||||
/// as flags, but still present for completeness.
|
||||
STR2NR_FORCE = (1 << 3),
|
||||
/// Recognize all formats vim_str2nr() can recognize.
|
||||
STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX,
|
||||
} ChStr2NrFlags;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "charset.h.generated.h"
|
||||
#endif
|
||||
|
@@ -6066,27 +6066,30 @@ void free_last_insert(void)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add character "c" to buffer "s". Escape the special meaning of K_SPECIAL
|
||||
* and CSI. Handle multi-byte characters.
|
||||
* Returns a pointer to after the added bytes.
|
||||
*/
|
||||
/// Add character "c" to buffer "s"
|
||||
///
|
||||
/// Escapes the special meaning of K_SPECIAL and CSI, handles multi-byte
|
||||
/// characters.
|
||||
///
|
||||
/// @param[in] c Character to add.
|
||||
/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
|
||||
///
|
||||
/// @return Pointer to after the added bytes.
|
||||
char_u *add_char2buf(int c, char_u *s)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
char_u temp[MB_MAXBYTES + 1];
|
||||
int i;
|
||||
int len;
|
||||
|
||||
len = (*mb_char2bytes)(c, temp);
|
||||
for (i = 0; i < len; ++i) {
|
||||
const int len = utf_char2bytes(c, temp);
|
||||
for (int i = 0; i < len; i++) {
|
||||
c = temp[i];
|
||||
/* Need to escape K_SPECIAL and CSI like in the typeahead buffer. */
|
||||
// Need to escape K_SPECIAL and CSI like in the typeahead buffer.
|
||||
if (c == K_SPECIAL) {
|
||||
*s++ = K_SPECIAL;
|
||||
*s++ = KS_SPECIAL;
|
||||
*s++ = KE_FILLER;
|
||||
} else
|
||||
} else {
|
||||
*s++ = c;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@@ -5925,9 +5925,10 @@ static void ex_colorscheme(exarg_T *eap)
|
||||
|
||||
static void ex_highlight(exarg_T *eap)
|
||||
{
|
||||
if (*eap->arg == NUL && eap->cmd[2] == '!')
|
||||
if (*eap->arg == NUL && eap->cmd[2] == '!') {
|
||||
MSG(_("Greetings, Vim user!"));
|
||||
do_highlight(eap->arg, eap->forceit, FALSE);
|
||||
}
|
||||
do_highlight((const char *)eap->arg, eap->forceit, false);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -66,6 +66,8 @@
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/highlight_defs.h"
|
||||
#include "nvim/viml/parser/parser.h"
|
||||
#include "nvim/viml/parser/expressions.h"
|
||||
|
||||
/// Command-line colors: one chunk
|
||||
///
|
||||
@@ -2428,6 +2430,63 @@ void free_cmdline_buf(void)
|
||||
|
||||
enum { MAX_CB_ERRORS = 1 };
|
||||
|
||||
/// Color expression cmdline using built-in expressions parser
|
||||
///
|
||||
/// @param[in] colored_ccline Command-line to color.
|
||||
/// @param[out] ret_ccline_colors What should be colored.
|
||||
///
|
||||
/// Always colors the whole cmdline.
|
||||
static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
|
||||
ColoredCmdline *const ret_ccline_colors)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
ParserLine plines[] = {
|
||||
{
|
||||
.data = (const char *)colored_ccline->cmdbuff,
|
||||
.size = STRLEN(colored_ccline->cmdbuff),
|
||||
.allocated = false,
|
||||
},
|
||||
{ NULL, 0, false },
|
||||
};
|
||||
ParserLine *plines_p = plines;
|
||||
ParserHighlight colors;
|
||||
kvi_init(colors);
|
||||
ParserState pstate;
|
||||
viml_parser_init(
|
||||
&pstate, parser_simple_get_line, &plines_p, &colors);
|
||||
ExprAST east = viml_pexpr_parse(&pstate, kExprFlagsDisallowEOC);
|
||||
viml_pexpr_free_ast(east);
|
||||
viml_parser_destroy(&pstate);
|
||||
kv_resize(ret_ccline_colors->colors, kv_size(colors));
|
||||
size_t prev_end = 0;
|
||||
for (size_t i = 0 ; i < kv_size(colors) ; i++) {
|
||||
const ParserHighlightChunk chunk = kv_A(colors, i);
|
||||
if (chunk.start.col != prev_end) {
|
||||
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
|
||||
.start = prev_end,
|
||||
.end = chunk.start.col,
|
||||
.attr = 0,
|
||||
}));
|
||||
}
|
||||
const int id = syn_name2id((const char_u *)chunk.group);
|
||||
const int attr = (id == 0 ? 0 : syn_id2attr(id));
|
||||
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
|
||||
.start = chunk.start.col,
|
||||
.end = chunk.end_col,
|
||||
.attr = attr,
|
||||
}));
|
||||
prev_end = chunk.end_col;
|
||||
}
|
||||
if (prev_end < (size_t)colored_ccline->cmdlen) {
|
||||
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
|
||||
.start = prev_end,
|
||||
.end = (size_t)colored_ccline->cmdlen,
|
||||
.attr = 0,
|
||||
}));
|
||||
}
|
||||
kvi_destroy(colors);
|
||||
}
|
||||
|
||||
/// Color command-line
|
||||
///
|
||||
/// Should use built-in command parser or user-specified one. Currently only the
|
||||
@@ -2510,13 +2569,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
|
||||
tl_ret = try_leave(&tstate, &err);
|
||||
can_free_cb = true;
|
||||
} else if (colored_ccline->cmdfirstc == '=') {
|
||||
try_enter(&tstate);
|
||||
err_errmsg = N_(
|
||||
"E5409: Unable to get g:Nvim_color_expr callback: %s");
|
||||
dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"),
|
||||
&color_cb);
|
||||
tl_ret = try_leave(&tstate, &err);
|
||||
can_free_cb = true;
|
||||
color_expr_cmdline(colored_ccline, ccline_colors);
|
||||
}
|
||||
if (!tl_ret || !dgc_ret) {
|
||||
goto color_cmdline_error;
|
||||
|
@@ -164,9 +164,40 @@ local pattern = concat(
|
||||
)
|
||||
|
||||
if fname == '--help' then
|
||||
print'Usage:'
|
||||
print()
|
||||
print' gendeclarations.lua definitions.c static.h non-static.h preprocessor.i'
|
||||
print([[
|
||||
Usage:
|
||||
|
||||
gendeclarations.lua definitions.c static.h non-static.h definitions.i
|
||||
|
||||
Generates declarations for a C file definitions.c, putting declarations for
|
||||
static functions into static.h and declarations for non-static functions into
|
||||
non-static.h. File `definitions.i' should contain an already preprocessed
|
||||
version of definitions.c and it is the only one which is actually parsed,
|
||||
definitions.c is needed only to determine functions from which file out of all
|
||||
functions found in definitions.i are needed.
|
||||
|
||||
Additionally uses the following environment variables:
|
||||
|
||||
NVIM_GEN_DECLARATIONS_LINE_NUMBERS:
|
||||
If set to 1 then all generated declarations receive a comment with file
|
||||
name and line number after the declaration. This may be useful for
|
||||
debugging gen_declarations script, but not much beyond that with
|
||||
configured development environment (i.e. with ctags/cscope/finding
|
||||
definitions with clang/etc).
|
||||
|
||||
WARNING: setting this to 1 will cause extensive rebuilds: declarations
|
||||
generator script will not regenerate non-static.h file if its
|
||||
contents did not change, but including line numbers will make
|
||||
contents actually change.
|
||||
|
||||
With contents changed timestamp of the file is regenerated even
|
||||
when no real changes were made (e.g. a few lines were added to
|
||||
a function which is not at the bottom of the file).
|
||||
|
||||
With changed timestamp build system will assume that header
|
||||
changed, triggering rebuilds of all C files which depend on the
|
||||
"changed" header.
|
||||
]])
|
||||
os.exit()
|
||||
end
|
||||
|
||||
@@ -249,8 +280,10 @@ while init ~= nil do
|
||||
declaration = declaration:gsub(' $', '')
|
||||
declaration = declaration:gsub('^ ', '')
|
||||
declaration = declaration .. ';'
|
||||
declaration = declaration .. (' // %s/%s:%u'):format(
|
||||
curdir, curfile, declline)
|
||||
if os.getenv('NVIM_GEN_DECLARATIONS_LINE_NUMBERS') == '1' then
|
||||
declaration = declaration .. (' // %s/%s:%u'):format(
|
||||
curdir, curfile, declline)
|
||||
end
|
||||
declaration = declaration .. '\n'
|
||||
if declaration:sub(1, 6) == 'static' then
|
||||
static = static .. declaration
|
||||
|
@@ -729,29 +729,6 @@ EXTERN int vr_lines_changed INIT(= 0); /* #Lines changed by "gR" so far */
|
||||
/// Encoding used when 'fencs' is set to "default"
|
||||
EXTERN char_u *fenc_default INIT(= NULL);
|
||||
|
||||
// To speed up BYTELEN(); keep a lookup table to quickly get the length in
|
||||
// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes
|
||||
// which are illegal when used as the first byte have a 1. The NUL byte has
|
||||
// length 1.
|
||||
EXTERN char utf8len_tab[256] INIT(= {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1,
|
||||
});
|
||||
|
||||
# if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
|
||||
/* Pointers to functions and variables to be loaded at runtime */
|
||||
EXTERN size_t (*iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft,
|
||||
|
@@ -24,23 +24,22 @@
|
||||
* Some useful tables.
|
||||
*/
|
||||
|
||||
static struct modmasktable {
|
||||
short mod_mask; /* Bit-mask for particular key modifier */
|
||||
short mod_flag; /* Bit(s) for particular key modifier */
|
||||
char_u name; /* Single letter name of modifier */
|
||||
} mod_mask_table[] =
|
||||
{
|
||||
{MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M'},
|
||||
{MOD_MASK_META, MOD_MASK_META, (char_u)'T'},
|
||||
{MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C'},
|
||||
{MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S'},
|
||||
{MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'},
|
||||
{MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'},
|
||||
{MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'},
|
||||
{MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'},
|
||||
static const struct modmasktable {
|
||||
uint16_t mod_mask; ///< Bit-mask for particular key modifier.
|
||||
uint16_t mod_flag; ///< Bit(s) for particular key modifier.
|
||||
char_u name; ///< Single letter name of modifier.
|
||||
} mod_mask_table[] = {
|
||||
{ MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M' },
|
||||
{ MOD_MASK_META, MOD_MASK_META, (char_u)'T' },
|
||||
{ MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C' },
|
||||
{ MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S' },
|
||||
{ MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2' },
|
||||
{ MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3' },
|
||||
{ MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4' },
|
||||
{ MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D' },
|
||||
// 'A' must be the last one
|
||||
{MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'},
|
||||
{0, 0, NUL}
|
||||
{ MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A' },
|
||||
{ 0, 0, NUL }
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -139,11 +138,10 @@ static char_u modifier_keys_table[] =
|
||||
NUL
|
||||
};
|
||||
|
||||
static struct key_name_entry {
|
||||
static const struct key_name_entry {
|
||||
int key; // Special key code or ascii value
|
||||
char *name; // Name of key
|
||||
} key_names_table[] =
|
||||
{
|
||||
const char *name; // Name of key
|
||||
} key_names_table[] = {
|
||||
{ ' ', "Space" },
|
||||
{ TAB, "Tab" },
|
||||
{ K_TAB, "Tab" },
|
||||
@@ -318,73 +316,73 @@ static struct mousetable {
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
/*
|
||||
* Return the modifier mask bit (MOD_MASK_*) which corresponds to the given
|
||||
* modifier name ('S' for Shift, 'C' for Ctrl etc).
|
||||
*/
|
||||
/// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name
|
||||
///
|
||||
/// E.g. 'S' for shift, 'C' for ctrl.
|
||||
int name_to_mod_mask(int c)
|
||||
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
int i;
|
||||
|
||||
c = TOUPPER_ASC(c);
|
||||
for (i = 0; mod_mask_table[i].mod_mask != 0; i++)
|
||||
if (c == mod_mask_table[i].name)
|
||||
for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) {
|
||||
if (c == mod_mask_table[i].name) {
|
||||
return mod_mask_table[i].mod_flag;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if if there is a special key code for "key" that includes the
|
||||
* modifiers specified.
|
||||
*/
|
||||
int simplify_key(int key, int *modifiers)
|
||||
/// Check if there is a special key code for "key" with specified modifiers
|
||||
///
|
||||
/// @param[in] key Initial key code.
|
||||
/// @param[in,out] modifiers Initial modifiers, is adjusted to have simplified
|
||||
/// modifiers.
|
||||
///
|
||||
/// @return Simplified key code.
|
||||
int simplify_key(const int key, int *modifiers)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
int i;
|
||||
int key0;
|
||||
int key1;
|
||||
|
||||
if (*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) {
|
||||
/* TAB is a special case */
|
||||
// TAB is a special case.
|
||||
if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) {
|
||||
*modifiers &= ~MOD_MASK_SHIFT;
|
||||
return K_S_TAB;
|
||||
}
|
||||
key0 = KEY2TERMCAP0(key);
|
||||
key1 = KEY2TERMCAP1(key);
|
||||
for (i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE)
|
||||
const int key0 = KEY2TERMCAP0(key);
|
||||
const int key1 = KEY2TERMCAP1(key);
|
||||
for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) {
|
||||
if (key0 == modifier_keys_table[i + 3]
|
||||
&& key1 == modifier_keys_table[i + 4]
|
||||
&& (*modifiers & modifier_keys_table[i])) {
|
||||
*modifiers &= ~modifier_keys_table[i];
|
||||
return TERMCAP2KEY(modifier_keys_table[i + 1],
|
||||
modifier_keys_table[i + 2]);
|
||||
modifier_keys_table[i + 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change <xHome> to <Home>, <xUp> to <Up>, etc.
|
||||
*/
|
||||
int handle_x_keys(int key)
|
||||
/// Change <xKey> to <Key>
|
||||
int handle_x_keys(const int key)
|
||||
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
switch (key) {
|
||||
case K_XUP: return K_UP;
|
||||
case K_XDOWN: return K_DOWN;
|
||||
case K_XLEFT: return K_LEFT;
|
||||
case K_XRIGHT: return K_RIGHT;
|
||||
case K_XHOME: return K_HOME;
|
||||
case K_ZHOME: return K_HOME;
|
||||
case K_XEND: return K_END;
|
||||
case K_ZEND: return K_END;
|
||||
case K_XF1: return K_F1;
|
||||
case K_XF2: return K_F2;
|
||||
case K_XF3: return K_F3;
|
||||
case K_XF4: return K_F4;
|
||||
case K_S_XF1: return K_S_F1;
|
||||
case K_S_XF2: return K_S_F2;
|
||||
case K_S_XF3: return K_S_F3;
|
||||
case K_S_XF4: return K_S_F4;
|
||||
case K_XUP: return K_UP;
|
||||
case K_XDOWN: return K_DOWN;
|
||||
case K_XLEFT: return K_LEFT;
|
||||
case K_XRIGHT: return K_RIGHT;
|
||||
case K_XHOME: return K_HOME;
|
||||
case K_ZHOME: return K_HOME;
|
||||
case K_XEND: return K_END;
|
||||
case K_ZEND: return K_END;
|
||||
case K_XF1: return K_F1;
|
||||
case K_XF2: return K_F2;
|
||||
case K_XF3: return K_F3;
|
||||
case K_XF4: return K_F4;
|
||||
case K_S_XF1: return K_S_F1;
|
||||
case K_S_XF2: return K_S_F2;
|
||||
case K_S_XF3: return K_S_F3;
|
||||
case K_S_XF4: return K_S_F4;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
@@ -508,7 +506,7 @@ unsigned int trans_special(const char_u **srcp, const size_t src_len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Put the appropriate modifier in a string */
|
||||
// Put the appropriate modifier in a string.
|
||||
if (modifiers != 0) {
|
||||
dst[dlen++] = K_SPECIAL;
|
||||
dst[dlen++] = KS_MODIFIER;
|
||||
@@ -569,15 +567,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
|
||||
|
||||
// Find end of modifier list
|
||||
last_dash = src;
|
||||
for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) {
|
||||
for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) {
|
||||
if (*bp == '-') {
|
||||
last_dash = bp;
|
||||
if (bp + 1 <= end) {
|
||||
if (has_mbyte) {
|
||||
l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1);
|
||||
} else {
|
||||
l = 1;
|
||||
}
|
||||
l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1);
|
||||
// Anything accepted, like <C-?>.
|
||||
// <C-"> or <M-"> are not special in strings as " is
|
||||
// the string delimiter. With a backslash it works: <M-\">
|
||||
@@ -702,33 +696,39 @@ int find_special_key_in_table(int c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; key_names_table[i].name != NULL; i++)
|
||||
if (c == key_names_table[i].key)
|
||||
for (i = 0; key_names_table[i].name != NULL; i++) {
|
||||
if (c == key_names_table[i].key) {
|
||||
break;
|
||||
if (key_names_table[i].name == NULL)
|
||||
}
|
||||
}
|
||||
if (key_names_table[i].name == NULL) {
|
||||
i = -1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the special key with the given name (the given string does not have to
|
||||
* end with NUL, the name is assumed to end before the first non-idchar).
|
||||
* If the name starts with "t_" the next two characters are interpreted as a
|
||||
* termcap name.
|
||||
* Return the key code, or 0 if not found.
|
||||
*/
|
||||
/// Find the special key with the given name
|
||||
///
|
||||
/// @param[in] name Name of the special. Does not have to end with NUL, it is
|
||||
/// assumed to end before the first non-idchar. If name starts
|
||||
/// with "t_" the next two characters are interpreted as
|
||||
/// a termcap name.
|
||||
///
|
||||
/// @return Key code or 0 if not found.
|
||||
int get_special_key_code(const char_u *name)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
char *table_name;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; key_names_table[i].name != NULL; i++) {
|
||||
table_name = key_names_table[i].name;
|
||||
for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++)
|
||||
if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j]))
|
||||
for (int i = 0; key_names_table[i].name != NULL; i++) {
|
||||
const char *const table_name = key_names_table[i].name;
|
||||
int j;
|
||||
for (j = 0; ascii_isident(name[j]) && table_name[j] != NUL; j++) {
|
||||
if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) {
|
||||
break;
|
||||
if (!vim_isIDc(name[j]) && table_name[j] == NUL)
|
||||
}
|
||||
}
|
||||
if (!ascii_isident(name[j]) && table_name[j] == NUL) {
|
||||
return key_names_table[i].key;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -62,7 +62,16 @@
|
||||
#define kv_pop(v) ((v).items[--(v).size])
|
||||
#define kv_size(v) ((v).size)
|
||||
#define kv_max(v) ((v).capacity)
|
||||
#define kv_last(v) kv_A(v, kv_size(v) - 1)
|
||||
#define kv_Z(v, i) kv_A(v, kv_size(v) - (i) - 1)
|
||||
#define kv_last(v) kv_Z(v, 0)
|
||||
|
||||
/// Drop last n items from kvec without resizing
|
||||
///
|
||||
/// Previously spelled as `(void)kv_pop(v)`, repeated n times.
|
||||
///
|
||||
/// @param[out] v Kvec to drop items from.
|
||||
/// @param[in] n Number of elements to drop.
|
||||
#define kv_drop(v, n) ((v).size -= (n))
|
||||
|
||||
#define kv_resize(v, s) \
|
||||
((v).capacity = (s), \
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#ifndef NVIM_LIB_RINGBUF_H
|
||||
#define NVIM_LIB_RINGBUF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
@@ -73,6 +74,32 @@ typedef struct { \
|
||||
RBType *buf_end; \
|
||||
} TypeName##RingBuffer;
|
||||
|
||||
/// Dummy item free macros, for use in RINGBUF_INIT
|
||||
///
|
||||
/// This macros actually does nothing.
|
||||
///
|
||||
/// @param[in] item Item to be freed.
|
||||
#define RINGBUF_DUMMY_FREE(item)
|
||||
|
||||
/// Static ring buffer
|
||||
///
|
||||
/// @warning Ring buffers created with this macros must neither be freed nor
|
||||
/// deallocated.
|
||||
///
|
||||
/// @param scope Ring buffer scope.
|
||||
/// @param TypeName Ring buffer type name.
|
||||
/// @param RBType Type of the single ring buffer element.
|
||||
/// @param varname Variable name.
|
||||
/// @param rbsize Ring buffer size.
|
||||
#define RINGBUF_STATIC(scope, TypeName, RBType, varname, rbsize) \
|
||||
static RBType _##varname##_buf[rbsize]; \
|
||||
scope TypeName##RingBuffer varname = { \
|
||||
.buf = _##varname##_buf, \
|
||||
.next = _##varname##_buf, \
|
||||
.first = NULL, \
|
||||
.buf_end = _##varname##_buf + rbsize - 1, \
|
||||
};
|
||||
|
||||
/// Initialize a new ring buffer
|
||||
///
|
||||
/// @param TypeName Ring buffer type name. Actual type name will be
|
||||
|
260
src/nvim/mbyte.c
260
src/nvim/mbyte.c
@@ -72,19 +72,49 @@ struct interval {
|
||||
# include "unicode_tables.generated.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Like utf8len_tab above, but using a zero for illegal lead bytes.
|
||||
*/
|
||||
const uint8_t utf8len_tab_zero[256] =
|
||||
{
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0,
|
||||
// To speed up BYTELEN(); keep a lookup table to quickly get the length in
|
||||
// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes
|
||||
// which are illegal when used as the first byte have a 1. The NUL byte has
|
||||
// length 1.
|
||||
const uint8_t utf8len_tab[] = {
|
||||
// ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B?
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C?
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D?
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E?
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, // F?
|
||||
};
|
||||
|
||||
// Like utf8len_tab above, but using a zero for illegal lead bytes.
|
||||
const uint8_t utf8len_tab_zero[] = {
|
||||
// ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7?
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8?
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9?
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A?
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B?
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C?
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D?
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E?
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // F?
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -528,45 +558,52 @@ int utf_off2cells(unsigned off, unsigned max_off)
|
||||
return (off + 1 < max_off && ScreenLines[off + 1] == 0) ? 2 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a UTF-8 byte sequence to a wide character.
|
||||
* If the sequence is illegal or truncated by a NUL the first byte is
|
||||
* returned.
|
||||
* Does not include composing characters, of course.
|
||||
*/
|
||||
int utf_ptr2char(const char_u *p)
|
||||
/// Convert a UTF-8 byte sequence to a wide character
|
||||
///
|
||||
/// If the sequence is illegal or truncated by a NUL then the first byte is
|
||||
/// returned. Does not include composing characters for obvious reasons.
|
||||
///
|
||||
/// @param[in] p String to convert.
|
||||
///
|
||||
/// @return Unicode codepoint or byte value.
|
||||
int utf_ptr2char(const char_u *const p)
|
||||
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
if (p[0] < 0x80) /* be quick for ASCII */
|
||||
if (p[0] < 0x80) { // Be quick for ASCII.
|
||||
return p[0];
|
||||
}
|
||||
|
||||
len = utf8len_tab_zero[p[0]];
|
||||
const uint8_t len = utf8len_tab_zero[p[0]];
|
||||
if (len > 1 && (p[1] & 0xc0) == 0x80) {
|
||||
if (len == 2)
|
||||
if (len == 2) {
|
||||
return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
|
||||
}
|
||||
if ((p[2] & 0xc0) == 0x80) {
|
||||
if (len == 3)
|
||||
return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
|
||||
+ (p[2] & 0x3f);
|
||||
if (len == 3) {
|
||||
return (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
|
||||
+ (p[2] & 0x3f));
|
||||
}
|
||||
if ((p[3] & 0xc0) == 0x80) {
|
||||
if (len == 4)
|
||||
return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
|
||||
+ ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
|
||||
if (len == 4) {
|
||||
return (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
|
||||
+ ((p[2] & 0x3f) << 6) + (p[3] & 0x3f));
|
||||
}
|
||||
if ((p[4] & 0xc0) == 0x80) {
|
||||
if (len == 5)
|
||||
return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
|
||||
+ ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
|
||||
+ (p[4] & 0x3f);
|
||||
if ((p[5] & 0xc0) == 0x80 && len == 6)
|
||||
return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
|
||||
+ ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
|
||||
+ ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
|
||||
if (len == 5) {
|
||||
return (((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
|
||||
+ ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
|
||||
+ (p[4] & 0x3f));
|
||||
}
|
||||
if ((p[5] & 0xc0) == 0x80 && len == 6) {
|
||||
return (((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
|
||||
+ ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
|
||||
+ ((p[4] & 0x3f) << 6) + (p[5] & 0x3f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Illegal value, just return the first byte */
|
||||
// Illegal value: just return the first byte.
|
||||
return p[0];
|
||||
}
|
||||
|
||||
@@ -767,23 +804,24 @@ int utfc_char2bytes(int off, char_u *buf)
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the length of a UTF-8 byte sequence, not including any following
|
||||
* composing characters.
|
||||
* Returns 0 for "".
|
||||
* Returns 1 for an illegal byte sequence.
|
||||
*/
|
||||
int utf_ptr2len(const char_u *p)
|
||||
/// Get the length of a UTF-8 byte sequence representing a single codepoint
|
||||
///
|
||||
/// @param[in] p UTF-8 string.
|
||||
///
|
||||
/// @return Sequence length, 0 for empty string and 1 for non-UTF-8 byte
|
||||
/// sequence.
|
||||
int utf_ptr2len(const char_u *const p)
|
||||
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
|
||||
if (*p == NUL)
|
||||
if (*p == NUL) {
|
||||
return 0;
|
||||
len = utf8len_tab[*p];
|
||||
for (i = 1; i < len; ++i)
|
||||
if ((p[i] & 0xc0) != 0x80)
|
||||
}
|
||||
const int len = utf8len_tab[*p];
|
||||
for (int i = 1; i < len; i++) {
|
||||
if ((p[i] & 0xc0) != 0x80) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -824,38 +862,38 @@ int utf_ptr2len_len(const char_u *p, int size)
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of bytes the UTF-8 encoding of the character at "p" takes.
|
||||
* This includes following composing characters.
|
||||
*/
|
||||
int utfc_ptr2len(const char_u *p)
|
||||
/// Return the number of bytes occupied by a UTF-8 character in a string
|
||||
///
|
||||
/// This includes following composing characters.
|
||||
int utfc_ptr2len(const char_u *const p)
|
||||
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
int len;
|
||||
int b0 = *p;
|
||||
int prevlen;
|
||||
uint8_t b0 = (uint8_t)(*p);
|
||||
|
||||
if (b0 == NUL)
|
||||
if (b0 == NUL) {
|
||||
return 0;
|
||||
if (b0 < 0x80 && p[1] < 0x80) /* be quick for ASCII */
|
||||
}
|
||||
if (b0 < 0x80 && p[1] < 0x80) { // be quick for ASCII
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Skip over first UTF-8 char, stopping at a NUL byte. */
|
||||
len = utf_ptr2len(p);
|
||||
// Skip over first UTF-8 char, stopping at a NUL byte.
|
||||
int len = utf_ptr2len(p);
|
||||
|
||||
/* Check for illegal byte. */
|
||||
if (len == 1 && b0 >= 0x80)
|
||||
// Check for illegal byte.
|
||||
if (len == 1 && b0 >= 0x80) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for composing characters. We can handle only the first six, but
|
||||
* skip all of them (otherwise the cursor would get stuck).
|
||||
*/
|
||||
prevlen = 0;
|
||||
for (;; ) {
|
||||
if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len))
|
||||
// Check for composing characters. We can handle only the first six, but
|
||||
// skip all of them (otherwise the cursor would get stuck).
|
||||
int prevlen = 0;
|
||||
for (;;) {
|
||||
if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len)) {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Skip over composing char */
|
||||
// Skip over composing char.
|
||||
prevlen = len;
|
||||
len += utf_ptr2len(p + len);
|
||||
}
|
||||
@@ -913,23 +951,22 @@ int utfc_ptr2len_len(const char_u *p, int size)
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of bytes the UTF-8 encoding of character "c" takes.
|
||||
* This does not include composing characters.
|
||||
*/
|
||||
int utf_char2len(int c)
|
||||
/// Determine how many bytes certain unicode codepoint will occupy
|
||||
int utf_char2len(const int c)
|
||||
{
|
||||
if (c < 0x80)
|
||||
if (c < 0x80) {
|
||||
return 1;
|
||||
if (c < 0x800)
|
||||
} else if (c < 0x800) {
|
||||
return 2;
|
||||
if (c < 0x10000)
|
||||
} else if (c < 0x10000) {
|
||||
return 3;
|
||||
if (c < 0x200000)
|
||||
} else if (c < 0x200000) {
|
||||
return 4;
|
||||
if (c < 0x4000000)
|
||||
} else if (c < 0x4000000) {
|
||||
return 5;
|
||||
return 6;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Unicode character to UTF-8 string
|
||||
@@ -937,46 +974,42 @@ int utf_char2len(int c)
|
||||
/// @param c character to convert to \p buf
|
||||
/// @param[out] buf UTF-8 string generated from \p c, does not add \0
|
||||
/// @return Number of bytes (1-6). Does not include composing characters.
|
||||
int utf_char2bytes(int c, char_u *const buf)
|
||||
int utf_char2bytes(const int c, char_u *const buf)
|
||||
{
|
||||
if (c < 0x80) { /* 7 bits */
|
||||
if (c < 0x80) { // 7 bits
|
||||
buf[0] = c;
|
||||
return 1;
|
||||
}
|
||||
if (c < 0x800) { /* 11 bits */
|
||||
} else if (c < 0x800) { // 11 bits
|
||||
buf[0] = 0xc0 + ((unsigned)c >> 6);
|
||||
buf[1] = 0x80 + (c & 0x3f);
|
||||
return 2;
|
||||
}
|
||||
if (c < 0x10000) { /* 16 bits */
|
||||
} else if (c < 0x10000) { // 16 bits
|
||||
buf[0] = 0xe0 + ((unsigned)c >> 12);
|
||||
buf[1] = 0x80 + (((unsigned)c >> 6) & 0x3f);
|
||||
buf[2] = 0x80 + (c & 0x3f);
|
||||
return 3;
|
||||
}
|
||||
if (c < 0x200000) { /* 21 bits */
|
||||
} else if (c < 0x200000) { // 21 bits
|
||||
buf[0] = 0xf0 + ((unsigned)c >> 18);
|
||||
buf[1] = 0x80 + (((unsigned)c >> 12) & 0x3f);
|
||||
buf[2] = 0x80 + (((unsigned)c >> 6) & 0x3f);
|
||||
buf[3] = 0x80 + (c & 0x3f);
|
||||
return 4;
|
||||
}
|
||||
if (c < 0x4000000) { /* 26 bits */
|
||||
} else if (c < 0x4000000) { // 26 bits
|
||||
buf[0] = 0xf8 + ((unsigned)c >> 24);
|
||||
buf[1] = 0x80 + (((unsigned)c >> 18) & 0x3f);
|
||||
buf[2] = 0x80 + (((unsigned)c >> 12) & 0x3f);
|
||||
buf[3] = 0x80 + (((unsigned)c >> 6) & 0x3f);
|
||||
buf[4] = 0x80 + (c & 0x3f);
|
||||
return 5;
|
||||
} else { // 31 bits
|
||||
buf[0] = 0xfc + ((unsigned)c >> 30);
|
||||
buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f);
|
||||
buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f);
|
||||
buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f);
|
||||
buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f);
|
||||
buf[5] = 0x80 + (c & 0x3f);
|
||||
return 6;
|
||||
}
|
||||
/* 31 bits */
|
||||
buf[0] = 0xfc + ((unsigned)c >> 30);
|
||||
buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f);
|
||||
buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f);
|
||||
buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f);
|
||||
buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f);
|
||||
buf[5] = 0x80 + (c & 0x3f);
|
||||
return 6;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1513,14 +1546,15 @@ int utf_head_off(const char_u *base, const char_u *p)
|
||||
return (int)(p - q);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a character from "*fp" to "*tp" and advance the pointers.
|
||||
*/
|
||||
void mb_copy_char(const char_u **fp, char_u **tp)
|
||||
/// Copy a character, advancing the pointers
|
||||
///
|
||||
/// @param[in,out] fp Source of the character to copy.
|
||||
/// @param[in,out] tp Destination to copy to.
|
||||
void mb_copy_char(const char_u **const fp, char_u **const tp)
|
||||
{
|
||||
int l = (*mb_ptr2len)(*fp);
|
||||
const size_t l = (size_t)utfc_ptr2len(*fp);
|
||||
|
||||
memmove(*tp, *fp, (size_t)l);
|
||||
memmove(*tp, *fp, l);
|
||||
*tp += l;
|
||||
*fp += l;
|
||||
}
|
||||
@@ -2262,9 +2296,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8,
|
||||
if (vcp->vc_type == CONV_ICONV && vcp->vc_fd != (iconv_t)-1)
|
||||
iconv_close(vcp->vc_fd);
|
||||
# endif
|
||||
vcp->vc_type = CONV_NONE;
|
||||
vcp->vc_factor = 1;
|
||||
vcp->vc_fail = false;
|
||||
*vcp = (vimconv_T)MBYTE_NONE_CONV;
|
||||
|
||||
/* No conversion when one of the names is empty or they are equal. */
|
||||
if (from == NULL || *from == NUL || to == NULL || *to == NUL
|
||||
|
@@ -60,6 +60,12 @@ typedef enum {
|
||||
CONV_ICONV = 5,
|
||||
} ConvFlags;
|
||||
|
||||
#define MBYTE_NONE_CONV { \
|
||||
.vc_type = CONV_NONE, \
|
||||
.vc_factor = 1, \
|
||||
.vc_fail = false, \
|
||||
}
|
||||
|
||||
/// Structure used for string conversions
|
||||
typedef struct {
|
||||
int vc_type; ///< Zero or more ConvFlags.
|
||||
@@ -73,6 +79,8 @@ typedef struct {
|
||||
|
||||
extern const uint8_t utf8len_tab_zero[256];
|
||||
|
||||
extern const uint8_t utf8len_tab[256];
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "mbyte.h.generated.h"
|
||||
#endif
|
||||
|
@@ -5930,9 +5930,9 @@ static void syntime_report(void)
|
||||
//
|
||||
// When making changes here, also change runtime/colors/default.vim!
|
||||
|
||||
static char *highlight_init_both[] =
|
||||
{
|
||||
"Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey",
|
||||
static const char *highlight_init_both[] = {
|
||||
"Conceal "
|
||||
"ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey",
|
||||
"Cursor guibg=fg guifg=bg",
|
||||
"lCursor guibg=fg guifg=bg",
|
||||
"DiffText cterm=bold ctermbg=Red gui=bold guibg=Red",
|
||||
@@ -5955,8 +5955,7 @@ static char *highlight_init_both[] =
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *highlight_init_light[] =
|
||||
{
|
||||
static const char *highlight_init_light[] = {
|
||||
"ColorColumn ctermbg=LightRed guibg=LightRed",
|
||||
"CursorColumn ctermbg=LightGrey guibg=Grey90",
|
||||
"CursorLine cterm=underline guibg=Grey90",
|
||||
@@ -5989,8 +5988,7 @@ static char *highlight_init_light[] =
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *highlight_init_dark[] =
|
||||
{
|
||||
static const char *highlight_init_dark[] = {
|
||||
"ColorColumn ctermbg=DarkRed guibg=DarkRed",
|
||||
"CursorColumn ctermbg=DarkGrey guibg=Grey40",
|
||||
"CursorLine cterm=underline guibg=Grey40",
|
||||
@@ -6023,18 +6021,223 @@ static char *highlight_init_dark[] =
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const highlight_init_cmdline[] = {
|
||||
// XXX When modifying a list modify it in both valid and invalid halfs.
|
||||
// TODO(ZyX-I): merge valid and invalid groups via a macros.
|
||||
|
||||
// NvimInternalError should appear only when highlighter has a bug.
|
||||
"NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
|
||||
|
||||
// Highlight groups (links) used by parser:
|
||||
|
||||
"default link NvimAssignment Operator",
|
||||
"default link NvimPlainAssignment NvimAssignment",
|
||||
"default link NvimAugmentedAssignment NvimAssignment",
|
||||
"default link NvimAssignmentWithAddition NvimAugmentedAssignment",
|
||||
"default link NvimAssignmentWithSubtraction NvimAugmentedAssignment",
|
||||
"default link NvimAssignmentWithConcatenation NvimAugmentedAssignment",
|
||||
|
||||
"default link NvimOperator Operator",
|
||||
|
||||
"default link NvimUnaryOperator NvimOperator",
|
||||
"default link NvimUnaryPlus NvimUnaryOperator",
|
||||
"default link NvimUnaryMinus NvimUnaryOperator",
|
||||
"default link NvimNot NvimUnaryOperator",
|
||||
|
||||
"default link NvimBinaryOperator NvimOperator",
|
||||
"default link NvimComparison NvimBinaryOperator",
|
||||
"default link NvimComparisonModifier NvimComparison",
|
||||
"default link NvimBinaryPlus NvimBinaryOperator",
|
||||
"default link NvimBinaryMinus NvimBinaryOperator",
|
||||
"default link NvimConcat NvimBinaryOperator",
|
||||
"default link NvimConcatOrSubscript NvimConcat",
|
||||
"default link NvimOr NvimBinaryOperator",
|
||||
"default link NvimAnd NvimBinaryOperator",
|
||||
"default link NvimMultiplication NvimBinaryOperator",
|
||||
"default link NvimDivision NvimBinaryOperator",
|
||||
"default link NvimMod NvimBinaryOperator",
|
||||
|
||||
"default link NvimTernary NvimOperator",
|
||||
"default link NvimTernaryColon NvimTernary",
|
||||
|
||||
"default link NvimParenthesis Delimiter",
|
||||
"default link NvimLambda NvimParenthesis",
|
||||
"default link NvimNestingParenthesis NvimParenthesis",
|
||||
"default link NvimCallingParenthesis NvimParenthesis",
|
||||
|
||||
"default link NvimSubscript NvimParenthesis",
|
||||
"default link NvimSubscriptBracket NvimSubscript",
|
||||
"default link NvimSubscriptColon NvimSubscript",
|
||||
"default link NvimCurly NvimSubscript",
|
||||
|
||||
"default link NvimContainer NvimParenthesis",
|
||||
"default link NvimDict NvimContainer",
|
||||
"default link NvimList NvimContainer",
|
||||
|
||||
"default link NvimIdentifier Identifier",
|
||||
"default link NvimIdentifierScope NvimIdentifier",
|
||||
"default link NvimIdentifierScopeDelimiter NvimIdentifier",
|
||||
"default link NvimIdentifierName NvimIdentifier",
|
||||
"default link NvimIdentifierKey NvimIdentifier",
|
||||
|
||||
"default link NvimColon Delimiter",
|
||||
"default link NvimComma Delimiter",
|
||||
"default link NvimArrow Delimiter",
|
||||
|
||||
"default link NvimRegister SpecialChar",
|
||||
"default link NvimNumber Number",
|
||||
"default link NvimFloat NvimNumber",
|
||||
"default link NvimNumberPrefix Type",
|
||||
|
||||
"default link NvimOptionSigil Type",
|
||||
"default link NvimOptionName NvimIdentifier",
|
||||
"default link NvimOptionScope NvimIdentifierScope",
|
||||
"default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter",
|
||||
|
||||
"default link NvimEnvironmentSigil NvimOptionSigil",
|
||||
"default link NvimEnvironmentName NvimIdentifier",
|
||||
|
||||
"default link NvimString String",
|
||||
"default link NvimStringBody NvimString",
|
||||
"default link NvimStringQuote NvimString",
|
||||
"default link NvimStringSpecial SpecialChar",
|
||||
|
||||
"default link NvimSingleQuote NvimStringQuote",
|
||||
"default link NvimSingleQuotedBody NvimStringBody",
|
||||
"default link NvimSingleQuotedQuote NvimStringSpecial",
|
||||
|
||||
"default link NvimDoubleQuote NvimStringQuote",
|
||||
"default link NvimDoubleQuotedBody NvimStringBody",
|
||||
"default link NvimDoubleQuotedEscape NvimStringSpecial",
|
||||
|
||||
"default link NvimFigureBrace NvimInternalError",
|
||||
"default link NvimSingleQuotedUnknownEscape NvimInternalError",
|
||||
|
||||
"default link NvimSpacing Normal",
|
||||
|
||||
// NvimInvalid groups:
|
||||
|
||||
"default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError",
|
||||
|
||||
"default link NvimInvalid Error",
|
||||
|
||||
"default link NvimInvalidAssignment NvimInvalid",
|
||||
"default link NvimInvalidPlainAssignment NvimInvalidAssignment",
|
||||
"default link NvimInvalidAugmentedAssignment NvimInvalidAssignment",
|
||||
"default link NvimInvalidAssignmentWithAddition "
|
||||
"NvimInvalidAugmentedAssignment",
|
||||
"default link NvimInvalidAssignmentWithSubtraction "
|
||||
"NvimInvalidAugmentedAssignment",
|
||||
"default link NvimInvalidAssignmentWithConcatenation "
|
||||
"NvimInvalidAugmentedAssignment",
|
||||
|
||||
"default link NvimInvalidOperator NvimInvalid",
|
||||
|
||||
"default link NvimInvalidUnaryOperator NvimInvalidOperator",
|
||||
"default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator",
|
||||
"default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator",
|
||||
"default link NvimInvalidNot NvimInvalidUnaryOperator",
|
||||
|
||||
"default link NvimInvalidBinaryOperator NvimInvalidOperator",
|
||||
"default link NvimInvalidComparison NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidComparisonModifier NvimInvalidComparison",
|
||||
"default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidConcat NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidConcatOrSubscript NvimInvalidConcat",
|
||||
"default link NvimInvalidOr NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidAnd NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidMultiplication NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidDivision NvimInvalidBinaryOperator",
|
||||
"default link NvimInvalidMod NvimInvalidBinaryOperator",
|
||||
|
||||
"default link NvimInvalidTernary NvimInvalidOperator",
|
||||
"default link NvimInvalidTernaryColon NvimInvalidTernary",
|
||||
|
||||
"default link NvimInvalidDelimiter NvimInvalid",
|
||||
|
||||
"default link NvimInvalidParenthesis NvimInvalidDelimiter",
|
||||
"default link NvimInvalidLambda NvimInvalidParenthesis",
|
||||
"default link NvimInvalidNestingParenthesis NvimInvalidParenthesis",
|
||||
"default link NvimInvalidCallingParenthesis NvimInvalidParenthesis",
|
||||
|
||||
"default link NvimInvalidSubscript NvimInvalidParenthesis",
|
||||
"default link NvimInvalidSubscriptBracket NvimInvalidSubscript",
|
||||
"default link NvimInvalidSubscriptColon NvimInvalidSubscript",
|
||||
"default link NvimInvalidCurly NvimInvalidSubscript",
|
||||
|
||||
"default link NvimInvalidContainer NvimInvalidParenthesis",
|
||||
"default link NvimInvalidDict NvimInvalidContainer",
|
||||
"default link NvimInvalidList NvimInvalidContainer",
|
||||
|
||||
"default link NvimInvalidValue NvimInvalid",
|
||||
|
||||
"default link NvimInvalidIdentifier NvimInvalidValue",
|
||||
"default link NvimInvalidIdentifierScope NvimInvalidIdentifier",
|
||||
"default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier",
|
||||
"default link NvimInvalidIdentifierName NvimInvalidIdentifier",
|
||||
"default link NvimInvalidIdentifierKey NvimInvalidIdentifier",
|
||||
|
||||
"default link NvimInvalidColon NvimInvalidDelimiter",
|
||||
"default link NvimInvalidComma NvimInvalidDelimiter",
|
||||
"default link NvimInvalidArrow NvimInvalidDelimiter",
|
||||
|
||||
"default link NvimInvalidRegister NvimInvalidValue",
|
||||
"default link NvimInvalidNumber NvimInvalidValue",
|
||||
"default link NvimInvalidFloat NvimInvalidNumber",
|
||||
"default link NvimInvalidNumberPrefix NvimInvalidNumber",
|
||||
|
||||
"default link NvimInvalidOptionSigil NvimInvalidIdentifier",
|
||||
"default link NvimInvalidOptionName NvimInvalidIdentifier",
|
||||
"default link NvimInvalidOptionScope NvimInvalidIdentifierScope",
|
||||
"default link NvimInvalidOptionScopeDelimiter "
|
||||
"NvimInvalidIdentifierScopeDelimiter",
|
||||
|
||||
"default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil",
|
||||
"default link NvimInvalidEnvironmentName NvimInvalidIdentifier",
|
||||
|
||||
// Invalid string bodies and specials are still highlighted as valid ones to
|
||||
// minimize the red area.
|
||||
"default link NvimInvalidString NvimInvalidValue",
|
||||
"default link NvimInvalidStringBody NvimStringBody",
|
||||
"default link NvimInvalidStringQuote NvimInvalidString",
|
||||
"default link NvimInvalidStringSpecial NvimStringSpecial",
|
||||
|
||||
"default link NvimInvalidSingleQuote NvimInvalidStringQuote",
|
||||
"default link NvimInvalidSingleQuotedBody NvimInvalidStringBody",
|
||||
"default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial",
|
||||
|
||||
"default link NvimInvalidDoubleQuote NvimInvalidStringQuote",
|
||||
"default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody",
|
||||
"default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial",
|
||||
"default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue",
|
||||
|
||||
"default link NvimInvalidFigureBrace NvimInvalidDelimiter",
|
||||
|
||||
"default link NvimInvalidSpacing ErrorMsg",
|
||||
|
||||
// Not actually invalid, but we highlight user that he is doing something
|
||||
// wrong.
|
||||
"default link NvimDoubleQuotedUnknownEscape NvimInvalidValue",
|
||||
NULL,
|
||||
};
|
||||
|
||||
/// Create default links for Nvim* highlight groups used for cmdline coloring
|
||||
void syn_init_cmdline_highlight(bool reset, bool init)
|
||||
{
|
||||
for (size_t i = 0 ; highlight_init_cmdline[i] != NULL ; i++) {
|
||||
do_highlight(highlight_init_cmdline[i], reset, init);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load colors from a file if "g:colors_name" is set, otherwise load builtin
|
||||
/// colors
|
||||
///
|
||||
/// @param both include groups where 'bg' doesn't matter
|
||||
/// @param reset clear groups first
|
||||
void
|
||||
init_highlight(int both, int reset)
|
||||
void init_highlight(bool both, bool reset)
|
||||
{
|
||||
int i;
|
||||
char **pp;
|
||||
static int had_both = FALSE;
|
||||
static int had_both = false;
|
||||
|
||||
// Try finding the color scheme file. Used when a color file was loaded
|
||||
// and 'background' or 't_Co' is changed.
|
||||
@@ -6054,10 +6257,10 @@ init_highlight(int both, int reset)
|
||||
* Didn't use a color file, use the compiled-in colors.
|
||||
*/
|
||||
if (both) {
|
||||
had_both = TRUE;
|
||||
pp = highlight_init_both;
|
||||
for (i = 0; pp[i] != NULL; i++) {
|
||||
do_highlight((char_u *)pp[i], reset, true);
|
||||
had_both = true;
|
||||
const char *const *const pp = highlight_init_both;
|
||||
for (size_t i = 0; pp[i] != NULL; i++) {
|
||||
do_highlight(pp[i], reset, true);
|
||||
}
|
||||
} else if (!had_both) {
|
||||
// Don't do anything before the call with both == TRUE from main().
|
||||
@@ -6066,10 +6269,11 @@ init_highlight(int both, int reset)
|
||||
return;
|
||||
}
|
||||
|
||||
pp = (*p_bg == 'l') ? highlight_init_light : highlight_init_dark;
|
||||
|
||||
for (i = 0; pp[i] != NULL; i++) {
|
||||
do_highlight((char_u *)pp[i], reset, true);
|
||||
const char *const *const pp = ((*p_bg == 'l')
|
||||
? highlight_init_light
|
||||
: highlight_init_dark);
|
||||
for (size_t i = 0; pp[i] != NULL; i++) {
|
||||
do_highlight(pp[i], reset, true);
|
||||
}
|
||||
|
||||
/* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
|
||||
@@ -6079,15 +6283,14 @@ init_highlight(int both, int reset)
|
||||
* Clear the attributes, needed when changing the t_Co value. */
|
||||
if (t_colors > 8) {
|
||||
do_highlight(
|
||||
(char_u *)(*p_bg == 'l'
|
||||
? "Visual cterm=NONE ctermbg=LightGrey"
|
||||
: "Visual cterm=NONE ctermbg=DarkGrey"), false,
|
||||
true);
|
||||
(*p_bg == 'l'
|
||||
? "Visual cterm=NONE ctermbg=LightGrey"
|
||||
: "Visual cterm=NONE ctermbg=DarkGrey"), false, true);
|
||||
} else {
|
||||
do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
|
||||
FALSE, TRUE);
|
||||
if (*p_bg == 'l')
|
||||
do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
|
||||
do_highlight("Visual cterm=reverse ctermbg=NONE", false, true);
|
||||
if (*p_bg == 'l') {
|
||||
do_highlight("Search ctermfg=black", false, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6104,6 +6307,7 @@ init_highlight(int both, int reset)
|
||||
recursive--;
|
||||
}
|
||||
}
|
||||
syn_init_cmdline_highlight(false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6138,17 +6342,22 @@ int load_colors(char_u *name)
|
||||
}
|
||||
|
||||
|
||||
/// Handle the ":highlight .." command.
|
||||
/// When using ":hi clear" this is called recursively for each group with
|
||||
/// "forceit" and "init" both TRUE.
|
||||
/// @param init TRUE when called for initializing
|
||||
void
|
||||
do_highlight(char_u *line, int forceit, int init) {
|
||||
char_u *name_end;
|
||||
char_u *linep;
|
||||
char_u *key_start;
|
||||
char_u *arg_start;
|
||||
char_u *key = NULL, *arg = NULL;
|
||||
/// Handle ":highlight" command
|
||||
///
|
||||
/// When using ":highlight clear" this is called recursively for each group with
|
||||
/// forceit and init being both true.
|
||||
///
|
||||
/// @param[in] line Command arguments.
|
||||
/// @param[in] forceit True when bang is given, allows to link group even if
|
||||
/// it has its own settings.
|
||||
/// @param[in] init True when initializing.
|
||||
void do_highlight(const char *line, const bool forceit, const bool init)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *name_end;
|
||||
const char *linep;
|
||||
const char *key_start;
|
||||
const char *arg_start;
|
||||
long i;
|
||||
int off;
|
||||
int len;
|
||||
@@ -6162,94 +6371,87 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
int color;
|
||||
bool is_normal_group = false; // "Normal" group
|
||||
|
||||
/*
|
||||
* If no argument, list current highlighting.
|
||||
*/
|
||||
if (ends_excmd(*line)) {
|
||||
// If no argument, list current highlighting.
|
||||
if (ends_excmd((uint8_t)(*line))) {
|
||||
for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
|
||||
// todo(vim): only call when the group has attributes set
|
||||
// TODO(brammool): only call when the group has attributes set
|
||||
highlight_list_one(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Isolate the name.
|
||||
*/
|
||||
name_end = skiptowhite(line);
|
||||
linep = skipwhite(name_end);
|
||||
// Isolate the name.
|
||||
name_end = (const char *)skiptowhite((const char_u *)line);
|
||||
linep = (const char *)skipwhite((const char_u *)name_end);
|
||||
|
||||
/*
|
||||
* Check for "default" argument.
|
||||
*/
|
||||
if (STRNCMP(line, "default", name_end - line) == 0) {
|
||||
dodefault = TRUE;
|
||||
// Check for "default" argument.
|
||||
if (strncmp(line, "default", name_end - line) == 0) {
|
||||
dodefault = true;
|
||||
line = linep;
|
||||
name_end = skiptowhite(line);
|
||||
linep = skipwhite(name_end);
|
||||
name_end = (const char *)skiptowhite((const char_u *)line);
|
||||
linep = (const char *)skipwhite((const char_u *)name_end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for "clear" or "link" argument.
|
||||
*/
|
||||
if (STRNCMP(line, "clear", name_end - line) == 0)
|
||||
doclear = TRUE;
|
||||
if (STRNCMP(line, "link", name_end - line) == 0)
|
||||
dolink = TRUE;
|
||||
// Check for "clear" or "link" argument.
|
||||
if (strncmp(line, "clear", name_end - line) == 0) {
|
||||
doclear = true;
|
||||
} else if (strncmp(line, "link", name_end - line) == 0) {
|
||||
dolink = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ":highlight {group-name}": list highlighting for one group.
|
||||
*/
|
||||
if (!doclear && !dolink && ends_excmd(*linep)) {
|
||||
id = syn_namen2id(line, (int)(name_end - line));
|
||||
if (id == 0)
|
||||
EMSG2(_("E411: highlight group not found: %s"), line);
|
||||
else
|
||||
// ":highlight {group-name}": list highlighting for one group.
|
||||
if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) {
|
||||
id = syn_namen2id((const char_u *)line, (int)(name_end - line));
|
||||
if (id == 0) {
|
||||
emsgf(_("E411: highlight group not found: %s"), line);
|
||||
} else {
|
||||
highlight_list_one(id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ":highlight link {from} {to}" command.
|
||||
*/
|
||||
// Handle ":highlight link {from} {to}" command.
|
||||
if (dolink) {
|
||||
char_u *from_start = linep;
|
||||
char_u *from_end;
|
||||
char_u *to_start;
|
||||
char_u *to_end;
|
||||
const char *from_start = linep;
|
||||
const char *from_end;
|
||||
const char *to_start;
|
||||
const char *to_end;
|
||||
int from_id;
|
||||
int to_id;
|
||||
|
||||
from_end = skiptowhite(from_start);
|
||||
to_start = skipwhite(from_end);
|
||||
to_end = skiptowhite(to_start);
|
||||
from_end = (const char *)skiptowhite((const char_u *)from_start);
|
||||
to_start = (const char *)skipwhite((const char_u *)from_end);
|
||||
to_end = (const char *)skiptowhite((const char_u *)to_start);
|
||||
|
||||
if (ends_excmd(*from_start) || ends_excmd(*to_start)) {
|
||||
EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
|
||||
from_start);
|
||||
if (ends_excmd((uint8_t)(*from_start))
|
||||
|| ends_excmd((uint8_t)(*to_start))) {
|
||||
emsgf(_("E412: Not enough arguments: \":highlight link %s\""),
|
||||
from_start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ends_excmd(*skipwhite(to_end))) {
|
||||
EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
|
||||
if (!ends_excmd(*skipwhite((const char_u *)to_end))) {
|
||||
emsgf(_("E413: Too many arguments: \":highlight link %s\""), from_start);
|
||||
return;
|
||||
}
|
||||
|
||||
from_id = syn_check_group(from_start, (int)(from_end - from_start));
|
||||
if (STRNCMP(to_start, "NONE", 4) == 0)
|
||||
from_id = syn_check_group((const char_u *)from_start,
|
||||
(int)(from_end - from_start));
|
||||
if (strncmp(to_start, "NONE", 4) == 0) {
|
||||
to_id = 0;
|
||||
else
|
||||
to_id = syn_check_group(to_start, (int)(to_end - to_start));
|
||||
} else {
|
||||
to_id = syn_check_group((const char_u *)to_start,
|
||||
(int)(to_end - to_start));
|
||||
}
|
||||
|
||||
if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) {
|
||||
/*
|
||||
* Don't allow a link when there already is some highlighting
|
||||
* for the group, unless '!' is used
|
||||
*/
|
||||
// Don't allow a link when there already is some highlighting
|
||||
// for the group, unless '!' is used
|
||||
if (to_id > 0 && !forceit && !init
|
||||
&& hl_has_settings(from_id - 1, dodefault)) {
|
||||
if (sourcing_name == NULL && !dodefault)
|
||||
if (sourcing_name == NULL && !dodefault) {
|
||||
EMSG(_("E414: group has settings, highlight link ignored"));
|
||||
}
|
||||
} else {
|
||||
if (!init)
|
||||
HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
|
||||
@@ -6259,43 +6461,38 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Only call highlight_changed() once, after sourcing a syntax file */
|
||||
need_highlight_changed = TRUE;
|
||||
// Only call highlight_changed() once, after sourcing a syntax file.
|
||||
need_highlight_changed = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (doclear) {
|
||||
/*
|
||||
* ":highlight clear [group]" command.
|
||||
*/
|
||||
// ":highlight clear [group]" command.
|
||||
line = linep;
|
||||
if (ends_excmd(*line)) {
|
||||
if (ends_excmd((uint8_t)(*line))) {
|
||||
do_unlet(S_LEN("colors_name"), true);
|
||||
restore_cterm_colors();
|
||||
|
||||
/*
|
||||
* Clear all default highlight groups and load the defaults.
|
||||
*/
|
||||
for (int idx = 0; idx < highlight_ga.ga_len; ++idx) {
|
||||
// Clear all default highlight groups and load the defaults.
|
||||
for (int idx = 0; idx < highlight_ga.ga_len; idx++) {
|
||||
highlight_clear(idx);
|
||||
}
|
||||
init_highlight(TRUE, TRUE);
|
||||
init_highlight(true, true);
|
||||
highlight_changed();
|
||||
redraw_later_clear();
|
||||
return;
|
||||
}
|
||||
name_end = skiptowhite(line);
|
||||
linep = skipwhite(name_end);
|
||||
name_end = (const char *)skiptowhite((const char_u *)line);
|
||||
linep = (const char *)skipwhite((const char_u *)name_end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the group name in the table. If it does not exist yet, add it.
|
||||
*/
|
||||
id = syn_check_group(line, (int)(name_end - line));
|
||||
if (id == 0) /* failed (out of memory) */
|
||||
// Find the group name in the table. If it does not exist yet, add it.
|
||||
id = syn_check_group((const char_u *)line, (int)(name_end - line));
|
||||
if (id == 0) { // Failed (out of memory).
|
||||
return;
|
||||
idx = id - 1; /* index is ID minus one */
|
||||
}
|
||||
idx = id - 1; // Index is ID minus one.
|
||||
|
||||
// Return if "default" was used and the group already has settings
|
||||
if (dodefault && hl_has_settings(idx, true)) {
|
||||
@@ -6304,19 +6501,21 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
|
||||
is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0);
|
||||
|
||||
/* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
|
||||
// Clear the highlighting for ":hi clear {group}" and ":hi clear".
|
||||
if (doclear || (forceit && init)) {
|
||||
highlight_clear(idx);
|
||||
if (!doclear)
|
||||
HL_TABLE()[idx].sg_set = 0;
|
||||
}
|
||||
|
||||
char *key = NULL;
|
||||
char *arg = NULL;
|
||||
if (!doclear) {
|
||||
while (!ends_excmd(*linep)) {
|
||||
while (!ends_excmd((uint8_t)(*linep))) {
|
||||
key_start = linep;
|
||||
if (*linep == '=') {
|
||||
EMSG2(_("E415: unexpected equal sign: %s"), key_start);
|
||||
error = TRUE;
|
||||
emsgf(_("E415: unexpected equal sign: %s"), key_start);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6326,61 +6525,58 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
linep++;
|
||||
}
|
||||
xfree(key);
|
||||
key = vim_strnsave_up(key_start, (int)(linep - key_start));
|
||||
linep = skipwhite(linep);
|
||||
key = (char *)vim_strnsave_up((const char_u *)key_start,
|
||||
(int)(linep - key_start));
|
||||
linep = (const char *)skipwhite((const char_u *)linep);
|
||||
|
||||
if (STRCMP(key, "NONE") == 0) {
|
||||
if (strcmp(key, "NONE") == 0) {
|
||||
if (!init || HL_TABLE()[idx].sg_set == 0) {
|
||||
if (!init)
|
||||
if (!init) {
|
||||
HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI;
|
||||
}
|
||||
highlight_clear(idx);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for the equal sign.
|
||||
*/
|
||||
// Check for the equal sign.
|
||||
if (*linep != '=') {
|
||||
EMSG2(_("E416: missing equal sign: %s"), key_start);
|
||||
error = TRUE;
|
||||
emsgf(_("E416: missing equal sign: %s"), key_start);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
++linep;
|
||||
linep++;
|
||||
|
||||
/*
|
||||
* Isolate the argument.
|
||||
*/
|
||||
linep = skipwhite(linep);
|
||||
if (*linep == '\'') { /* guifg='color name' */
|
||||
// Isolate the argument.
|
||||
linep = (const char *)skipwhite((const char_u *)linep);
|
||||
if (*linep == '\'') { // guifg='color name'
|
||||
arg_start = ++linep;
|
||||
linep = vim_strchr(linep, '\'');
|
||||
linep = strchr(linep, '\'');
|
||||
if (linep == NULL) {
|
||||
EMSG2(_(e_invarg2), key_start);
|
||||
error = TRUE;
|
||||
emsgf(_(e_invarg2), key_start);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
arg_start = linep;
|
||||
linep = skiptowhite(linep);
|
||||
linep = (const char *)skiptowhite((const char_u *)linep);
|
||||
}
|
||||
if (linep == arg_start) {
|
||||
EMSG2(_("E417: missing argument: %s"), key_start);
|
||||
error = TRUE;
|
||||
emsgf(_("E417: missing argument: %s"), key_start);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
xfree(arg);
|
||||
arg = vim_strnsave(arg_start, (int)(linep - arg_start));
|
||||
arg = xstrndup(arg_start, (size_t)(linep - arg_start));
|
||||
|
||||
if (*linep == '\'')
|
||||
++linep;
|
||||
if (*linep == '\'') {
|
||||
linep++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the argument.
|
||||
*/
|
||||
if ( STRCMP(key, "TERM") == 0
|
||||
|| STRCMP(key, "CTERM") == 0
|
||||
|| STRCMP(key, "GUI") == 0) {
|
||||
// Store the argument.
|
||||
if (strcmp(key, "TERM") == 0
|
||||
|| strcmp(key, "CTERM") == 0
|
||||
|| strcmp(key, "GUI") == 0) {
|
||||
attr = 0;
|
||||
off = 0;
|
||||
while (arg[off] != NUL) {
|
||||
@@ -6393,26 +6589,30 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
EMSG2(_("E418: Illegal value: %s"), arg);
|
||||
error = TRUE;
|
||||
emsgf(_("E418: Illegal value: %s"), arg);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (arg[off] == ',') /* another one follows */
|
||||
++off;
|
||||
if (arg[off] == ',') { // Another one follows.
|
||||
off++;
|
||||
}
|
||||
}
|
||||
if (error)
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
if (*key == 'C') {
|
||||
if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
|
||||
if (!init)
|
||||
if (!init) {
|
||||
HL_TABLE()[idx].sg_set |= SG_CTERM;
|
||||
}
|
||||
HL_TABLE()[idx].sg_cterm = attr;
|
||||
HL_TABLE()[idx].sg_cterm_bold = FALSE;
|
||||
}
|
||||
} else if (*key == 'G') {
|
||||
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
|
||||
if (!init)
|
||||
if (!init) {
|
||||
HL_TABLE()[idx].sg_set |= SG_GUI;
|
||||
}
|
||||
HL_TABLE()[idx].sg_gui = attr;
|
||||
}
|
||||
}
|
||||
@@ -6431,14 +6631,14 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
HL_TABLE()[idx].sg_cterm_bold = FALSE;
|
||||
}
|
||||
|
||||
if (ascii_isdigit(*arg))
|
||||
if (ascii_isdigit(*arg)) {
|
||||
color = atoi((char *)arg);
|
||||
else if (STRICMP(arg, "fg") == 0) {
|
||||
if (cterm_normal_fg_color)
|
||||
} else if (STRICMP(arg, "fg") == 0) {
|
||||
if (cterm_normal_fg_color) {
|
||||
color = cterm_normal_fg_color - 1;
|
||||
else {
|
||||
} else {
|
||||
EMSG(_("E419: FG color unknown"));
|
||||
error = TRUE;
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
} else if (STRICMP(arg, "bg") == 0) {
|
||||
@@ -6446,70 +6646,84 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
color = cterm_normal_bg_color - 1;
|
||||
else {
|
||||
EMSG(_("E420: BG color unknown"));
|
||||
error = TRUE;
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
static char *(color_names[28]) = {
|
||||
static const char *color_names[] = {
|
||||
"Black", "DarkBlue", "DarkGreen", "DarkCyan",
|
||||
"DarkRed", "DarkMagenta", "Brown", "DarkYellow",
|
||||
"Gray", "Grey",
|
||||
"LightGray", "LightGrey", "DarkGray", "DarkGrey",
|
||||
"Blue", "LightBlue", "Green", "LightGreen",
|
||||
"Cyan", "LightCyan", "Red", "LightRed", "Magenta",
|
||||
"LightMagenta", "Yellow", "LightYellow", "White", "NONE"
|
||||
"LightMagenta", "Yellow", "LightYellow", "White",
|
||||
"NONE"
|
||||
};
|
||||
static const int color_numbers_16[] = {
|
||||
0, 1, 2, 3,
|
||||
4, 5, 6, 6,
|
||||
7, 7,
|
||||
7, 7, 8, 8,
|
||||
9, 9, 10, 10,
|
||||
11, 11, 12, 12, 13,
|
||||
13, 14, 14, 15,
|
||||
-1
|
||||
};
|
||||
// For xterm with 88 colors:
|
||||
static int color_numbers_88[] = {
|
||||
0, 4, 2, 6,
|
||||
1, 5, 32, 72,
|
||||
84, 84,
|
||||
7, 7, 82, 82,
|
||||
12, 43, 10, 61,
|
||||
14, 63, 9, 74, 13,
|
||||
75, 11, 78, 15,
|
||||
-1
|
||||
};
|
||||
// For xterm with 256 colors:
|
||||
static int color_numbers_256[] = {
|
||||
0, 4, 2, 6,
|
||||
1, 5, 130, 130,
|
||||
248, 248,
|
||||
7, 7, 242, 242,
|
||||
12, 81, 10, 121,
|
||||
14, 159, 9, 224, 13,
|
||||
225, 11, 229, 15,
|
||||
-1
|
||||
};
|
||||
// For terminals with less than 16 colors:
|
||||
static int color_numbers_8[28] = {
|
||||
0, 4, 2, 6,
|
||||
1, 5, 3, 3,
|
||||
7, 7,
|
||||
7, 7, 0+8, 0+8,
|
||||
4+8, 4+8, 2+8, 2+8,
|
||||
6+8, 6+8, 1+8, 1+8, 5+8,
|
||||
5+8, 3+8, 3+8, 7+8,
|
||||
-1
|
||||
};
|
||||
static int color_numbers_16[28] = {0, 1, 2, 3,
|
||||
4, 5, 6, 6,
|
||||
7, 7,
|
||||
7, 7, 8, 8,
|
||||
9, 9, 10, 10,
|
||||
11, 11, 12, 12, 13,
|
||||
13, 14, 14, 15, -1};
|
||||
/* for xterm with 88 colors... */
|
||||
static int color_numbers_88[28] = {0, 4, 2, 6,
|
||||
1, 5, 32, 72,
|
||||
84, 84,
|
||||
7, 7, 82, 82,
|
||||
12, 43, 10, 61,
|
||||
14, 63, 9, 74, 13,
|
||||
75, 11, 78, 15, -1};
|
||||
/* for xterm with 256 colors... */
|
||||
static int color_numbers_256[28] = {0, 4, 2, 6,
|
||||
1, 5, 130, 130,
|
||||
248, 248,
|
||||
7, 7, 242, 242,
|
||||
12, 81, 10, 121,
|
||||
14, 159, 9, 224, 13,
|
||||
225, 11, 229, 15, -1};
|
||||
/* for terminals with less than 16 colors... */
|
||||
static int color_numbers_8[28] = {0, 4, 2, 6,
|
||||
1, 5, 3, 3,
|
||||
7, 7,
|
||||
7, 7, 0+8, 0+8,
|
||||
4+8, 4+8, 2+8, 2+8,
|
||||
6+8, 6+8, 1+8, 1+8, 5+8,
|
||||
5+8, 3+8, 3+8, 7+8, -1};
|
||||
|
||||
/* reduce calls to STRICMP a bit, it can be slow */
|
||||
// Reduce calls to STRICMP a bit, it can be slow.
|
||||
off = TOUPPER_ASC(*arg);
|
||||
for (i = ARRAY_SIZE(color_names); --i >= 0; )
|
||||
for (i = ARRAY_SIZE(color_names); --i >= 0; ) {
|
||||
if (off == color_names[i][0]
|
||||
&& STRICMP(arg + 1, color_names[i] + 1) == 0)
|
||||
&& STRICMP(arg + 1, color_names[i] + 1) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
EMSG2(_(
|
||||
"E421: Color name or number not recognized: %s"),
|
||||
key_start);
|
||||
error = TRUE;
|
||||
emsgf(_("E421: Color name or number not recognized: %s"),
|
||||
key_start);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the _16 table to check if its a valid color name. */
|
||||
// Use the _16 table to check if its a valid color name.
|
||||
color = color_numbers_16[i];
|
||||
if (color >= 0) {
|
||||
if (t_colors == 8) {
|
||||
/* t_Co is 8: use the 8 colors table */
|
||||
// t_Co is 8: use the 8 colors table.
|
||||
color = color_numbers_8[i];
|
||||
if (key[5] == 'F') {
|
||||
/* set/reset bold attribute to get light foreground
|
||||
@@ -6533,16 +6747,14 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add one to the argument, to avoid zero. Zero is used for
|
||||
* "NONE", then "color" is -1. */
|
||||
// Add one to the argument, to avoid zero. Zero is used for
|
||||
// "NONE", then "color" is -1.
|
||||
if (key[5] == 'F') {
|
||||
HL_TABLE()[idx].sg_cterm_fg = color + 1;
|
||||
if (is_normal_group) {
|
||||
cterm_normal_fg_color = color + 1;
|
||||
cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
|
||||
{
|
||||
must_redraw = CLEAR;
|
||||
}
|
||||
must_redraw = CLEAR;
|
||||
}
|
||||
} else {
|
||||
HL_TABLE()[idx].sg_cterm_bg = color + 1;
|
||||
@@ -6566,15 +6778,15 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (STRCMP(key, "GUIFG") == 0) {
|
||||
} else if (strcmp(key, "GUIFG") == 0) {
|
||||
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
|
||||
if (!init)
|
||||
HL_TABLE()[idx].sg_set |= SG_GUI;
|
||||
|
||||
xfree(HL_TABLE()[idx].sg_rgb_fg_name);
|
||||
if (STRCMP(arg, "NONE")) {
|
||||
HL_TABLE()[idx].sg_rgb_fg_name = (uint8_t *)xstrdup((char *)arg);
|
||||
HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg);
|
||||
if (strcmp(arg, "NONE")) {
|
||||
HL_TABLE()[idx].sg_rgb_fg_name = (char_u *)xstrdup((char *)arg);
|
||||
HL_TABLE()[idx].sg_rgb_fg = name_to_color((const char_u *)arg);
|
||||
} else {
|
||||
HL_TABLE()[idx].sg_rgb_fg_name = NULL;
|
||||
HL_TABLE()[idx].sg_rgb_fg = -1;
|
||||
@@ -6591,8 +6803,8 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
|
||||
xfree(HL_TABLE()[idx].sg_rgb_bg_name);
|
||||
if (STRCMP(arg, "NONE") != 0) {
|
||||
HL_TABLE()[idx].sg_rgb_bg_name = (uint8_t *)xstrdup((char *)arg);
|
||||
HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg);
|
||||
HL_TABLE()[idx].sg_rgb_bg_name = (char_u *)xstrdup((char *)arg);
|
||||
HL_TABLE()[idx].sg_rgb_bg = name_to_color((const char_u *)arg);
|
||||
} else {
|
||||
HL_TABLE()[idx].sg_rgb_bg_name = NULL;
|
||||
HL_TABLE()[idx].sg_rgb_bg = -1;
|
||||
@@ -6602,15 +6814,15 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
if (is_normal_group) {
|
||||
normal_bg = HL_TABLE()[idx].sg_rgb_bg;
|
||||
}
|
||||
} else if (STRCMP(key, "GUISP") == 0) {
|
||||
} else if (strcmp(key, "GUISP") == 0) {
|
||||
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
|
||||
if (!init)
|
||||
HL_TABLE()[idx].sg_set |= SG_GUI;
|
||||
|
||||
xfree(HL_TABLE()[idx].sg_rgb_sp_name);
|
||||
if (STRCMP(arg, "NONE") != 0) {
|
||||
HL_TABLE()[idx].sg_rgb_sp_name = (uint8_t *)xstrdup((char *)arg);
|
||||
HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg);
|
||||
if (strcmp(arg, "NONE") != 0) {
|
||||
HL_TABLE()[idx].sg_rgb_sp_name = (char_u *)xstrdup((char *)arg);
|
||||
HL_TABLE()[idx].sg_rgb_sp = name_to_color((const char_u *)arg);
|
||||
} else {
|
||||
HL_TABLE()[idx].sg_rgb_sp_name = NULL;
|
||||
HL_TABLE()[idx].sg_rgb_sp = -1;
|
||||
@@ -6620,31 +6832,25 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
if (is_normal_group) {
|
||||
normal_sp = HL_TABLE()[idx].sg_rgb_sp;
|
||||
}
|
||||
} else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) {
|
||||
} else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) {
|
||||
// Ignored for now
|
||||
} else {
|
||||
EMSG2(_("E423: Illegal argument: %s"), key_start);
|
||||
error = TRUE;
|
||||
emsgf(_("E423: Illegal argument: %s"), key_start);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* When highlighting has been given for a group, don't link it.
|
||||
*/
|
||||
// When highlighting has been given for a group, don't link it.
|
||||
if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) {
|
||||
HL_TABLE()[idx].sg_link = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Continue with next argument.
|
||||
*/
|
||||
linep = skipwhite(linep);
|
||||
// Continue with next argument.
|
||||
linep = (const char *)skipwhite((const char_u *)linep);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is an error, and it's a new entry, remove it from the table.
|
||||
*/
|
||||
// If there is an error, and it's a new entry, remove it from the table.
|
||||
if (error && idx == highlight_ga.ga_len) {
|
||||
syn_unadd_group();
|
||||
} else {
|
||||
@@ -7202,7 +7408,7 @@ char_u *syn_id2name(int id)
|
||||
/*
|
||||
* Like syn_name2id(), but take a pointer + length argument.
|
||||
*/
|
||||
int syn_namen2id(char_u *linep, int len)
|
||||
int syn_namen2id(const char_u *linep, int len)
|
||||
{
|
||||
char_u *name = vim_strnsave(linep, len);
|
||||
int id = syn_name2id(name);
|
||||
@@ -7218,7 +7424,7 @@ int syn_namen2id(char_u *linep, int len)
|
||||
/// @param len length of \p pp
|
||||
///
|
||||
/// @return 0 for failure else the id of the group
|
||||
int syn_check_group(char_u *pp, int len)
|
||||
int syn_check_group(const char_u *pp, int len)
|
||||
{
|
||||
char_u *name = vim_strnsave(pp, len);
|
||||
int id = syn_name2id(name);
|
||||
@@ -8221,7 +8427,7 @@ color_name_table_T color_name_table[] = {
|
||||
///
|
||||
/// @param[in] name string value to convert to RGB
|
||||
/// return the hex value or -1 if could not find a correct value
|
||||
RgbValue name_to_color(const uint8_t *name)
|
||||
RgbValue name_to_color(const char_u *name)
|
||||
{
|
||||
|
||||
if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2])
|
||||
|
@@ -45,6 +45,9 @@ typedef struct {
|
||||
} color_name_table_T;
|
||||
extern color_name_table_T color_name_table[];
|
||||
|
||||
/// Array of highlight definitions, used for unit testing
|
||||
extern const char *const highlight_init_cmdline[];
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "syntax.h.generated.h"
|
||||
#endif
|
||||
|
@@ -30,8 +30,8 @@
|
||||
// for ":version", ":intro", and "nvim --version"
|
||||
#ifndef NVIM_VERSION_MEDIUM
|
||||
#define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR)\
|
||||
"." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\
|
||||
NVIM_VERSION_PRERELEASE
|
||||
"." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\
|
||||
NVIM_VERSION_PRERELEASE
|
||||
#endif
|
||||
#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM
|
||||
|
||||
@@ -47,33 +47,33 @@ char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS;
|
||||
|
||||
static char *features[] = {
|
||||
#ifdef HAVE_ACL
|
||||
"+acl",
|
||||
"+acl",
|
||||
#else
|
||||
"-acl",
|
||||
"-acl",
|
||||
#endif
|
||||
|
||||
#if (defined(HAVE_ICONV_H) && defined(USE_ICONV)) || defined(DYNAMIC_ICONV)
|
||||
# ifdef DYNAMIC_ICONV
|
||||
"+iconv/dyn",
|
||||
"+iconv/dyn",
|
||||
# else
|
||||
"+iconv",
|
||||
"+iconv",
|
||||
# endif
|
||||
#else
|
||||
"-iconv",
|
||||
"-iconv",
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_JEMALLOC
|
||||
"+jemalloc",
|
||||
"+jemalloc",
|
||||
#else
|
||||
"-jemalloc",
|
||||
"-jemalloc",
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_TUI
|
||||
"+tui",
|
||||
"+tui",
|
||||
#else
|
||||
"-tui",
|
||||
"-tui",
|
||||
#endif
|
||||
NULL
|
||||
NULL
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
@@ -205,8 +205,8 @@ static const int included_patches[] = {
|
||||
// 1233,
|
||||
// 1232,
|
||||
// 1231,
|
||||
// 1230,
|
||||
// 1229,
|
||||
1230,
|
||||
1229,
|
||||
// 1228,
|
||||
// 1227,
|
||||
// 1226,
|
||||
|
@@ -26,13 +26,6 @@
|
||||
/// length of a buffer to store a number in ASCII (64 bits binary + NUL)
|
||||
enum { NUMBUFLEN = 65 };
|
||||
|
||||
// flags for vim_str2nr()
|
||||
#define STR2NR_BIN 1
|
||||
#define STR2NR_OCT 2
|
||||
#define STR2NR_HEX 4
|
||||
#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX)
|
||||
#define STR2NR_FORCE 8 // only when ONE of the above is used
|
||||
|
||||
#define MAX_TYPENR 65535
|
||||
|
||||
#define ROOT_UID 0
|
||||
|
3102
src/nvim/viml/parser/expressions.c
Normal file
3102
src/nvim/viml/parser/expressions.c
Normal file
File diff suppressed because it is too large
Load Diff
389
src/nvim/viml/parser/expressions.h
Normal file
389
src/nvim/viml/parser/expressions.h
Normal file
@@ -0,0 +1,389 @@
|
||||
#ifndef NVIM_VIML_PARSER_EXPRESSIONS_H
|
||||
#define NVIM_VIML_PARSER_EXPRESSIONS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nvim/types.h"
|
||||
#include "nvim/viml/parser/parser.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
|
||||
// Defines whether to ignore case:
|
||||
// == kCCStrategyUseOption
|
||||
// ==# kCCStrategyMatchCase
|
||||
// ==? kCCStrategyIgnoreCase
|
||||
typedef enum {
|
||||
kCCStrategyUseOption = 0, // 0 for xcalloc
|
||||
kCCStrategyMatchCase = '#',
|
||||
kCCStrategyIgnoreCase = '?',
|
||||
} ExprCaseCompareStrategy;
|
||||
|
||||
/// Lexer token type
|
||||
typedef enum {
|
||||
kExprLexInvalid = 0, ///< Invalid token, indicaten an error.
|
||||
kExprLexMissing, ///< Missing token, for use in parser.
|
||||
kExprLexSpacing, ///< Spaces, tabs, newlines, etc.
|
||||
kExprLexEOC, ///< End of command character: NL, |, just end of stream.
|
||||
|
||||
kExprLexQuestion, ///< Question mark, for use in ternary.
|
||||
kExprLexColon, ///< Colon, for use in ternary.
|
||||
kExprLexOr, ///< Logical or operator.
|
||||
kExprLexAnd, ///< Logical and operator.
|
||||
kExprLexComparison, ///< One of the comparison operators.
|
||||
kExprLexPlus, ///< Plus sign.
|
||||
kExprLexMinus, ///< Minus sign.
|
||||
kExprLexDot, ///< Dot: either concat or subscript, also part of the float.
|
||||
kExprLexMultiplication, ///< Multiplication, division or modulo operator.
|
||||
|
||||
kExprLexNot, ///< Not: !.
|
||||
|
||||
kExprLexNumber, ///< Integer number literal, or part of a float.
|
||||
kExprLexSingleQuotedString, ///< Single quoted string literal.
|
||||
kExprLexDoubleQuotedString, ///< Double quoted string literal.
|
||||
kExprLexOption, ///< &optionname option value.
|
||||
kExprLexRegister, ///< @r register value.
|
||||
kExprLexEnv, ///< Environment $variable value.
|
||||
kExprLexPlainIdentifier, ///< Identifier without scope: `abc`, `foo#bar`.
|
||||
|
||||
kExprLexBracket, ///< Bracket, either opening or closing.
|
||||
kExprLexFigureBrace, ///< Figure brace, either opening or closing.
|
||||
kExprLexParenthesis, ///< Parenthesis, either opening or closing.
|
||||
kExprLexComma, ///< Comma.
|
||||
kExprLexArrow, ///< Arrow, like from lambda expressions.
|
||||
kExprLexAssignment, ///< Assignment: `=` or `{op}=`.
|
||||
// XXX When modifying this enum you need to also modify eltkn_type_tab in
|
||||
// expressions.c and tests and, possibly, viml_pexpr_repr_token.
|
||||
} LexExprTokenType;
|
||||
|
||||
typedef enum {
|
||||
kExprCmpEqual, ///< Equality, unequality.
|
||||
kExprCmpMatches, ///< Matches regex, not matches regex.
|
||||
kExprCmpGreater, ///< `>` or `<=`
|
||||
kExprCmpGreaterOrEqual, ///< `>=` or `<`.
|
||||
kExprCmpIdentical, ///< `is` or `isnot`
|
||||
} ExprComparisonType;
|
||||
|
||||
/// All possible option scopes
|
||||
typedef enum {
|
||||
kExprOptScopeUnspecified = 0,
|
||||
kExprOptScopeGlobal = 'g',
|
||||
kExprOptScopeLocal = 'l',
|
||||
} ExprOptScope;
|
||||
|
||||
/// All possible assignment types: `=` and `{op}=`.
|
||||
typedef enum {
|
||||
kExprAsgnPlain = 0, ///< Plain assignment: `=`.
|
||||
kExprAsgnAdd, ///< Assignment augmented with addition: `+=`.
|
||||
kExprAsgnSubtract, ///< Assignment augmented with subtraction: `-=`.
|
||||
kExprAsgnConcat, ///< Assignment augmented with concatenation: `.=`.
|
||||
} ExprAssignmentType;
|
||||
|
||||
#define EXPR_OPT_SCOPE_LIST \
|
||||
((char[]){ kExprOptScopeGlobal, kExprOptScopeLocal })
|
||||
|
||||
/// All possible variable scopes
|
||||
typedef enum {
|
||||
kExprVarScopeMissing = 0,
|
||||
kExprVarScopeScript = 's',
|
||||
kExprVarScopeGlobal = 'g',
|
||||
kExprVarScopeVim = 'v',
|
||||
kExprVarScopeBuffer = 'b',
|
||||
kExprVarScopeWindow = 'w',
|
||||
kExprVarScopeTabpage = 't',
|
||||
kExprVarScopeLocal = 'l',
|
||||
kExprVarScopeArguments = 'a',
|
||||
} ExprVarScope;
|
||||
|
||||
#define EXPR_VAR_SCOPE_LIST \
|
||||
((char[]) { \
|
||||
kExprVarScopeScript, kExprVarScopeGlobal, kExprVarScopeVim, \
|
||||
kExprVarScopeBuffer, kExprVarScopeWindow, kExprVarScopeTabpage, \
|
||||
kExprVarScopeLocal, kExprVarScopeBuffer, kExprVarScopeArguments, \
|
||||
})
|
||||
|
||||
/// Lexer token
|
||||
typedef struct {
|
||||
ParserPosition start;
|
||||
size_t len;
|
||||
LexExprTokenType type;
|
||||
union {
|
||||
struct {
|
||||
ExprComparisonType type; ///< Comparison type.
|
||||
ExprCaseCompareStrategy ccs; ///< Case comparison strategy.
|
||||
bool inv; ///< True if comparison is to be inverted.
|
||||
} cmp; ///< For kExprLexComparison.
|
||||
|
||||
struct {
|
||||
enum {
|
||||
kExprLexMulMul, ///< Real multiplication.
|
||||
kExprLexMulDiv, ///< Division.
|
||||
kExprLexMulMod, ///< Modulo.
|
||||
} type; ///< Multiplication type.
|
||||
} mul; ///< For kExprLexMultiplication.
|
||||
|
||||
struct {
|
||||
bool closing; ///< True if bracket/etc is a closing one.
|
||||
} brc; ///< For brackets/braces/parenthesis.
|
||||
|
||||
struct {
|
||||
int name; ///< Register name, may be -1 if name not present.
|
||||
} reg; ///< For kExprLexRegister.
|
||||
|
||||
struct {
|
||||
bool closed; ///< True if quote was closed.
|
||||
} str; ///< For kExprLexSingleQuotedString and kExprLexDoubleQuotedString.
|
||||
|
||||
struct {
|
||||
const char *name; ///< Option name start.
|
||||
size_t len; ///< Option name length.
|
||||
ExprOptScope scope; ///< Option scope: &l:, &g: or not specified.
|
||||
} opt; ///< Option properties.
|
||||
|
||||
struct {
|
||||
ExprVarScope scope; ///< Scope character or 0 if not present.
|
||||
bool autoload; ///< Has autoload characters.
|
||||
} var; ///< For kExprLexPlainIdentifier
|
||||
|
||||
struct {
|
||||
LexExprTokenType type; ///< Suggested type for parsing incorrect code.
|
||||
const char *msg; ///< Error message.
|
||||
} err; ///< For kExprLexInvalid
|
||||
|
||||
struct {
|
||||
union {
|
||||
float_T floating;
|
||||
uvarnumber_T integer;
|
||||
} val; ///< Number value.
|
||||
uint8_t base; ///< Base: 2, 8, 10 or 16.
|
||||
bool is_float; ///< True if number is a floating-point.
|
||||
} num; ///< For kExprLexNumber
|
||||
|
||||
struct {
|
||||
ExprAssignmentType type;
|
||||
} ass; ///< For kExprLexAssignment
|
||||
} data; ///< Additional data, if needed.
|
||||
} LexExprToken;
|
||||
|
||||
typedef enum {
|
||||
/// If set, “pointer” to the current byte in pstate will not be shifted
|
||||
kELFlagPeek = (1 << 0),
|
||||
/// Determines whether scope is allowed to come before the identifier
|
||||
kELFlagForbidScope = (1 << 1),
|
||||
/// Determines whether floating-point numbers are allowed
|
||||
///
|
||||
/// I.e. whether dot is a decimal point separator or is not a part of
|
||||
/// a number at all.
|
||||
kELFlagAllowFloat = (1 << 2),
|
||||
/// Determines whether `is` and `isnot` are seen as comparison operators
|
||||
///
|
||||
/// If set they are supposed to be just regular identifiers.
|
||||
kELFlagIsNotCmp = (1 << 3),
|
||||
/// Determines whether EOC tokens are allowed
|
||||
///
|
||||
/// If set then it will yield Invalid token with E15 in place of EOC one if
|
||||
/// “EOC” is something like "|". It is fine with emitting EOC at the end of
|
||||
/// string still, with or without this flag set.
|
||||
kELFlagForbidEOC = (1 << 4),
|
||||
// XXX Whenever you add a new flag, alter klee_assume() statement in
|
||||
// viml_expressions_lexer.c.
|
||||
} LexExprFlags;
|
||||
|
||||
/// Expression AST node type
|
||||
typedef enum {
|
||||
kExprNodeMissing = 0,
|
||||
kExprNodeOpMissing,
|
||||
kExprNodeTernary, ///< Ternary operator.
|
||||
kExprNodeTernaryValue, ///< Ternary operator, colon.
|
||||
kExprNodeRegister, ///< Register.
|
||||
kExprNodeSubscript, ///< Subscript.
|
||||
kExprNodeListLiteral, ///< List literal.
|
||||
kExprNodeUnaryPlus,
|
||||
kExprNodeBinaryPlus,
|
||||
kExprNodeNested, ///< Nested parenthesised expression.
|
||||
kExprNodeCall, ///< Function call.
|
||||
/// Plain identifier: simple variable/function name
|
||||
///
|
||||
/// Looks like "string", "g:Foo", etc: consists from a single
|
||||
/// kExprLexPlainIdentifier token.
|
||||
kExprNodePlainIdentifier,
|
||||
/// Plain dictionary key, for use with kExprNodeConcatOrSubscript
|
||||
kExprNodePlainKey,
|
||||
/// Complex identifier: variable/function name with curly braces
|
||||
kExprNodeComplexIdentifier,
|
||||
/// Figure brace expression which is not yet known
|
||||
///
|
||||
/// May resolve to any of kExprNodeDictLiteral, kExprNodeLambda or
|
||||
/// kExprNodeCurlyBracesIdentifier.
|
||||
kExprNodeUnknownFigure,
|
||||
kExprNodeLambda, ///< Lambda.
|
||||
kExprNodeDictLiteral, ///< Dictionary literal.
|
||||
kExprNodeCurlyBracesIdentifier, ///< Part of the curly braces name.
|
||||
kExprNodeComma, ///< Comma “operator”.
|
||||
kExprNodeColon, ///< Colon “operator”.
|
||||
kExprNodeArrow, ///< Arrow “operator”.
|
||||
kExprNodeComparison, ///< Various comparison operators.
|
||||
/// Concat operator
|
||||
///
|
||||
/// To be only used in cases when it is known for sure it is not a subscript.
|
||||
kExprNodeConcat,
|
||||
/// Concat or subscript operator
|
||||
///
|
||||
/// For cases when it is not obvious whether expression is a concat or
|
||||
/// a subscript. May only have either number or plain identifier as the second
|
||||
/// child. To make it easier to avoid curly braces in place of
|
||||
/// kExprNodePlainIdentifier node kExprNodePlainKey is used.
|
||||
kExprNodeConcatOrSubscript,
|
||||
kExprNodeInteger, ///< Integral number.
|
||||
kExprNodeFloat, ///< Floating-point number.
|
||||
kExprNodeSingleQuotedString,
|
||||
kExprNodeDoubleQuotedString,
|
||||
kExprNodeOr,
|
||||
kExprNodeAnd,
|
||||
kExprNodeUnaryMinus,
|
||||
kExprNodeBinaryMinus,
|
||||
kExprNodeNot,
|
||||
kExprNodeMultiplication,
|
||||
kExprNodeDivision,
|
||||
kExprNodeMod,
|
||||
kExprNodeOption,
|
||||
kExprNodeEnvironment,
|
||||
kExprNodeAssignment,
|
||||
// XXX When modifying this list also modify east_node_type_tab both in parser
|
||||
// and in tests, and you most likely will also have to alter list of
|
||||
// highlight groups stored in highlight_init_cmdline variable.
|
||||
} ExprASTNodeType;
|
||||
|
||||
typedef struct expr_ast_node ExprASTNode;
|
||||
|
||||
/// Structure representing one AST node
|
||||
struct expr_ast_node {
|
||||
ExprASTNodeType type; ///< Node type.
|
||||
/// Node children: e.g. for 1 + 2 nodes 1 and 2 will be children of +.
|
||||
ExprASTNode *children;
|
||||
/// Next node: e.g. for 1 + 2 child nodes 1 and 2 are put into a single-linked
|
||||
/// list: `(+)->children` references only node 1, node 2 is in
|
||||
/// `(+)->children->next`.
|
||||
ExprASTNode *next;
|
||||
ParserPosition start;
|
||||
size_t len;
|
||||
union {
|
||||
struct {
|
||||
int name; ///< Register name, may be -1 if name not present.
|
||||
} reg; ///< For kExprNodeRegister.
|
||||
struct {
|
||||
/// Which nodes UnknownFigure can’t possibly represent.
|
||||
struct {
|
||||
/// True if UnknownFigure may actually represent dictionary literal.
|
||||
bool allow_dict;
|
||||
/// True if UnknownFigure may actually represent lambda.
|
||||
bool allow_lambda;
|
||||
/// True if UnknownFigure may actually be part of curly braces name.
|
||||
bool allow_ident;
|
||||
} type_guesses;
|
||||
/// Highlight chunk index, used for rehighlighting if needed
|
||||
size_t opening_hl_idx;
|
||||
} fig; ///< For kExprNodeUnknownFigure.
|
||||
struct {
|
||||
ExprVarScope scope; ///< Scope character or 0 if not present.
|
||||
/// Actual identifier without scope.
|
||||
///
|
||||
/// Points to inside parser reader state.
|
||||
const char *ident;
|
||||
size_t ident_len; ///< Actual identifier length.
|
||||
} var; ///< For kExprNodePlainIdentifier and kExprNodePlainKey.
|
||||
struct {
|
||||
bool got_colon; ///< True if colon was seen.
|
||||
} ter; ///< For kExprNodeTernaryValue.
|
||||
struct {
|
||||
ExprComparisonType type; ///< Comparison type.
|
||||
ExprCaseCompareStrategy ccs; ///< Case comparison strategy.
|
||||
bool inv; ///< True if comparison is to be inverted.
|
||||
} cmp; ///< For kExprNodeComparison.
|
||||
struct {
|
||||
uvarnumber_T value;
|
||||
} num; ///< For kExprNodeInteger.
|
||||
struct {
|
||||
float_T value;
|
||||
} flt; ///< For kExprNodeFloat.
|
||||
struct {
|
||||
char *value;
|
||||
size_t size;
|
||||
} str; ///< For kExprNodeSingleQuotedString and
|
||||
///< kExprNodeDoubleQuotedString.
|
||||
struct {
|
||||
const char *ident; ///< Option name start.
|
||||
size_t ident_len; ///< Option name length.
|
||||
ExprOptScope scope; ///< Option scope: &l:, &g: or not specified.
|
||||
} opt; ///< For kExprNodeOption.
|
||||
struct {
|
||||
const char *ident; ///< Environment variable name start.
|
||||
size_t ident_len; ///< Environment variable name length.
|
||||
} env; ///< For kExprNodeEnvironment.
|
||||
struct {
|
||||
ExprAssignmentType type;
|
||||
} ass; ///< For kExprNodeAssignment
|
||||
} data;
|
||||
};
|
||||
|
||||
enum {
|
||||
/// Allow multiple expressions in a row: e.g. for :echo
|
||||
///
|
||||
/// Parser will still parse only one of them though.
|
||||
kExprFlagsMulti = (1 << 0),
|
||||
/// Allow NL, NUL and bar to be EOC
|
||||
///
|
||||
/// When parsing expressions input by user bar is assumed to be a binary
|
||||
/// operator and other two are spacings.
|
||||
kExprFlagsDisallowEOC = (1 << 1),
|
||||
/// Parse :let argument
|
||||
///
|
||||
/// That mean that top level node must be an assignment and first nodes
|
||||
/// belong to lvalues.
|
||||
kExprFlagsParseLet = (1 << 2),
|
||||
// XXX whenever you add a new flag, alter klee_assume() statement in
|
||||
// viml_expressions_parser.c, nvim_parse_expression() flags parsing
|
||||
// alongside with its documentation and flag sets in check_parsing()
|
||||
// function in expressions parser functional and unit tests.
|
||||
} ExprParserFlags;
|
||||
|
||||
/// AST error definition
|
||||
typedef struct {
|
||||
/// Error message. Must contain a single printf format atom: %.*s.
|
||||
const char *msg;
|
||||
/// Error message argument: points to the location of the error.
|
||||
const char *arg;
|
||||
/// Message argument length: length till the end of string.
|
||||
int arg_len;
|
||||
} ExprASTError;
|
||||
|
||||
/// Structure representing complety AST for one expression
|
||||
typedef struct {
|
||||
/// When AST is not correct this message will be printed.
|
||||
///
|
||||
/// Uses `emsgf(msg, arg_len, arg);`, `msg` is assumed to contain only `%.*s`.
|
||||
ExprASTError err;
|
||||
/// Root node of the AST.
|
||||
ExprASTNode *root;
|
||||
} ExprAST;
|
||||
|
||||
/// Array mapping ExprASTNodeType to maximum amount of children node may have
|
||||
extern const uint8_t node_maxchildren[];
|
||||
|
||||
/// Array mapping ExprASTNodeType values to their stringified versions
|
||||
extern const char *const east_node_type_tab[];
|
||||
|
||||
/// Array mapping ExprComparisonType values to their stringified versions
|
||||
extern const char *const eltkn_cmp_type_tab[];
|
||||
|
||||
/// Array mapping ExprCaseCompareStrategy values to their stringified versions
|
||||
extern const char *const ccs_tab[];
|
||||
|
||||
/// Array mapping ExprAssignmentType values to their stringified versions
|
||||
extern const char *const expr_asgn_type_tab[];
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "viml/parser/expressions.h.generated.h"
|
||||
#endif
|
||||
|
||||
#endif // NVIM_VIML_PARSER_EXPRESSIONS_H
|
13
src/nvim/viml/parser/parser.c
Normal file
13
src/nvim/viml/parser/parser.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "nvim/viml/parser/parser.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "viml/parser/parser.c.generated.h"
|
||||
#endif
|
||||
|
||||
|
||||
void parser_simple_get_line(void *cookie, ParserLine *ret_pline)
|
||||
{
|
||||
ParserLine **plines_p = (ParserLine **)cookie;
|
||||
*ret_pline = **plines_p;
|
||||
(*plines_p)++;
|
||||
}
|
244
src/nvim/viml/parser/parser.h
Normal file
244
src/nvim/viml/parser/parser.h
Normal file
@@ -0,0 +1,244 @@
|
||||
#ifndef NVIM_VIML_PARSER_PARSER_H
|
||||
#define NVIM_VIML_PARSER_PARSER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memory.h"
|
||||
|
||||
/// One parsed line
|
||||
typedef struct {
|
||||
const char *data; ///< Parsed line pointer
|
||||
size_t size; ///< Parsed line size
|
||||
bool allocated; ///< True if line may be freed.
|
||||
} ParserLine;
|
||||
|
||||
/// Line getter type for parser
|
||||
///
|
||||
/// Line getter must return {NULL, 0} for EOF.
|
||||
typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline);
|
||||
|
||||
/// Parser position in the input
|
||||
typedef struct {
|
||||
size_t line; ///< Line index in ParserInputReader.lines.
|
||||
size_t col; ///< Byte index in the line.
|
||||
} ParserPosition;
|
||||
|
||||
/// Parser state item.
|
||||
typedef struct {
|
||||
enum {
|
||||
kPTopStateParsingCommand = 0,
|
||||
kPTopStateParsingExpression,
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
enum {
|
||||
kExprUnknown = 0,
|
||||
} type;
|
||||
} expr;
|
||||
} data;
|
||||
} ParserStateItem;
|
||||
|
||||
/// Structure defining input reader
|
||||
typedef struct {
|
||||
/// Function used to get next line.
|
||||
ParserLineGetter get_line;
|
||||
/// Data for get_line function.
|
||||
void *cookie;
|
||||
/// All lines obtained by get_line.
|
||||
kvec_withinit_t(ParserLine, 4) lines;
|
||||
/// Conversion, for :scriptencoding.
|
||||
vimconv_T conv;
|
||||
} ParserInputReader;
|
||||
|
||||
/// Highlighted region definition
|
||||
///
|
||||
/// Note: one chunk may highlight only one line.
|
||||
typedef struct {
|
||||
ParserPosition start; ///< Start of the highlight: line and column.
|
||||
size_t end_col; ///< End column, points to the start of the next character.
|
||||
const char *group; ///< Highlight group.
|
||||
} ParserHighlightChunk;
|
||||
|
||||
/// Highlighting defined by a parser
|
||||
typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight;
|
||||
|
||||
/// Structure defining parser state
|
||||
typedef struct {
|
||||
/// Line reader.
|
||||
ParserInputReader reader;
|
||||
/// Position up to which input was parsed.
|
||||
ParserPosition pos;
|
||||
/// Parser state stack.
|
||||
kvec_withinit_t(ParserStateItem, 16) stack;
|
||||
/// Highlighting support.
|
||||
ParserHighlight *colors;
|
||||
/// True if line continuation can be used.
|
||||
bool can_continuate;
|
||||
} ParserState;
|
||||
|
||||
static inline void viml_parser_init(
|
||||
ParserState *const ret_pstate,
|
||||
const ParserLineGetter get_line, void *const cookie,
|
||||
ParserHighlight *const colors)
|
||||
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2);
|
||||
|
||||
/// Initialize a new parser state instance
|
||||
///
|
||||
/// @param[out] ret_pstate Parser state to initialize.
|
||||
/// @param[in] get_line Line getter function.
|
||||
/// @param[in] cookie Argument for the get_line function.
|
||||
/// @param[in] colors Where to save highlighting. May be NULL if it is not
|
||||
/// needed.
|
||||
static inline void viml_parser_init(
|
||||
ParserState *const ret_pstate,
|
||||
const ParserLineGetter get_line, void *const cookie,
|
||||
ParserHighlight *const colors)
|
||||
{
|
||||
*ret_pstate = (ParserState) {
|
||||
.reader = {
|
||||
.get_line = get_line,
|
||||
.cookie = cookie,
|
||||
.conv = MBYTE_NONE_CONV,
|
||||
},
|
||||
.pos = { 0, 0 },
|
||||
.colors = colors,
|
||||
.can_continuate = false,
|
||||
};
|
||||
kvi_init(ret_pstate->reader.lines);
|
||||
kvi_init(ret_pstate->stack);
|
||||
}
|
||||
|
||||
static inline void viml_parser_destroy(ParserState *const pstate)
|
||||
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
|
||||
|
||||
/// Free all memory allocated by the parser on heap
|
||||
///
|
||||
/// @param pstate Parser state to free.
|
||||
static inline void viml_parser_destroy(ParserState *const pstate)
|
||||
{
|
||||
for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) {
|
||||
ParserLine pline = kv_A(pstate->reader.lines, i);
|
||||
if (pline.allocated) {
|
||||
xfree((void *)pline.data);
|
||||
}
|
||||
}
|
||||
kvi_destroy(pstate->reader.lines);
|
||||
kvi_destroy(pstate->stack);
|
||||
}
|
||||
|
||||
static inline void viml_preader_get_line(ParserInputReader *const preader,
|
||||
ParserLine *const ret_pline)
|
||||
REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Get one line from ParserInputReader
|
||||
static inline void viml_preader_get_line(ParserInputReader *const preader,
|
||||
ParserLine *const ret_pline)
|
||||
{
|
||||
ParserLine pline;
|
||||
preader->get_line(preader->cookie, &pline);
|
||||
if (preader->conv.vc_type != CONV_NONE && pline.size) {
|
||||
ParserLine cpline = {
|
||||
.allocated = true,
|
||||
.size = pline.size,
|
||||
};
|
||||
cpline.data = (char *)string_convert(&preader->conv,
|
||||
(char_u *)pline.data,
|
||||
&cpline.size);
|
||||
if (pline.allocated) {
|
||||
xfree((void *)pline.data);
|
||||
}
|
||||
pline = cpline;
|
||||
}
|
||||
kvi_push(preader->lines, pline);
|
||||
*ret_pline = pline;
|
||||
}
|
||||
|
||||
static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
|
||||
ParserLine *const ret_pline)
|
||||
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Get currently parsed line, shifted to pstate->pos.col
|
||||
///
|
||||
/// @param pstate Parser state to operate on.
|
||||
///
|
||||
/// @return True if there is a line, false in case of EOF.
|
||||
static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
|
||||
ParserLine *const ret_pline)
|
||||
{
|
||||
const size_t num_lines = kv_size(pstate->reader.lines);
|
||||
if (pstate->pos.line == num_lines) {
|
||||
viml_preader_get_line(&pstate->reader, ret_pline);
|
||||
} else {
|
||||
*ret_pline = kv_last(pstate->reader.lines);
|
||||
}
|
||||
assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1);
|
||||
if (ret_pline->data != NULL) {
|
||||
ret_pline->data += pstate->pos.col;
|
||||
ret_pline->size -= pstate->pos.col;
|
||||
}
|
||||
return ret_pline->data != NULL;
|
||||
}
|
||||
|
||||
static inline void viml_parser_advance(ParserState *const pstate,
|
||||
const size_t len)
|
||||
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Advance position by a given number of bytes
|
||||
///
|
||||
/// At maximum advances to the next line.
|
||||
///
|
||||
/// @param pstate Parser state to advance.
|
||||
/// @param[in] len Number of bytes to advance.
|
||||
static inline void viml_parser_advance(ParserState *const pstate,
|
||||
const size_t len)
|
||||
{
|
||||
assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1);
|
||||
const ParserLine pline = kv_last(pstate->reader.lines);
|
||||
if (pstate->pos.col + len >= pline.size) {
|
||||
pstate->pos.line++;
|
||||
pstate->pos.col = 0;
|
||||
} else {
|
||||
pstate->pos.col += len;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void viml_parser_highlight(ParserState *const pstate,
|
||||
const ParserPosition start,
|
||||
const size_t end_col,
|
||||
const char *const group)
|
||||
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Record highlighting of some region of text
|
||||
///
|
||||
/// @param pstate Parser state to work with.
|
||||
/// @param[in] start Start position of the highlight.
|
||||
/// @param[in] len Highlighting chunk length.
|
||||
/// @param[in] group Highlight group.
|
||||
static inline void viml_parser_highlight(ParserState *const pstate,
|
||||
const ParserPosition start,
|
||||
const size_t len,
|
||||
const char *const group)
|
||||
{
|
||||
if (pstate->colors == NULL || len == 0) {
|
||||
return;
|
||||
}
|
||||
assert(kv_size(*pstate->colors) == 0
|
||||
|| kv_Z(*pstate->colors, 0).start.line < start.line
|
||||
|| kv_Z(*pstate->colors, 0).end_col <= start.col);
|
||||
kvi_push(*pstate->colors, ((ParserHighlightChunk) {
|
||||
.start = start,
|
||||
.end_col = start.col + len,
|
||||
.group = group,
|
||||
}));
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "viml/parser/parser.h.generated.h"
|
||||
#endif
|
||||
|
||||
#endif // NVIM_VIML_PARSER_PARSER_H
|
Reference in New Issue
Block a user