diff options
81 files changed, 4681 insertions, 3857 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 9c3c143045..4af13a3bbf 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -594,7 +594,8 @@ nvim__id_float({flt}) *nvim__id_float()* its argument. nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* - TODO: Documentation + NB: if your UI doesn't use hlstate, this will not return + hlstate first time. nvim__runtime_inspect() *nvim__runtime_inspect()* TODO: Documentation @@ -709,7 +710,7 @@ nvim_call_atomic({calls}) *nvim_call_atomic()* be returned. nvim_chan_send({chan}, {data}) *nvim_chan_send()* - Send data to channel `id` . For a job, it writes it to the + Send data to channel `id`. For a job, it writes it to the stdin of the process. For the stdio channel |channel-stdio|, it writes to Nvim's stdout. For an internal terminal instance (|nvim_open_term()|) it writes directly to terminal output. @@ -849,7 +850,7 @@ nvim_exec_lua({code}, {args}) *nvim_exec_lua()* inside the chunk. The chunk can return a value. Only statements are executed. To evaluate an expression, - prefix it with `return` : return my_function(...) + prefix it with `return`: return my_function(...) Parameters: ~ {code} Lua code to execute @@ -1132,13 +1133,13 @@ nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()* Option value nvim_get_proc({pid}) *nvim_get_proc()* - Gets info describing process `pid` . + Gets info describing process `pid`. Return: ~ Map of process properties, or NIL if process not found. nvim_get_proc_children({pid}) *nvim_get_proc_children()* - Gets the immediate children of process `pid` . + Gets the immediate children of process `pid`. Return: ~ Array of child process ids, empty if process not found. @@ -1247,8 +1248,8 @@ nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col}) nvim_list_bufs() *nvim_list_bufs()* Gets the current list of buffer handles - Includes unlisted (unloaded/deleted) buffers, like `:ls!` . - Use |nvim_buf_is_loaded()| to check if a buffer is loaded. + Includes unlisted (unloaded/deleted) buffers, like `:ls!`. Use + |nvim_buf_is_loaded()| to check if a buffer is loaded. Return: ~ List of buffer handles @@ -1357,7 +1358,7 @@ nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err` but do not affect the return value (which - is strictly decided by `vim.paste()` ). On error, subsequent + is strictly decided by `vim.paste()`). On error, subsequent calls are ignored ("drained") until the next paste is initiated (phase 1 or -1). @@ -1434,7 +1435,7 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) {insert} Whether the selection should be inserted in the buffer. {finish} Finish the completion and dismiss the popupmenu. - Implies `insert` . + Implies `insert`. {opts} Optional parameters. Reserved for future use. *nvim_set_client_info()* @@ -1547,18 +1548,21 @@ nvim_set_hl({ns_id}, {name}, {*val}) *nvim_set_hl()* Sets a highlight group. Parameters: ~ - {ns_id} Namespace id for this highlight |nvim_create_namespace()|. - Use 0 to set a highlight group globally |:highlight|. + {ns_id} Namespace id for this highlight + |nvim_create_namespace()|. Use 0 to set a + highlight group globally |:highlight|. {name} Highlight group name, e.g. "ErrorMsg" {val} Highlight definition map, like |synIDattr()|. In addition, the following keys are recognized: - • default: Don't override existing definition |:hi-default| - • ctermfg: Sets foreground of cterm color |highlight-ctermfg| - • ctermbg: Sets background of cterm color |highlight-ctermbg| + • default: Don't override existing definition + |:hi-default| + • ctermfg: Sets foreground of cterm color + |highlight-ctermfg| + • ctermbg: Sets background of cterm color + |highlight-ctermbg| • cterm: cterm attribute map, like - |highlight-args|. - Note: Attributes default to those set for `gui` - if not set. + |highlight-args|. Note: Attributes default to + those set for `gui` if not set. nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()* Sets a global |mapping| for the given mode. @@ -1627,7 +1631,7 @@ nvim_set_vvar({name}, {value}) *nvim_set_vvar()* {value} Variable value nvim_strwidth({text}) *nvim_strwidth()* - Calculates the number of display cells occupied by `text` . + Calculates the number of display cells occupied by `text`. <Tab> counts as one cell. Parameters: ~ @@ -1879,9 +1883,10 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* {buffer} Buffer handle, or 0 for current buffer {send_buffer} True if the initial notification should contain the whole buffer: first - notification will be `nvim_buf_lines_event` - . Else the first notification will be - `nvim_buf_changedtick_event` . Not for Lua + notification will be + `nvim_buf_lines_event`. Else the first + notification will be + `nvim_buf_changedtick_event`. Not for Lua callbacks. {opts} Optional parameters. • on_lines: Lua callback invoked on change. @@ -1937,7 +1942,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* • utf_sizes: include UTF-32 and UTF-16 size of the replaced region, as args to - `on_lines` . + `on_lines`. • preview: also attach to command preview (i.e. 'inccommand') events. @@ -2352,7 +2357,7 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, namespace. All highlights in the same namespace can then be cleared with single call to |nvim_buf_clear_namespace()|. If the highlight never will be deleted by an API call, pass - `ns_id = -1` . + `ns_id = -1`. As a shorthand, `ns_id = 0` can be used to create a new namespace for the highlight, the allocated id is then @@ -2432,8 +2437,8 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) < - If `end` is less than `start` , traversal works backwards. - (Useful with `limit` , to get the first marks prior to a given + If `end` is less than `start`, traversal works backwards. + (Useful with `limit`, to get the first marks prior to a given position.) Example: @@ -2780,7 +2785,7 @@ nvim_win_hide({window}) *nvim_win_hide()* |:hide| with a |window-ID|). Like |:hide| the buffer becomes hidden unless another window - is editing it, or 'bufhidden' is `unload` , `delete` or `wipe` + is editing it, or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to |:close| or |nvim_win_close|, which will close the buffer. @@ -3028,7 +3033,7 @@ nvim_win_set_config({window}, {*config}) *nvim_win_set_config()* layouts). When reconfiguring a floating window, absent option keys will - not be changed. `row` / `col` and `relative` must be + not be changed. `row`/`col` and `relative` must be reconfigured together. Parameters: ~ diff --git a/runtime/doc/arabic.txt b/runtime/doc/arabic.txt index 5d3bf7a761..0df861111c 100644 --- a/runtime/doc/arabic.txt +++ b/runtime/doc/arabic.txt @@ -175,7 +175,7 @@ o Enable Arabic settings [short-cut] vertical separator like "l" or "𝖨" may be used. It may also be hidden by changing its color to the foreground color: > :set fillchars=vert:l - :hi VertSplit ctermbg=White + :hi WinSeparator ctermbg=White < Note that this is a workaround, not a proper solution. If, on the other hand, you'd like to be verbose and explicit and diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index e33c786482..32936a7ee6 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -328,11 +328,11 @@ config({opts}, {namespace}) *vim.diagnostic.config()* Note: Each of the configuration options below accepts one of the following: - • `false` : Disable this feature - • `true` : Enable this feature, use default settings. - • `table` : Enable this feature with overrides. Use an + • `false`: Disable this feature + • `true`: Enable this feature, use default settings. + • `table`: Enable this feature with overrides. Use an empty table to use default values. - • `function` : Function with signature (namespace, bufnr) + • `function`: Function with signature (namespace, bufnr) that returns any of the above. Parameters: ~ diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 54c648e171..a9ebcd27ae 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -563,7 +563,7 @@ buf_request_all({bufnr}, {method}, {params}, {callback}) Return: ~ (function) A function that will cancel all requests which - is the same as the one returned from `buf_request` . + is the same as the one returned from `buf_request`. *vim.lsp.buf_request_sync()* buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) @@ -600,9 +600,9 @@ client() *vim.lsp.client* {handler} is not specified, If one is not found there, then an error will occur. Returns: {status}, {[client_id]}. {status} is a boolean indicating if the - notification was successful. If it is `false` , then it + notification was successful. If it is `false`, then it will always be `false` (the client has shutdown). If - {status} is `true` , the function returns {request_id} as + {status} is `true`, the function returns {request_id} as the second result. You can use this with `client.cancel_request(request_id)` to cancel the request. • request_sync(method, params, timeout_ms, bufnr) Sends a @@ -612,13 +612,13 @@ client() *vim.lsp.client* `err` and `result` come from the |lsp-handler|. On timeout, cancel or error, returns `(nil, err)` where `err` is a string describing the failure reason. If the request - was unsuccessful returns `nil` . + was unsuccessful returns `nil`. • notify(method, params) Sends a notification to an LSP server. Returns: a boolean to indicate if the notification was successful. If it is false, then it will always be false (the client has shutdown). • cancel_request(id) Cancels a request with a given request - id. Returns: same as `notify()` . + id. Returns: same as `notify()`. • stop([force]) Stops a client, optionally with force. By default, it will just ask the server to shutdown without force. If you request to stop a client which has @@ -639,14 +639,14 @@ client() *vim.lsp.client* interaction with the client. See |vim.lsp.rpc.start()|. • {offset_encoding} (string): The encoding used for communicating with the server. You can modify this in the - `config` 's `on_init` method before text is sent to the + `config`'s `on_init` method before text is sent to the server. • {handlers} (table): The handlers used by the client as described in |lsp-handler|. • {requests} (table): The current pending requests in flight to the server. Entries are key-value pairs with the key being the request ID while the value is a table with - `type` , `bufnr` , and `method` key-value pairs. `type` is + `type`, `bufnr`, and `method` key-value pairs. `type` is either "pending" for an active request, or "cancel" for a cancel request. • {config} (table): copy of the table that was passed by the @@ -655,7 +655,7 @@ client() *vim.lsp.client* sent on `initialize` describing the server's capabilities. • {resolved_capabilities} (table): Normalized table of capabilities that we have detected based on the initialize - response from the server in `server_capabilities` . + response from the server in `server_capabilities`. client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* Checks whether a client is stopped. @@ -775,7 +775,7 @@ start_client({config}) *vim.lsp.start_client()* initiates the LSP client. {cmd_cwd} (string, default=|getcwd()|) Directory to launch the `cmd` - process. Not related to `root_dir` . + process. Not related to `root_dir`. {cmd_env} (table) Environment flags to pass to the LSP on spawn. Can be specified using keys like a map or as a list @@ -800,15 +800,15 @@ start_client({config}) *vim.lsp.start_client()* its result. • Note: To send an empty dictionary use - `{[vim.type_idx]=vim.types.dictionary}` - , else it will be encoded as an + `{[vim.type_idx]=vim.types.dictionary}`, + else it will be encoded as an array. {handlers} Map of language server method names to |lsp-handler| {settings} Map with language server specific settings. These are returned to the language server if requested via - `workspace/configuration` . Keys are + `workspace/configuration`. Keys are case-sensitive. {commands} table Table that maps string of clientside commands to user-defined @@ -821,7 +821,7 @@ start_client({config}) *vim.lsp.start_client()* action, code lenses, ...) triggers the command. {init_options} Values to pass in the initialization - request as `initializationOptions` . + request as `initializationOptions`. See `initialize` in the LSP spec. {name} (string, default=client-id) Name in log messages. @@ -976,7 +976,7 @@ code_action({context}) *vim.lsp.buf.code_action()* • only: (string|nil) LSP `CodeActionKind` used to filter the code actions. Most language servers support values like `refactor` or - `quickfix` . + `quickfix`. See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction @@ -1007,7 +1007,7 @@ definition() *vim.lsp.buf.definition()* document_highlight() *vim.lsp.buf.document_highlight()* Send request to the server to resolve document highlights for the current text document position. This request can be - triggered by a key mapping or by events such as `CursorHold` , + triggered by a key mapping or by events such as `CursorHold`, e.g.: > autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight() @@ -1124,7 +1124,7 @@ range_code_action({context}, {start_pos}, {end_pos}) • only: (string|nil) LSP `CodeActionKind` used to filter the code actions. Most language servers support values like - `refactor` or `quickfix` . + `refactor` or `quickfix`. {start_pos} ({number, number}, optional) mark-indexed position. Defaults to the start of the last visual selection. @@ -1246,8 +1246,8 @@ display({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.display()* Display the lenses using virtual text Parameters: ~ - {lenses} table of lenses to display ( `CodeLens[] | - null` ) + {lenses} table of lenses to display (`CodeLens[] | + null`) {bufnr} number {client_id} number @@ -1259,7 +1259,7 @@ get({bufnr}) *vim.lsp.codelens.get()* current buffer. Return: ~ - table ( `CodeLens[]` ) + table (`CodeLens[]`) *vim.lsp.codelens.on_codelens()* on_codelens({err}, {result}, {ctx}, {_}) @@ -1281,8 +1281,8 @@ save({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.save()* Store lenses for a specific buffer and client Parameters: ~ - {lenses} table of lenses to store ( `CodeLens[] | - null` ) + {lenses} table of lenses to store (`CodeLens[] | + null`) {bufnr} number {client_id} number @@ -1333,7 +1333,7 @@ Lua module: vim.lsp.util *lsp-util* *vim.lsp.util.apply_text_document_edit()* apply_text_document_edit({text_document_edit}, {index}, {offset_encoding}) - Applies a `TextDocumentEdit` , which is a list of changes to a + Applies a `TextDocumentEdit`, which is a list of changes to a single document. Parameters: ~ @@ -1360,7 +1360,7 @@ apply_text_edits({text_edits}, {bufnr}, {offset_encoding}) *vim.lsp.util.apply_workspace_edit()* apply_workspace_edit({workspace_edit}, {offset_encoding}) - Applies a `WorkspaceEdit` . + Applies a `WorkspaceEdit`. Parameters: ~ {workspace_edit} table `WorkspaceEdit` @@ -1408,13 +1408,13 @@ convert_input_to_markdown_lines({input}, {contents}) Converts any of `MarkedString` | `MarkedString[]` | `MarkupContent` into a list of lines containing valid markdown. Useful to populate the hover window for - `textDocument/hover` , for parsing the result of - `textDocument/signatureHelp` , and potentially others. + `textDocument/hover`, for parsing the result of + `textDocument/signatureHelp`, and potentially others. Parameters: ~ - {input} ( `MarkedString` | `MarkedString[]` | - `MarkupContent` ) - {contents} (table, optional, default `{}` ) List of + {input} (`MarkedString` | `MarkedString[]` | + `MarkupContent`) + {contents} (table, optional, default `{}`) List of strings to extend with converted lines Return: ~ @@ -1475,7 +1475,7 @@ jump_to_location({location}, {offset_encoding}) Jumps to a location. Parameters: ~ - {location} table ( `Location` | `LocationLink` ) + {location} table (`Location`|`LocationLink`) {offset_encoding} string utf-8|utf-16|utf-32 (required) Return: ~ @@ -1491,8 +1491,8 @@ locations_to_items({locations}, {offset_encoding}) |setqflist()| or |setloclist()|. Parameters: ~ - {locations} table list of `Location` s or - `LocationLink` s + {locations} table list of `Location`s or + `LocationLink`s {offset_encoding} string offset_encoding for locations utf-8|utf-16|utf-32 @@ -1526,7 +1526,7 @@ make_floating_popup_options({width}, {height}, {opts}) • border (string or table) override `border` • focusable (string or table) override `focusable` - • zindex (string or table) override `zindex` , + • zindex (string or table) override `zindex`, defaults to 50 Return: ~ @@ -1566,7 +1566,7 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) Return: ~ { textDocument = { uri = `current_file_uri` }, range = { - start = `start_position` , end = `end_position` } } + start = `start_position`, end = `end_position` } } *vim.lsp.util.make_position_params()* make_position_params({window}, {offset_encoding}) @@ -1590,9 +1590,9 @@ make_position_params({window}, {offset_encoding}) make_range_params({window}, {offset_encoding}) Using the current position in the current buffer, creates an object that can be used as a building block for several LSP - requests, such as `textDocument/codeAction` , - `textDocument/colorPresentation` , - `textDocument/rangeFormatting` . + requests, such as `textDocument/codeAction`, + `textDocument/colorPresentation`, + `textDocument/rangeFormatting`. Parameters: ~ {window} (optional, number): window handle or 0 @@ -1603,7 +1603,7 @@ make_range_params({window}, {offset_encoding}) Return: ~ { textDocument = { uri = `current_file_uri` }, range = { - start = `current_position` , end = `current_position` } } + start = `current_position`, end = `current_position` } } *vim.lsp.util.make_text_document_params()* make_text_document_params({bufnr}) @@ -1657,8 +1657,8 @@ open_floating_preview({contents}, {syntax}, {opts}) closes the floating window • focusable: (boolean, default true) Make float focusable - • focus: (boolean, default true) If `true` , - and if {focusable} is also `true` , focus an + • focus: (boolean, default true) If `true`, + and if {focusable} is also `true`, focus an existing floating window with the same {focus_id} @@ -1757,7 +1757,7 @@ text_document_completion_list_to_complete_items({result}, {prefix}) Parameters: ~ {result} The result of a `textDocument/completion` call, e.g. from |vim.lsp.buf.completion()|, which may - be one of `CompletionItem[]` , `CompletionList` + be one of `CompletionItem[]`, `CompletionList` or `null` {prefix} (string) the prefix to filter the completion items diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 11629332ae..93386ddfe9 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1418,7 +1418,7 @@ deep_equal({a}, {b}) *vim.deep_equal()* {b} second value Return: ~ - `true` if values are equals, else `false` . + `true` if values are equals, else `false`. deepcopy({orig}) *vim.deepcopy()* Returns a deep copy of the given object. Non-table objects are @@ -1435,7 +1435,7 @@ deepcopy({orig}) *vim.deepcopy()* New table of copied keys and (nested) values. endswith({s}, {suffix}) *vim.endswith()* - Tests if `s` ends with `suffix` . + Tests if `s` ends with `suffix`. Parameters: ~ {s} (string) a string @@ -1539,7 +1539,7 @@ split({s}, {sep}, {kwargs}) *vim.split()* |vim.gsplit()| startswith({s}, {prefix}) *vim.startswith()* - Tests if `s` starts with `prefix` . + Tests if `s` starts with `prefix`. Parameters: ~ {s} (string) a string @@ -1556,7 +1556,7 @@ tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* {o} table The table to add the reverse to. tbl_contains({t}, {value}) *vim.tbl_contains()* - Checks if a list-like (vector) table contains `value` . + Checks if a list-like (vector) table contains `value`. Parameters: ~ {t} Table to check @@ -1566,7 +1566,7 @@ tbl_contains({t}, {value}) *vim.tbl_contains()* true if `t` contains `value` tbl_count({t}) *vim.tbl_count()* - Counts the number of non-nil values in table `t` . + Counts the number of non-nil values in table `t`. > vim.tbl_count({ a=1, b=2 }) => 2 @@ -1651,7 +1651,7 @@ tbl_islist({t}) *vim.tbl_islist()* {t} Table Return: ~ - `true` if array-like table, else `false` . + `true` if array-like table, else `false`. tbl_keys({t}) *vim.tbl_keys()* Return a list of all keys used in a table. However, the order @@ -1813,7 +1813,7 @@ input({opts}, {on_confirm}) *vim.ui.input()* Parameters: ~ {opts} table Additional options. See |input()| • prompt (string|nil) Text of the prompt. - Defaults to `Input:` . + Defaults to `Input:`. • default (string|nil) Default reply to the input • completion (string|nil) Specifies type of @@ -1856,16 +1856,16 @@ select({items}, {opts}, {on_choice}) *vim.ui.select()* Defaults to `Select one of:` • format_item (function item -> text) Function to format an individual item from - `items` . Defaults to `tostring` . + `items`. Defaults to `tostring`. • kind (string|nil) Arbitrary hint string indicating the item shape. Plugins reimplementing `vim.ui.select` may wish to use this to infer the structure or - semantics of `items` , or the context in + semantics of `items`, or the context in which select() was called. {on_choice} function ((item|nil, idx|nil) -> ()) Called once the user made a choice. `idx` is the - 1-based index of `item` within `item` . `nil` + 1-based index of `item` within `item`. `nil` if the user aborted the dialog. @@ -1990,9 +1990,9 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* vim.keymap.set('n', 'asdf', require('jkl').my_fun) < - the require('jkl') gets evaluated during this call in order to - access the function. If you want to avoid this cost at startup - you can wrap it in a function, for example: > + the require('jkl )` gets evaluated during this call in order to access the + function. If you want to avoid this cost at startup you can + wrap it in a function, for example: > vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end) < @@ -2014,7 +2014,7 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* result of Lua expr maps. • remap: (boolean) Make the mapping recursive. This is the inverse of the "noremap" option from - |nvim_set_keymap()|. Default `false` . + |nvim_set_keymap()|. Default `false`. See also: ~ |nvim_set_keymap()| diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 82214a2527..b2bbf5ddad 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2441,7 +2441,14 @@ A jump table for the options with a short description can be found at |Q_op|. item default Used for ~ stl:c ' ' or '^' statusline of the current window stlnc:c ' ' or '=' statusline of the non-current windows + horiz:c '─' or '-' horizontal separators |:split| + horizup:c '┴' or '-' upwards facing horizontal separator + horizdown:c '┬' or '-' downwards facing horizontal separator vert:c '│' or '|' vertical separators |:vsplit| + vertleft:c '┤' or '|' left facing vertical separator + vertright:c '├' or '|' right facing vertical separator + verthoriz:c '┼' or '+' overlapping vertical and horizontal + separator fold:c '·' or '-' filling 'foldtext' foldopen:c '-' mark the beginning of a fold foldclose:c '+' show a closed fold @@ -2454,8 +2461,13 @@ A jump table for the options with a short description can be found at |Q_op|. "stlnc" the space will be used when there is highlighting, '^' or '=' otherwise. - If 'ambiwidth' is "double" then "vert", "foldsep" and "fold" default to - single-byte alternatives. + Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and + "verthoriz" are only used when 'laststatus' is 3, since only vertical + window separators are used otherwise. + + If 'ambiwidth' is "double" then "horiz", "horizup", "horizdown", + "vert", "vertleft", "vertright", "verthoriz", "foldsep" and "fold" + default to single-byte alternatives. Example: > :set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- @@ -2469,7 +2481,13 @@ A jump table for the options with a short description can be found at |Q_op|. item highlight group ~ stl:c StatusLine |hl-StatusLine| stlnc:c StatusLineNC |hl-StatusLineNC| - vert:c VertSplit |hl-VertSplit| + horiz:c WinSeparator |hl-WinSeparator| + horizup:c WinSeparator |hl-WinSeparator| + horizdown:c WinSeparator |hl-WinSeparator| + vert:c WinSeparator |hl-WinSeparator| + vertleft:c WinSeparator |hl-WinSeparator| + vertright:c WinSeparator |hl-WinSeparator| + verthoriz:c WinSeparator |hl-WinSeparator| fold:c Folded |hl-Folded| diff:c DiffDelete |hl-DiffDelete| eob:c EndOfBuffer |hl-EndOfBuffer| @@ -3652,6 +3670,8 @@ A jump table for the options with a short description can be found at |Q_op|. 0: never 1: only if there are at least two windows 2: always + 3: have a global statusline at the bottom instead of one for + each window The screen looks nicer with a status line if you have several windows, but it takes another screen line. |status-line| @@ -5929,7 +5949,7 @@ A jump table for the options with a short description can be found at |Q_op|. empty to avoid further errors. Otherwise screen updating would loop. Note that the only effect of 'ruler' when this option is set (and - 'laststatus' is 2) is controlling the output of |CTRL-G|. + 'laststatus' is 2 or 3) is controlling the output of |CTRL-G|. field meaning ~ - Left justify the item. The default is right justified diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index b8991be738..0c1e3438de 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -86,7 +86,7 @@ You can not put options there! 2. Missing functionality *E5600* *clientserver-missing* Vim supports additional functionality in clientserver that's not yet -implemented in Nvim. In particular, none of the 'wait' variants are supported +implemented in Nvim. In particular, none of the "wait" variants are supported yet. The following command line arguments are not yet available: argument meaning ~ diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index c5f93fd66f..0ff31e81ab 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5119,8 +5119,8 @@ TermCursor cursor in a focused terminal TermCursorNC cursor in an unfocused terminal *hl-ErrorMsg* ErrorMsg error messages on the command line - *hl-VertSplit* -VertSplit the column separating vertically split windows + *hl-WinSeparator* +WinSeparator separators between window splits *hl-Folded* Folded line used for closed folds *hl-FoldColumn* diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 02be20c5e8..a9d9f81849 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -468,7 +468,7 @@ parse_query({lang}, {query}) *parse_query()* Exposes `info` and `captures` with additional context about {query}. • `captures` contains the list of unique capture names defined - in {query}. - `info.captures` also points to `captures` . + in {query}. -`info.captures` also points to `captures`. • `info.patterns` contains information about predicates. Parameters: ~ @@ -528,8 +528,8 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop}) a table mapping capture indices to nodes, and metadata from any directives processing the match. If the query has more than one pattern the capture table might be sparse, and e.g. - `pairs()` method should be used over `ipairs` . Here an - example iterating over all captures in every match: + `pairs()` method should be used over `ipairs`. Here an example + iterating over all captures in every match: > for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt index 8ccaa73006..1d20913a14 100644 --- a/runtime/doc/usr_08.txt +++ b/runtime/doc/usr_08.txt @@ -482,6 +482,8 @@ statusline: 0 never 1 only when there are split windows (the default) 2 always + 3 have a global statusline at the bottom instead of one for each + window Many commands that edit another file have a variant that splits the window. For Command-line commands this is done by prepending an "s". For example: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 7f944bbfe6..9ca5faf112 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -211,6 +211,7 @@ Highlight groups: |hl-Substitute| |hl-TermCursor| |hl-TermCursorNC| + |hl-WinSeparator| highlights window separators |hl-Whitespace| highlights 'listchars' whitespace Input/Mappings: @@ -227,9 +228,11 @@ Options: 'cpoptions' flags: |cpo-_| 'display' flags: "msgsep" minimizes scrolling when showing messages 'guicursor' works in the terminal - 'fillchars' flags: "msgsep" (see 'display') + 'fillchars' flags: "msgsep" (see 'display'), "horiz", "horizup", + "horizdown", "vertleft", "vertright", "verthoriz" 'foldcolumn' supports up to 9 dynamic/fixed columns 'inccommand' shows interactive results for |:substitute|-like commands + 'laststatus' global statusline support 'pumblend' pseudo-transparent popupmenu 'scrollback' 'signcolumn' supports up to 9 dynamic/fixed columns @@ -348,6 +351,7 @@ Highlight groups: |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other groups |hl-CursorLine| is low-priority unless foreground color is set + *hl-VertSplit* superseded by |hl-WinSeparator| Macro/|recording| behavior Replay of a macro recorded during :lmap produces the same actions as when it diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index bd29cd1d7a..cd5425336f 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -104,6 +104,8 @@ when the last window also has a status line: 'laststatus' = 0 never a status line 'laststatus' = 1 status line if there is more than one window 'laststatus' = 2 always a status line + 'laststatus' = 3 have a global statusline at the bottom instead + of one for each window You can change the contents of the status line with the 'statusline' option. This option can be local to the window, so that you can have a different diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 7f12372502..1be40b0081 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -25,8 +25,8 @@ local keymap = {} --- vim.keymap.set('n', 'asdf', require('jkl').my_fun) --- </pre> --- ---- the `require('jkl')` gets evaluated during this call in order to access the function. If you want to ---- avoid this cost at startup you can wrap it in a function, for example: +--- the ``require('jkl')`` gets evaluated during this call in order to access the function. +--- If you want to avoid this cost at startup you can wrap it in a function, for example: --- <pre> --- vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end) --- </pre> diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 8d11b4621c..105e7c4621 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1689,7 +1689,7 @@ end --- --- Currently only supports a single client. This can be set via --- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach` ---- via `vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`. +--- via ``vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')``. --- ---@param opts table options for customizing the formatting expression which takes the --- following optional keys: diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 8124b23eb1..3eb332279a 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -331,7 +331,7 @@ end --- Add the reverse lookup values to an existing table. --- For example: ---- `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }` +--- ``tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }`` -- --Do note that it *modifies* the input. ---@param o table The table to add the reverse to. diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index af49d57492..7152f1005f 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -84,8 +84,6 @@ CONFIG = { 'api': { 'mode': 'c', 'filename': 'api.txt', - # String used to find the start of the generated part of the doc. - 'section_start_token': '*api-global*', # Section ordering. 'section_order': [ 'vim.c', @@ -98,8 +96,8 @@ CONFIG = { 'autocmd.c', 'ui.c', ], - # List of files/directories for doxygen to read, separated by blanks - 'files': os.path.join(base_dir, 'src/nvim/api'), + # List of files/directories for doxygen to read, relative to `base_dir` + 'files': ['src/nvim/api'], # file patterns used by doxygen 'file_patterns': '*.h *.c', # Only function with this prefix are considered @@ -122,7 +120,6 @@ CONFIG = { 'lua': { 'mode': 'lua', 'filename': 'lua.txt', - 'section_start_token': '*lua-vim*', 'section_order': [ '_editor.lua', 'shared.lua', @@ -131,14 +128,14 @@ CONFIG = { 'filetype.lua', 'keymap.lua', ], - 'files': ' '.join([ - os.path.join(base_dir, 'runtime/lua/vim/_editor.lua'), - os.path.join(base_dir, 'runtime/lua/vim/shared.lua'), - os.path.join(base_dir, 'runtime/lua/vim/uri.lua'), - os.path.join(base_dir, 'runtime/lua/vim/ui.lua'), - os.path.join(base_dir, 'runtime/lua/vim/filetype.lua'), - os.path.join(base_dir, 'runtime/lua/vim/keymap.lua'), - ]), + 'files': [ + 'runtime/lua/vim/_editor.lua', + 'runtime/lua/vim/shared.lua', + 'runtime/lua/vim/uri.lua', + 'runtime/lua/vim/ui.lua', + 'runtime/lua/vim/filetype.lua', + 'runtime/lua/vim/keymap.lua', + ], 'file_patterns': '*.lua', 'fn_name_prefix': '', 'section_name': { @@ -171,7 +168,6 @@ CONFIG = { 'lsp': { 'mode': 'lua', 'filename': 'lsp.txt', - 'section_start_token': '*lsp-core*', 'section_order': [ 'lsp.lua', 'buf.lua', @@ -185,10 +181,10 @@ CONFIG = { 'sync.lua', 'protocol.lua', ], - 'files': ' '.join([ - os.path.join(base_dir, 'runtime/lua/vim/lsp'), - os.path.join(base_dir, 'runtime/lua/vim/lsp.lua'), - ]), + 'files': [ + 'runtime/lua/vim/lsp', + 'runtime/lua/vim/lsp.lua', + ], 'file_patterns': '*.lua', 'fn_name_prefix': '', 'section_name': {'lsp.lua': 'lsp'}, @@ -214,11 +210,10 @@ CONFIG = { 'diagnostic': { 'mode': 'lua', 'filename': 'diagnostic.txt', - 'section_start_token': '*diagnostic-api*', 'section_order': [ 'diagnostic.lua', ], - 'files': os.path.join(base_dir, 'runtime/lua/vim/diagnostic.lua'), + 'files': ['runtime/lua/vim/diagnostic.lua'], 'file_patterns': '*.lua', 'fn_name_prefix': '', 'section_name': {'diagnostic.lua': 'diagnostic'}, @@ -231,7 +226,6 @@ CONFIG = { 'treesitter': { 'mode': 'lua', 'filename': 'treesitter.txt', - 'section_start_token': '*lua-treesitter-core*', 'section_order': [ 'treesitter.lua', 'language.lua', @@ -239,10 +233,10 @@ CONFIG = { 'highlighter.lua', 'languagetree.lua', ], - 'files': ' '.join([ - os.path.join(base_dir, 'runtime/lua/vim/treesitter.lua'), - os.path.join(base_dir, 'runtime/lua/vim/treesitter/'), - ]), + 'files': [ + 'runtime/lua/vim/treesitter.lua', + 'runtime/lua/vim/treesitter/', + ], 'file_patterns': '*.lua', 'fn_name_prefix': '', 'section_name': {}, @@ -349,14 +343,6 @@ def self_or_child(n): return n.childNodes[0] -def clean_text(text): - """Cleans text. - - Only cleans superfluous whitespace at the moment. - """ - return ' '.join(text.split()).strip() - - def clean_lines(text): """Removes superfluous lines. @@ -377,12 +363,12 @@ def get_text(n, preformatted=False): if n.nodeName == 'computeroutput': for node in n.childNodes: text += get_text(node) - return '`{}` '.format(text) + return '`{}`'.format(text) for node in n.childNodes: if node.nodeType == node.TEXT_NODE: - text += node.data if preformatted else clean_text(node.data) + text += node.data elif node.nodeType == node.ELEMENT_NODE: - text += ' ' + get_text(node, preformatted) + text += get_text(node, preformatted) return text @@ -849,7 +835,9 @@ def extract_from_xml(filename, target, width): 'seealso': [], } if fmt_vimhelp: - fn['desc_node'] = desc # HACK :( + # HACK :( + fn['desc_node'] = desc + fn['brief_desc_node'] = brief_desc for m in paras: if 'text' in m: @@ -897,6 +885,8 @@ def fmt_doxygen_xml_as_vimhelp(filename, target): # Generate Vim :help for parameters. if fn['desc_node']: doc = fmt_node_as_vimhelp(fn['desc_node']) + if not doc and fn['brief_desc_node']: + doc = fmt_node_as_vimhelp(fn['brief_desc_node']) if not doc: doc = 'TODO: Documentation' @@ -1006,7 +996,8 @@ def main(config, args): stderr=(subprocess.STDOUT if debug else subprocess.DEVNULL)) p.communicate( config.format( - input=CONFIG[target]['files'], + input=' '.join( + [f'"{file}"' for file in CONFIG[target]['files']]), output=output_dir, filter=filter_cmd, file_patterns=CONFIG[target]['file_patterns']) @@ -1096,6 +1087,7 @@ def main(config, args): raise RuntimeError( 'found new modules "{}"; update the "section_order" map'.format( set(sections).difference(CONFIG[target]['section_order']))) + first_section_tag = sections[CONFIG[target]['section_order'][0]][1] docs = '' @@ -1121,7 +1113,8 @@ def main(config, args): doc_file = os.path.join(base_dir, 'runtime', 'doc', CONFIG[target]['filename']) - delete_lines_below(doc_file, CONFIG[target]['section_start_token']) + if os.path.exists(doc_file): + delete_lines_below(doc_file, first_section_tag) with open(doc_file, 'ab') as fp: fp.write(docs.encode('utf8')) diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 591c658e6b..c68ab3a751 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -236,6 +236,10 @@ preprocess_patch() { LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/session\(\.[ch]\)/\1\/ex_session\2/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename highlight.c to highlight_group.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/highlight\(\.[ch]\)/\1\/highlight_group\2/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename test_urls.vim to check_urls.vim LC_ALL=C sed -e 's@\( [ab]\)/runtime/doc/test\(_urls.vim\)@\1/scripts/check\2@g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 96e5d1e77c..8e17f94abc 100644..100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,6 +39,7 @@ set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h) set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h) +set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) @@ -271,6 +272,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_UI_EVENTS_REMOTE}" "${GENERATED_UI_EVENTS_BRIDGE}" "${GENERATED_KEYSETS}" + "${GENERATED_UI_EVENTS_CLIENT}" ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") @@ -368,6 +370,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} + ${GENERATED_UI_EVENTS_CLIENT} COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h ${GENERATED_UI_EVENTS} @@ -375,6 +378,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} + ${GENERATED_UI_EVENTS_CLIENT} DEPENDS ${API_UI_EVENTS_GENERATOR} ${GENERATOR_C_GRAMMAR} diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e355f82f4d..c02688a815 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -10,10 +10,10 @@ #include "nvim/api/private/helpers.h" #include "nvim/decoration_provider.h" #include "nvim/extmark.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/memline.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/extmark.c.generated.h" @@ -856,7 +856,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In int hl_id = 0; if (hl_group.size > 0) { - hl_id = syn_check_group(hl_group.data, (int)hl_group.size); + hl_id = syn_check_group(hl_group.data, hl_group.size); } else { return ns_id; } diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 49e3cf7df7..82ec1ad0d8 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -64,7 +64,14 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ + ufunc_T *fp = find_func(fun); \ + assert(fp != NULL); \ + if (fp->uf_cb == nlua_CFunction_func_call) { \ + LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ + kvi_push(edata->stack, LUAREF_OBJ(ref)); \ + } else { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + } \ goto typval_encode_stop_converting_one_item; \ } while (0) @@ -231,14 +238,6 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { - if (obj->v_type == VAR_FUNC) { - ufunc_T *fp = find_func(obj->vval.v_string); - assert(fp != NULL); - if (fp->uf_cb == nlua_CFunction_func_call) { - LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); - return LUAREF_OBJ(ref); - } - } EncodedData edata; kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9f41393c6b..88954a1aa2 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -22,6 +22,7 @@ #include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/lib/kvec.h" #include "nvim/lua/executor.h" #include "nvim/map.h" @@ -32,7 +33,6 @@ #include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/option_defs.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim.h" @@ -1293,7 +1293,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err) { if (obj.type == kObjectTypeString) { String str = obj.data.string; - return str.size ? syn_check_group(str.data, (int)str.size) : 0; + return str.size ? syn_check_group(str.data, str.size) : 0; } else if (obj.type == kObjectTypeInteger) { return MAX((int)obj.data.integer, 0); } else { @@ -1327,7 +1327,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err) String hl = chunk.items[1].data.string; if (hl.size > 0) { // TODO(bfredl): use object_to_hl_id and allow integer - int hl_id = syn_check_group(hl.data, (int)hl.size); + int hl_id = syn_check_group(hl.data, hl.size); attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; } } diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 03fe5c5058..db348359eb 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -78,13 +78,13 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, void hl_group_set(String name, Integer id) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL; void grid_resize(Integer grid, Integer width, Integer height) - FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; + FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL; void grid_clear(Integer grid) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; void grid_cursor_goto(Integer grid, Integer row, Integer col) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_line(Integer grid, Integer row, Integer col_start, Array data) - FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY FUNC_API_CLIENT_IMPL; void grid_scroll(Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3292ee2ef8..bdeac1a9f4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -35,6 +35,7 @@ #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" @@ -50,7 +51,6 @@ #include "nvim/popupmnu.h" #include "nvim/screen.h" #include "nvim/state.h" -#include "nvim/syntax.h" #include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -112,7 +112,7 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err) Integer nvim_get_hl_id_by_name(String name) FUNC_API_SINCE(7) { - return syn_check_group(name.data, (int)name.size); + return syn_check_group(name.data, name.size); } Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) @@ -127,7 +127,7 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// /// Note: Unlike the `:highlight` command which can update a highlight group, /// this function completely replaces the definition. For example: -/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group 'Visual'. +/// ``nvim_set_hl(0, 'Visual', {})`` will clear the highlight group 'Visual'. /// /// @param ns_id Namespace id for this highlight |nvim_create_namespace()|. /// Use 0 to set a highlight group globally |:highlight|. @@ -147,7 +147,7 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) FUNC_API_SINCE(7) { - int hl_id = syn_check_group(name.data, (int)name.size); + int hl_id = syn_check_group(name.data, name.size); int link_id = -1; HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); @@ -2329,7 +2329,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * maxwidth = (int)opts->maxwidth.data.integer; } else { - maxwidth = use_tabline ? Columns : wp->w_width; + maxwidth = (use_tabline || global_stl_height() > 0) ? Columns : wp->w_width; } char buf[MAXPATHL]; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index ceb7f71423..b0267f5ed0 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -10,6 +10,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/win_config.h" #include "nvim/ascii.h" +#include "nvim/highlight_group.h" #include "nvim/option.h" #include "nvim/screen.h" #include "nvim/strings.h" @@ -131,7 +132,7 @@ /// An empty string can be used to turn off a specific border, for instance, /// [ "", "", "", ">", "", "", "", "<" ] /// will only make vertical borders but not horizontal ones. -/// By default, `FloatBorder` highlight is used, which links to `VertSplit` +/// By default, `FloatBorder` highlight is used, which links to `WinSeparator` /// when not defined. It could also be specified by character: /// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. /// - noautocmd: If true then no buffer-related autocommand events such as diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 9c473ff724..be43708604 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -71,7 +71,7 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) } /// Sets the (1,0)-indexed cursor position in the window. |api-indexing| -/// Unlike |win_execute()| this scrolls the window. +/// This scrolls the window even if it is not the current one. /// /// @param window Window handle, or 0 for current window /// @param pos (row, col) tuple representing the new position diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 8e4b043169..df336d8703 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -397,7 +397,7 @@ int augroup_add(char *name) } /// Delete the augroup that matches name. -/// @param stupid_legacy_mode bool: This paremeter determines whether to run the augroup +/// @param stupid_legacy_mode bool: This parameter determines whether to run the augroup /// deletion in the same fashion as `:augroup! {name}` where if there are any remaining /// autocmds left in the augroup, it will change the name of the augroup to `--- DELETED ---` /// but leave the autocmds existing. These are _separate_ augroups, so if you do this for diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3fdc111b6f..1293edb1da 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -51,6 +51,7 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/main.h" @@ -4947,8 +4948,8 @@ void ex_buffer_all(exarg_T *eap) wpnext = wp->w_next; if ((wp->w_buffer->b_nwindows > 1 || ((cmdmod.split & WSP_VERT) - ? wp->w_height + wp->w_status_height < Rows - p_ch - - tabline_height() + ? wp->w_height + wp->w_hsep_height + wp->w_status_height < Rows - p_ch + - tabline_height() - global_stl_height() : wp->w_width != Columns) || (had_tab > 0 && wp != firstwin)) && !ONE_WINDOW @@ -5481,6 +5482,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added) buf->b_signcols.max++; } buf->b_signcols.size++; + redraw_buf_later(buf, NOT_VALID); return; } @@ -5501,6 +5503,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added) buf->b_signcols.size = linesum; buf->b_signcols.max = linesum; buf->b_signcols.sentinel = added->se_lnum; + redraw_buf_later(buf, NOT_VALID); } } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 7acb646980..f9541a55a3 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1232,7 +1232,13 @@ struct window_S { struct { int stl; int stlnc; + int horiz; + int horizup; + int horizdown; int vert; + int vertleft; + int vertright; + int verthoriz; int fold; int foldopen; ///< when fold is open int foldclosed; ///< when fold is closed @@ -1278,7 +1284,8 @@ struct window_S { int w_status_height; // number of status lines (0 or 1) int w_wincol; // Leftmost column of window in screen. int w_width; // Width of window, excluding separation. - int w_vsep_width; // Number of separator columns (0 or 1). + int w_hsep_height; // Number of horizontal separator rows (0 or 1) + int w_vsep_width; // Number of vertical separator columns (0 or 1). pos_save_T w_save_cursor; // backup of cursor pos and topline // inner size of window, which can be overridden by external UI diff --git a/src/nvim/change.c b/src/nvim/change.c index 607414ac3c..6c3dbf72e4 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -408,7 +408,7 @@ void deleted_lines(linenr_T lnum, long count) /// be triggered to display the cursor. void deleted_lines_mark(linenr_T lnum, long count) { - // if we deleted the entire buffer, we need to implicity add a new empty line + // if we deleted the entire buffer, we need to implicitly add a new empty line bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY; mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index cb8c8cddf6..0e4a4bcfb0 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -9,8 +9,8 @@ #include "nvim/charset.h" #include "nvim/cursor_shape.h" #include "nvim/ex_getln.h" +#include "nvim/highlight_group.h" #include "nvim/strings.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -226,11 +226,11 @@ char *parse_shape_opt(int what) slashp = vim_strchr(p, '/'); if (slashp != NULL && slashp < endp) { // "group/langmap_group" - i = syn_check_group((char *)p, (int)(slashp - p)); + i = syn_check_group((char *)p, (size_t)(slashp - p)); p = slashp + 1; } if (round == 2) { - shape_table[idx].id = syn_check_group((char *)p, (int)(endp - p)); + shape_table[idx].id = syn_check_group((char *)p, (size_t)(endp - p)); shape_table[idx].id_lm = shape_table[idx].id; if (slashp != NULL && slashp < endp) { shape_table[idx].id = i; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 94bf1feeee..fb709d12ff 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -5,10 +5,10 @@ #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/move.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 095e082f61..c087948810 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -26,6 +26,7 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" @@ -5116,10 +5117,10 @@ static int ins_complete(int c, bool enable_pum) || ctrl_x_mode == CTRL_X_PATH_PATTERNS || ctrl_x_mode == CTRL_X_PATH_DEFINES) { if (compl_startpos.lnum != curwin->w_cursor.lnum) { - /* line (probably) wrapped, set compl_startpos to the - * first non_blank in the line, if it is not a wordchar - * include it to get a better pattern, but then we don't - * want the "\\<" prefix, check it bellow */ + // line (probably) wrapped, set compl_startpos to the + // first non_blank in the line, if it is not a wordchar + // include it to get a better pattern, but then we don't + // want the "\\<" prefix, check it below. compl_col = (colnr_T)getwhitecols(line); compl_startpos.col = compl_col; compl_startpos.lnum = curwin->w_cursor.lnum; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f4ce6e3b39..af7c3d4985 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -33,6 +33,7 @@ #include "nvim/ex_session.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 738ed7f85e..6e98f229b2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -31,6 +31,7 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/globals.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -1500,7 +1501,7 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Set the cursor position. -/// If 'charcol' is true, then use the column number as a character offet. +/// If 'charcol' is true, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) { @@ -3745,7 +3746,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) wp = mouse_find_win(&grid, &row, &col); if (wp != NULL) { - int height = wp->w_height + wp->w_status_height; + int height = wp->w_height + wp->w_hsep_height + wp->w_status_height; // The height is adjusted by 1 when there is a bottom border. This is not // necessary for a top border since `row` starts at -1 in that case. if (row < height + wp->w_border_adj[2]) { @@ -7781,7 +7782,7 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "reduce(list, { accumlator, element -> value } [, initial])" function +/// "reduce(list, { accumulator, element -> value } [, initial])" function static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { @@ -8885,7 +8886,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Set the cursor or mark position. -/// If 'charpos' is TRUE, then use the column number as a character offet. +/// If 'charpos' is TRUE, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index c7910e148d..ff803c3553 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -38,6 +38,7 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/input.h" #include "nvim/log.h" @@ -225,9 +226,7 @@ void do_ascii(const exarg_T *const eap) msg((char *)IObuff); } -/* - * ":left", ":center" and ":right": align text. - */ +/// ":left", ":center" and ":right": align text. void ex_align(exarg_T *eap) { pos_T save_curpos; @@ -324,9 +323,7 @@ void ex_align(exarg_T *eap) beginline(BL_WHITE | BL_FIX); } -/* - * Get the length of the current line, excluding trailing white space. - */ +/// @return the length of the current line, excluding trailing white space. static int linelen(int *has_tab) { char_u *line; @@ -452,7 +449,7 @@ static int sort_compare(const void *s1, const void *s2) return result; } -// ":sort". +/// ":sort". void ex_sort(exarg_T *eap) { regmatch_T regmatch; @@ -723,9 +720,7 @@ sortend: } } -/* - * ":retab". - */ +/// ":retab". void ex_retab(exarg_T *eap) { linenr_T lnum; @@ -907,11 +902,9 @@ void ex_retab(exarg_T *eap) u_clearline(); } -/* - * :move command - move lines line1-line2 to line dest - * - * return FAIL for failure, OK otherwise - */ +/// :move command - move lines line1-line2 to line dest +/// +/// @return FAIL for failure, OK otherwise int do_move(linenr_T line1, linenr_T line2, linenr_T dest) { char_u *str; @@ -1062,9 +1055,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) return OK; } -/* - * ":copy" - */ +/// ":copy" void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) { linenr_T count; @@ -1132,11 +1123,9 @@ void free_prev_shellcmd(void) #endif -/* - * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" - * Bangs in the argument are replaced with the previously entered command. - * Remember the argument. - */ +/// Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" +/// Bangs in the argument are replaced with the previously entered command. +/// Remember the argument. void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out) FUNC_ATTR_NONNULL_ALL { @@ -1684,9 +1673,7 @@ void print_line_no_prefix(linenr_T lnum, int use_number, int list) msg_prt_line(ml_get(lnum), list); } -/* - * Print a text line. Also in silent mode ("ex -s"). - */ +/// Print a text line. Also in silent mode ("ex -s"). void print_line(linenr_T lnum, int use_number, int list) { int save_silent = silent_mode; @@ -1754,9 +1741,7 @@ int rename_buffer(char_u *new_fname) return OK; } -/* - * ":file[!] [fname]". - */ +/// ":file[!] [fname]". void ex_file(exarg_T *eap) { // ":0file" removes the file name. Check for illegal uses ":3file", @@ -1782,9 +1767,7 @@ void ex_file(exarg_T *eap) } } -/* - * ":update". - */ +/// ":update". void ex_update(exarg_T *eap) { if (curbufIsChanged()) { @@ -1792,9 +1775,7 @@ void ex_update(exarg_T *eap) } } -/* - * ":write" and ":saveas". - */ +/// ":write" and ":saveas". void ex_write(exarg_T *eap) { if (eap->cmdidx == CMD_saveas) { @@ -1810,14 +1791,12 @@ void ex_write(exarg_T *eap) } } -/* - * write current buffer to file 'eap->arg' - * if 'eap->append' is TRUE, append to the file - * - * if *eap->arg == NUL write to current file - * - * return FAIL for failure, OK otherwise - */ +/// write current buffer to file 'eap->arg' +/// if 'eap->append' is TRUE, append to the file +/// +/// if *eap->arg == NUL write to current file +/// +/// @return FAIL for failure, OK otherwise int do_write(exarg_T *eap) { int other; @@ -2070,9 +2049,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int return OK; } -/* - * Handle ":wnext", ":wNext" and ":wprevious" commands. - */ +/// Handle ":wnext", ":wNext" and ":wprevious" commands. void ex_wnext(exarg_T *eap) { int i; @@ -2089,9 +2066,7 @@ void ex_wnext(exarg_T *eap) } } -/* - * ":wall", ":wqall" and ":xall": Write all changed files (and exit). - */ +/// ":wall", ":wqall" and ":xall": Write all changed files (and exit). void do_wqall(exarg_T *eap) { int error = 0; @@ -2149,8 +2124,9 @@ void do_wqall(exarg_T *eap) } } -// Check the 'write' option. -// Return true and give a message when it's not st. +/// Check the 'write' option. +/// +/// @return true and give a message when it's not st. bool not_writing(void) { if (p_write) { @@ -2160,11 +2136,9 @@ bool not_writing(void) return true; } -/* - * Check if a buffer is read-only (either 'readonly' option is set or file is - * read-only). Ask for overruling in a dialog. Return TRUE and give an error - * message when the buffer is readonly. - */ +/// Check if a buffer is read-only (either 'readonly' option is set or file is +/// read-only). Ask for overruling in a dialog. Return TRUE and give an error +/// message when the buffer is readonly. static int check_readonly(int *forceit, buf_T *buf) { // Handle a file being readonly when the 'readonly' option is set or when @@ -2205,15 +2179,16 @@ static int check_readonly(int *forceit, buf_T *buf) return FALSE; } -// Try to abandon the current file and edit a new or existing file. -// "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg". -// "lnum" is the line number for the cursor in the new file (if non-zero). -// -// Return: -// GETFILE_ERROR for "normal" error, -// GETFILE_NOT_WRITTEN for "not written" error, -// GETFILE_SAME_FILE for success -// GETFILE_OPEN_OTHER for successfully opening another file. +/// Try to abandon the current file and edit a new or existing file. +/// +/// @param fnum the number of the file, if zero use "ffname_arg"/"sfname_arg". +/// @param lnum the line number for the cursor in the new file (if non-zero). +/// +/// @return: +/// GETFILE_ERROR for "normal" error, +/// GETFILE_NOT_WRITTEN for "not written" error, +/// GETFILE_SAME_FILE for success +/// GETFILE_OPEN_OTHER for successfully opening another file. int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, linenr_T lnum, int forceit) { char_u *ffname = ffname_arg; @@ -2934,9 +2909,7 @@ static void delbuf_msg(char_u *name) static int append_indent = 0; // autoindent for first line -/* - * ":insert" and ":append", also used by ":change" - */ +/// ":insert" and ":append", also used by ":change" void ex_append(exarg_T *eap) { char_u *theline; @@ -3078,9 +3051,7 @@ void ex_append(exarg_T *eap) ex_no_reprint = true; } -/* - * ":change" - */ +/// ":change" void ex_change(exarg_T *eap) { linenr_T lnum; @@ -3247,9 +3218,9 @@ void ex_z(exarg_T *eap) ex_no_reprint = true; } -// Check if the secure flag is set (.exrc or .vimrc in current directory). -// If so, give an error message and return true. -// Otherwise, return false. +/// @return true if the secure flag is set (.exrc or .vimrc in current directory) +/// and also give an error message. +/// Otherwise, return false. bool check_secure(void) { if (secure) { @@ -4533,22 +4504,20 @@ static void global_exe_one(char_u *const cmd, const linenr_T lnum) } } -/* - * Execute a global command of the form: - * - * g/pattern/X : execute X on all lines where pattern matches - * v/pattern/X : execute X on all lines where pattern does not match - * - * where 'X' is an EX command - * - * The command character (as well as the trailing slash) is optional, and - * is assumed to be 'p' if missing. - * - * This is implemented in two passes: first we scan the file for the pattern and - * set a mark for each line that (not) matches. Secondly we execute the command - * for each line that has a mark. This is required because after deleting - * lines we do not know where to search for the next match. - */ +/// Execute a global command of the form: +/// +/// g/pattern/X : execute X on all lines where pattern matches +/// v/pattern/X : execute X on all lines where pattern does not match +/// +/// where 'X' is an EX command +/// +/// The command character (as well as the trailing slash) is optional, and +/// is assumed to be 'p' if missing. +/// +/// This is implemented in two passes: first we scan the file for the pattern and +/// set a mark for each line that (not) matches. Secondly we execute the command +/// for each line that has a mark. This is required because after deleting +/// lines we do not know where to search for the next match. void ex_global(exarg_T *eap) { linenr_T lnum; // line number according to old situation @@ -4764,9 +4733,7 @@ bool prepare_tagpreview(bool undo_sync) } -/* - * ":help": open a read-only window on a help file - */ +/// ":help": open a read-only window on a help file void ex_help(exarg_T *eap) { char_u *arg; @@ -4949,11 +4916,10 @@ erret: } -/* - * In an argument search for a language specifiers in the form "@xx". - * Changes the "@" to NUL if found, and returns a pointer to "xx". - * Returns NULL if not found. - */ +/// In an argument search for a language specifiers in the form "@xx". +/// Changes the "@" to NUL if found, and returns a pointer to "xx". +/// +/// @return NULL if not found. char_u *check_help_lang(char_u *arg) { int len = (int)STRLEN(arg); @@ -5019,10 +4985,8 @@ int help_heuristic(char_u *matched_string, int offset, int wrong_case) return (int)(100 * num_letters + STRLEN(matched_string) + offset); } -/* - * Compare functions for qsort() below, that checks the help heuristics number - * that has been put after the tagname by find_tags(). - */ +/// Compare functions for qsort() below, that checks the help heuristics number +/// that has been put after the tagname by find_tags(). static int help_compare(const void *s1, const void *s2) { char *p1; @@ -5033,10 +4997,10 @@ static int help_compare(const void *s1, const void *s2) return strcmp(p1, p2); } -// Find all help tags matching "arg", sort them and return in matches[], with -// the number of matches in num_matches. -// The matches will be sorted with a "best" match algorithm. -// When "keep_lang" is true try keeping the language of the current buffer. +/// Find all help tags matching "arg", sort them and return in matches[], with +/// the number of matches in num_matches. +/// The matches will be sorted with a "best" match algorithm. +/// When "keep_lang" is true try keeping the language of the current buffer. int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool keep_lang) { int i; @@ -5308,10 +5272,8 @@ static void prepare_help_buffer(void) set_buflisted(FALSE); } -/* - * After reading a help file: May cleanup a help buffer when syntax - * highlighting is not used. - */ +/// After reading a help file: May cleanup a help buffer when syntax +/// highlighting is not used. void fix_help_buffer(void) { linenr_T lnum; @@ -5507,17 +5469,13 @@ void fix_help_buffer(void) } } -/* - * ":exusage" - */ +/// ":exusage" void ex_exusage(exarg_T *eap) { do_cmdline_cmd("help ex-cmd-index"); } -/* - * ":viusage" - */ +/// ":viusage" void ex_viusage(exarg_T *eap) { do_cmdline_cmd("help normal-index"); @@ -5826,9 +5784,7 @@ static void helptags_cb(char_u *fname, void *cookie) do_helptags(fname, *(bool *)cookie, true); } -/* - * ":helptags" - */ +/// ":helptags" void ex_helptags(exarg_T *eap) { expand_T xpc; @@ -5857,9 +5813,7 @@ void ex_helptags(exarg_T *eap) } } -/* - * ":helpclose": Close one help window - */ +/// ":helpclose": Close one help window void ex_helpclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { @@ -5873,7 +5827,7 @@ void ex_helpclose(exarg_T *eap) /// Tries to enter to an existing window of given buffer. If no existing buffer /// is found, creates a new split. /// -/// Returns OK/FAIL. +/// @return OK/FAIL. int sub_preview_win(buf_T *preview_buf) { if (preview_buf != NULL) { @@ -6115,9 +6069,11 @@ void ex_substitute(exarg_T *eap) /// Skip over the pattern argument of ":vimgrep /pat/[g][j]". /// Put the start of the pattern in "*s", unless "s" is NULL. -/// If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP. -/// If "s" is not NULL terminate the pattern with a NUL. -/// Return a pointer to the char just past the pattern plus flags. +/// +/// @param flags if not NULL, put the flags in it: VGR_GLOBAL, VGR_NOJUMP. +/// @param s if not NULL, terminate the pattern with a NUL. +/// +/// @return a pointer to the char just past the pattern plus flags. char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) { int c; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 5c040adc1c..fe1dec7298 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -436,8 +436,8 @@ static void script_dump_profile(FILE *fd) } } -/// Return true when a function defined in the current script should be -/// profiled. +/// @return true when a function defined in the current script should be +/// profiled. bool prof_def_func(void) { if (current_sctx.sc_sid > 0) { @@ -492,7 +492,7 @@ void autowrite_all(void) } } -/// Return true if buffer was changed and cannot be abandoned. +/// @return true if buffer was changed and cannot be abandoned. /// For flags use the CCGD_ values. bool check_changed(buf_T *buf, int flags) { @@ -616,7 +616,7 @@ bool dialog_close_terminal(buf_T *buf) return ret == VIM_YES; } -/// Return true if the buffer "buf" can be abandoned, either by making it +/// @return true if the buffer "buf" can be abandoned, either by making it /// hidden, autowriting it or unloading it. bool can_abandon(buf_T *buf, int forceit) { @@ -771,8 +771,8 @@ theend: return ret; } -/// Return FAIL if there is no file name, OK if there is one. -/// Give error message for FAIL. +/// @return FAIL if there is no file name, OK if there is one. +/// Give error message for FAIL. int check_fname(void) { if (curbuf->b_ffname == NULL) { @@ -784,7 +784,7 @@ int check_fname(void) /// Flush the contents of a buffer, unless it has no file name. /// -/// @return FAIL for failure, OK otherwise +/// @return FAIL for failure, OK otherwise int buf_write_all(buf_T *buf, int forceit) { int retval; @@ -808,7 +808,8 @@ int buf_write_all(buf_T *buf, int forceit) /// Isolate one argument, taking backticks. /// Changes the argument in-place, puts a NUL after it. Backticks remain. -/// Return a pointer to the start of the next argument. +/// +/// @return a pointer to the start of the next argument. static char_u *do_one_arg(char_u *str) { char_u *p; @@ -859,7 +860,7 @@ static void get_arglist(garray_T *gap, char_u *str, int escaped) /// Parse a list of arguments (file names), expand them and return in /// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'. /// -/// @return FAIL or OK. +/// @return FAIL or OK. int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) { garray_T ga; @@ -889,7 +890,7 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) /// 0 means before first one /// @param will_edit will edit added argument /// -/// @return FAIL for failure, OK otherwise. +/// @return FAIL for failure, OK otherwise. static int do_arglist(char_u *str, int what, int after, bool will_edit) FUNC_ATTR_NONNULL_ALL { @@ -988,8 +989,8 @@ static void alist_check_arg_idx(void) } } -/// Return true if window "win" is editing the file at the current argument -/// index. +/// @return true if window "win" is editing the file at the current argument +/// index. static bool editing_arg_idx(win_T *win) { return !(win->w_arg_idx >= WARGCOUNT(win) @@ -1717,13 +1718,13 @@ linenr_T *source_breakpoint(void *cookie) return &((struct source_cookie *)cookie)->breakpoint; } -/// Return the address holding the debug tick for a source cookie. +/// @return the address holding the debug tick for a source cookie. int *source_dbg_tick(void *cookie) { return &((struct source_cookie *)cookie)->dbg_tick; } -/// Return the nesting level for a source cookie. +/// @return the nesting level for a source cookie. int source_level(void *cookie) { return ((struct source_cookie *)cookie)->level; @@ -1788,7 +1789,8 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat) /// /// @param name File name of the script. NULL for anonymous :source. /// @param[out] sid_out SID of the new item. -/// @return pointer to the created script item. +/// +/// @return pointer to the created script item. scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out) { static scid_T last_current_SID = 0; @@ -1896,7 +1898,7 @@ int do_source_str(const char *cmd, const char *traceback_name) /// @param check_other check for .vimrc and _vimrc /// @param is_vimrc DOSO_ value /// -/// @return FAIL if file could not be opened, OK otherwise +/// @return FAIL if file could not be opened, OK otherwise int do_source(char *fname, int check_other, int is_vimrc) { struct source_cookie cookie; @@ -2145,6 +2147,7 @@ theend: /// Check if fname was sourced before to finds its SID. /// If it's new, generate a new SID. +/// /// @param[in] fname file path of script /// @param[out] ret_sctx sctx of this script scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) @@ -2614,9 +2617,9 @@ void do_finish(exarg_T *eap, int reanimate) } -/// Return true when a sourced file had the ":finish" command: Don't give error -/// message for missing ":endif". -/// Return false when not sourcing a file. +/// @return true when a sourced file had the ":finish" command: Don't give error +/// message for missing ":endif". +/// false when not sourcing a file. bool source_finished(LineGetter fgetline, void *cookie) { return getline_equal(fgetline, cookie, getsourceline) @@ -2653,8 +2656,8 @@ static char *get_locale_val(int what) } #endif -// Return true when "lang" starts with a valid language name. -// Rejects NULL, empty string, "C", "C.UTF-8" and others. +/// @return true when "lang" starts with a valid language name. +/// Rejects NULL, empty string, "C", "C.UTF-8" and others. static bool is_valid_mess_lang(char *lang) { return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]); @@ -2757,11 +2760,10 @@ void set_lang_var(void) } #ifdef HAVE_WORKING_LIBINTL -/// + /// ":language": Set the language (locale). /// /// @param eap -/// void ex_language(exarg_T *eap) { char *loc; @@ -2870,8 +2872,9 @@ static char_u **locales = NULL; // Array of all available locales # ifndef WIN32 static bool did_init_locales = false; -/// Return an array of strings for all available locales + NULL for the -/// last element. Return NULL in case of error. +/// @return an array of strings for all available locales + NULL for the +/// last element or, +/// NULL in case of error. static char_u **find_locales(void) { garray_T locales_ga; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 76bf4d60c1..511dd44c9f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -38,6 +38,7 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/hardcopy.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/input.h" #include "nvim/keymap.h" @@ -9475,7 +9476,7 @@ static void ex_filetype(exarg_T *eap) /// Source ftplugin.vim and indent.vim to create the necessary FileType /// autocommands. We do this separately from filetype.vim so that these -/// autocommands will always fire first (and thus can be overriden) while still +/// autocommands will always fire first (and thus can be overridden) while still /// allowing general filetype detection to be disabled in the user's init file. void filetype_plugin_enable(void) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f52f3afe7d..84fca137d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -35,6 +35,7 @@ #include "nvim/getchar.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/keymap.h" @@ -231,7 +232,7 @@ static int compl_selected; /// |:checkhealth| completion items /// -/// Regenerates on every new command line prompt, to accomodate changes on the +/// Regenerates on every new command line prompt, to accommodate changes on the /// runtime files. typedef struct { garray_T names; // healthcheck names @@ -632,7 +633,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat validate_cursor(); // May redraw the status line to show the cursor position. - if (p_ru && curwin->w_status_height > 0) { + if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { curwin->w_redr_status = true; } @@ -3631,7 +3632,7 @@ void compute_cmdrow(void) } else { win_T *wp = lastwin_nofloating(); cmdline_row = wp->w_winrow + wp->w_height - + wp->w_status_height; + + wp->w_hsep_height + wp->w_status_height + global_stl_height(); } lines_left = cmdline_row; } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index e398c1ee64..0e2b7c0ece 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -72,7 +72,7 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) n++; // restore height when not full height - if (wp->w_height + wp->w_status_height < topframe->fr_height + if (wp->w_height + wp->w_hsep_height + wp->w_status_height < topframe->fr_height && (fprintf(fd, "exe '%dresize ' . ((&lines * %" PRId64 " + %" PRId64 ") / %" PRId64 ")\n", diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 546345eeac..54430d46af 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -121,9 +121,7 @@ static size_t foldendmarkerlen; // Exported folding functions. {{{1 // copyFoldingState() {{{2 -/* - * Copy that folding state from window "wp_from" to window "wp_to". - */ +/// Copy that folding state from window "wp_from" to window "wp_to". void copyFoldingState(win_T *wp_from, win_T *wp_to) { wp_to->w_fold_manual = wp_from->w_fold_manual; @@ -132,9 +130,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to) } // hasAnyFolding() {{{2 -/* - * Return TRUE if there may be folded lines in the current window. - */ +/// @return TRUE if there may be folded lines in the current window. int hasAnyFolding(win_T *win) { // very simple now, but can become more complex later @@ -259,9 +255,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp } // foldLevel() {{{2 -/* - * Return fold level at line number "lnum" in the current window. - */ +/// @return fold level at line number "lnum" in the current window. int foldLevel(linenr_T lnum) { // While updating the folds lines between invalid_top and invalid_bot have @@ -283,15 +277,16 @@ int foldLevel(linenr_T lnum) } // lineFolded() {{{2 -// Low level function to check if a line is folded. Doesn't use any caching. -// Return true if line is folded. -// Return false if line is not folded. +/// Low level function to check if a line is folded. Doesn't use any caching. +/// +/// @return true if line is folded or, +/// false if line is not folded. bool lineFolded(win_T *const win, const linenr_T lnum) { return fold_info(win, lnum).fi_lines != 0; } -/// fold_info() {{{2 +// fold_info() {{{2 /// /// Count the number of lines that are folded at line number "lnum". /// Normally "lnum" is the first line of a possible fold, and the returned @@ -316,61 +311,49 @@ foldinfo_T fold_info(win_T *win, linenr_T lnum) } // foldmethodIsManual() {{{2 -/* - * Return TRUE if 'foldmethod' is "manual" - */ +/// @return TRUE if 'foldmethod' is "manual" int foldmethodIsManual(win_T *wp) { return wp->w_p_fdm[3] == 'u'; } // foldmethodIsIndent() {{{2 -/* - * Return TRUE if 'foldmethod' is "indent" - */ +/// @return TRUE if 'foldmethod' is "indent" int foldmethodIsIndent(win_T *wp) { return wp->w_p_fdm[0] == 'i'; } // foldmethodIsExpr() {{{2 -/* - * Return TRUE if 'foldmethod' is "expr" - */ +/// @return TRUE if 'foldmethod' is "expr" int foldmethodIsExpr(win_T *wp) { return wp->w_p_fdm[1] == 'x'; } // foldmethodIsMarker() {{{2 -/* - * Return TRUE if 'foldmethod' is "marker" - */ +/// @return TRUE if 'foldmethod' is "marker" int foldmethodIsMarker(win_T *wp) { return wp->w_p_fdm[2] == 'r'; } // foldmethodIsSyntax() {{{2 -/* - * Return TRUE if 'foldmethod' is "syntax" - */ +/// @return TRUE if 'foldmethod' is "syntax" int foldmethodIsSyntax(win_T *wp) { return wp->w_p_fdm[0] == 's'; } // foldmethodIsDiff() {{{2 -/* - * Return TRUE if 'foldmethod' is "diff" - */ +/// @return TRUE if 'foldmethod' is "diff" int foldmethodIsDiff(win_T *wp) { return wp->w_p_fdm[0] == 'd'; } // closeFold() {{{2 -/// Close fold for current window at line "lnum". +/// Close fold for current window at position "pos". /// Repeat "count" times. void closeFold(pos_T pos, long count) { @@ -378,9 +361,7 @@ void closeFold(pos_T pos, long count) } // closeFoldRecurse() {{{2 -/* - * Close fold for current window at line "lnum" recursively. - */ +/// Close fold for current window at position `pos` recursively. void closeFoldRecurse(pos_T pos) { (void)setManualFold(pos, false, true, NULL); @@ -427,28 +408,22 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int ha } // openFold() {{{2 -/* - * Open fold for current window at line "lnum". - * Repeat "count" times. - */ +/// Open fold for current window at position "pos". +/// Repeat "count" times. void openFold(pos_T pos, long count) { setFoldRepeat(pos, count, true); } // openFoldRecurse() {{{2 -/* - * Open fold for current window at line "lnum" recursively. - */ +/// Open fold for current window at position `pos` recursively. void openFoldRecurse(pos_T pos) { (void)setManualFold(pos, true, true, NULL); } // foldOpenCursor() {{{2 -/* - * Open folds until the cursor line is not in a closed fold. - */ +/// Open folds until the cursor line is not in a closed fold. void foldOpenCursor(void) { int done; @@ -466,9 +441,7 @@ void foldOpenCursor(void) } // newFoldLevel() {{{2 -/* - * Set new foldlevel for current window. - */ +/// Set new foldlevel for current window. void newFoldLevel(void) { newFoldLevelWin(curwin); @@ -505,9 +478,7 @@ static void newFoldLevelWin(win_T *wp) } // foldCheckClose() {{{2 -/* - * Apply 'foldlevel' to all folds that don't contain the cursor. - */ +/// Apply 'foldlevel' to all folds that don't contain the cursor. void foldCheckClose(void) { if (*p_fcl != NUL) { // can only be "all" right now @@ -543,8 +514,8 @@ static int checkCloseRec(garray_T *gap, linenr_T lnum, int level) } // foldCreateAllowed() {{{2 -/// Return TRUE if it's allowed to manually create or delete a fold. -/// Give an error message and return FALSE if not. +/// @return TRUE if it's allowed to manually create or delete a fold or, +/// give an error message and return FALSE if not. int foldManualAllowed(bool create) { if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) { @@ -790,9 +761,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const } // clearFolding() {{{2 -/* - * Remove all folding for window "win". - */ +/// Remove all folding for window "win". void clearFolding(win_T *win) { deleteFoldRecurse(win->w_buffer, &win->w_folds); @@ -800,12 +769,10 @@ void clearFolding(win_T *win) } // foldUpdate() {{{2 -/* - * Update folds for changes in the buffer of a window. - * Note that inserted/deleted lines must have already been taken care of by - * calling foldMarkAdjust(). - * The changes in lines from top to bot (inclusive). - */ +/// Update folds for changes in the buffer of a window. +/// Note that inserted/deleted lines must have already been taken care of by +/// calling foldMarkAdjust(). +/// The changes in lines from top to bot (inclusive). void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) { if (compl_busy || State & INSERT) { @@ -856,12 +823,10 @@ void foldUpdateAfterInsert(void) } // foldUpdateAll() {{{2 -/* - * Update all lines in a window for folding. - * Used when a fold setting changes or after reloading the buffer. - * The actual updating is postponed until fold info is used, to avoid doing - * every time a setting is changed or a syntax item is added. - */ +/// Update all lines in a window for folding. +/// Used when a fold setting changes or after reloading the buffer. +/// The actual updating is postponed until fold info is used, to avoid doing +/// every time a setting is changed or a syntax item is added. void foldUpdateAll(win_T *win) { win->w_foldinvalid = true; @@ -992,21 +957,18 @@ int foldMoveTo(const bool updown, const int dir, const long count) } // foldInitWin() {{{2 -/* - * Init the fold info in a new window. - */ +/// Init the fold info in a new window. void foldInitWin(win_T *new_win) { ga_init(&new_win->w_folds, (int)sizeof(fold_T), 10); } // find_wl_entry() {{{2 -/* - * Find an entry in the win->w_lines[] array for buffer line "lnum". - * Only valid entries are considered (for entries where wl_valid is FALSE the - * line number can be wrong). - * Returns index of entry or -1 if not found. - */ +/// Find an entry in the win->w_lines[] array for buffer line "lnum". +/// Only valid entries are considered (for entries where wl_valid is FALSE the +/// line number can be wrong). +/// +/// @return index of entry or -1 if not found. int find_wl_entry(win_T *win, linenr_T lnum) { int i; @@ -1025,9 +987,7 @@ int find_wl_entry(win_T *win, linenr_T lnum) } // foldAdjustVisual() {{{2 -/* - * Adjust the Visual area to include any fold at the start or end completely. - */ +/// Adjust the Visual area to include any fold at the start or end completely. void foldAdjustVisual(void) { pos_T *start, *end; @@ -1059,9 +1019,7 @@ void foldAdjustVisual(void) } // cursor_foldstart() {{{2 -/* - * Move the cursor to the first line of a closed fold. - */ +/// Move the cursor to the first line of a closed fold. void foldAdjustCursor(void) { (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); @@ -1069,9 +1027,7 @@ void foldAdjustCursor(void) // Internal functions for "fold_T" {{{1 // cloneFoldGrowArray() {{{2 -/* - * Will "clone" (i.e deep copy) a garray_T of folds. - */ +/// Will "clone" (i.e deep copy) a garray_T of folds. void cloneFoldGrowArray(garray_T *from, garray_T *to) { fold_T *from_p; @@ -1143,9 +1099,7 @@ static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp) } // foldLevelWin() {{{2 -/* - * Return fold level at line number "lnum" in window "wp". - */ +/// @return fold level at line number "lnum" in window "wp". static int foldLevelWin(win_T *wp, linenr_T lnum) { fold_T *fp; @@ -1169,9 +1123,7 @@ static int foldLevelWin(win_T *wp, linenr_T lnum) } // checkupdate() {{{2 -/* - * Check if the folds in window "wp" are invalid and update them if needed. - */ +/// Check if the folds in window "wp" are invalid and update them if needed. static void checkupdate(win_T *wp) { if (wp->w_foldinvalid) { @@ -1181,10 +1133,8 @@ static void checkupdate(win_T *wp) } // setFoldRepeat() {{{2 -/* - * Open or close fold for current window at line "lnum". - * Repeat "count" times. - */ +/// Open or close fold for current window at position `pos`. +/// Repeat "count" times. static void setFoldRepeat(pos_T pos, long count, int do_open) { int done; @@ -1204,7 +1154,6 @@ static void setFoldRepeat(pos_T pos, long count, int do_open) } // setManualFold() {{{2 -/// /// Open or close the fold in the current window which contains "lnum". /// Also does this for other windows in diff mode when needed. /// @@ -1344,9 +1293,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu } // foldOpenNested() {{{2 -/* - * Open all nested folds in fold "fpr" recursively. - */ +/// Open all nested folds in fold "fpr" recursively. static void foldOpenNested(fold_T *fpr) { fold_T *fp; @@ -1359,9 +1306,10 @@ static void foldOpenNested(fold_T *fpr) } // deleteFoldEntry() {{{2 -// Delete fold "idx" from growarray "gap". -// When "recursive" is true also delete all the folds contained in it. -// When "recursive" is false contained folds are moved one level up. +/// Delete fold "idx" from growarray "gap". +/// +/// @param recursive when true, also delete all the folds contained in it. +/// when false, contained folds are moved one level up. static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, const bool recursive) { @@ -1408,9 +1356,7 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, } // deleteFoldRecurse() {{{2 -/* - * Delete nested folds in a fold. - */ +/// Delete nested folds in a fold. void deleteFoldRecurse(buf_T *bp, garray_T *gap) { #define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(bp, &((fd)->fd_nested)) @@ -1418,9 +1364,7 @@ void deleteFoldRecurse(buf_T *bp, garray_T *gap) } // foldMarkAdjust() {{{2 -/* - * Update line numbers of folds for inserted/deleted lines. - */ +/// Update line numbers of folds for inserted/deleted lines. void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after) { // If deleting marks from line1 to line2, but not deleting all those @@ -1538,10 +1482,8 @@ static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, line } // getDeepestNesting() {{{2 -/* - * Get the lowest 'foldlevel' value that makes the deepest nested fold in the - * current window open. - */ +/// Get the lowest 'foldlevel' value that makes the deepest nested fold in +/// window `wp`. int getDeepestNesting(win_T *wp) { checkupdate(wp); @@ -1633,7 +1575,7 @@ static void checkSmall(win_T *const wp, fold_T *const fp, const linenr_T lnum_of } // setSmallMaybe() {{{2 -// Set small flags in "gap" to kNone. +/// Set small flags in "gap" to kNone. static void setSmallMaybe(garray_T *gap) { fold_T *fp = (fold_T *)gap->ga_data; @@ -1643,10 +1585,8 @@ static void setSmallMaybe(garray_T *gap) } // foldCreateMarkers() {{{2 -/* - * Create a fold from line "start" to line "end" (inclusive) in the current - * window by adding markers. - */ +/// Create a fold from line "start" to line "end" (inclusive) in window `wp` +/// by adding markers. static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end) { buf_T *buf = wp->w_buffer; @@ -1672,9 +1612,7 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end) } // foldAddMarker() {{{2 -/* - * Add "marker[markerlen]" in 'commentstring' to line "lnum". - */ +/// Add "marker[markerlen]" in 'commentstring' to position `pos`. static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen) { char_u *cms = buf->b_p_cms; @@ -1731,11 +1669,10 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu } // foldDelMarker() {{{2 -// -// Delete marker "marker[markerlen]" at the end of line "lnum". -// Delete 'commentstring' if it matches. -// If the marker is not found, there is no error message. Could be a missing -// close-marker. +/// Delete marker "marker[markerlen]" at the end of line "lnum". +/// Delete 'commentstring' if it matches. +/// If the marker is not found, there is no error message. Could be a missing +/// close-marker. static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t markerlen) { char_u *newline; @@ -1892,9 +1829,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin } // foldtext_cleanup() {{{2 -/* - * Remove 'foldmarker' and 'commentstring' from "str" (in-place). - */ +/// Remove 'foldmarker' and 'commentstring' from "str" (in-place). void foldtext_cleanup(char_u *str) { char_u *s; @@ -1974,10 +1909,8 @@ void foldtext_cleanup(char_u *str) // Function declarations. {{{2 // foldUpdateIEMS() {{{2 -/* - * Update the folding for window "wp", at least from lines "top" to "bot". - * IEMS = "Indent Expr Marker Syntax" - */ +/// Update the folding for window "wp", at least from lines "top" to "bot". +/// IEMS = "Indent Expr Marker Syntax" static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) { fline_T fline; @@ -2640,9 +2573,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level, } // foldInsert() {{{2 -/* - * Insert a new fold in "gap" at position "i". - */ +/// Insert a new fold in "gap" at position "i". static void foldInsert(garray_T *gap, int i) { fold_T *fp; @@ -2658,13 +2589,11 @@ static void foldInsert(garray_T *gap, int i) } // foldSplit() {{{2 -/* - * Split the "i"th fold in "gap", which starts before "top" and ends below - * "bot" in two pieces, one ending above "top" and the other starting below - * "bot". - * The caller must first have taken care of any nested folds from "top" to - * "bot"! - */ +/// Split the "i"th fold in "gap", which starts before "top" and ends below +/// "bot" in two pieces, one ending above "top" and the other starting below +/// "bot". +/// The caller must first have taken care of any nested folds from "top" to +/// "bot"! static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr_T top, const linenr_T bot) { @@ -2704,24 +2633,22 @@ static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr } // foldRemove() {{{2 -/* - * Remove folds within the range "top" to and including "bot". - * Check for these situations: - * 1 2 3 - * 1 2 3 - * top 2 3 4 5 - * 2 3 4 5 - * bot 2 3 4 5 - * 3 5 6 - * 3 5 6 - * - * 1: not changed - * 2: truncate to stop above "top" - * 3: split in two parts, one stops above "top", other starts below "bot". - * 4: deleted - * 5: made to start below "bot". - * 6: not changed - */ +/// Remove folds within the range "top" to and including "bot". +/// Check for these situations: +/// 1 2 3 +/// 1 2 3 +/// top 2 3 4 5 +/// 2 3 4 5 +/// bot 2 3 4 5 +/// 3 5 6 +/// 3 5 6 +/// +/// 1: not changed +/// 2: truncate to stop above "top" +/// 3: split in two parts, one stops above "top", other starts below "bot". +/// 4: deleted +/// 5: made to start below "bot". +/// 6: not changed static void foldRemove(win_T *const wp, garray_T *gap, linenr_T top, linenr_T bot) { fold_T *fp = NULL; @@ -2786,35 +2713,35 @@ static void foldReverseOrder(garray_T *gap, const linenr_T start_arg, const line } // foldMoveRange() {{{2 -// Move folds within the inclusive range "line1" to "line2" to after "dest" -// require "line1" <= "line2" <= "dest" -// -// There are the following situations for the first fold at or below line1 - 1. -// 1 2 3 4 -// 1 2 3 4 -// line1 2 3 4 -// 2 3 4 5 6 7 -// line2 3 4 5 6 7 -// 3 4 6 7 8 9 -// dest 4 7 8 9 -// 4 7 8 10 -// 4 7 8 10 -// -// In the following descriptions, "moved" means moving in the buffer, *and* in -// the fold array. -// Meanwhile, "shifted" just means moving in the buffer. -// 1. not changed -// 2. truncated above line1 -// 3. length reduced by line2 - line1, folds starting between the end of 3 and -// dest are truncated and shifted up -// 4. internal folds moved (from [line1, line2] to dest) -// 5. moved to dest. -// 6. truncated below line2 and moved. -// 7. length reduced by line2 - dest, folds starting between line2 and dest are -// removed, top is moved down by move_len. -// 8. truncated below dest and shifted up. -// 9. shifted up -// 10. not changed +/// Move folds within the inclusive range "line1" to "line2" to after "dest" +/// require "line1" <= "line2" <= "dest" +/// +/// There are the following situations for the first fold at or below line1 - 1. +/// 1 2 3 4 +/// 1 2 3 4 +/// line1 2 3 4 +/// 2 3 4 5 6 7 +/// line2 3 4 5 6 7 +/// 3 4 6 7 8 9 +/// dest 4 7 8 9 +/// 4 7 8 10 +/// 4 7 8 10 +/// +/// In the following descriptions, "moved" means moving in the buffer, *and* in +/// the fold array. +/// Meanwhile, "shifted" just means moving in the buffer. +/// 1. not changed +/// 2. truncated above line1 +/// 3. length reduced by line2 - line1, folds starting between the end of 3 and +/// dest are truncated and shifted up +/// 4. internal folds moved (from [line1, line2] to dest) +/// 5. moved to dest. +/// 6. truncated below line2 and moved. +/// 7. length reduced by line2 - dest, folds starting between line2 and dest are +/// removed, top is moved down by move_len. +/// 8. truncated below dest and shifted up. +/// 9. shifted up +/// 10. not changed static void truncate_fold(win_T *const wp, fold_T *fp, linenr_T end) { // I want to stop *at here*, foldRemove() stops *above* top @@ -2929,13 +2856,11 @@ void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const l #undef FOLD_INDEX // foldMerge() {{{2 -/* - * Merge two adjacent folds (and the nested ones in them). - * This only works correctly when the folds are really adjacent! Thus "fp1" - * must end just above "fp2". - * The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1". - * Fold entry "fp2" in "gap" is deleted. - */ +/// Merge two adjacent folds (and the nested ones in them). +/// This only works correctly when the folds are really adjacent! Thus "fp1" +/// must end just above "fp2". +/// The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1". +/// Fold entry "fp2" in "gap" is deleted. static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) { fold_T *fp3; @@ -2968,11 +2893,10 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) } // foldlevelIndent() {{{2 -/* - * Low level function to get the foldlevel for the "indent" method. - * Doesn't use any caching. - * Returns a level of -1 if the foldlevel depends on surrounding lines. - */ +/// Low level function to get the foldlevel for the "indent" method. +/// Doesn't use any caching. +/// +/// @return a level of -1 if the foldlevel depends on surrounding lines. static void foldlevelIndent(fline_T *flp) { char_u *s; @@ -3000,10 +2924,8 @@ static void foldlevelIndent(fline_T *flp) } // foldlevelDiff() {{{2 -/* - * Low level function to get the foldlevel for the "diff" method. - * Doesn't use any caching. - */ +/// Low level function to get the foldlevel for the "diff" method. +/// Doesn't use any caching. static void foldlevelDiff(fline_T *flp) { if (diff_infold(flp->wp, flp->lnum + flp->off)) { @@ -3014,11 +2936,10 @@ static void foldlevelDiff(fline_T *flp) } // foldlevelExpr() {{{2 -/* - * Low level function to get the foldlevel for the "expr" method. - * Doesn't use any caching. - * Returns a level of -1 if the foldlevel depends on surrounding lines. - */ +/// Low level function to get the foldlevel for the "expr" method. +/// Doesn't use any caching. +/// +/// @return a level of -1 if the foldlevel depends on surrounding lines. static void foldlevelExpr(fline_T *flp) { win_T *win; @@ -3113,11 +3034,9 @@ static void foldlevelExpr(fline_T *flp) } // parseMarker() {{{2 -/* - * Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and - * "foldendmarkerlen". - * Relies on the option value to have been checked for correctness already. - */ +/// Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and +/// "foldendmarkerlen". +/// Relies on the option value to have been checked for correctness already. static void parseMarker(win_T *wp) { foldendmarker = vim_strchr(wp->w_p_fmr, ','); @@ -3126,15 +3045,13 @@ static void parseMarker(win_T *wp) } // foldlevelMarker() {{{2 -/* - * Low level function to get the foldlevel for the "marker" method. - * "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been - * set before calling this. - * Requires that flp->lvl is set to the fold level of the previous line! - * Careful: This means you can't call this function twice on the same line. - * Doesn't use any caching. - * Sets flp->start when a start marker was found. - */ +/// Low level function to get the foldlevel for the "marker" method. +/// "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been +/// set before calling this. +/// Requires that flp->lvl is set to the fold level of the previous line! +/// Careful: This means you can't call this function twice on the same line. +/// Doesn't use any caching. +/// Sets flp->start when a start marker was found. static void foldlevelMarker(fline_T *flp) { char_u *startmarker; @@ -3205,10 +3122,8 @@ static void foldlevelMarker(fline_T *flp) } // foldlevelSyntax() {{{2 -/* - * Low level function to get the foldlevel for the "syntax" method. - * Doesn't use any caching. - */ +/// Low level function to get the foldlevel for the "syntax" method. +/// Doesn't use any caching. static void foldlevelSyntax(fline_T *flp) { linenr_T lnum = flp->lnum + flp->off; @@ -3228,11 +3143,9 @@ static void foldlevelSyntax(fline_T *flp) // functions for storing the fold state in a View {{{1 // put_folds() {{{2 - -/* - * Write commands to "fd" to restore the manual folds in window "wp". - * Return FAIL if writing fails. - */ +/// Write commands to "fd" to restore the manual folds in window "wp". +/// +/// @return FAIL if writing fails. int put_folds(FILE *fd, win_T *wp) { if (foldmethodIsManual(wp)) { @@ -3252,10 +3165,9 @@ int put_folds(FILE *fd, win_T *wp) } // put_folds_recurse() {{{2 -/* - * Write commands to "fd" to recreate manually created folds. - * Returns FAIL when writing failed. - */ +/// Write commands to "fd" to recreate manually created folds. +/// +/// @return FAIL when writing failed. static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) { fold_T *fp = (fold_T *)gap->ga_data; @@ -3276,10 +3188,9 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) } // put_foldopen_recurse() {{{2 -/* - * Write commands to "fd" to open and close manually opened/closed folds. - * Returns FAIL when writing failed. - */ +/// Write commands to "fd" to open and close manually opened/closed folds. +/// +/// @return FAIL when writing failed. static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off) { int level; @@ -3325,10 +3236,9 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off } // put_fold_open_close() {{{2 -/* - * Write the open or close command to "fd". - * Returns FAIL when writing failed. - */ +/// Write the open or close command to "fd". +/// +/// @return FAIL when writing failed. static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off) { if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0 diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index f35817c466..70a7be86b5 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -49,6 +49,7 @@ local c_proto = Ct( (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * (fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) * + (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1) * fill * P(';') ) diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 3cb117d8b5..5e70442dce 100644..100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -3,13 +3,14 @@ local mpack = require('mpack') local nvimdir = arg[1] package.path = nvimdir .. '/?.lua;' .. package.path -assert(#arg == 7) +assert(#arg == 8) local input = io.open(arg[2], 'rb') local proto_output = io.open(arg[3], 'wb') local call_output = io.open(arg[4], 'wb') local remote_output = io.open(arg[5], 'wb') local bridge_output = io.open(arg[6], 'wb') local metadata_output = io.open(arg[7], 'wb') +local client_output = io.open(arg[8], 'wb') local c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) @@ -50,6 +51,52 @@ local function write_arglist(output, ev, need_copy) end end +local function call_ui_event_method(output, ev) + output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') + + local hlattrs_args_count = 0 + if #ev.parameters > 0 then + output:write(' if (args.size < '..(#ev.parameters)) + for j = 1, #ev.parameters do + local kind = ev.parameters[j][1] + if kind ~= "Object" then + if kind == 'HlAttrs' then kind = 'Dictionary' end + output:write('\n || args.items['..(j-1)..'].type != kObjectType'..kind..'') + end + end + output:write(') {\n') + output:write(' ELOG("Error handling ui event \''..ev.name..'\'");\n') + output:write(' return;\n') + output:write(' }\n') + end + + for j = 1, #ev.parameters do + local param = ev.parameters[j] + local kind = param[1] + output:write(' '..kind..' arg_'..j..' = ') + if kind == 'HlAttrs' then + -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs + output:write('ui_client_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') + hlattrs_args_count = hlattrs_args_count + 1 + elseif kind == 'Object' then + output:write('args.items['..(j-1)..'];\n') + else + output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n') + end + end + + output:write(' ui_call_'..ev.name..'(') + for j = 1, #ev.parameters do + output:write('arg_'..j) + if j ~= #ev.parameters then + output:write(', ') + end + end + output:write(');\n') + + output:write('}\n\n') +end + for i = 1, #events do local ev = events[i] assert(ev.return_type == 'void') @@ -160,12 +207,35 @@ for i = 1, #events do call_output:write(";\n") call_output:write("}\n\n") end + + if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then + call_ui_event_method(client_output, ev) + end end +-- Generate the map_init method for client handlers +client_output:write([[ +void ui_client_methods_table_init(void) +{ + +]]) + +for i = 1, #events do + local fn = events[i] + if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then + client_output:write(' add_ui_client_event_handler('.. + '(String) {.data = "'..fn.name..'", '.. + '.size = sizeof("'..fn.name..'") - 1}, '.. + '(UIClientHandler) ui_client_event_'..fn.name..');\n') + end +end + +client_output:write('\n}\n\n') + proto_output:close() call_output:close() remote_output:close() -bridge_output:close() +client_output:close() -- don't expose internal attributes like "impl_name" in public metadata local exported_attributes = {'name', 'parameters', diff --git a/src/nvim/globals.h b/src/nvim/globals.h index cbd67afb09..605a2183f3 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -361,6 +361,11 @@ EXTERN int provider_call_nesting INIT(= 0); EXTERN int t_colors INIT(= 256); // int value of T_CCO +// Flags to indicate an additional string for highlight name completion. +EXTERN int include_none INIT(= 0); // when 1 include "None" +EXTERN int include_default INIT(= 0); // when 1 include "default" +EXTERN int include_link INIT(= 0); // when 2 include "link" and "clear" + // When highlight_match is true, highlight a match, starting at the cursor // position. Search_match_lines is the number of lines after the match (0 for // a match within one line), search_match_endcol the column number of the diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 93c8c53e33..575b239f5a 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -22,6 +22,7 @@ #include "nvim/fileio.h" #include "nvim/garray.h" #include "nvim/hardcopy.h" +#include "nvim/highlight_group.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index abbda1e9fa..7d91f38d56 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -8,13 +8,13 @@ #include "nvim/decoration_provider.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/map.h" #include "nvim/message.h" #include "nvim/option.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/vim.h" diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index dcfb9358bb..e0ee649013 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -74,7 +74,8 @@ typedef enum { HLF_R, // return to continue message and yes/no questions HLF_S, // status lines HLF_SNC, // status lines of not-current windows - HLF_C, // column to separate vertically split windows + HLF_C, // window split separators + HLF_VSP, // VertSplit HLF_T, // Titles for output from ":set all", ":autocmd" etc. HLF_V, // Visual mode HLF_VNC, // Visual mode, autoselecting and not clipboard owner @@ -133,10 +134,11 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_R] = "Question", [HLF_S] = "StatusLine", [HLF_SNC] = "StatusLineNC", - [HLF_C] = "VertSplit", + [HLF_C] = "WinSeparator", [HLF_T] = "Title", [HLF_V] = "Visual", [HLF_VNC] = "VisualNC", + [HLF_VSP] = "VertSplit", [HLF_W] = "WarningMsg", [HLF_WM] = "WildMenu", [HLF_FL] = "Folded", diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c new file mode 100644 index 0000000000..f342ada3db --- /dev/null +++ b/src/nvim/highlight_group.c @@ -0,0 +1,2799 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// highlight_group.c: code for managing highlight groups + +#include "nvim/autocmd.h" +#include "nvim/api/private/helpers.h" +#include "nvim/charset.h" +#include "nvim/cursor_shape.h" +#include "nvim/ex_docmd.h" +#include "nvim/garray.h" +#include "nvim/highlight.h" +#include "nvim/highlight_group.h" +#include "nvim/lua/executor.h" +#include "nvim/map.h" +#include "nvim/option.h" +#include "nvim/runtime.h" +#include "nvim/screen.h" + +/// \addtogroup SG_SET +/// @{ +#define SG_CTERM 2 // cterm has been set +#define SG_GUI 4 // gui has been set +#define SG_LINK 8 // link has been set +/// @} + +#define MAX_SYN_NAME 200 + +// builtin |highlight-groups| +static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; + +Map(cstr_t, int) highlight_unames = MAP_INIT; + +/// The "term", "cterm" and "gui" arguments can be any combination of the +/// following names, separated by commas (but no spaces!). +static char *(hl_name_table[]) = + { "bold", "standout", "underline", "underlineline", "undercurl", "underdot", + "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; +static int hl_attr_table[] = + { HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH, + HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; + +/// Structure that stores information about a highlight group. +/// The ID of a highlight group is also called group ID. It is the index in +/// the highlight_ga array PLUS ONE. +typedef struct { + char_u *sg_name; ///< highlight group name + char *sg_name_u; ///< uppercase of sg_name + bool sg_cleared; ///< "hi clear" was used + int sg_attr; ///< Screen attr @see ATTR_ENTRY + int sg_link; ///< link to this highlight group ID + int sg_deflink; ///< default link; restored in highlight_clear() + int sg_set; ///< combination of flags in \ref SG_SET + sctx_T sg_deflink_sctx; ///< script where the default link was set + sctx_T sg_script_ctx; ///< script in which the group was last set + // for terminal UIs + int sg_cterm; ///< "cterm=" highlighting attr + ///< (combination of \ref HlAttrFlags) + int sg_cterm_fg; ///< terminal fg color number + 1 + int sg_cterm_bg; ///< terminal bg color number + 1 + bool sg_cterm_bold; ///< bold attr was set for light color + // for RGB UIs + int sg_gui; ///< "gui=" highlighting attributes + ///< (combination of \ref HlAttrFlags) + RgbValue sg_rgb_fg; ///< RGB foreground color + RgbValue sg_rgb_bg; ///< RGB background color + RgbValue sg_rgb_sp; ///< RGB special color + char *sg_rgb_fg_name; ///< RGB foreground color name + char *sg_rgb_bg_name; ///< RGB background color name + char *sg_rgb_sp_name; ///< RGB special color name + + int sg_blend; ///< blend level (0-100 inclusive), -1 if unset +} HlGroup; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "highlight_group.c.generated.h" +#endif + +static inline HlGroup *HL_TABLE(void) +{ + return ((HlGroup *)((highlight_ga.ga_data))); +} + +// The default highlight groups. These are compiled-in for fast startup and +// they still work when the runtime files can't be found. +// +// When making changes here, also change runtime/colors/default.vim! + +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", + "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White", + "IncSearch cterm=reverse gui=reverse", + "ModeMsg cterm=bold gui=bold", + "NonText ctermfg=Blue gui=bold guifg=Blue", + "Normal cterm=NONE gui=NONE", + "PmenuSbar ctermbg=Grey guibg=Grey", + "StatusLine cterm=reverse,bold gui=reverse,bold", + "StatusLineNC cterm=reverse gui=reverse", + "TabLineFill cterm=reverse gui=reverse", + "TabLineSel cterm=bold gui=bold", + "TermCursor cterm=reverse gui=reverse", + "VertSplit cterm=reverse gui=reverse", + "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", + "default link WinSeparator VertSplit", + "default link EndOfBuffer NonText", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", + "default link QuickFixLine Search", + "default link CursorLineSign SignColumn", + "default link CursorLineFold FoldColumn", + "default link Substitute Search", + "default link Whitespace NonText", + "default link MsgSeparator StatusLine", + "default link NormalFloat Pmenu", + "default link FloatBorder WinSeparator", + "default FloatShadow blend=80 guibg=Black", + "default FloatShadowThrough blend=100 guibg=Black", + "RedrawDebugNormal cterm=reverse gui=reverse", + "RedrawDebugClear ctermbg=Yellow guibg=Yellow", + "RedrawDebugComposed ctermbg=Green guibg=Green", + "RedrawDebugRecompose ctermbg=Red guibg=Red", + "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red", + "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow", + "default link String Constant", + "default link Character Constant", + "default link Number Constant", + "default link Boolean Constant", + "default link Float Number", + "default link Function Identifier", + "default link Conditional Statement", + "default link Repeat Statement", + "default link Label Statement", + "default link Operator Statement", + "default link Keyword Statement", + "default link Exception Statement", + "default link Include PreProc", + "default link Define PreProc", + "default link Macro PreProc", + "default link PreCondit PreProc", + "default link StorageClass Type", + "default link Structure Type", + "default link Typedef Type", + "default link Tag Special", + "default link SpecialChar Special", + "default link Delimiter Special", + "default link SpecialComment Special", + "default link Debug Special", + "default DiagnosticError ctermfg=1 guifg=Red", + "default DiagnosticWarn ctermfg=3 guifg=Orange", + "default DiagnosticInfo ctermfg=4 guifg=LightBlue", + "default DiagnosticHint ctermfg=7 guifg=LightGrey", + "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red", + "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange", + "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue", + "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey", + "default link DiagnosticVirtualTextError DiagnosticError", + "default link DiagnosticVirtualTextWarn DiagnosticWarn", + "default link DiagnosticVirtualTextInfo DiagnosticInfo", + "default link DiagnosticVirtualTextHint DiagnosticHint", + "default link DiagnosticFloatingError DiagnosticError", + "default link DiagnosticFloatingWarn DiagnosticWarn", + "default link DiagnosticFloatingInfo DiagnosticInfo", + "default link DiagnosticFloatingHint DiagnosticHint", + "default link DiagnosticSignError DiagnosticError", + "default link DiagnosticSignWarn DiagnosticWarn", + "default link DiagnosticSignInfo DiagnosticInfo", + "default link DiagnosticSignHint DiagnosticHint", + NULL +}; + +// Default colors only used with a light background. +static const char *highlight_init_light[] = { + "ColorColumn ctermbg=LightRed guibg=LightRed", + "CursorColumn ctermbg=LightGrey guibg=Grey90", + "CursorLine cterm=underline guibg=Grey90", + "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown", + "DiffAdd ctermbg=LightBlue guibg=LightBlue", + "DiffChange ctermbg=LightMagenta guibg=LightMagenta", + "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan", + "Directory ctermfg=DarkBlue guifg=Blue", + "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", + "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue", + "LineNr ctermfg=Brown guifg=Brown", + "MatchParen ctermbg=Cyan guibg=Cyan", + "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen", + "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta", + "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey", + "PmenuThumb ctermbg=Black guibg=Black", + "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen", + "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE", + "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", + "SpecialKey ctermfg=DarkBlue guifg=Blue", + "SpellBad ctermbg=LightRed guisp=Red gui=undercurl", + "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl", + "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl", + "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl", + "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey", + "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", + "Visual guibg=LightGrey", + "WarningMsg ctermfg=DarkRed guifg=Red", + "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE", + "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE", + "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE", + "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE", + "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE", + "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE", + "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE", + "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue", + "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE", + NULL +}; + +// Default colors only used with a dark background. +static const char *highlight_init_dark[] = { + "ColorColumn ctermbg=DarkRed guibg=DarkRed", + "CursorColumn ctermbg=DarkGrey guibg=Grey40", + "CursorLine cterm=underline guibg=Grey40", + "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow", + "DiffAdd ctermbg=DarkBlue guibg=DarkBlue", + "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta", + "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan", + "Directory ctermfg=LightCyan guifg=Cyan", + "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", + "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan", + "LineNr ctermfg=Yellow guifg=Yellow", + "MatchParen ctermbg=DarkCyan guibg=DarkCyan", + "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen", + "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta", + "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey", + "PmenuThumb ctermbg=White guibg=White", + "Question ctermfg=LightGreen gui=bold guifg=Green", + "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", + "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", + "SpecialKey ctermfg=LightBlue guifg=Cyan", + "SpellBad ctermbg=Red guisp=Red gui=undercurl", + "SpellCap ctermbg=Blue guisp=Blue gui=undercurl", + "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl", + "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl", + "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey", + "Title ctermfg=LightMagenta gui=bold guifg=Magenta", + "Visual guibg=DarkGrey", + "WarningMsg ctermfg=LightRed guifg=Red", + "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE", + "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE", + "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE", + "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE", + "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE", + "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE", + "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE", + "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff", + "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE", + NULL +}; + +const char *const highlight_init_cmdline[] = { + // XXX When modifying a list modify it in both valid and invalid halves. + // 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, +}; + +/// Returns the number of highlight groups. +int highlight_num_groups(void) +{ + return highlight_ga.ga_len; +} + +/// Returns the name of a highlight group. +char_u *highlight_group_name(int id) +{ + return HL_TABLE()[id].sg_name; +} + +/// Returns the ID of the link to a highlight group. +int highlight_link_id(int id) +{ + return HL_TABLE()[id].sg_link; +} + +/// 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(bool both, bool reset) +{ + 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. + char_u *p = get_var_value("g:colors_name"); + if (p != NULL) { + // Value of g:colors_name could be freed in load_colors() and make + // p invalid, so copy it. + char_u *copy_p = vim_strsave(p); + bool okay = load_colors(copy_p); + xfree(copy_p); + if (okay) { + return; + } + } + + // Didn't use a color file, use the compiled-in colors. + if (both) { + 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(). + // Not everything has been setup then, and that call will overrule + // everything anyway. + return; + } + + 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 + // depend on the number of colors available. + // With 8 colors brown is equal to yellow, need to use black for Search fg + // to avoid Statement highlighted text disappears. + // Clear the attributes, needed when changing the t_Co value. + if (t_colors > 8) { + do_highlight((*p_bg == 'l' + ? "Visual cterm=NONE ctermbg=LightGrey" + : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); + } else { + do_highlight("Visual cterm=reverse ctermbg=NONE", false, true); + if (*p_bg == 'l') { + do_highlight("Search ctermfg=black", false, true); + } + } + + syn_init_cmdline_highlight(false, false); +} + +/// Load color file "name". +/// Return OK for success, FAIL for failure. +int load_colors(char_u *name) +{ + char_u *buf; + int retval = FAIL; + static bool recursive = false; + + // When being called recursively, this is probably because setting + // 'background' caused the highlighting to be reloaded. This means it is + // working, thus we should return OK. + if (recursive) { + return OK; + } + + recursive = true; + size_t buflen = STRLEN(name) + 12; + buf = xmalloc(buflen); + apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); + snprintf((char *)buf, buflen, "colors/%s.vim", name); + retval = source_runtime((char *)buf, DIP_START + DIP_OPT); + if (retval == FAIL) { + snprintf((char *)buf, buflen, "colors/%s.lua", name); + retval = source_runtime((char *)buf, DIP_START + DIP_OPT); + } + xfree(buf); + apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf); + + recursive = false; + + return retval; +} + +static char *(color_names[28]) = { + "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" +}; +// indices: +// 0, 1, 2, 3, +// 4, 5, 6, 7, +// 8, 9, 10, 11, +// 12, 13, +// 14, 15, 16, 17, +// 18, 19, 20, 21, 22, +// 23, 24, 25, 26, 27 +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, 3, + 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 }; + +// Lookup the "cterm" value to be used for color with index "idx" in +// color_names[]. +// "boldp" will be set to TRUE or FALSE for a foreground color when using 8 +// colors, otherwise it will be unchanged. +int lookup_color(const int idx, const bool foreground, TriState *const boldp) +{ + int color = color_numbers_16[idx]; + + // Use the _16 table to check if it's a valid color name. + if (color < 0) { + return -1; + } + + if (t_colors == 8) { + // t_Co is 8: use the 8 colors table + color = color_numbers_8[idx]; + if (foreground) { + // set/reset bold attribute to get light foreground + // colors (on some terminals, e.g. "linux") + if (color & 8) { + *boldp = kTrue; + } else { + *boldp = kFalse; + } + } + color &= 7; // truncate to 8 colors + } else if (t_colors == 16) { + color = color_numbers_8[idx]; + } else if (t_colors == 88) { + color = color_numbers_88[idx]; + } else if (t_colors >= 256) { + color = color_numbers_256[idx]; + } + return color; +} + +void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) +{ + int idx = id - 1; // Index is ID minus one. + + bool is_default = attrs.rgb_ae_attr & HL_DEFAULT; + + // Return if "default" was used and the group already has settings + if (is_default && hl_has_settings(idx, true)) { + return; + } + + HlGroup *g = &HL_TABLE()[idx]; + + if (link_id > 0) { + g->sg_cleared = false; + g->sg_link = link_id; + g->sg_script_ctx = current_sctx; + g->sg_script_ctx.sc_lnum += sourcing_lnum; + g->sg_set |= SG_LINK; + if (is_default) { + g->sg_deflink = link_id; + g->sg_deflink_sctx = current_sctx; + g->sg_deflink_sctx.sc_lnum += sourcing_lnum; + } + return; + } + + g->sg_cleared = false; + g->sg_link = 0; + g->sg_gui = attrs.rgb_ae_attr; + + g->sg_rgb_fg = attrs.rgb_fg_color; + g->sg_rgb_bg = attrs.rgb_bg_color; + g->sg_rgb_sp = attrs.rgb_sp_color; + + struct { + char **dest; RgbValue val; Object name; + } cattrs[] = { + { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, + { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, + { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, + { NULL, -1, NIL }, + }; + + char hex_name[8]; + char *name; + + for (int j = 0; cattrs[j].dest; j++) { + if (cattrs[j].val < 0) { + XFREE_CLEAR(*cattrs[j].dest); + continue; + } + + if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { + name = cattrs[j].name.data.string.data; + } else { + snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); + name = hex_name; + } + + if (!*cattrs[j].dest + || STRCMP(*cattrs[j].dest, name) != 0) { + xfree(*cattrs[j].dest); + *cattrs[j].dest = xstrdup(name); + } + } + + g->sg_cterm = attrs.cterm_ae_attr; + g->sg_cterm_bg = attrs.cterm_bg_color; + g->sg_cterm_fg = attrs.cterm_fg_color; + g->sg_cterm_bold = g->sg_cterm & HL_BOLD; + g->sg_blend = attrs.hl_blend; + + g->sg_script_ctx = current_sctx; + g->sg_script_ctx.sc_lnum += sourcing_lnum; + + // 'Normal' is special + if (STRCMP(g->sg_name_u, "NORMAL") == 0) { + cterm_normal_fg_color = g->sg_cterm_fg; + cterm_normal_bg_color = g->sg_cterm_bg; + normal_fg = g->sg_rgb_fg; + normal_bg = g->sg_rgb_bg; + normal_sp = g->sg_rgb_sp; + ui_default_colors_set(); + } else { + g->sg_attr = hl_get_syn_attr(0, id, attrs); + + // a cursor style uses this syn_id, make sure its attribute is updated. + if (cursor_mode_uses_syn_id(id)) { + ui_mode_info_set(); + } + } +} + +/// 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; + int off; + int len; + int attr; + int id; + int idx; + HlGroup item_before; + bool did_change = false; + bool dodefault = false; + bool doclear = false; + bool dolink = false; + bool error = false; + int color; + bool is_normal_group = false; // "Normal" group + bool did_highlight_changed = false; + + // 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(brammool): only call when the group has attributes set + highlight_list_one(i); + } + return; + } + + // 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", (size_t)(name_end - line)) == 0) { + dodefault = true; + line = linep; + 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", (size_t)(name_end - line)) == 0) { + doclear = true; + } else if (strncmp(line, "link", (size_t)(name_end - line)) == 0) { + dolink = true; + } + + // ":highlight {group-name}": list highlighting for one group. + if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) { + id = syn_name2id_len((const char_u *)line, (size_t)(name_end - line)); + if (id == 0) { + semsg(_("E411: highlight group not found: %s"), line); + } else { + highlight_list_one(id); + } + return; + } + + // Handle ":highlight link {from} {to}" command. + if (dolink) { + const char *from_start = linep; + const char *from_end; + const char *to_start; + const char *to_end; + int from_id; + int to_id; + HlGroup *hlgroup = NULL; + + 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((uint8_t)(*from_start)) + || ends_excmd((uint8_t)(*to_start))) { + semsg(_("E412: Not enough arguments: \":highlight link %s\""), + from_start); + return; + } + + if (!ends_excmd(*skipwhite((const char_u *)to_end))) { + semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start); + return; + } + + from_id = syn_check_group(from_start, (size_t)(from_end - from_start)); + if (strncmp(to_start, "NONE", 4) == 0) { + to_id = 0; + } else { + to_id = syn_check_group(to_start, (size_t)(to_end - to_start)); + } + + if (from_id > 0) { + hlgroup = &HL_TABLE()[from_id - 1]; + if (dodefault && (forceit || hlgroup->sg_deflink == 0)) { + hlgroup->sg_deflink = to_id; + hlgroup->sg_deflink_sctx = current_sctx; + hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hlgroup->sg_deflink_sctx); + } + } + + if (from_id > 0 && (!init || hlgroup->sg_set == 0)) { + // 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) { + emsg(_("E414: group has settings, highlight link ignored")); + } + } else if (hlgroup->sg_link != to_id + || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid + || hlgroup->sg_cleared) { + if (!init) { + hlgroup->sg_set |= SG_LINK; + } + hlgroup->sg_link = to_id; + hlgroup->sg_script_ctx = current_sctx; + hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hlgroup->sg_script_ctx); + hlgroup->sg_cleared = false; + redraw_all_later(SOME_VALID); + + // Only call highlight changed() once after multiple changes + need_highlight_changed = true; + } + } + + return; + } + + if (doclear) { + // ":highlight clear [group]" command. + line = linep; + 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 j = 0; j < highlight_ga.ga_len; j++) { + highlight_clear(j); + } + init_highlight(true, true); + highlight_changed(); + redraw_all_later(NOT_VALID); + return; + } + 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, (size_t)(name_end - line)); + if (id == 0) { // Failed (out of memory). + return; + } + 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)) { + return; + } + + // Make a copy so we can check if any attribute actually changed + item_before = HL_TABLE()[idx]; + is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); + + // 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((uint8_t)(*linep))) { + key_start = linep; + if (*linep == '=') { + semsg(_("E415: unexpected equal sign: %s"), key_start); + error = true; + break; + } + + // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg", + // "guibg" or "guisp"). + while (*linep && !ascii_iswhite(*linep) && *linep != '=') { + linep++; + } + xfree(key); + key = (char *)vim_strnsave_up((const char_u *)key_start, + (size_t)(linep - key_start)); + linep = (const char *)skipwhite((const char_u *)linep); + + if (strcmp(key, "NONE") == 0) { + if (!init || HL_TABLE()[idx].sg_set == 0) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI; + } + highlight_clear(idx); + } + continue; + } + + // Check for the equal sign. + if (*linep != '=') { + semsg(_("E416: missing equal sign: %s"), key_start); + error = true; + break; + } + linep++; + + // Isolate the argument. + linep = (const char *)skipwhite((const char_u *)linep); + if (*linep == '\'') { // guifg='color name' + arg_start = ++linep; + linep = strchr(linep, '\''); + if (linep == NULL) { + semsg(_(e_invarg2), key_start); + error = true; + break; + } + } else { + arg_start = linep; + linep = (const char *)skiptowhite((const char_u *)linep); + } + if (linep == arg_start) { + semsg(_("E417: missing argument: %s"), key_start); + error = true; + break; + } + xfree(arg); + arg = xstrndup(arg_start, (size_t)(linep - arg_start)); + + if (*linep == '\'') { + linep++; + } + + // Store the argument. + if (strcmp(key, "TERM") == 0 + || strcmp(key, "CTERM") == 0 + || strcmp(key, "GUI") == 0) { + attr = 0; + off = 0; + int i; + while (arg[off] != NUL) { + for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) { + len = (int)STRLEN(hl_name_table[i]); + if (STRNICMP(arg + off, hl_name_table[i], len) == 0) { + attr |= hl_attr_table[i]; + off += len; + break; + } + } + if (i < 0) { + semsg(_("E418: Illegal value: %s"), arg); + error = true; + break; + } + if (arg[off] == ',') { // Another one follows. + off++; + } + } + if (error) { + break; + } + if (*key == 'C') { + if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + 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) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + HL_TABLE()[idx].sg_gui = attr; + } + } + } else if (STRCMP(key, "FONT") == 0) { + // in non-GUI fonts are simply ignored + } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { + if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_CTERM; + } + + // When setting the foreground color, and previously the "bold" + // flag was set for a light color, reset it now + if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { + HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + HL_TABLE()[idx].sg_cterm_bold = false; + } + + if (ascii_isdigit(*arg)) { + color = atoi(arg); + } else if (STRICMP(arg, "fg") == 0) { + if (cterm_normal_fg_color) { + color = cterm_normal_fg_color - 1; + } else { + emsg(_("E419: FG color unknown")); + error = true; + break; + } + } else if (STRICMP(arg, "bg") == 0) { + if (cterm_normal_bg_color > 0) { + color = cterm_normal_bg_color - 1; + } else { + emsg(_("E420: BG color unknown")); + error = true; + break; + } + } else { + // Reduce calls to STRICMP a bit, it can be slow. + off = TOUPPER_ASC(*arg); + int i; + for (i = ARRAY_SIZE(color_names); --i >= 0;) { + if (off == color_names[i][0] + && STRICMP(arg + 1, color_names[i] + 1) == 0) { + break; + } + } + if (i < 0) { + semsg(_("E421: Color name or number not recognized: %s"), + key_start); + error = true; + break; + } + + TriState bold = kNone; + color = lookup_color(i, key[5] == 'F', &bold); + + // set/reset bold attribute to get light foreground + // colors (on some terminals, e.g. "linux") + if (bold == kTrue) { + HL_TABLE()[idx].sg_cterm |= HL_BOLD; + HL_TABLE()[idx].sg_cterm_bold = true; + } else if (bold == kFalse) { + HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + } + } + // 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; + } + } else { + HL_TABLE()[idx].sg_cterm_bg = color + 1; + if (is_normal_group) { + cterm_normal_bg_color = color + 1; + if (!ui_rgb_attached()) { + if (color >= 0) { + int dark = -1; + + if (t_colors < 16) { + dark = (color == 0 || color == 4); + } else if (color < 16) { + // Limit the heuristic to the standard 16 colors + dark = (color < 7 || color == 8); + } + // Set the 'background' option if the value is + // wrong. + if (dark != -1 + && dark != (*p_bg == 'd') + && !option_was_set("bg")) { + set_option_value("bg", 0L, (dark ? "dark" : "light"), 0); + reset_option_was_set("bg"); + } + } + } + } + } + } + } else if (strcmp(key, "GUIFG") == 0) { + char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; + + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + + if (*namep == NULL || STRCMP(*namep, arg) != 0) { + xfree(*namep); + if (strcmp(arg, "NONE") != 0) { + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); + } else { + *namep = NULL; + HL_TABLE()[idx].sg_rgb_fg = -1; + } + did_change = true; + } + } + + if (is_normal_group) { + normal_fg = HL_TABLE()[idx].sg_rgb_fg; + } + } else if (STRCMP(key, "GUIBG") == 0) { + char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; + + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + + if (*namep == NULL || STRCMP(*namep, arg) != 0) { + xfree(*namep); + if (STRCMP(arg, "NONE") != 0) { + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); + } else { + *namep = NULL; + HL_TABLE()[idx].sg_rgb_bg = -1; + } + did_change = true; + } + } + + if (is_normal_group) { + normal_bg = HL_TABLE()[idx].sg_rgb_bg; + } + } else if (strcmp(key, "GUISP") == 0) { + char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; + + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + + if (*namep == NULL || STRCMP(*namep, arg) != 0) { + xfree(*namep); + if (strcmp(arg, "NONE") != 0) { + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); + } else { + *namep = NULL; + HL_TABLE()[idx].sg_rgb_sp = -1; + } + did_change = true; + } + } + + if (is_normal_group) { + normal_sp = HL_TABLE()[idx].sg_rgb_sp; + } + } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { + // Ignored for now + } else if (strcmp(key, "BLEND") == 0) { + if (strcmp(arg, "NONE") != 0) { + HL_TABLE()[idx].sg_blend = (int)strtol(arg, NULL, 10); + } else { + HL_TABLE()[idx].sg_blend = -1; + } + } else { + semsg(_("E423: Illegal argument: %s"), key_start); + error = true; + break; + } + HL_TABLE()[idx].sg_cleared = false; + + // 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 = (const char *)skipwhite((const char_u *)linep); + } + } + + // 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 { + if (!error && is_normal_group) { + // Need to update all groups, because they might be using "bg" and/or + // "fg", which have been changed now. + highlight_attr_set_all(); + + if (!ui_has(kUILinegrid) && starting == 0) { + // Older UIs assume that we clear the screen after normal group is + // changed + ui_refresh(); + } else { + // TUI and newer UIs will repaint the screen themselves. NOT_VALID + // redraw below will still handle usages of guibg=fg etc. + ui_default_colors_set(); + } + did_highlight_changed = true; + redraw_all_later(NOT_VALID); + } else { + set_hl_attr(idx); + } + HL_TABLE()[idx].sg_script_ctx = current_sctx; + HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); + } + xfree(key); + xfree(arg); + + // Only call highlight_changed() once, after a sequence of highlight + // commands, and only if an attribute actually changed + if ((did_change + || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) + && !did_highlight_changed) { + // Do not trigger a redraw when highlighting is changed while + // redrawing. This may happen when evaluating 'statusline' changes the + // StatusLine group. + if (!updating_screen) { + redraw_all_later(NOT_VALID); + } + need_highlight_changed = true; + } +} + +#if defined(EXITFREE) +void free_highlight(void) +{ + for (int i = 0; i < highlight_ga.ga_len; i++) { + highlight_clear(i); + xfree(HL_TABLE()[i].sg_name); + xfree(HL_TABLE()[i].sg_name_u); + } + ga_clear(&highlight_ga); + map_destroy(cstr_t, int)(&highlight_unames); +} + +#endif + +/// Reset the cterm colors to what they were before Vim was started, if +/// possible. Otherwise reset them to zero. +void restore_cterm_colors(void) +{ + normal_fg = -1; + normal_bg = -1; + normal_sp = -1; + cterm_normal_fg_color = 0; + cterm_normal_bg_color = 0; +} + +/// @param check_link if true also check for an existing link. +/// +/// @return TRUE if highlight group "idx" has any settings. +static int hl_has_settings(int idx, bool check_link) +{ + return HL_TABLE()[idx].sg_cleared == 0 + && (HL_TABLE()[idx].sg_attr != 0 + || HL_TABLE()[idx].sg_cterm_fg != 0 + || HL_TABLE()[idx].sg_cterm_bg != 0 + || HL_TABLE()[idx].sg_rgb_fg_name != NULL + || HL_TABLE()[idx].sg_rgb_bg_name != NULL + || HL_TABLE()[idx].sg_rgb_sp_name != NULL + || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); +} + +/// Clear highlighting for one group. +static void highlight_clear(int idx) +{ + HL_TABLE()[idx].sg_cleared = true; + + HL_TABLE()[idx].sg_attr = 0; + HL_TABLE()[idx].sg_cterm = 0; + HL_TABLE()[idx].sg_cterm_bold = false; + HL_TABLE()[idx].sg_cterm_fg = 0; + HL_TABLE()[idx].sg_cterm_bg = 0; + HL_TABLE()[idx].sg_gui = 0; + HL_TABLE()[idx].sg_rgb_fg = -1; + HL_TABLE()[idx].sg_rgb_bg = -1; + HL_TABLE()[idx].sg_rgb_sp = -1; + XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name); + XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name); + XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name); + HL_TABLE()[idx].sg_blend = -1; + // Restore default link and context if they exist. Otherwise clears. + HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink; + // Since we set the default link, set the location to where the default + // link was set. + HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx; +} + +/// \addtogroup LIST_XXX +/// @{ +#define LIST_ATTR 1 +#define LIST_STRING 2 +#define LIST_INT 3 +/// @} + +static void highlight_list_one(const int id) +{ + const HlGroup *sgp = &HL_TABLE()[id - 1]; // index is ID minus one + bool didh = false; + + if (message_filtered(sgp->sg_name)) { + return; + } + + didh = highlight_list_arg(id, didh, LIST_ATTR, + sgp->sg_cterm, NULL, "cterm"); + didh = highlight_list_arg(id, didh, LIST_INT, + sgp->sg_cterm_fg, NULL, "ctermfg"); + didh = highlight_list_arg(id, didh, LIST_INT, + sgp->sg_cterm_bg, NULL, "ctermbg"); + + didh = highlight_list_arg(id, didh, LIST_ATTR, + sgp->sg_gui, NULL, "gui"); + didh = highlight_list_arg(id, didh, LIST_STRING, + 0, sgp->sg_rgb_fg_name, "guifg"); + didh = highlight_list_arg(id, didh, LIST_STRING, + 0, sgp->sg_rgb_bg_name, "guibg"); + didh = highlight_list_arg(id, didh, LIST_STRING, + 0, sgp->sg_rgb_sp_name, "guisp"); + + didh = highlight_list_arg(id, didh, LIST_INT, + sgp->sg_blend+1, NULL, "blend"); + + if (sgp->sg_link && !got_int) { + (void)syn_list_header(didh, 0, id, true); + didh = true; + msg_puts_attr("links to", HL_ATTR(HLF_D)); + msg_putchar(' '); + msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); + } + + if (!didh) { + highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", ""); + } + if (p_verbose > 0) { + last_set_msg(sgp->sg_script_ctx); + } +} + +Dictionary get_global_hl_defs(void) +{ + Dictionary rv = ARRAY_DICT_INIT; + for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + Dictionary attrs = ARRAY_DICT_INIT; + HlGroup *h = &HL_TABLE()[i - 1]; + if (h->sg_attr > 0) { + attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + } else if (h->sg_link > 0) { + const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; + PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); + } + PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); + } + + return rv; +} + +/// Outputs a highlight when doing ":hi MyHighlight" +/// +/// @param type one of \ref LIST_XXX +/// @param iarg integer argument used if \p type == LIST_INT +/// @param sarg string used if \p type == LIST_STRING +static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, + const char *const name) +{ + char buf[100]; + + if (got_int) { + return false; + } + if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { + char *ts = buf; + if (type == LIST_INT) { + snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); + } else if (type == LIST_STRING) { + ts = sarg; + } else { // type == LIST_ATTR + buf[0] = NUL; + for (int i = 0; hl_attr_table[i] != 0; i++) { + if (iarg & hl_attr_table[i]) { + if (buf[0] != NUL) { + xstrlcat(buf, ",", 100); + } + xstrlcat(buf, hl_name_table[i], 100); + iarg &= ~hl_attr_table[i]; // don't want "inverse" + } + } + } + + (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + (int)STRLEN(name) + + 1), id, false); + didh = true; + if (!got_int) { + if (*name != NUL) { + msg_puts_attr(name, HL_ATTR(HLF_D)); + msg_puts_attr("=", HL_ATTR(HLF_D)); + } + msg_outtrans((char_u *)ts); + } + } + return didh; +} + +/// Check whether highlight group has attribute +/// +/// @param[in] id Highlight group to check. +/// @param[in] flag Attribute to check. +/// @param[in] modec 'g' for GUI, 'c' for term. +/// +/// @return "1" if highlight group has attribute, NULL otherwise. +const char *highlight_has_attr(const int id, const int flag, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + int attr; + + if (id <= 0 || id > highlight_ga.ga_len) { + return NULL; + } + + if (modec == 'g') { + attr = HL_TABLE()[id - 1].sg_gui; + } else { + attr = HL_TABLE()[id - 1].sg_cterm; + } + + return (attr & flag) ? "1" : NULL; +} + +/// Return color name of the given highlight group +/// +/// @param[in] id Highlight group to work with. +/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", +/// "bg#" or "sp#". +/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. +/// +/// @return color name, possibly in a static buffer. Buffer will be overwritten +/// on next highlight_color() call. May return NULL. +const char *highlight_color(const int id, const char *const what, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + static char name[20]; + int n; + bool fg = false; + bool sp = false; + bool font = false; + + if (id <= 0 || id > highlight_ga.ga_len) { + return NULL; + } + + if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { + fg = true; + } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' + && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { + font = true; + } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { + sp = true; + } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { + return NULL; + } + if (modec == 'g') { + if (what[2] == '#' && ui_rgb_attached()) { + if (fg) { + n = HL_TABLE()[id - 1].sg_rgb_fg; + } else if (sp) { + n = HL_TABLE()[id - 1].sg_rgb_sp; + } else { + n = HL_TABLE()[id - 1].sg_rgb_bg; + } + if (n < 0 || n > 0xffffff) { + return NULL; + } + snprintf(name, sizeof(name), "#%06x", n); + return name; + } + if (fg) { + return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; + } + if (sp) { + return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; + } + return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; + } + if (font || sp) { + return NULL; + } + if (modec == 'c') { + if (fg) { + n = HL_TABLE()[id - 1].sg_cterm_fg - 1; + } else { + n = HL_TABLE()[id - 1].sg_cterm_bg - 1; + } + if (n < 0) { + return NULL; + } + snprintf(name, sizeof(name), "%d", n); + return name; + } + // term doesn't have color. + return NULL; +} + +/// Output the syntax list header. +/// +/// @param did_header did header already +/// @param outlen length of string that comes +/// @param id highlight group id +/// @param force_newline always start a new line +/// @return true when started a new line. +bool syn_list_header(const bool did_header, const int outlen, const int id, + bool force_newline) +{ + int endcol = 19; + bool newline = true; + int name_col = 0; + bool adjust = true; + + if (!did_header) { + msg_putchar('\n'); + if (got_int) { + return true; + } + msg_outtrans(HL_TABLE()[id - 1].sg_name); + name_col = msg_col; + endcol = 15; + } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { + msg_putchar(' '); + adjust = false; + } else if (msg_col + outlen + 1 >= Columns || force_newline) { + msg_putchar('\n'); + if (got_int) { + return true; + } + } else { + if (msg_col >= endcol) { // wrap around is like starting a new line + newline = false; + } + } + + if (adjust) { + if (msg_col >= endcol) { + // output at least one space + endcol = msg_col + 1; + } + + msg_advance(endcol); + } + + // Show "xxx" with the attributes. + if (!did_header) { + if (endcol == Columns - 1 && endcol <= name_col) { + msg_putchar(' '); + } + msg_puts_attr("xxx", syn_id2attr(id)); + msg_putchar(' '); + } + + return newline; +} + +/// Set the attribute numbers for a highlight group. +/// Called after one of the attributes has changed. +/// @param idx corrected highlight index +static void set_hl_attr(int idx) +{ + HlAttrs at_en = HLATTRS_INIT; + HlGroup *sgp = HL_TABLE() + idx; + + at_en.cterm_ae_attr = (int16_t)sgp->sg_cterm; + at_en.cterm_fg_color = sgp->sg_cterm_fg; + at_en.cterm_bg_color = sgp->sg_cterm_bg; + at_en.rgb_ae_attr = (int16_t)sgp->sg_gui; + // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is + // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name + // before setting attr_entry->{f,g}g_color to a other than -1 + at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; + at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; + at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; + at_en.hl_blend = sgp->sg_blend; + + sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en); + + // a cursor style uses this syn_id, make sure its attribute is updated. + if (cursor_mode_uses_syn_id(idx+1)) { + ui_mode_info_set(); + } +} + +int syn_name2id(const char *name) + FUNC_ATTR_NONNULL_ALL +{ + return syn_name2id_len((char_u *)name, STRLEN(name)); +} + +/// Lookup a highlight group name and return its ID. +/// +/// @param highlight name e.g. 'Cursor', 'Normal' +/// @return the highlight id, else 0 if \p name does not exist +int syn_name2id_len(const char_u *name, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + char name_u[MAX_SYN_NAME + 1]; + + if (len == 0 || len > MAX_SYN_NAME) { + return 0; + } + + // Avoid using stricmp() too much, it's slow on some systems */ + // Avoid alloc()/free(), these are slow too. + memcpy(name_u, name, len); + name_u[len] = '\0'; + vim_strup((char_u *)name_u); + + // map_get(..., int) returns 0 when no key is present, which is + // the expected value for missing highlight group. + return map_get(cstr_t, int)(&highlight_unames, name_u); +} + +/// Lookup a highlight group name and return its attributes. +/// Return zero if not found. +int syn_name2attr(const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + int id = syn_name2id((char *)name); + + if (id != 0) { + return syn_id2attr(id); + } + return 0; +} + +/// Return TRUE if highlight group "name" exists. +int highlight_exists(const char *name) +{ + return syn_name2id(name) > 0; +} + +/// Return the name of highlight group "id". +/// When not a valid ID return an empty string. +char_u *syn_id2name(int id) +{ + if (id <= 0 || id > highlight_ga.ga_len) { + return (char_u *)""; + } + return HL_TABLE()[id - 1].sg_name; +} + +/// Find highlight group name in the table and return its ID. +/// If it doesn't exist yet, a new entry is created. +/// +/// @param pp Highlight group name +/// @param len length of \p pp +/// +/// @return 0 for failure else the id of the group +int syn_check_group(const char *name, size_t len) +{ + if (len > MAX_SYN_NAME) { + emsg(_(e_highlight_group_name_too_long)); + return 0; + } + int id = syn_name2id_len((char_u *)name, len); + if (id == 0) { // doesn't exist yet + return syn_add_group(vim_strnsave((char_u *)name, len)); + } + return id; +} + +/// Add new highlight group and return its ID. +/// +/// @param name must be an allocated string, it will be consumed. +/// @return 0 for failure, else the allocated group id +/// @see syn_check_group syn_unadd_group +static int syn_add_group(char_u *name) +{ + char_u *p; + + // Check that the name is ASCII letters, digits and underscore. + for (p = name; *p != NUL; p++) { + if (!vim_isprintc(*p)) { + emsg(_("E669: Unprintable character in group name")); + xfree(name); + return 0; + } else if (!ASCII_ISALNUM(*p) && *p != '_') { + // This is an error, but since there previously was no check only give a warning. + msg_source(HL_ATTR(HLF_W)); + msg(_("W18: Invalid character in group name")); + break; + } + } + + // First call for this growarray: init growing array. + if (highlight_ga.ga_data == NULL) { + highlight_ga.ga_itemsize = sizeof(HlGroup); + ga_set_growsize(&highlight_ga, 10); + } + + if (highlight_ga.ga_len >= MAX_HL_ID) { + emsg(_("E849: Too many highlight and syntax groups")); + xfree(name); + return 0; + } + + char *const name_up = (char *)vim_strsave_up(name); + + // Append another syntax_highlight entry. + HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga); + memset(hlgp, 0, sizeof(*hlgp)); + hlgp->sg_name = name; + hlgp->sg_rgb_bg = -1; + hlgp->sg_rgb_fg = -1; + hlgp->sg_rgb_sp = -1; + hlgp->sg_blend = -1; + hlgp->sg_name_u = name_up; + + int id = highlight_ga.ga_len; // ID is index plus one + + map_put(cstr_t, int)(&highlight_unames, name_up, id); + + return id; +} + +/// When, just after calling syn_add_group(), an error is discovered, this +/// function deletes the new name. +static void syn_unadd_group(void) +{ + highlight_ga.ga_len--; + HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; + map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); + xfree(item->sg_name); + xfree(item->sg_name_u); +} + +/// Translate a group ID to highlight attributes. +/// @see syn_attr2entry +int syn_id2attr(int hl_id) +{ + hl_id = syn_get_final_id(hl_id); + HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + + int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set); + if (attr >= 0) { + return attr; + } + return sgp->sg_attr; +} + +/// Translate a group ID to the final group ID (following links). +int syn_get_final_id(int hl_id) +{ + int count; + + if (hl_id > highlight_ga.ga_len || hl_id < 1) { + return 0; // Can be called from eval!! + } + + // Follow links until there is no more. + // Look out for loops! Break after 100 links. + for (count = 100; --count >= 0;) { + HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + + // ACHTUNG: when using "tmp" attribute (no link) the function might be + // called twice. it needs be smart enough to remember attr only to + // syn_id2attr time + int check = ns_get_hl(-1, hl_id, true, sgp->sg_set); + if (check == 0) { + return hl_id; // how dare! it broke the link! + } else if (check > 0) { + hl_id = check; + continue; + } + + + if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) { + break; + } + hl_id = sgp->sg_link; + } + + return hl_id; +} + +/// Refresh the color attributes of all highlight groups. +void highlight_attr_set_all(void) +{ + for (int idx = 0; idx < highlight_ga.ga_len; idx++) { + HlGroup *sgp = &HL_TABLE()[idx]; + if (sgp->sg_rgb_bg_name != NULL) { + sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name); + } + if (sgp->sg_rgb_fg_name != NULL) { + sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name); + } + if (sgp->sg_rgb_sp_name != NULL) { + sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name); + } + set_hl_attr(idx); + } +} + +// Apply difference between User[1-9] and HLF_S to HLF_SNC. +static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) + FUNC_ATTR_NONNULL_ALL +{ + HlGroup *const hlt = HL_TABLE(); + + if (id_alt == 0) { + memset(&hlt[hlcnt + i], 0, sizeof(HlGroup)); + hlt[hlcnt + i].sg_cterm = highlight_attr[hlf]; + hlt[hlcnt + i].sg_gui = highlight_attr[hlf]; + } else { + memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(HlGroup)); + } + hlt[hlcnt + i].sg_link = 0; + + hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; + if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) { + hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; + } + if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) { + hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; + } + hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; + if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) { + hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg; + } + if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) { + hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; + } + if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) { + hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp; + } + highlight_ga.ga_len = hlcnt + i + 1; + set_hl_attr(hlcnt + i); // At long last we can apply + table[i] = syn_id2attr(hlcnt + i + 1); +} + +/// Translate highlight groups into attributes in highlight_attr[] and set up +/// the user highlights User1..9. A set of corresponding highlights to use on +/// top of HLF_SNC is computed. Called only when nvim starts and upon first +/// screen redraw after any :highlight command. +void highlight_changed(void) +{ + int id; + char userhl[30]; // use 30 to avoid compiler warning + int id_S = -1; + int id_SNC = 0; + int hlcnt; + + need_highlight_changed = false; + + /// Translate builtin highlight groups into attributes for quick lookup. + for (int hlf = 0; hlf < HLF_COUNT; hlf++) { + id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf])); + if (id == 0) { + abort(); + } + int final_id = syn_get_final_id(id); + if (hlf == HLF_SNC) { + id_SNC = final_id; + } else if (hlf == HLF_S) { + id_S = final_id; + } + + highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id, + hlf == HLF_INACTIVE); + + if (highlight_attr[hlf] != highlight_attr_last[hlf]) { + if (hlf == HLF_MSG) { + clear_cmdline = true; + } + ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), + highlight_attr[hlf]); + highlight_attr_last[hlf] = highlight_attr[hlf]; + } + } + + // + // Setup the user highlights + // + // Temporarily utilize 10 more hl entries: + // 9 for User1-User9 combined with StatusLineNC + // 1 for StatusLine default + // Must to be in there simultaneously in case of table overflows in + // get_attr_entry() + ga_grow(&highlight_ga, 10); + hlcnt = highlight_ga.ga_len; + if (id_S == -1) { + // Make sure id_S is always valid to simplify code below. Use the last entry + memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(HlGroup)); + id_S = hlcnt + 10; + } + for (int i = 0; i < 9; i++) { + snprintf(userhl, sizeof(userhl), "User%d", i + 1); + id = syn_name2id(userhl); + if (id == 0) { + highlight_user[i] = 0; + highlight_stlnc[i] = 0; + } else { + highlight_user[i] = syn_id2attr(id); + combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc); + } + } + highlight_ga.ga_len = hlcnt; +} + +/// Handle command line completion for :highlight command. +void set_context_in_highlight_cmd(expand_T *xp, const char *arg) +{ + // Default: expand group names. + xp->xp_context = EXPAND_HIGHLIGHT; + xp->xp_pattern = (char_u *)arg; + include_link = 2; + include_default = 1; + + // (part of) subcommand already typed + if (*arg != NUL) { + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past "default" or group name. + include_default = 0; + if (strncmp("default", arg, (unsigned)(p - arg)) == 0) { + arg = (const char *)skipwhite((const char_u *)p); + xp->xp_pattern = (char_u *)arg; + p = (const char *)skiptowhite((const char_u *)arg); + } + if (*p != NUL) { // past group name + include_link = 0; + if (arg[1] == 'i' && arg[0] == 'N') { + highlight_list(); + } + if (strncmp("link", arg, (unsigned)(p - arg)) == 0 + || strncmp("clear", arg, (unsigned)(p - arg)) == 0) { + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + if (*p != NUL) { // Past first group name. + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + } + } + if (*p != NUL) { // Past group name(s). + xp->xp_context = EXPAND_NOTHING; + } + } + } + } +} + +/// List highlighting matches in a nice way. +static void highlight_list(void) +{ + int i; + + for (i = 10; --i >= 0;) { + highlight_list_two(i, HL_ATTR(HLF_D)); + } + for (i = 40; --i >= 0;) { + highlight_list_two(99, 0); + } +} + +static void highlight_list_two(int cnt, int attr) +{ + msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr); + msg_clr_eos(); + ui_flush(); + os_delay(cnt == 99 ? 40L : (uint64_t)cnt * 50L, false); +} + +/// Function given to ExpandGeneric() to obtain the list of group names. +const char *get_highlight_name(expand_T *const xp, int idx) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + return get_highlight_name_ext(xp, idx, true); +} + +/// Obtain a highlight group name. +/// +/// @param skip_cleared if true don't return a cleared entry. +const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (idx < 0) { + return NULL; + } + + // Items are never removed from the table, skip the ones that were cleared. + if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { + return ""; + } + + if (idx == highlight_ga.ga_len && include_none != 0) { + return "none"; + } else if (idx == highlight_ga.ga_len + include_none + && include_default != 0) { + return "default"; + } else if (idx == highlight_ga.ga_len + include_none + include_default + && include_link != 0) { + return "link"; + } else if (idx == highlight_ga.ga_len + include_none + include_default + 1 + && include_link != 0) { + return "clear"; + } else if (idx >= highlight_ga.ga_len) { + return NULL; + } + return (const char *)HL_TABLE()[idx].sg_name; +} + +color_name_table_T color_name_table[] = { + // Colors from rgb.txt + { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) }, + { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) }, + { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) }, + { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) }, + { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) }, + { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) }, + { "Aqua", RGB_(0x00, 0xff, 0xff) }, + { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) }, + { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) }, + { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) }, + { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) }, + { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) }, + { "Azure", RGB_(0xf0, 0xff, 0xff) }, + { "Azure1", RGB_(0xf0, 0xff, 0xff) }, + { "Azure2", RGB_(0xe0, 0xee, 0xee) }, + { "Azure3", RGB_(0xc1, 0xcd, 0xcd) }, + { "Azure4", RGB_(0x83, 0x8b, 0x8b) }, + { "Beige", RGB_(0xf5, 0xf5, 0xdc) }, + { "Bisque", RGB_(0xff, 0xe4, 0xc4) }, + { "Bisque1", RGB_(0xff, 0xe4, 0xc4) }, + { "Bisque2", RGB_(0xee, 0xd5, 0xb7) }, + { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) }, + { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) }, + { "Black", RGB_(0x00, 0x00, 0x00) }, + { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) }, + { "Blue", RGB_(0x00, 0x00, 0xff) }, + { "Blue1", RGB_(0x0, 0x0, 0xff) }, + { "Blue2", RGB_(0x0, 0x0, 0xee) }, + { "Blue3", RGB_(0x0, 0x0, 0xcd) }, + { "Blue4", RGB_(0x0, 0x0, 0x8b) }, + { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) }, + { "Brown", RGB_(0xa5, 0x2a, 0x2a) }, + { "Brown1", RGB_(0xff, 0x40, 0x40) }, + { "Brown2", RGB_(0xee, 0x3b, 0x3b) }, + { "Brown3", RGB_(0xcd, 0x33, 0x33) }, + { "Brown4", RGB_(0x8b, 0x23, 0x23) }, + { "BurlyWood", RGB_(0xde, 0xb8, 0x87) }, + { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) }, + { "Burlywood2", RGB_(0xee, 0xc5, 0x91) }, + { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) }, + { "Burlywood4", RGB_(0x8b, 0x73, 0x55) }, + { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) }, + { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) }, + { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) }, + { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) }, + { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) }, + { "ChartReuse", RGB_(0x7f, 0xff, 0x00) }, + { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) }, + { "Chartreuse2", RGB_(0x76, 0xee, 0x0) }, + { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) }, + { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) }, + { "Chocolate", RGB_(0xd2, 0x69, 0x1e) }, + { "Chocolate1", RGB_(0xff, 0x7f, 0x24) }, + { "Chocolate2", RGB_(0xee, 0x76, 0x21) }, + { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) }, + { "Chocolate4", RGB_(0x8b, 0x45, 0x13) }, + { "Coral", RGB_(0xff, 0x7f, 0x50) }, + { "Coral1", RGB_(0xff, 0x72, 0x56) }, + { "Coral2", RGB_(0xee, 0x6a, 0x50) }, + { "Coral3", RGB_(0xcd, 0x5b, 0x45) }, + { "Coral4", RGB_(0x8b, 0x3e, 0x2f) }, + { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) }, + { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) }, + { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) }, + { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) }, + { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) }, + { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) }, + { "Crimson", RGB_(0xdc, 0x14, 0x3c) }, + { "Cyan", RGB_(0x00, 0xff, 0xff) }, + { "Cyan1", RGB_(0x0, 0xff, 0xff) }, + { "Cyan2", RGB_(0x0, 0xee, 0xee) }, + { "Cyan3", RGB_(0x0, 0xcd, 0xcd) }, + { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, + { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, + { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, + { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, + { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, + { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, + { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, + { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) }, + { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) }, + { "DarkGreen", RGB_(0x00, 0x64, 0x00) }, + { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) }, + { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) }, + { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) }, + { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) }, + { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) }, + { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) }, + { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) }, + { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) }, + { "DarkOrange", RGB_(0xff, 0x8c, 0x00) }, + { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) }, + { "DarkOrange2", RGB_(0xee, 0x76, 0x0) }, + { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) }, + { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) }, + { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) }, + { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) }, + { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) }, + { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) }, + { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) }, + { "DarkRed", RGB_(0x8b, 0x00, 0x00) }, + { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) }, + { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) }, + { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) }, + { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) }, + { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) }, + { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) }, + { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) }, + { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) }, + { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) }, + { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) }, + { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) }, + { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) }, + { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) }, + { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) }, + { "DarkViolet", RGB_(0x94, 0x00, 0xd3) }, + { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) }, + { "DeepPink", RGB_(0xff, 0x14, 0x93) }, + { "DeepPink1", RGB_(0xff, 0x14, 0x93) }, + { "DeepPink2", RGB_(0xee, 0x12, 0x89) }, + { "DeepPink3", RGB_(0xcd, 0x10, 0x76) }, + { "DeepPink4", RGB_(0x8b, 0xa, 0x50) }, + { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) }, + { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) }, + { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) }, + { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) }, + { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) }, + { "DimGray", RGB_(0x69, 0x69, 0x69) }, + { "DimGrey", RGB_(0x69, 0x69, 0x69) }, + { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) }, + { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) }, + { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) }, + { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) }, + { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) }, + { "Firebrick", RGB_(0xb2, 0x22, 0x22) }, + { "Firebrick1", RGB_(0xff, 0x30, 0x30) }, + { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) }, + { "Firebrick3", RGB_(0xcd, 0x26, 0x26) }, + { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) }, + { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) }, + { "ForestGreen", RGB_(0x22, 0x8b, 0x22) }, + { "Fuchsia", RGB_(0xff, 0x00, 0xff) }, + { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) }, + { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) }, + { "Gold", RGB_(0xff, 0xd7, 0x00) }, + { "Gold1", RGB_(0xff, 0xd7, 0x0) }, + { "Gold2", RGB_(0xee, 0xc9, 0x0) }, + { "Gold3", RGB_(0xcd, 0xad, 0x0) }, + { "Gold4", RGB_(0x8b, 0x75, 0x0) }, + { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, + { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, + { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, + { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, + { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) }, + { "Gray", RGB_(0x80, 0x80, 0x80) }, + { "Gray0", RGB_(0x0, 0x0, 0x0) }, + { "Gray1", RGB_(0x3, 0x3, 0x3) }, + { "Gray10", RGB_(0x1a, 0x1a, 0x1a) }, + { "Gray100", RGB_(0xff, 0xff, 0xff) }, + { "Gray11", RGB_(0x1c, 0x1c, 0x1c) }, + { "Gray12", RGB_(0x1f, 0x1f, 0x1f) }, + { "Gray13", RGB_(0x21, 0x21, 0x21) }, + { "Gray14", RGB_(0x24, 0x24, 0x24) }, + { "Gray15", RGB_(0x26, 0x26, 0x26) }, + { "Gray16", RGB_(0x29, 0x29, 0x29) }, + { "Gray17", RGB_(0x2b, 0x2b, 0x2b) }, + { "Gray18", RGB_(0x2e, 0x2e, 0x2e) }, + { "Gray19", RGB_(0x30, 0x30, 0x30) }, + { "Gray2", RGB_(0x5, 0x5, 0x5) }, + { "Gray20", RGB_(0x33, 0x33, 0x33) }, + { "Gray21", RGB_(0x36, 0x36, 0x36) }, + { "Gray22", RGB_(0x38, 0x38, 0x38) }, + { "Gray23", RGB_(0x3b, 0x3b, 0x3b) }, + { "Gray24", RGB_(0x3d, 0x3d, 0x3d) }, + { "Gray25", RGB_(0x40, 0x40, 0x40) }, + { "Gray26", RGB_(0x42, 0x42, 0x42) }, + { "Gray27", RGB_(0x45, 0x45, 0x45) }, + { "Gray28", RGB_(0x47, 0x47, 0x47) }, + { "Gray29", RGB_(0x4a, 0x4a, 0x4a) }, + { "Gray3", RGB_(0x8, 0x8, 0x8) }, + { "Gray30", RGB_(0x4d, 0x4d, 0x4d) }, + { "Gray31", RGB_(0x4f, 0x4f, 0x4f) }, + { "Gray32", RGB_(0x52, 0x52, 0x52) }, + { "Gray33", RGB_(0x54, 0x54, 0x54) }, + { "Gray34", RGB_(0x57, 0x57, 0x57) }, + { "Gray35", RGB_(0x59, 0x59, 0x59) }, + { "Gray36", RGB_(0x5c, 0x5c, 0x5c) }, + { "Gray37", RGB_(0x5e, 0x5e, 0x5e) }, + { "Gray38", RGB_(0x61, 0x61, 0x61) }, + { "Gray39", RGB_(0x63, 0x63, 0x63) }, + { "Gray4", RGB_(0xa, 0xa, 0xa) }, + { "Gray40", RGB_(0x66, 0x66, 0x66) }, + { "Gray41", RGB_(0x69, 0x69, 0x69) }, + { "Gray42", RGB_(0x6b, 0x6b, 0x6b) }, + { "Gray43", RGB_(0x6e, 0x6e, 0x6e) }, + { "Gray44", RGB_(0x70, 0x70, 0x70) }, + { "Gray45", RGB_(0x73, 0x73, 0x73) }, + { "Gray46", RGB_(0x75, 0x75, 0x75) }, + { "Gray47", RGB_(0x78, 0x78, 0x78) }, + { "Gray48", RGB_(0x7a, 0x7a, 0x7a) }, + { "Gray49", RGB_(0x7d, 0x7d, 0x7d) }, + { "Gray5", RGB_(0xd, 0xd, 0xd) }, + { "Gray50", RGB_(0x7f, 0x7f, 0x7f) }, + { "Gray51", RGB_(0x82, 0x82, 0x82) }, + { "Gray52", RGB_(0x85, 0x85, 0x85) }, + { "Gray53", RGB_(0x87, 0x87, 0x87) }, + { "Gray54", RGB_(0x8a, 0x8a, 0x8a) }, + { "Gray55", RGB_(0x8c, 0x8c, 0x8c) }, + { "Gray56", RGB_(0x8f, 0x8f, 0x8f) }, + { "Gray57", RGB_(0x91, 0x91, 0x91) }, + { "Gray58", RGB_(0x94, 0x94, 0x94) }, + { "Gray59", RGB_(0x96, 0x96, 0x96) }, + { "Gray6", RGB_(0xf, 0xf, 0xf) }, + { "Gray60", RGB_(0x99, 0x99, 0x99) }, + { "Gray61", RGB_(0x9c, 0x9c, 0x9c) }, + { "Gray62", RGB_(0x9e, 0x9e, 0x9e) }, + { "Gray63", RGB_(0xa1, 0xa1, 0xa1) }, + { "Gray64", RGB_(0xa3, 0xa3, 0xa3) }, + { "Gray65", RGB_(0xa6, 0xa6, 0xa6) }, + { "Gray66", RGB_(0xa8, 0xa8, 0xa8) }, + { "Gray67", RGB_(0xab, 0xab, 0xab) }, + { "Gray68", RGB_(0xad, 0xad, 0xad) }, + { "Gray69", RGB_(0xb0, 0xb0, 0xb0) }, + { "Gray7", RGB_(0x12, 0x12, 0x12) }, + { "Gray70", RGB_(0xb3, 0xb3, 0xb3) }, + { "Gray71", RGB_(0xb5, 0xb5, 0xb5) }, + { "Gray72", RGB_(0xb8, 0xb8, 0xb8) }, + { "Gray73", RGB_(0xba, 0xba, 0xba) }, + { "Gray74", RGB_(0xbd, 0xbd, 0xbd) }, + { "Gray75", RGB_(0xbf, 0xbf, 0xbf) }, + { "Gray76", RGB_(0xc2, 0xc2, 0xc2) }, + { "Gray77", RGB_(0xc4, 0xc4, 0xc4) }, + { "Gray78", RGB_(0xc7, 0xc7, 0xc7) }, + { "Gray79", RGB_(0xc9, 0xc9, 0xc9) }, + { "Gray8", RGB_(0x14, 0x14, 0x14) }, + { "Gray80", RGB_(0xcc, 0xcc, 0xcc) }, + { "Gray81", RGB_(0xcf, 0xcf, 0xcf) }, + { "Gray82", RGB_(0xd1, 0xd1, 0xd1) }, + { "Gray83", RGB_(0xd4, 0xd4, 0xd4) }, + { "Gray84", RGB_(0xd6, 0xd6, 0xd6) }, + { "Gray85", RGB_(0xd9, 0xd9, 0xd9) }, + { "Gray86", RGB_(0xdb, 0xdb, 0xdb) }, + { "Gray87", RGB_(0xde, 0xde, 0xde) }, + { "Gray88", RGB_(0xe0, 0xe0, 0xe0) }, + { "Gray89", RGB_(0xe3, 0xe3, 0xe3) }, + { "Gray9", RGB_(0x17, 0x17, 0x17) }, + { "Gray90", RGB_(0xe5, 0xe5, 0xe5) }, + { "Gray91", RGB_(0xe8, 0xe8, 0xe8) }, + { "Gray92", RGB_(0xeb, 0xeb, 0xeb) }, + { "Gray93", RGB_(0xed, 0xed, 0xed) }, + { "Gray94", RGB_(0xf0, 0xf0, 0xf0) }, + { "Gray95", RGB_(0xf2, 0xf2, 0xf2) }, + { "Gray96", RGB_(0xf5, 0xf5, 0xf5) }, + { "Gray97", RGB_(0xf7, 0xf7, 0xf7) }, + { "Gray98", RGB_(0xfa, 0xfa, 0xfa) }, + { "Gray99", RGB_(0xfc, 0xfc, 0xfc) }, + { "Green", RGB_(0x00, 0x80, 0x00) }, + { "Green1", RGB_(0x0, 0xff, 0x0) }, + { "Green2", RGB_(0x0, 0xee, 0x0) }, + { "Green3", RGB_(0x0, 0xcd, 0x0) }, + { "Green4", RGB_(0x0, 0x8b, 0x0) }, + { "GreenYellow", RGB_(0xad, 0xff, 0x2f) }, + { "Grey", RGB_(0x80, 0x80, 0x80) }, + { "Grey0", RGB_(0x0, 0x0, 0x0) }, + { "Grey1", RGB_(0x3, 0x3, 0x3) }, + { "Grey10", RGB_(0x1a, 0x1a, 0x1a) }, + { "Grey100", RGB_(0xff, 0xff, 0xff) }, + { "Grey11", RGB_(0x1c, 0x1c, 0x1c) }, + { "Grey12", RGB_(0x1f, 0x1f, 0x1f) }, + { "Grey13", RGB_(0x21, 0x21, 0x21) }, + { "Grey14", RGB_(0x24, 0x24, 0x24) }, + { "Grey15", RGB_(0x26, 0x26, 0x26) }, + { "Grey16", RGB_(0x29, 0x29, 0x29) }, + { "Grey17", RGB_(0x2b, 0x2b, 0x2b) }, + { "Grey18", RGB_(0x2e, 0x2e, 0x2e) }, + { "Grey19", RGB_(0x30, 0x30, 0x30) }, + { "Grey2", RGB_(0x5, 0x5, 0x5) }, + { "Grey20", RGB_(0x33, 0x33, 0x33) }, + { "Grey21", RGB_(0x36, 0x36, 0x36) }, + { "Grey22", RGB_(0x38, 0x38, 0x38) }, + { "Grey23", RGB_(0x3b, 0x3b, 0x3b) }, + { "Grey24", RGB_(0x3d, 0x3d, 0x3d) }, + { "Grey25", RGB_(0x40, 0x40, 0x40) }, + { "Grey26", RGB_(0x42, 0x42, 0x42) }, + { "Grey27", RGB_(0x45, 0x45, 0x45) }, + { "Grey28", RGB_(0x47, 0x47, 0x47) }, + { "Grey29", RGB_(0x4a, 0x4a, 0x4a) }, + { "Grey3", RGB_(0x8, 0x8, 0x8) }, + { "Grey30", RGB_(0x4d, 0x4d, 0x4d) }, + { "Grey31", RGB_(0x4f, 0x4f, 0x4f) }, + { "Grey32", RGB_(0x52, 0x52, 0x52) }, + { "Grey33", RGB_(0x54, 0x54, 0x54) }, + { "Grey34", RGB_(0x57, 0x57, 0x57) }, + { "Grey35", RGB_(0x59, 0x59, 0x59) }, + { "Grey36", RGB_(0x5c, 0x5c, 0x5c) }, + { "Grey37", RGB_(0x5e, 0x5e, 0x5e) }, + { "Grey38", RGB_(0x61, 0x61, 0x61) }, + { "Grey39", RGB_(0x63, 0x63, 0x63) }, + { "Grey4", RGB_(0xa, 0xa, 0xa) }, + { "Grey40", RGB_(0x66, 0x66, 0x66) }, + { "Grey41", RGB_(0x69, 0x69, 0x69) }, + { "Grey42", RGB_(0x6b, 0x6b, 0x6b) }, + { "Grey43", RGB_(0x6e, 0x6e, 0x6e) }, + { "Grey44", RGB_(0x70, 0x70, 0x70) }, + { "Grey45", RGB_(0x73, 0x73, 0x73) }, + { "Grey46", RGB_(0x75, 0x75, 0x75) }, + { "Grey47", RGB_(0x78, 0x78, 0x78) }, + { "Grey48", RGB_(0x7a, 0x7a, 0x7a) }, + { "Grey49", RGB_(0x7d, 0x7d, 0x7d) }, + { "Grey5", RGB_(0xd, 0xd, 0xd) }, + { "Grey50", RGB_(0x7f, 0x7f, 0x7f) }, + { "Grey51", RGB_(0x82, 0x82, 0x82) }, + { "Grey52", RGB_(0x85, 0x85, 0x85) }, + { "Grey53", RGB_(0x87, 0x87, 0x87) }, + { "Grey54", RGB_(0x8a, 0x8a, 0x8a) }, + { "Grey55", RGB_(0x8c, 0x8c, 0x8c) }, + { "Grey56", RGB_(0x8f, 0x8f, 0x8f) }, + { "Grey57", RGB_(0x91, 0x91, 0x91) }, + { "Grey58", RGB_(0x94, 0x94, 0x94) }, + { "Grey59", RGB_(0x96, 0x96, 0x96) }, + { "Grey6", RGB_(0xf, 0xf, 0xf) }, + { "Grey60", RGB_(0x99, 0x99, 0x99) }, + { "Grey61", RGB_(0x9c, 0x9c, 0x9c) }, + { "Grey62", RGB_(0x9e, 0x9e, 0x9e) }, + { "Grey63", RGB_(0xa1, 0xa1, 0xa1) }, + { "Grey64", RGB_(0xa3, 0xa3, 0xa3) }, + { "Grey65", RGB_(0xa6, 0xa6, 0xa6) }, + { "Grey66", RGB_(0xa8, 0xa8, 0xa8) }, + { "Grey67", RGB_(0xab, 0xab, 0xab) }, + { "Grey68", RGB_(0xad, 0xad, 0xad) }, + { "Grey69", RGB_(0xb0, 0xb0, 0xb0) }, + { "Grey7", RGB_(0x12, 0x12, 0x12) }, + { "Grey70", RGB_(0xb3, 0xb3, 0xb3) }, + { "Grey71", RGB_(0xb5, 0xb5, 0xb5) }, + { "Grey72", RGB_(0xb8, 0xb8, 0xb8) }, + { "Grey73", RGB_(0xba, 0xba, 0xba) }, + { "Grey74", RGB_(0xbd, 0xbd, 0xbd) }, + { "Grey75", RGB_(0xbf, 0xbf, 0xbf) }, + { "Grey76", RGB_(0xc2, 0xc2, 0xc2) }, + { "Grey77", RGB_(0xc4, 0xc4, 0xc4) }, + { "Grey78", RGB_(0xc7, 0xc7, 0xc7) }, + { "Grey79", RGB_(0xc9, 0xc9, 0xc9) }, + { "Grey8", RGB_(0x14, 0x14, 0x14) }, + { "Grey80", RGB_(0xcc, 0xcc, 0xcc) }, + { "Grey81", RGB_(0xcf, 0xcf, 0xcf) }, + { "Grey82", RGB_(0xd1, 0xd1, 0xd1) }, + { "Grey83", RGB_(0xd4, 0xd4, 0xd4) }, + { "Grey84", RGB_(0xd6, 0xd6, 0xd6) }, + { "Grey85", RGB_(0xd9, 0xd9, 0xd9) }, + { "Grey86", RGB_(0xdb, 0xdb, 0xdb) }, + { "Grey87", RGB_(0xde, 0xde, 0xde) }, + { "Grey88", RGB_(0xe0, 0xe0, 0xe0) }, + { "Grey89", RGB_(0xe3, 0xe3, 0xe3) }, + { "Grey9", RGB_(0x17, 0x17, 0x17) }, + { "Grey90", RGB_(0xe5, 0xe5, 0xe5) }, + { "Grey91", RGB_(0xe8, 0xe8, 0xe8) }, + { "Grey92", RGB_(0xeb, 0xeb, 0xeb) }, + { "Grey93", RGB_(0xed, 0xed, 0xed) }, + { "Grey94", RGB_(0xf0, 0xf0, 0xf0) }, + { "Grey95", RGB_(0xf2, 0xf2, 0xf2) }, + { "Grey96", RGB_(0xf5, 0xf5, 0xf5) }, + { "Grey97", RGB_(0xf7, 0xf7, 0xf7) }, + { "Grey98", RGB_(0xfa, 0xfa, 0xfa) }, + { "Grey99", RGB_(0xfc, 0xfc, 0xfc) }, + { "Honeydew", RGB_(0xf0, 0xff, 0xf0) }, + { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) }, + { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) }, + { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) }, + { "Honeydew4", RGB_(0x83, 0x8b, 0x83) }, + { "HotPink", RGB_(0xff, 0x69, 0xb4) }, + { "HotPink1", RGB_(0xff, 0x6e, 0xb4) }, + { "HotPink2", RGB_(0xee, 0x6a, 0xa7) }, + { "HotPink3", RGB_(0xcd, 0x60, 0x90) }, + { "HotPink4", RGB_(0x8b, 0x3a, 0x62) }, + { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) }, + { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) }, + { "IndianRed2", RGB_(0xee, 0x63, 0x63) }, + { "IndianRed3", RGB_(0xcd, 0x55, 0x55) }, + { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) }, + { "Indigo", RGB_(0x4b, 0x00, 0x82) }, + { "Ivory", RGB_(0xff, 0xff, 0xf0) }, + { "Ivory1", RGB_(0xff, 0xff, 0xf0) }, + { "Ivory2", RGB_(0xee, 0xee, 0xe0) }, + { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) }, + { "Ivory4", RGB_(0x8b, 0x8b, 0x83) }, + { "Khaki", RGB_(0xf0, 0xe6, 0x8c) }, + { "Khaki1", RGB_(0xff, 0xf6, 0x8f) }, + { "Khaki2", RGB_(0xee, 0xe6, 0x85) }, + { "Khaki3", RGB_(0xcd, 0xc6, 0x73) }, + { "Khaki4", RGB_(0x8b, 0x86, 0x4e) }, + { "Lavender", RGB_(0xe6, 0xe6, 0xfa) }, + { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) }, + { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) }, + { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) }, + { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) }, + { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) }, + { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) }, + { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) }, + { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) }, + { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) }, + { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) }, + { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) }, + { "LightBlue", RGB_(0xad, 0xd8, 0xe6) }, + { "LightBlue1", RGB_(0xbf, 0xef, 0xff) }, + { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) }, + { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) }, + { "LightBlue4", RGB_(0x68, 0x83, 0x8b) }, + { "LightCoral", RGB_(0xf0, 0x80, 0x80) }, + { "LightCyan", RGB_(0xe0, 0xff, 0xff) }, + { "LightCyan1", RGB_(0xe0, 0xff, 0xff) }, + { "LightCyan2", RGB_(0xd1, 0xee, 0xee) }, + { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) }, + { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) }, + { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) }, + { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) }, + { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, + { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, + { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, + { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, + { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, + { "LightGreen", RGB_(0x90, 0xee, 0x90) }, + { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, + { "LightMagenta", RGB_(0xff, 0xbb, 0xff) }, + { "LightPink", RGB_(0xff, 0xb6, 0xc1) }, + { "LightPink1", RGB_(0xff, 0xae, 0xb9) }, + { "LightPink2", RGB_(0xee, 0xa2, 0xad) }, + { "LightPink3", RGB_(0xcd, 0x8c, 0x95) }, + { "LightPink4", RGB_(0x8b, 0x5f, 0x65) }, + { "LightRed", RGB_(0xff, 0xbb, 0xbb) }, + { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) }, + { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) }, + { "LightSalmon2", RGB_(0xee, 0x95, 0x72) }, + { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) }, + { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) }, + { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) }, + { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) }, + { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) }, + { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) }, + { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) }, + { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) }, + { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) }, + { "LightSlateGray", RGB_(0x77, 0x88, 0x99) }, + { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) }, + { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) }, + { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) }, + { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) }, + { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) }, + { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) }, + { "LightYellow", RGB_(0xff, 0xff, 0xe0) }, + { "LightYellow1", RGB_(0xff, 0xff, 0xe0) }, + { "LightYellow2", RGB_(0xee, 0xee, 0xd1) }, + { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) }, + { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) }, + { "Lime", RGB_(0x00, 0xff, 0x00) }, + { "LimeGreen", RGB_(0x32, 0xcd, 0x32) }, + { "Linen", RGB_(0xfa, 0xf0, 0xe6) }, + { "Magenta", RGB_(0xff, 0x00, 0xff) }, + { "Magenta1", RGB_(0xff, 0x0, 0xff) }, + { "Magenta2", RGB_(0xee, 0x0, 0xee) }, + { "Magenta3", RGB_(0xcd, 0x0, 0xcd) }, + { "Magenta4", RGB_(0x8b, 0x0, 0x8b) }, + { "Maroon", RGB_(0x80, 0x00, 0x00) }, + { "Maroon1", RGB_(0xff, 0x34, 0xb3) }, + { "Maroon2", RGB_(0xee, 0x30, 0xa7) }, + { "Maroon3", RGB_(0xcd, 0x29, 0x90) }, + { "Maroon4", RGB_(0x8b, 0x1c, 0x62) }, + { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) }, + { "MediumBlue", RGB_(0x00, 0x00, 0xcd) }, + { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) }, + { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) }, + { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) }, + { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) }, + { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) }, + { "MediumPurple", RGB_(0x93, 0x70, 0xdb) }, + { "MediumPurple1", RGB_(0xab, 0x82, 0xff) }, + { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) }, + { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) }, + { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) }, + { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) }, + { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) }, + { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) }, + { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) }, + { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) }, + { "MidnightBlue", RGB_(0x19, 0x19, 0x70) }, + { "MintCream", RGB_(0xf5, 0xff, 0xfa) }, + { "MistyRose", RGB_(0xff, 0xe4, 0xe1) }, + { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) }, + { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) }, + { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) }, + { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) }, + { "Moccasin", RGB_(0xff, 0xe4, 0xb5) }, + { "NavajoWhite", RGB_(0xff, 0xde, 0xad) }, + { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) }, + { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) }, + { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) }, + { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) }, + { "Navy", RGB_(0x00, 0x00, 0x80) }, + { "NavyBlue", RGB_(0x0, 0x0, 0x80) }, + { "OldLace", RGB_(0xfd, 0xf5, 0xe6) }, + { "Olive", RGB_(0x80, 0x80, 0x00) }, + { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) }, + { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) }, + { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) }, + { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) }, + { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) }, + { "Orange", RGB_(0xff, 0xa5, 0x00) }, + { "Orange1", RGB_(0xff, 0xa5, 0x0) }, + { "Orange2", RGB_(0xee, 0x9a, 0x0) }, + { "Orange3", RGB_(0xcd, 0x85, 0x0) }, + { "Orange4", RGB_(0x8b, 0x5a, 0x0) }, + { "OrangeRed", RGB_(0xff, 0x45, 0x00) }, + { "OrangeRed1", RGB_(0xff, 0x45, 0x0) }, + { "OrangeRed2", RGB_(0xee, 0x40, 0x0) }, + { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) }, + { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) }, + { "Orchid", RGB_(0xda, 0x70, 0xd6) }, + { "Orchid1", RGB_(0xff, 0x83, 0xfa) }, + { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, + { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, + { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, + { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, + { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, + { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, + { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, + { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) }, + { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) }, + { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) }, + { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) }, + { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) }, + { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) }, + { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) }, + { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) }, + { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) }, + { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) }, + { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) }, + { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) }, + { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) }, + { "PeachPuff", RGB_(0xff, 0xda, 0xb9) }, + { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) }, + { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) }, + { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) }, + { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) }, + { "Peru", RGB_(0xcd, 0x85, 0x3f) }, + { "Pink", RGB_(0xff, 0xc0, 0xcb) }, + { "Pink1", RGB_(0xff, 0xb5, 0xc5) }, + { "Pink2", RGB_(0xee, 0xa9, 0xb8) }, + { "Pink3", RGB_(0xcd, 0x91, 0x9e) }, + { "Pink4", RGB_(0x8b, 0x63, 0x6c) }, + { "Plum", RGB_(0xdd, 0xa0, 0xdd) }, + { "Plum1", RGB_(0xff, 0xbb, 0xff) }, + { "Plum2", RGB_(0xee, 0xae, 0xee) }, + { "Plum3", RGB_(0xcd, 0x96, 0xcd) }, + { "Plum4", RGB_(0x8b, 0x66, 0x8b) }, + { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) }, + { "Purple", RGB_(0x80, 0x00, 0x80) }, + { "Purple1", RGB_(0x9b, 0x30, 0xff) }, + { "Purple2", RGB_(0x91, 0x2c, 0xee) }, + { "Purple3", RGB_(0x7d, 0x26, 0xcd) }, + { "Purple4", RGB_(0x55, 0x1a, 0x8b) }, + { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) }, + { "Red", RGB_(0xff, 0x00, 0x00) }, + { "Red1", RGB_(0xff, 0x0, 0x0) }, + { "Red2", RGB_(0xee, 0x0, 0x0) }, + { "Red3", RGB_(0xcd, 0x0, 0x0) }, + { "Red4", RGB_(0x8b, 0x0, 0x0) }, + { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) }, + { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) }, + { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) }, + { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) }, + { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) }, + { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) }, + { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) }, + { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) }, + { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) }, + { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) }, + { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) }, + { "Salmon", RGB_(0xfa, 0x80, 0x72) }, + { "Salmon1", RGB_(0xff, 0x8c, 0x69) }, + { "Salmon2", RGB_(0xee, 0x82, 0x62) }, + { "Salmon3", RGB_(0xcd, 0x70, 0x54) }, + { "Salmon4", RGB_(0x8b, 0x4c, 0x39) }, + { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) }, + { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) }, + { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) }, + { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) }, + { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) }, + { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) }, + { "SeaShell", RGB_(0xff, 0xf5, 0xee) }, + { "Seashell1", RGB_(0xff, 0xf5, 0xee) }, + { "Seashell2", RGB_(0xee, 0xe5, 0xde) }, + { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) }, + { "Seashell4", RGB_(0x8b, 0x86, 0x82) }, + { "Sienna", RGB_(0xa0, 0x52, 0x2d) }, + { "Sienna1", RGB_(0xff, 0x82, 0x47) }, + { "Sienna2", RGB_(0xee, 0x79, 0x42) }, + { "Sienna3", RGB_(0xcd, 0x68, 0x39) }, + { "Sienna4", RGB_(0x8b, 0x47, 0x26) }, + { "Silver", RGB_(0xc0, 0xc0, 0xc0) }, + { "SkyBlue", RGB_(0x87, 0xce, 0xeb) }, + { "SkyBlue1", RGB_(0x87, 0xce, 0xff) }, + { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) }, + { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) }, + { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) }, + { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) }, + { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) }, + { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) }, + { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) }, + { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) }, + { "SlateGray", RGB_(0x70, 0x80, 0x90) }, + { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) }, + { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) }, + { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) }, + { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) }, + { "SlateGrey", RGB_(0x70, 0x80, 0x90) }, + { "Snow", RGB_(0xff, 0xfa, 0xfa) }, + { "Snow1", RGB_(0xff, 0xfa, 0xfa) }, + { "Snow2", RGB_(0xee, 0xe9, 0xe9) }, + { "Snow3", RGB_(0xcd, 0xc9, 0xc9) }, + { "Snow4", RGB_(0x8b, 0x89, 0x89) }, + { "SpringGreen", RGB_(0x00, 0xff, 0x7f) }, + { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) }, + { "SpringGreen2", RGB_(0x0, 0xee, 0x76) }, + { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) }, + { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) }, + { "SteelBlue", RGB_(0x46, 0x82, 0xb4) }, + { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) }, + { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) }, + { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) }, + { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) }, + { "Tan", RGB_(0xd2, 0xb4, 0x8c) }, + { "Tan1", RGB_(0xff, 0xa5, 0x4f) }, + { "Tan2", RGB_(0xee, 0x9a, 0x49) }, + { "Tan3", RGB_(0xcd, 0x85, 0x3f) }, + { "Tan4", RGB_(0x8b, 0x5a, 0x2b) }, + { "Teal", RGB_(0x00, 0x80, 0x80) }, + { "Thistle", RGB_(0xd8, 0xbf, 0xd8) }, + { "Thistle1", RGB_(0xff, 0xe1, 0xff) }, + { "Thistle2", RGB_(0xee, 0xd2, 0xee) }, + { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) }, + { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) }, + { "Tomato", RGB_(0xff, 0x63, 0x47) }, + { "Tomato1", RGB_(0xff, 0x63, 0x47) }, + { "Tomato2", RGB_(0xee, 0x5c, 0x42) }, + { "Tomato3", RGB_(0xcd, 0x4f, 0x39) }, + { "Tomato4", RGB_(0x8b, 0x36, 0x26) }, + { "Turquoise", RGB_(0x40, 0xe0, 0xd0) }, + { "Turquoise1", RGB_(0x0, 0xf5, 0xff) }, + { "Turquoise2", RGB_(0x0, 0xe5, 0xee) }, + { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) }, + { "Turquoise4", RGB_(0x0, 0x86, 0x8b) }, + { "Violet", RGB_(0xee, 0x82, 0xee) }, + { "VioletRed", RGB_(0xd0, 0x20, 0x90) }, + { "VioletRed1", RGB_(0xff, 0x3e, 0x96) }, + { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) }, + { "VioletRed3", RGB_(0xcd, 0x32, 0x78) }, + { "VioletRed4", RGB_(0x8b, 0x22, 0x52) }, + { "WebGray", RGB_(0x80, 0x80, 0x80) }, + { "WebGreen", RGB_(0x0, 0x80, 0x0) }, + { "WebGrey", RGB_(0x80, 0x80, 0x80) }, + { "WebMaroon", RGB_(0x80, 0x0, 0x0) }, + { "WebPurple", RGB_(0x80, 0x0, 0x80) }, + { "Wheat", RGB_(0xf5, 0xde, 0xb3) }, + { "Wheat1", RGB_(0xff, 0xe7, 0xba) }, + { "Wheat2", RGB_(0xee, 0xd8, 0xae) }, + { "Wheat3", RGB_(0xcd, 0xba, 0x96) }, + { "Wheat4", RGB_(0x8b, 0x7e, 0x66) }, + { "White", RGB_(0xff, 0xff, 0xff) }, + { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) }, + { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) }, + { "X11Green", RGB_(0x0, 0xff, 0x0) }, + { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) }, + { "X11Maroon", RGB_(0xb0, 0x30, 0x60) }, + { "X11Purple", RGB_(0xa0, 0x20, 0xf0) }, + { "Yellow", RGB_(0xff, 0xff, 0x00) }, + { "Yellow1", RGB_(0xff, 0xff, 0x0) }, + { "Yellow2", RGB_(0xee, 0xee, 0x0) }, + { "Yellow3", RGB_(0xcd, 0xcd, 0x0) }, + { "Yellow4", RGB_(0x8b, 0x8b, 0x0) }, + { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) }, + { NULL, 0 }, +}; + +/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), +/// else look into color_name_table to translate a color name to its +/// hex value +/// +/// @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 char *name) +{ + if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) + && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) + && isxdigit(name[6]) && name[7] == NUL) { + // rgb hex string + return (RgbValue)strtol((char *)(name + 1), NULL, 16); + } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) { + return normal_bg; + } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) { + return normal_fg; + } + + for (int i = 0; color_name_table[i].name != NULL; i++) { + if (!STRICMP(name, color_name_table[i].name)) { + return color_name_table[i].color; + } + } + + return -1; +} + +int name_to_ctermcolor(const char *name) +{ + int i; + int off = TOUPPER_ASC(*name); + for (i = ARRAY_SIZE(color_names); --i >= 0;) { + if (off == color_names[i][0] + && STRICMP(name+1, color_names[i]+1) == 0) { + break; + } + } + if (i < 0) { + return -1; + } + TriState bold = kNone; + return lookup_color(i, false, &bold); +} diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h new file mode 100644 index 0000000000..325113a4ab --- /dev/null +++ b/src/nvim/highlight_group.h @@ -0,0 +1,19 @@ +#ifndef NVIM_HIGHLIGHT_GROUP_H +#define NVIM_HIGHLIGHT_GROUP_H + +#include "nvim/types.h" +#include "nvim/eval.h" + +#define MAX_HL_ID 20000 // maximum value for a highlight ID. + +typedef struct { + char *name; + RgbValue color; +} color_name_table_T; +extern color_name_table_T color_name_table[]; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "highlight_group.h.generated.h" +#endif + +#endif // NVIM_HIGHLIGHT_GROUP_H diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index beea1959e1..0bb224c729 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -476,7 +476,13 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ + ufunc_T *fp = find_func(fun); \ + assert(fp != NULL); \ + if (fp->uf_cb == nlua_CFunction_func_call) { \ + nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ + } else { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + } \ goto typval_encode_stop_converting_one_item; \ } while (0) @@ -615,14 +621,6 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); return false; } - if (tv->v_type == VAR_FUNC) { - ufunc_T *fp = find_func(tv->vval.v_string); - assert(fp != NULL); - if (fp->uf_cb == nlua_CFunction_func_call) { - nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); - return true; - } - } if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { return false; } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 054f6c9483..21625854fb 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -92,7 +92,22 @@ static void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { size_t len; - const char *const str = lua_tolstring(lstate, -1, &len); + const char *str = NULL; + + if (luaL_getmetafield(lstate, -1, "__tostring")) { + if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) { + // call __tostring, convert the result and pop result. + str = lua_tolstring(lstate, -1, &len); + lua_pop(lstate, 1); + } + // pop __tostring. + lua_pop(lstate, 1); + } + + if (!str) { + // defer to lua default conversion, this will render tables as [NULL]. + str = lua_tolstring(lstate, -1, &len); + } msg_ext_set_kind("lua_error"); semsg_multiline(msg, (int)len, str); diff --git a/src/nvim/main.c b/src/nvim/main.c index 95ef306745..dec1ae93e7 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -25,6 +25,7 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/iconv.h" #include "nvim/if_cscope.h" #include "nvim/lua/executor.h" @@ -112,7 +113,6 @@ static const char *err_too_many_args = N_("Too many edit arguments"); static const char *err_extra_cmd = N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"); - void event_init(void) { loop_init(&main_loop, NULL); @@ -344,6 +344,12 @@ int main(int argc, char **argv) TIME_MSG("init screen for UI"); } + if (ui_client_channel_id) { + ui_client_init(ui_client_channel_id); + ui_client_execute(ui_client_channel_id); + abort(); // unreachable + } + init_default_mappings(); // Default mappings. TIME_MSG("init default mappings"); @@ -840,9 +846,8 @@ static void remote_request(mparm_T *params, int remote_args, exit(1); } - ui_client_init(chan); - ui_client_execute(chan); - abort(); // unreachable + ui_client_channel_id = chan; + return; } Array args = ARRAY_DICT_INIT; diff --git a/src/nvim/map.c b/src/nvim/map.c index 4e39eb8c07..b3f48ad5d6 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -179,6 +179,7 @@ MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER) +MAP_IMPL(String, UIClientHandler, NULL) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 00f72386a7..693ef50127 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -8,6 +8,7 @@ #include "nvim/extmark_defs.h" #include "nvim/highlight_defs.h" #include "nvim/map_defs.h" +#include "nvim/ui_client.h" #if defined(__NetBSD__) # undef uint64_t @@ -48,6 +49,7 @@ MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) +MAP_DECLS(String, UIClientHandler) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/memory.c b/src/nvim/memory.c index d68ca6b62e..6cdc4f1fde 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -13,6 +13,7 @@ #include "nvim/decoration_provider.h" #include "nvim/eval.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/memfile.h" #include "nvim/memory.h" diff --git a/src/nvim/message.c b/src/nvim/message.c index b39450cdc6..b3fefbc0f4 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -206,11 +206,10 @@ void msg_grid_validate(void) } } -/* - * msg(s) - displays the string 's' on the status line - * When terminal not initialized (yet) mch_errmsg(..) is used. - * return TRUE if wait_return not called - */ +/// Displays the string 's' on the status line +/// When terminal not initialized (yet) mch_errmsg(..) is used. +/// +/// @return TRUE if wait_return not called int msg(char *s) { return msg_attr_keep(s, 0, false, false); @@ -232,7 +231,7 @@ int msg_attr(const char *s, const int attr) return msg_attr_keep(s, attr, false, false); } -/// similar to msg_outtrans_attr, but support newlines and tabs. +/// Similar to msg_outtrans_attr, but support newlines and tabs. void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clear) FUNC_ATTR_NONNULL_ALL { @@ -339,7 +338,8 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) } /// Truncate a string such that it can be printed without causing a scroll. -/// Returns an allocated string or NULL when no truncating is done. +/// +/// @return an allocated string or NULL when no truncating is done. /// /// @param force always truncate char_u *msg_strtrunc(char_u *s, int force) @@ -371,10 +371,8 @@ char_u *msg_strtrunc(char_u *s, int force) return buf; } -/* - * Truncate a string "s" to "buf" with cell width "room". - * "s" and "buf" may be equal. - */ +/// Truncate a string "s" to "buf" with cell width "room". +/// "s" and "buf" may be equal. void trunc_string(char_u *s, char_u *buf, int room_in, int buflen) { size_t room = room_in - 3; // "..." takes 3 chars @@ -498,19 +496,15 @@ int smsg_attr_keep(int attr, const char *s, ...) static int last_sourcing_lnum = 0; static char_u *last_sourcing_name = NULL; -/* - * Reset the last used sourcing name/lnum. Makes sure it is displayed again - * for the next error message; - */ +/// Reset the last used sourcing name/lnum. Makes sure it is displayed again +/// for the next error message; void reset_last_sourcing(void) { XFREE_CLEAR(last_sourcing_name); last_sourcing_lnum = 0; } -/* - * Return TRUE if "sourcing_name" differs from "last_sourcing_name". - */ +/// @return TRUE if "sourcing_name" differs from "last_sourcing_name". static int other_sourcing_name(void) { if (sourcing_name != NULL) { @@ -560,11 +554,9 @@ static char *get_emsg_lnum(void) return NULL; } -/* - * Display name and line number for the source of an error. - * Remember the file name and line number, so that for the next error the info - * is only displayed if it changed. - */ +/// Display name and line number for the source of an error. +/// Remember the file name and line number, so that for the next error the info +/// is only displayed if it changed. void msg_source(int attr) { no_wait_return++; @@ -592,12 +584,10 @@ void msg_source(int attr) --no_wait_return; } -/* - * Return TRUE if not giving error messages right now: - * If "emsg_off" is set: no error messages at the moment. - * If "msg" is in 'debug': do error message but without side effects. - * If "emsg_skip" is set: never do error messages. - */ +/// @return TRUE if not giving error messages right now: +/// If "emsg_off" is set: no error messages at the moment. +/// If "msg" is in 'debug': do error message but without side effects. +/// If "emsg_skip" is set: never do error messages. int emsg_not_now(void) { if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL @@ -840,10 +830,11 @@ void msg_schedule_semsg(const char *const fmt, ...) multiqueue_put(main_loop.events, msg_semsg_event, 1, s); } -// Like msg(), but truncate to a single line if p_shm contains 't', or when -// "force" is true. This truncates in another way as for normal messages. -// Careful: The string may be changed by msg_may_trunc()! -// Returns a pointer to the printed message, if wait_return() not called. +/// Like msg(), but truncate to a single line if p_shm contains 't', or when +/// "force" is true. This truncates in another way as for normal messages. +/// Careful: The string may be changed by msg_may_trunc()! +/// +/// @return a pointer to the printed message, if wait_return() not called. char *msg_trunc_attr(char *s, bool force, int attr) { int n; @@ -863,11 +854,11 @@ char *msg_trunc_attr(char *s, bool force, int attr) return NULL; } -/* - * Check if message "s" should be truncated at the start (for filenames). - * Return a pointer to where the truncated message starts. - * Note: May change the message by replacing a character with '<'. - */ +/// Check if message "s" should be truncated at the start (for filenames). +/// +/// @return a pointer to where the truncated message starts. +/// +/// @note: May change the message by replacing a character with '<'. char_u *msg_may_trunc(bool force, char_u *s) { int room; @@ -967,10 +958,9 @@ static void add_msg_hist(const char *s, int len, int attr, bool multiline) msg_hist_len++; } -/* - * Delete the first (oldest) message from the history. - * Returns FAIL if there are no messages. - */ +/// Delete the first (oldest) message from the history. +/// +/// @return FAIL if there are no messages. int delete_first_msg(void) { struct msg_hist *p; @@ -1056,10 +1046,8 @@ void ex_messages(void *const eap_p) } } -/* - * Call this after prompting the user. This will avoid a hit-return message - * and a delay. - */ +/// Call this after prompting the user. This will avoid a hit-return message +/// and a delay. void msg_end_prompt(void) { msg_ext_clear_later(); @@ -1073,9 +1061,9 @@ void msg_end_prompt(void) /// Wait for the user to hit a key (normally Enter) /// -/// If 'redraw' is true, redraw the entire screen NOT_VALID -/// If 'redraw' is false, do a normal redraw -/// If 'redraw' is -1, don't redraw at all +/// @param redraw if true, redraw the entire screen NOT_VALID +/// if false, do a normal redraw +/// if -1, don't redraw at all void wait_return(int redraw) { int c; @@ -1265,9 +1253,7 @@ void wait_return(int redraw) } } -/* - * Write the hit-return prompt. - */ +/// Write the hit-return prompt. static void hit_return_msg(void) { int save_p_more = p_more; @@ -1288,9 +1274,7 @@ static void hit_return_msg(void) p_more = save_p_more; } -/* - * Set "keep_msg" to "s". Free the old value and check for NULL pointer. - */ +/// Set "keep_msg" to "s". Free the old value and check for NULL pointer. void set_keep_msg(char *s, int attr) { xfree(keep_msg); @@ -1357,9 +1341,7 @@ void msg_ext_set_kind(const char *msg_kind) msg_ext_kind = msg_kind; } -/* - * Prepare for outputting characters in the command line. - */ +/// Prepare for outputting characters in the command line. void msg_start(void) { int did_return = false; @@ -1406,9 +1388,7 @@ void msg_start(void) } } -/* - * Note that the current msg position is where messages start. - */ +/// Note that the current msg position is where messages start. void msg_starthere(void) { lines_left = cmdline_row; @@ -1462,12 +1442,11 @@ static void msg_home_replace_attr(char_u *fname, int attr) xfree(name); } -/* - * Output 'len' characters in 'str' (including NULs) with translation - * if 'len' is -1, output up to a NUL character. - * Use attributes 'attr'. - * Return the number of characters it takes on the screen. - */ +/// Output 'len' characters in 'str' (including NULs) with translation +/// if 'len' is -1, output up to a NUL character. +/// Use attributes 'attr'. +/// +/// @return the number of characters it takes on the screen. int msg_outtrans(char_u *str) { return msg_outtrans_attr(str, 0); @@ -1483,10 +1462,10 @@ int msg_outtrans_len(const char_u *str, int len) return msg_outtrans_len_attr(str, len, 0); } -/* - * Output one character at "p". Return pointer to the next character. - * Handles multi-byte characters. - */ +/// Output one character at "p". +/// Handles multi-byte characters. +/// +/// @return pointer to the next character. char_u *msg_outtrans_one(char_u *p, int attr) { int l; @@ -1752,9 +1731,7 @@ void str2specialbuf(const char *sp, char *buf, size_t len) *buf = NUL; } -/* - * print line for :print or :list command - */ +/// print line for :print or :list command void msg_prt_line(char_u *s, int list) { int c; @@ -1903,8 +1880,9 @@ void msg_prt_line(char_u *s, int list) msg_clr_eos(); } -// Use grid_puts() to output one multi-byte character. -// Return the pointer "s" advanced to the next character. +/// Use grid_puts() to output one multi-byte character. +/// +/// @return the pointer "s" advanced to the next character. static char_u *screen_puts_mbyte(char_u *s, int l, int attr) { int cw; @@ -1936,10 +1914,8 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr) return s + l; } -/* - * Output a string to the screen at position msg_row, msg_col. - * Update msg_row and msg_col for the next message. - */ +/// Output a string to the screen at position msg_row, msg_col. +/// Update msg_row and msg_col for the next message. void msg_puts(const char *s) { msg_puts_attr(s, 0); @@ -1950,11 +1926,9 @@ void msg_puts_title(const char *s) msg_puts_attr(s, HL_ATTR(HLF_T)); } -/* - * Show a message in such a way that it always fits in the line. Cut out a - * part in the middle and replace it with "..." when necessary. - * Does not handle multi-byte characters! - */ +/// Show a message in such a way that it always fits in the line. Cut out a +/// part in the middle and replace it with "..." when necessary. +/// Does not handle multi-byte characters! void msg_outtrans_long_attr(char_u *longstr, int attr) { msg_outtrans_long_len_attr(longstr, (int)STRLEN(longstr), attr); @@ -1974,9 +1948,7 @@ void msg_outtrans_long_len_attr(char_u *longstr, int len, int attr) msg_outtrans_len_attr(longstr + len - slen, slen, attr); } -/* - * Basic function for writing a message with highlight attributes. - */ +/// Basic function for writing a message with highlight attributes. void msg_puts_attr(const char *const s, const int attr) { msg_puts_attr_len(s, -1, attr); @@ -2078,10 +2050,8 @@ static void msg_ext_emit_chunk(void) ADD(msg_ext_chunks, ARRAY_OBJ(chunk)); } -/* - * The display part of msg_puts_attr_len(). - * May be called recursively to display scroll-back text. - */ +/// The display part of msg_puts_attr_len(). +/// May be called recursively to display scroll-back text. static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurse) { const char_u *s = str; @@ -2286,8 +2256,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs msg_check(); } -/// Return true when ":filter pattern" was used and "msg" does not match -/// "pattern". +/// @return true when ":filter pattern" was used and "msg" does not match +/// "pattern". bool message_filtered(char_u *msg) { if (cmdmod.filter_regmatch.regprog == NULL) { @@ -2419,9 +2389,7 @@ void msg_reset_scroll(void) msg_scrolled_at_flush = 0; } -/* - * Increment "msg_scrolled". - */ +/// Increment "msg_scrolled". static void inc_msg_scrolled(void) { if (*get_vim_var_str(VV_SCROLLSTART) == NUL) { @@ -2500,9 +2468,7 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int *sb_col = 0; } -/* - * Finished showing messages, clear the scroll-back text on the next message. - */ +/// Finished showing messages, clear the scroll-back text on the next message. void may_clear_sb_text(void) { do_clear_sb_text = SB_CLEAR_ALL; @@ -2545,9 +2511,7 @@ void clear_sb_text(int all) } } -/* - * "g<" command. - */ +/// "g<" command. void show_sb_text(void) { msgchunk_T *mp; @@ -2563,9 +2527,7 @@ void show_sb_text(void) } } -/* - * Move to the start of screen line in already displayed text. - */ +/// Move to the start of screen line in already displayed text. static msgchunk_T *msg_sb_start(msgchunk_T *mps) { msgchunk_T *mp = mps; @@ -2576,9 +2538,7 @@ static msgchunk_T *msg_sb_start(msgchunk_T *mps) return mp; } -/* - * Mark the last message chunk as finishing the line. - */ +/// Mark the last message chunk as finishing the line. void msg_sb_eol(void) { if (last_msgchunk != NULL) { @@ -2586,10 +2546,9 @@ void msg_sb_eol(void) } } -/* - * Display a screen line from previously displayed text at row "row". - * Returns a pointer to the text for the next line (can be NULL). - */ +/// Display a screen line from previously displayed text at row "row". +/// +/// @return a pointer to the text for the next line (can be NULL). static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) { msgchunk_T *mp = smp; @@ -2612,9 +2571,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) return mp->sb_next; } -/* - * Output any postponed text for msg_puts_attr_len(). - */ +/// Output any postponed text for msg_puts_attr_len(). static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr) { attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); @@ -2635,9 +2592,9 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr) } } -// Returns TRUE when messages should be printed to stdout/stderr: -// - "batch mode" ("silent mode", -es/-Es) -// - no UI and not embedded +/// @return TRUE when messages should be printed to stdout/stderr: +/// - "batch mode" ("silent mode", -es/-Es) +/// - no UI and not embedded int msg_use_printf(void) { return !embedded_mode && !ui_active(); @@ -2698,13 +2655,12 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) msg_didout = true; // assume that line is not empty } -/* - * Show the more-prompt and handle the user response. - * This takes care of scrolling back and displaying previously displayed text. - * When at hit-enter prompt "typed_char" is the already typed character, - * otherwise it's NUL. - * Returns TRUE when jumping ahead to "confirm_msg_tail". - */ +/// Show the more-prompt and handle the user response. +/// This takes care of scrolling back and displaying previously displayed text. +/// When at hit-enter prompt "typed_char" is the already typed character, +/// otherwise it's NUL. +/// +/// @return TRUE when jumping ahead to "confirm_msg_tail". static int do_more_prompt(int typed_char) { static bool entered = false; @@ -2962,7 +2918,7 @@ void mch_errmsg(char *str) } } -// Give a message. To be used when the UI is not initialized yet. +/// Give a message. To be used when the UI is not initialized yet. void mch_msg(char *str) { assert(str != NULL); @@ -2977,10 +2933,8 @@ void mch_msg(char *str) } #endif // WIN32 -/* - * Put a character on the screen at the current message position and advance - * to the next position. Only for printable ASCII! - */ +/// Put a character on the screen at the current message position and advance +/// to the next position. Only for printable ASCII! static void msg_screen_putchar(int c, int attr) { attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); @@ -3013,10 +2967,8 @@ void msg_moremsg(int full) } } -/* - * Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or - * exmode_active. - */ +/// Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or +/// exmode_active. void repeat_message(void) { if (State == ASKMORE) { @@ -3041,10 +2993,8 @@ void repeat_message(void) } } -/* - * Clear from current message position to end of screen. - * Skip this when ":silent" was used, no need to clear for redirection. - */ +/// Clear from current message position to end of screen. +/// Skip this when ":silent" was used, no need to clear for redirection. void msg_clr_eos(void) { if (msg_silent == 0) { @@ -3052,11 +3002,9 @@ void msg_clr_eos(void) } } -/* - * Clear from current message position to end of screen. - * Note: msg_col is not updated, so we remember the end of the message - * for msg_check(). - */ +/// Clear from current message position to end of screen. +/// Note: msg_col is not updated, so we remember the end of the message +/// for msg_check(). void msg_clr_eos_force(void) { if (ui_has(kUIMessages)) { @@ -3083,9 +3031,7 @@ void msg_clr_eos_force(void) } } -/* - * Clear the command line. - */ +/// Clear the command line. void msg_clr_cmdline(void) { msg_row = cmdline_row; @@ -3093,11 +3039,10 @@ void msg_clr_cmdline(void) msg_clr_eos_force(); } -/* - * end putting a message on the screen - * call wait_return if the message does not fit in the available space - * return TRUE if wait_return not called. - */ +/// end putting a message on the screen +/// call wait_return if the message does not fit in the available space +/// +/// @return TRUE if wait_return not called. int msg_end(void) { /* @@ -3187,10 +3132,8 @@ bool msg_ext_is_visible(void) return ui_has(kUIMessages) && msg_ext_visible > 0; } -/* - * If the written message runs into the shown command or ruler, we have to - * wait for hit-return and redraw the window later. - */ +/// If the written message runs into the shown command or ruler, we have to +/// wait for hit-return and redraw the window later. void msg_check(void) { if (ui_has(kUIMessages)) { @@ -3202,10 +3145,9 @@ void msg_check(void) } } -/* - * May write a string to the redirection file. - * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes. - */ +/// May write a string to the redirection file. +/// +/// @param maxlen if -1, write the whole string, otherwise up to "maxlen" bytes. static void redir_write(const char *const str, const ptrdiff_t maxlen) { const char_u *s = (char_u *)str; @@ -3290,10 +3232,8 @@ int redirecting(void) || redir_reg || redir_vname || capture_ga != NULL; } -/* - * Before giving verbose message. - * Must always be called paired with verbose_leave()! - */ +/// Before giving verbose message. +/// Must always be called paired with verbose_leave()! void verbose_enter(void) { if (*p_vfile != NUL) { @@ -3301,10 +3241,8 @@ void verbose_enter(void) } } -/* - * After giving verbose message. - * Must always be called paired with verbose_enter()! - */ +/// After giving verbose message. +/// Must always be called paired with verbose_enter()! void verbose_leave(void) { if (*p_vfile != NUL) { @@ -3314,9 +3252,7 @@ void verbose_leave(void) } } -/* - * Like verbose_enter() and set msg_scroll when displaying the message. - */ +/// Like verbose_enter() and set msg_scroll when displaying the message. void verbose_enter_scroll(void) { if (*p_vfile != NUL) { @@ -3327,9 +3263,7 @@ void verbose_enter_scroll(void) } } -/* - * Like verbose_leave() and set cmdline_row when displaying the message. - */ +/// Like verbose_leave() and set cmdline_row when displaying the message. void verbose_leave_scroll(void) { if (*p_vfile != NUL) { @@ -3341,9 +3275,7 @@ void verbose_leave_scroll(void) } } -/* - * Called when 'verbosefile' is set: stop writing to the file. - */ +/// Called when 'verbosefile' is set: stop writing to the file. void verbose_stop(void) { if (verbose_fd != NULL) { @@ -3353,10 +3285,9 @@ void verbose_stop(void) verbose_did_open = FALSE; } -/* - * Open the file 'verbosefile'. - * Return FAIL or OK. - */ +/// Open the file 'verbosefile'. +/// +/// @return FAIL or OK. int verbose_open(void) { if (verbose_fd == NULL && !verbose_did_open) { @@ -3372,10 +3303,8 @@ int verbose_open(void) return OK; } -/* - * Give a warning message (for searching). - * Use 'w' highlighting and may repeat the message after redrawing - */ +/// Give a warning message (for searching). +/// Use 'w' highlighting and may repeat the message after redrawing void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) { // Don't do this for ":silent". @@ -3414,9 +3343,7 @@ void give_warning2(char_u *const message, char_u *const a1, bool hl) give_warning(IObuff, hl); } -/* - * Advance msg cursor to column "col". - */ +/// Advance msg cursor to column "col". void msg_advance(int col) { if (msg_silent != 0) { // nothing to advance to @@ -3626,15 +3553,13 @@ static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool return xmalloc(lenhotkey); } -/* - * Format the dialog string, and display it at the bottom of - * the screen. Return a string of hotkey chars (if defined) for - * each 'button'. If a button has no hotkey defined, the first character of - * the button is used. - * The hotkeys can be multi-byte characters, but without combining chars. - * - * Returns an allocated string with hotkeys. - */ +/// Format the dialog string, and display it at the bottom of +/// the screen. Return a string of hotkey chars (if defined) for +/// each 'button'. If a button has no hotkey defined, the first character of +/// the button is used. +/// The hotkeys can be multi-byte characters, but without combining chars. +/// +/// @return an allocated string with hotkeys. static char_u *msg_show_console_dialog(char_u *message, char_u *buttons, int dfltbutton) FUNC_ATTR_NONNULL_RET { @@ -3727,9 +3652,7 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int def *msgp = NUL; } -/* - * Display the ":confirm" message. Also called when screen resized. - */ +/// Display the ":confirm" message. Also called when screen resized. void display_confirm_msg(void) { // Avoid that 'q' at the more prompt truncates the message here. diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index f4e836fa81..48ecd5d0ea 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -547,12 +547,8 @@ void rpc_close(Channel *channel) channel->rpc.closed = true; channel_decref(channel); - if (channel->id == ui_client_channel_id) { - // TODO(bfredl): handle this in ui_client, where os_exit() is safe - exit(0); - } - - if (channel->streamtype == kChannelStreamStdio) { + if (channel->streamtype == kChannelStreamStdio + || channel->id == ui_client_channel_id) { multiqueue_put(main_loop.fast_events, exit_event, 0); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 191b635dc0..ffd009be89 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -47,6 +47,7 @@ #include "nvim/getchar.h" #include "nvim/hardcopy.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/macros.h" @@ -2910,7 +2911,7 @@ ambw_end: || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) { errmsg = e_invarg; } else { - if (curwin->w_status_height) { + if (curwin->w_status_height || global_stl_height()) { curwin->w_redr_status = true; redraw_later(curwin, VALID); } @@ -3553,16 +3554,22 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) struct chars_tab *tab; struct chars_tab fcs_tab[] = { - { &wp->w_p_fcs_chars.stl, "stl", ' ' }, - { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, - { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ - { &wp->w_p_fcs_chars.fold, "fold", 183 }, // · - { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' }, - { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' }, - { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │ - { &wp->w_p_fcs_chars.diff, "diff", '-' }, - { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, - { &wp->w_p_fcs_chars.eob, "eob", '~' }, + { &wp->w_p_fcs_chars.stl, "stl", ' ' }, + { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, + { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─ + { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴ + { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬ + { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ + { &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤ + { &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├ + { &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼ + { &wp->w_p_fcs_chars.fold, "fold", 183 }, // · + { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' }, + { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' }, + { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │ + { &wp->w_p_fcs_chars.diff, "diff", '-' }, + { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, + { &wp->w_p_fcs_chars.eob, "eob", '~' }, }; struct chars_tab lcs_tab[] = { { &wp->w_p_lcs_chars.eol, "eol", NUL }, @@ -3589,15 +3596,17 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) varp = &p_fcs; } if (*p_ambw == 'd') { - // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is - // forbidden (TUI limitation?). Set old defaults. - fcs_tab[2].def = '|'; - fcs_tab[6].def = '|'; - fcs_tab[3].def = '-'; - } else { - fcs_tab[2].def = 9474; // │ - fcs_tab[6].def = 9474; // │ - fcs_tab[3].def = 183; // · + // XXX: If ambiwidth=double then some characters take 2 columns, + // which is forbidden (TUI limitation?). Set old defaults. + fcs_tab[2].def = '-'; + fcs_tab[3].def = '-'; + fcs_tab[4].def = '-'; + fcs_tab[5].def = '|'; + fcs_tab[6].def = '|'; + fcs_tab[7].def = '|'; + fcs_tab[8].def = '+'; + fcs_tab[9].def = '-'; + fcs_tab[12].def = '|'; } } @@ -3845,7 +3854,7 @@ static bool parse_winhl_opt(win_T *wp) size_t nlen = (size_t)(colon-p); char *hi = colon+1; char *commap = xstrchrnul(hi, ','); - int len = (int)(commap-hi); + size_t len = (size_t)(commap-hi); int hl_id = len ? syn_check_group(hi, len) : -1; if (strncmp("Normal", p, nlen) == 0) { @@ -4475,6 +4484,20 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // 'winminwidth' win_setminwidth(); } else if (pp == &p_ls) { + // When switching to global statusline, decrease topframe height + // Also clear the cmdline to remove the ruler if there is one + if (value == 3 && old_value != 3) { + frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false); + (void)win_comp_pos(); + clear_cmdline = true; + } + // When switching from global statusline, increase height of topframe by STATUS_HEIGHT + // in order to to re-add the space that was previously taken by the global statusline + if (old_value == 3 && value != 3) { + frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false); + (void)win_comp_pos(); + } + last_status(false); // (re)set last window status line. } else if (pp == &p_stal) { // (re)set tab page line @@ -5646,7 +5669,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value) void comp_col(void) { - int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW)); + int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW)); sc_col = 0; ru_col = 0; diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 58cb1b8f84..4a49c0b162 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -86,7 +86,7 @@ static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, ioctl(slave, I_PUSH, "ptem"); // ldterm provides most of the termio terminal interface ioctl(slave, I_PUSH, "ldterm"); - // ttcompat compatability with older terminal ioctls + // ttcompat compatibility with older terminal ioctls ioctl(slave, I_PUSH, "ttcompat"); if (termp) { diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index ed9f4636e3..4fb9e30a96 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -307,7 +307,7 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// Build the command line to pass to CreateProcessW. /// /// @param[in] argv Array with string arguments. -/// @param[out] cmd_line Location where saved builded cmd line. +/// @param[out] cmd_line Location where saved built cmd line. /// /// @returns zero on success, or error code of MultiByteToWideChar function. /// diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 72fd035cbd..d868fe8284 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -22,6 +22,7 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/highlight_group.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -39,7 +40,6 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/strings.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/vim.h" #include "nvim/window.h" @@ -3635,7 +3635,7 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp win_setwidth(sz); } } else if (sz != win->w_height - && (win->w_height + win->w_status_height + tabline_height() + && (win->w_height + win->w_hsep_height + win->w_status_height + tabline_height() < cmdline_row)) { win_setheight(sz); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index ac07e60632..60de4e479e 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -88,6 +88,7 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" @@ -286,7 +287,8 @@ void update_curbuf(int type) void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && wp->w_status_height) { + if (wp->w_buffer == buf + && (wp->w_status_height || (wp == curwin && global_stl_height()))) { wp->w_redr_status = true; if (must_redraw < VALID) { must_redraw = VALID; @@ -316,6 +318,7 @@ void redraw_win_signcol(win_T *wp) int update_screen(int type) { static bool did_intro = false; + bool is_stl_global = global_stl_height() > 0; // Don't do anything if the screen structures are (not yet) valid. // A VimResized autocmd can invoke redrawing in the middle of a resize, @@ -398,10 +401,13 @@ int update_screen(int type) if (W_ENDROW(wp) > valid) { wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID); } - if (W_ENDROW(wp) + wp->w_status_height > valid) { + if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) { wp->w_redr_status = true; } } + if (is_stl_global && Rows - p_ch - 1 > valid) { + curwin->w_redr_status = true; + } } msg_grid_set_pos(Rows-p_ch, false); msg_grid_invalid = false; @@ -423,13 +429,15 @@ int update_screen(int type) wp->w_redr_type = REDRAW_TOP; } else { wp->w_redr_type = NOT_VALID; - if (W_ENDROW(wp) + wp->w_status_height - <= msg_scrolled) { - wp->w_redr_status = TRUE; + if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) { + wp->w_redr_status = true; } } } } + if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) { + curwin->w_redr_status = true; + } redraw_cmdline = true; redraw_tabline = true; } @@ -740,8 +748,11 @@ static void win_update(win_T *wp, DecorProviders *providers) wp->w_lines_valid = 0; } - // Window is zero-height: nothing to draw. + // Window is zero-height: Only need to draw the separator if (wp->w_grid.Rows == 0) { + // draw the horizontal separator below this window + draw_hsep_win(wp); + draw_sep_connectors_win(wp); wp->w_redr_type = 0; return; } @@ -749,7 +760,8 @@ static void win_update(win_T *wp, DecorProviders *providers) // Window is zero-width: Only need to draw the separator. if (wp->w_grid.Columns == 0) { // draw the vertical separator right of this window - draw_vsep_win(wp, 0); + draw_vsep_win(wp); + draw_sep_connectors_win(wp); wp->w_redr_type = 0; return; } @@ -1664,7 +1676,9 @@ static void win_update(win_T *wp, DecorProviders *providers) kvi_destroy(line_providers); if (wp->w_redr_type >= REDRAW_TOP) { - draw_vsep_win(wp, 0); + draw_vsep_win(wp); + draw_hsep_win(wp); + draw_sep_connectors_win(wp); } syn_set_timeout(NULL); @@ -2609,9 +2623,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } next_search_hl(wp, shl, lnum, (colnr_T)v, shl == &search_hl ? NULL : cur); - if (wp->w_s->b_syn_slow) { - has_syntax = false; - } // Need to get the line again, a multi-line regexp may have made it // invalid. @@ -3369,6 +3380,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc did_emsg = save_did_emsg; } + if (wp->w_s->b_syn_slow) { + has_syntax = false; + } + // Need to get the line again, a multi-line regexp may // have made it invalid. line = ml_get_buf(wp->w_buffer, lnum, false); @@ -4881,10 +4896,15 @@ void rl_mirror(char_u *str) */ void status_redraw_all(void) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height) { - wp->w_redr_status = true; - redraw_later(wp, VALID); + if (global_stl_height()) { + curwin->w_redr_status = true; + redraw_later(curwin, VALID); + } else { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_status_height) { + wp->w_redr_status = true; + redraw_later(wp, VALID); + } } } } @@ -4898,10 +4918,15 @@ void status_redraw_curbuf(void) /// Marks all status lines of the specified buffer for redraw. void status_redraw_buf(buf_T *buf) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height != 0 && wp->w_buffer == buf) { - wp->w_redr_status = true; - redraw_later(wp, VALID); + if (global_stl_height() != 0 && curwin->w_buffer == buf) { + curwin->w_redr_status = true; + redraw_later(curwin, VALID); + } else { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_status_height != 0 && wp->w_buffer == buf) { + wp->w_redr_status = true; + redraw_later(wp, VALID); + } } } } @@ -4943,10 +4968,8 @@ void win_redraw_last_status(const frame_T *frp) } } -/* - * Draw the verticap separator right of window "wp" starting with line "row". - */ -static void draw_vsep_win(win_T *wp, int row) +/// Draw the vertical separator right of window "wp" +static void draw_vsep_win(win_T *wp) { int hl; int c; @@ -4954,15 +4977,97 @@ static void draw_vsep_win(win_T *wp, int row) if (wp->w_vsep_width) { // draw the vertical separator right of this window c = fillchar_vsep(wp, &hl); - grid_fill(&default_grid, wp->w_winrow + row, W_ENDROW(wp), + grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); } } +/// Draw the horizontal separator below window "wp" +static void draw_hsep_win(win_T *wp) +{ + int hl; + int c; -/* - * Get the length of an item as it will be shown in the status line. - */ + if (wp->w_hsep_height) { + // draw the horizontal separator below this window + c = fillchar_hsep(wp, &hl); + grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, + wp->w_wincol, W_ENDCOL(wp), c, c, hl); + } +} + +/// Get the separator connector for specified window corner of window "wp" +static int get_corner_sep_connector(win_T *wp, WindowCorner corner) +{ + // It's impossible for windows to be connected neither vertically nor horizontally + // So if they're not vertically connected, assume they're horizontally connected + if (vsep_connected(wp, corner)) { + if (hsep_connected(wp, corner)) { + return wp->w_p_fcs_chars.verthoriz; + } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) { + return wp->w_p_fcs_chars.vertright; + } else { + return wp->w_p_fcs_chars.vertleft; + } + } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) { + return wp->w_p_fcs_chars.horizdown; + } else { + return wp->w_p_fcs_chars.horizup; + } +} + +/// Draw seperator connecting characters on the corners of window "wp" +static void draw_sep_connectors_win(win_T *wp) +{ + // Don't draw separator connectors unless global statusline is enabled and the window has + // either a horizontal or vertical separator + if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) { + return; + } + + int hl = win_hl_attr(wp, HLF_C); + + // Determine which edges of the screen the window is located on so we can avoid drawing separators + // on corners contained in those edges + bool win_at_top; + bool win_at_bottom = wp->w_hsep_height == 0; + bool win_at_left; + bool win_at_right = wp->w_vsep_width == 0; + frame_T *frp; + + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) { + break; + } + } + win_at_top = frp->fr_parent == NULL; + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) { + break; + } + } + win_at_left = frp->fr_parent == NULL; + + // Draw the appropriate separator connector in every corner where drawing them is necessary + if (!(win_at_top || win_at_left)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT), + wp->w_winrow - 1, wp->w_wincol - 1, hl); + } + if (!(win_at_top || win_at_right)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT), + wp->w_winrow - 1, W_ENDCOL(wp), hl); + } + if (!(win_at_bottom || win_at_left)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), + W_ENDROW(wp), wp->w_wincol - 1, hl); + } + if (!(win_at_bottom || win_at_right)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), + W_ENDROW(wp), W_ENDCOL(wp), hl); + } +} + +/// Get the length of an item as it will be shown in the status line. static int status_match_len(expand_T *xp, char_u *s) { int len = 0; @@ -5163,7 +5268,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in // Create status line if needed by setting 'laststatus' to 2. // Set 'winminheight' to zero to avoid that the window is // resized. - if (lastwin->w_status_height == 0) { + if (lastwin->w_status_height == 0 && global_stl_height() == 0) { save_p_ls = p_ls; save_p_wmh = p_wmh; p_ls = 2; @@ -5199,12 +5304,15 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in static void win_redr_status(win_T *wp) { int row; + int col; char_u *p; int len; int fillchar; int attr; + int width; int this_ru_col; - static int busy = FALSE; + bool is_stl_global = global_stl_height() > 0; + static int busy = false; // May get here recursively when 'statusline' (indirectly) // invokes ":redrawstatus". Simply ignore the call then. @@ -5215,9 +5323,9 @@ static void win_redr_status(win_T *wp) } busy = true; - wp->w_redr_status = FALSE; - if (wp->w_status_height == 0) { - // no status line, can only be last window + wp->w_redr_status = false; + if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) { + // no status line, either global statusline is enabled or the window is a last window redraw_cmdline = true; } else if (!redrawing()) { // Don't redraw right now, do it later. Don't update status line when @@ -5228,6 +5336,7 @@ static void win_redr_status(win_T *wp) redraw_custom_statusline(wp); } else { fillchar = fillchar_status(&attr, wp); + width = is_stl_global ? Columns : wp->w_width; get_trans_bufname(wp->w_buffer); p = NameBuff; @@ -5256,9 +5365,9 @@ static void win_redr_status(win_T *wp) // len += (int)STRLEN(p + len); // dead assignment } - this_ru_col = ru_col - (Columns - wp->w_width); - if (this_ru_col < (wp->w_width + 1) / 2) { - this_ru_col = (wp->w_width + 1) / 2; + this_ru_col = ru_col - (Columns - width); + if (this_ru_col < (width + 1) / 2) { + this_ru_col = (width + 1) / 2; } if (this_ru_col <= 1) { p = (char_u *)"<"; // No room for file name! @@ -5283,10 +5392,11 @@ static void win_redr_status(win_T *wp) } } - row = W_ENDROW(wp); - grid_puts(&default_grid, p, row, wp->w_wincol, attr); - grid_fill(&default_grid, row, row + 1, len + wp->w_wincol, - this_ru_col + wp->w_wincol, fillchar, fillchar, attr); + row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + col = is_stl_global ? 0 : wp->w_wincol; + grid_puts(&default_grid, p, row, col, attr); + grid_fill(&default_grid, row, row + 1, len + col, + this_ru_col + col, fillchar, fillchar, attr); if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { @@ -5365,6 +5475,76 @@ bool stl_connected(win_T *wp) return false; } +/// Check if horizontal separator of window "wp" at specified window corner is connected to the +/// horizontal separator of another window +/// Assumes global statusline is enabled +static bool hsep_connected(win_T *wp, WindowCorner corner) +{ + bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT); + int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) + ? wp->w_winrow - 1 : W_ENDROW(wp); + frame_T *fr = wp->w_frame; + + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) { + fr = before ? fr->fr_prev : fr->fr_next; + break; + } + fr = fr->fr_parent; + } + if (fr->fr_parent == NULL) { + return false; + } + while (fr->fr_layout != FR_LEAF) { + fr = fr->fr_child; + if (fr->fr_parent->fr_layout == FR_ROW && before) { + while (fr->fr_next != NULL) { + fr = fr->fr_next; + } + } else { + while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) { + fr = fr->fr_next; + } + } + } + + return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win)); +} + +/// Check if vertical separator of window "wp" at specified window corner is connected to the +/// vertical separator of another window +static bool vsep_connected(win_T *wp, WindowCorner corner) +{ + bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT); + int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) + ? wp->w_wincol - 1 : W_ENDCOL(wp); + frame_T *fr = wp->w_frame; + + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) { + fr = before ? fr->fr_prev : fr->fr_next; + break; + } + fr = fr->fr_parent; + } + if (fr->fr_parent == NULL) { + return false; + } + while (fr->fr_layout != FR_LEAF) { + fr = fr->fr_child; + if (fr->fr_parent->fr_layout == FR_COL && before) { + while (fr->fr_next != NULL) { + fr = fr->fr_next; + } + } else { + while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) { + fr = fr->fr_next; + } + } + } + + return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win)); +} /// Get the value to show for the language mappings, active 'keymap'. /// @@ -5431,6 +5611,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) int use_sandbox = false; win_T *ewp; int p_crb_save; + bool is_stl_global = global_stl_height() > 0; ScreenGrid *grid = &default_grid; @@ -5452,9 +5633,9 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) maxwidth = Columns; use_sandbox = was_set_insecurely(wp, "tabline", 0); } else { - row = W_ENDROW(wp); + row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); - maxwidth = wp->w_width; + maxwidth = is_stl_global ? Columns : wp->w_width; if (draw_ruler) { stl = p_ruf; @@ -5472,12 +5653,12 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) stl = p_ruf; } } - col = ru_col - (Columns - wp->w_width); - if (col < (wp->w_width + 1) / 2) { - col = (wp->w_width + 1) / 2; + col = ru_col - (Columns - maxwidth); + if (col < (maxwidth + 1) / 2) { + col = (maxwidth + 1) / 2; } - maxwidth = wp->w_width - col; - if (!wp->w_status_height) { + maxwidth = maxwidth - col; + if (!wp->w_status_height && !is_stl_global) { grid = &msg_grid_adj; row = Rows - 1; maxwidth--; // writing in last column may cause scrolling @@ -5495,7 +5676,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); } - col += wp->w_wincol; + col += is_stl_global ? 0 : wp->w_wincol; } if (maxwidth <= 0) { @@ -7077,10 +7258,10 @@ int showmode(void) clear_showcmd(); } - // If the last window has no status line, the ruler is after the mode - // message and must be redrawn + // If the last window has no status line and global statusline is disabled, + // the ruler is after the mode message and must be redrawn win_T *last = lastwin_nofloating(); - if (redrawing() && last->w_status_height == 0) { + if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) { win_redr_ruler(last, true); } redraw_cmdline = false; @@ -7395,16 +7576,22 @@ int fillchar_status(int *attr, win_T *wp) return '='; } -/* - * Get the character to use in a separator between vertically split windows. - * Get its attributes in "*attr". - */ +/// Get the character to use in a separator between vertically split windows. +/// Get its attributes in "*attr". static int fillchar_vsep(win_T *wp, int *attr) { *attr = win_hl_attr(wp, HLF_C); return wp->w_p_fcs_chars.vert; } +/// Get the character to use in a separator between horizontally split windows. +/// Get its attributes in "*attr". +static int fillchar_hsep(win_T *wp, int *attr) +{ + *attr = win_hl_attr(wp, HLF_C); + return wp->w_p_fcs_chars.horiz; +} + /* * Return TRUE if redrawing should currently be done. */ @@ -7430,7 +7617,8 @@ void showruler(bool always) if (!always && !redrawing()) { return; } - if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) { + if ((*p_stl != NUL || *curwin->w_p_stl != NUL) + && (curwin->w_status_height || global_stl_height())) { redraw_custom_statusline(curwin); } else { win_redr_ruler(curwin, always); @@ -7449,6 +7637,7 @@ void showruler(bool always) static void win_redr_ruler(win_T *wp, bool always) { + bool is_stl_global = global_stl_height() > 0; static bool did_show_ext_ruler = false; // If 'ruler' off or redrawing disabled, don't do anything @@ -7466,7 +7655,7 @@ static void win_redr_ruler(win_T *wp, bool always) // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. - if (wp == lastwin && lastwin->w_status_height == 0) { + if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) { if (edit_submode != NULL) { return; } @@ -7521,6 +7710,12 @@ static void win_redr_ruler(win_T *wp, bool always) off = wp->w_wincol; width = wp->w_width; part_of_status = true; + } else if (is_stl_global) { + row = Rows - p_ch - 1; + fillchar = fillchar_status(&attr, wp); + off = 0; + width = Columns; + part_of_status = true; } else { row = Rows - 1; fillchar = ' '; @@ -7560,7 +7755,7 @@ static void win_redr_ruler(win_T *wp, bool always) int i = (int)STRLEN(buffer); get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); int o = i + vim_strsize(buffer + i + 1); - if (wp->w_status_height == 0) { // can't use last char of screen + if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen o++; } int this_ru_col = ru_col - (Columns - width); diff --git a/src/nvim/screen.h b/src/nvim/screen.h index 9e8a034d93..d15e4b7e45 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -19,6 +19,14 @@ #define NOT_VALID 40 // buffer needs complete redraw #define CLEAR 50 // screen messed up, clear it +/// corner value flags for hsep_connected and vsep_connected +typedef enum { + WC_TOP_LEFT = 0, + WC_TOP_RIGHT, + WC_BOTTOM_LEFT, + WC_BOTTOM_RIGHT +} WindowCorner; + /// By default, all widows are draw on a single rectangular grid, represented by /// this ScreenGrid instance. In multigrid mode each window will have its own /// grid, then this is only used for global screen elements that hasn't been diff --git a/src/nvim/search.c b/src/nvim/search.c index cc7c2ecf06..a69bd641f8 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -5689,7 +5689,7 @@ search_line: // we read a line, set "already" to check this "line" later // if depth >= 0 we'll increase files[depth].lnum far - // bellow -- Acevedo + // below -- Acevedo already = aux = p = skipwhite(line); p = find_word_start(p); p = find_word_end(p); diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 6d0ac30003..50400852b8 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -13,6 +13,7 @@ #include "nvim/edit.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" +#include "nvim/highlight_group.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/screen.h" @@ -954,7 +955,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*linehl == NUL) { sp->sn_line_hl = 0; } else { - sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl)); + sp->sn_line_hl = syn_check_group((char *)linehl, STRLEN(linehl)); } } @@ -962,7 +963,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*texthl == NUL) { sp->sn_text_hl = 0; } else { - sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl)); + sp->sn_text_hl = syn_check_group((char *)texthl, STRLEN(texthl)); } } @@ -970,7 +971,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*culhl == NUL) { sp->sn_cul_hl = 0; } else { - sp->sn_cul_hl = syn_check_group((char *)culhl, (int)STRLEN(culhl)); + sp->sn_cul_hl = syn_check_group((char *)culhl, STRLEN(culhl)); } } @@ -978,7 +979,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*numhl == NUL) { sp->sn_num_hl = 0; } else { - sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + sp->sn_num_hl = syn_check_group(numhl, STRLEN(numhl)); } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 54fce3d968..f829b6a270 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -25,6 +25,7 @@ #include "nvim/garray.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/lua/executor.h" @@ -51,56 +52,6 @@ static bool did_syntax_onoff = false; -/// Structure that stores information about a highlight group. -/// The ID of a highlight group is also called group ID. It is the index in -/// the highlight_ga array PLUS ONE. -typedef struct hl_group { - char_u *sg_name; ///< highlight group name - char *sg_name_u; ///< uppercase of sg_name - bool sg_cleared; ///< "hi clear" was used - int sg_attr; ///< Screen attr @see ATTR_ENTRY - int sg_link; ///< link to this highlight group ID - int sg_deflink; ///< default link; restored in highlight_clear() - int sg_set; ///< combination of flags in \ref SG_SET - sctx_T sg_deflink_sctx; ///< script where the default link was set - sctx_T sg_script_ctx; ///< script in which the group was last set - // for terminal UIs - int sg_cterm; ///< "cterm=" highlighting attr - ///< (combination of \ref HlAttrFlags) - int sg_cterm_fg; ///< terminal fg color number + 1 - int sg_cterm_bg; ///< terminal bg color number + 1 - bool sg_cterm_bold; ///< bold attr was set for light color - // for RGB UIs - int sg_gui; ///< "gui=" highlighting attributes - ///< (combination of \ref HlAttrFlags) - RgbValue sg_rgb_fg; ///< RGB foreground color - RgbValue sg_rgb_bg; ///< RGB background color - RgbValue sg_rgb_sp; ///< RGB special color - char *sg_rgb_fg_name; ///< RGB foreground color name - char *sg_rgb_bg_name; ///< RGB background color name - char *sg_rgb_sp_name; ///< RGB special color name - - int sg_blend; ///< blend level (0-100 inclusive), -1 if unset -} HlGroup; - -/// \addtogroup SG_SET -/// @{ -#define SG_CTERM 2 // cterm has been set -#define SG_GUI 4 // gui has been set -#define SG_LINK 8 // link has been set -/// @} - -// builtin |highlight-groups| -static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; -Map(cstr_t, int) highlight_unames = MAP_INIT; - -static inline struct hl_group *HL_TABLE(void) -{ - return ((struct hl_group *)((highlight_ga.ga_data))); -} - -#define MAX_HL_ID 20000 // maximum value for a highlight ID. - // different types of offsets that are possible #define SPO_MS_OFF 0 // match start offset #define SPO_ME_OFF 1 // match end offset @@ -111,22 +62,6 @@ static inline struct hl_group *HL_TABLE(void) #define SPO_LC_OFF 6 // leading context offset #define SPO_COUNT 7 -// Flags to indicate an additional string for highlight name completion. -static int include_none = 0; // when 1 include "nvim/None" -static int include_default = 0; // when 1 include "nvim/default" -static int include_link = 0; // when 2 include "nvim/link" and "clear" - -#define MAX_SYN_NAME 200 - -/// The "term", "cterm" and "gui" arguments can be any combination of the -/// following names, separated by commas (but no spaces!). -static char *(hl_name_table[]) = -{ "bold", "standout", "underline", "underlineline", "undercurl", "underdot", - "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; -static int hl_attr_table[] = -{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH, - HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; - static char e_illegal_arg[] = N_("E390: Illegal argument: %s"); // The patterns that are being searched for are stored in a syn_pattern. @@ -3609,7 +3544,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing) /* * No argument: List all group IDs and all syntax clusters. */ - for (int id = 1; id <= highlight_ga.ga_len && !got_int; id++) { + for (int id = 1; id <= highlight_num_groups() && !got_int; id++) { syn_list_one(id, syncing, false); } for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id) { @@ -3767,8 +3702,8 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only) } msg_putchar(' '); if (spp->sp_sync_idx >= 0) { - msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s) - [spp->sp_sync_idx].sp_syn.id - 1].sg_name); + msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s) + [spp->sp_sync_idx].sp_syn.id - 1)); } else { msg_puts("NONE"); } @@ -3777,11 +3712,11 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only) } // list the link, if there is one - if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) { + if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int) { (void)syn_list_header(did_header, 0, id, true); msg_puts_attr("links to", attr); msg_putchar(' '); - msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); + msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1)); } } @@ -3845,7 +3780,7 @@ static void put_id_list(const char *const name, const int16_t *const list, const msg_putchar('@'); msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name); } else { - msg_outtrans(HL_TABLE()[*p - 1].sg_name); + msg_outtrans(highlight_group_name(*p - 1)); } if (p[1]) { msg_putchar(','); @@ -3867,7 +3802,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const if (last_matchgroup == 0) { msg_outtrans((char_u *)"NONE"); } else { - msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name); + msg_outtrans(highlight_group_name(last_matchgroup - 1)); } msg_putchar(' '); } @@ -5459,8 +5394,8 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis regmatch.rm_ic = TRUE; id = 0; - for (int i = highlight_ga.ga_len; --i >= 0;) { - if (vim_regexec(®match, HL_TABLE()[i].sg_name, (colnr_T)0)) { + for (int i = highlight_num_groups(); --i >= 0;) { + if (vim_regexec(®match, highlight_group_name(i), (colnr_T)0)) { if (round == 2) { // Got more items than expected; can happen // when adding items that match: @@ -6121,7 +6056,7 @@ static void syntime_report(void) msg_puts(profile_msg(p->average)); msg_puts(" "); msg_advance(50); - msg_outtrans(HL_TABLE()[p->id - 1].sg_name); + msg_outtrans(highlight_group_name(p->id - 1)); msg_puts(" "); msg_advance(69); @@ -6146,2740 +6081,3 @@ static void syntime_report(void) msg_puts("\n"); } } - -/************************************** -* Highlighting stuff * -**************************************/ - -// The default highlight groups. These are compiled-in for fast startup and -// they still work when the runtime files can't be found. -// -// When making changes here, also change runtime/colors/default.vim! - -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", - "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White", - "IncSearch cterm=reverse gui=reverse", - "ModeMsg cterm=bold gui=bold", - "NonText ctermfg=Blue gui=bold guifg=Blue", - "Normal cterm=NONE gui=NONE", - "PmenuSbar ctermbg=Grey guibg=Grey", - "StatusLine cterm=reverse,bold gui=reverse,bold", - "StatusLineNC cterm=reverse gui=reverse", - "TabLineFill cterm=reverse gui=reverse", - "TabLineSel cterm=bold gui=bold", - "TermCursor cterm=reverse gui=reverse", - "VertSplit cterm=reverse gui=reverse", - "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", - "default link EndOfBuffer NonText", - "default link LineNrAbove LineNr", - "default link LineNrBelow LineNr", - "default link QuickFixLine Search", - "default link CursorLineSign SignColumn", - "default link CursorLineFold FoldColumn", - "default link Substitute Search", - "default link Whitespace NonText", - "default link MsgSeparator StatusLine", - "default link NormalFloat Pmenu", - "default link FloatBorder VertSplit", - "default FloatShadow blend=80 guibg=Black", - "default FloatShadowThrough blend=100 guibg=Black", - "RedrawDebugNormal cterm=reverse gui=reverse", - "RedrawDebugClear ctermbg=Yellow guibg=Yellow", - "RedrawDebugComposed ctermbg=Green guibg=Green", - "RedrawDebugRecompose ctermbg=Red guibg=Red", - "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red", - "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow", - "default link String Constant", - "default link Character Constant", - "default link Number Constant", - "default link Boolean Constant", - "default link Float Number", - "default link Function Identifier", - "default link Conditional Statement", - "default link Repeat Statement", - "default link Label Statement", - "default link Operator Statement", - "default link Keyword Statement", - "default link Exception Statement", - "default link Include PreProc", - "default link Define PreProc", - "default link Macro PreProc", - "default link PreCondit PreProc", - "default link StorageClass Type", - "default link Structure Type", - "default link Typedef Type", - "default link Tag Special", - "default link SpecialChar Special", - "default link Delimiter Special", - "default link SpecialComment Special", - "default link Debug Special", - "default DiagnosticError ctermfg=1 guifg=Red", - "default DiagnosticWarn ctermfg=3 guifg=Orange", - "default DiagnosticInfo ctermfg=4 guifg=LightBlue", - "default DiagnosticHint ctermfg=7 guifg=LightGrey", - "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red", - "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange", - "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue", - "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey", - "default link DiagnosticVirtualTextError DiagnosticError", - "default link DiagnosticVirtualTextWarn DiagnosticWarn", - "default link DiagnosticVirtualTextInfo DiagnosticInfo", - "default link DiagnosticVirtualTextHint DiagnosticHint", - "default link DiagnosticFloatingError DiagnosticError", - "default link DiagnosticFloatingWarn DiagnosticWarn", - "default link DiagnosticFloatingInfo DiagnosticInfo", - "default link DiagnosticFloatingHint DiagnosticHint", - "default link DiagnosticSignError DiagnosticError", - "default link DiagnosticSignWarn DiagnosticWarn", - "default link DiagnosticSignInfo DiagnosticInfo", - "default link DiagnosticSignHint DiagnosticHint", - NULL -}; - -// Default colors only used with a light background. -static const char *highlight_init_light[] = { - "ColorColumn ctermbg=LightRed guibg=LightRed", - "CursorColumn ctermbg=LightGrey guibg=Grey90", - "CursorLine cterm=underline guibg=Grey90", - "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown", - "DiffAdd ctermbg=LightBlue guibg=LightBlue", - "DiffChange ctermbg=LightMagenta guibg=LightMagenta", - "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan", - "Directory ctermfg=DarkBlue guifg=Blue", - "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", - "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue", - "LineNr ctermfg=Brown guifg=Brown", - "MatchParen ctermbg=Cyan guibg=Cyan", - "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen", - "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta", - "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey", - "PmenuThumb ctermbg=Black guibg=Black", - "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen", - "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE", - "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", - "SpecialKey ctermfg=DarkBlue guifg=Blue", - "SpellBad ctermbg=LightRed guisp=Red gui=undercurl", - "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl", - "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl", - "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl", - "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey", - "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", - "Visual guibg=LightGrey", - "WarningMsg ctermfg=DarkRed guifg=Red", - "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE", - "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE", - "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE", - "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE", - "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE", - "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE", - "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE", - "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue", - "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE", - NULL -}; - -// Default colors only used with a dark background. -static const char *highlight_init_dark[] = { - "ColorColumn ctermbg=DarkRed guibg=DarkRed", - "CursorColumn ctermbg=DarkGrey guibg=Grey40", - "CursorLine cterm=underline guibg=Grey40", - "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow", - "DiffAdd ctermbg=DarkBlue guibg=DarkBlue", - "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta", - "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan", - "Directory ctermfg=LightCyan guifg=Cyan", - "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", - "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan", - "LineNr ctermfg=Yellow guifg=Yellow", - "MatchParen ctermbg=DarkCyan guibg=DarkCyan", - "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen", - "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta", - "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey", - "PmenuThumb ctermbg=White guibg=White", - "Question ctermfg=LightGreen gui=bold guifg=Green", - "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", - "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", - "SpecialKey ctermfg=LightBlue guifg=Cyan", - "SpellBad ctermbg=Red guisp=Red gui=undercurl", - "SpellCap ctermbg=Blue guisp=Blue gui=undercurl", - "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl", - "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl", - "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey", - "Title ctermfg=LightMagenta gui=bold guifg=Magenta", - "Visual guibg=DarkGrey", - "WarningMsg ctermfg=LightRed guifg=Red", - "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE", - "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE", - "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE", - "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE", - "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE", - "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE", - "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE", - "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff", - "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE", - NULL -}; - -const char *const highlight_init_cmdline[] = { - // XXX When modifying a list modify it in both valid and invalid halves. - // 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(bool both, bool reset) -{ - 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. - char_u *p = get_var_value("g:colors_name"); - if (p != NULL) { - // Value of g:colors_name could be freed in load_colors() and make - // p invalid, so copy it. - char_u *copy_p = vim_strsave(p); - bool okay = load_colors(copy_p); - xfree(copy_p); - if (okay) { - return; - } - } - - /* - * Didn't use a color file, use the compiled-in colors. - */ - if (both) { - 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(). - // Not everything has been setup then, and that call will overrule - // everything anyway. - return; - } - - 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 - * depend on the number of colors available. - * With 8 colors brown is equal to yellow, need to use black for Search fg - * to avoid Statement highlighted text disappears. - * Clear the attributes, needed when changing the t_Co value. */ - if (t_colors > 8) { - do_highlight((*p_bg == 'l' - ? "Visual cterm=NONE ctermbg=LightGrey" - : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); - } else { - do_highlight("Visual cterm=reverse ctermbg=NONE", false, true); - if (*p_bg == 'l') { - do_highlight("Search ctermfg=black", false, true); - } - } - - syn_init_cmdline_highlight(false, false); -} - -/* - * Load color file "name". - * Return OK for success, FAIL for failure. - */ -int load_colors(char_u *name) -{ - char_u *buf; - int retval = FAIL; - static bool recursive = false; - - // When being called recursively, this is probably because setting - // 'background' caused the highlighting to be reloaded. This means it is - // working, thus we should return OK. - if (recursive) { - return OK; - } - - recursive = true; - size_t buflen = STRLEN(name) + 12; - buf = xmalloc(buflen); - apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); - snprintf((char *)buf, buflen, "colors/%s.vim", name); - retval = source_runtime((char *)buf, DIP_START + DIP_OPT); - if (retval == FAIL) { - snprintf((char *)buf, buflen, "colors/%s.lua", name); - retval = source_runtime((char *)buf, DIP_START + DIP_OPT); - } - xfree(buf); - apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf); - - recursive = false; - - return retval; -} - -static char *(color_names[28]) = { - "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" -}; -// indices: -// 0, 1, 2, 3, -// 4, 5, 6, 7, -// 8, 9, 10, 11, -// 12, 13, -// 14, 15, 16, 17, -// 18, 19, 20, 21, 22, -// 23, 24, 25, 26, 27 -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, 3, - 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 }; - -// Lookup the "cterm" value to be used for color with index "idx" in -// color_names[]. -// "boldp" will be set to TRUE or FALSE for a foreground color when using 8 -// colors, otherwise it will be unchanged. -int lookup_color(const int idx, const bool foreground, TriState *const boldp) -{ - int color = color_numbers_16[idx]; - - // Use the _16 table to check if it's a valid color name. - if (color < 0) { - return -1; - } - - if (t_colors == 8) { - // t_Co is 8: use the 8 colors table - color = color_numbers_8[idx]; - if (foreground) { - // set/reset bold attribute to get light foreground - // colors (on some terminals, e.g. "linux") - if (color & 8) { - *boldp = kTrue; - } else { - *boldp = kFalse; - } - } - color &= 7; // truncate to 8 colors - } else if (t_colors == 16) { - color = color_numbers_8[idx]; - } else if (t_colors == 88) { - color = color_numbers_88[idx]; - } else if (t_colors >= 256) { - color = color_numbers_256[idx]; - } - return color; -} - -void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) -{ - int idx = id - 1; // Index is ID minus one. - - bool is_default = attrs.rgb_ae_attr & HL_DEFAULT; - - // Return if "default" was used and the group already has settings - if (is_default && hl_has_settings(idx, true)) { - return; - } - - HlGroup *g = &HL_TABLE()[idx]; - - if (link_id > 0) { - g->sg_cleared = false; - g->sg_link = link_id; - g->sg_script_ctx = current_sctx; - g->sg_script_ctx.sc_lnum += sourcing_lnum; - g->sg_set |= SG_LINK; - if (is_default) { - g->sg_deflink = link_id; - g->sg_deflink_sctx = current_sctx; - g->sg_deflink_sctx.sc_lnum += sourcing_lnum; - } - return; - } - - g->sg_cleared = false; - g->sg_link = 0; - g->sg_gui = attrs.rgb_ae_attr; - - g->sg_rgb_fg = attrs.rgb_fg_color; - g->sg_rgb_bg = attrs.rgb_bg_color; - g->sg_rgb_sp = attrs.rgb_sp_color; - - struct { - char **dest; RgbValue val; Object name; - } cattrs[] = { - { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, - { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, - { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, - { NULL, -1, NIL }, - }; - - char hex_name[8]; - char *name; - - for (int j = 0; cattrs[j].dest; j++) { - if (cattrs[j].val < 0) { - XFREE_CLEAR(*cattrs[j].dest); - continue; - } - - if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { - name = cattrs[j].name.data.string.data; - } else { - snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); - name = hex_name; - } - - if (!*cattrs[j].dest - || STRCMP(*cattrs[j].dest, name) != 0) { - xfree(*cattrs[j].dest); - *cattrs[j].dest = xstrdup(name); - } - } - - g->sg_cterm = attrs.cterm_ae_attr; - g->sg_cterm_bg = attrs.cterm_bg_color; - g->sg_cterm_fg = attrs.cterm_fg_color; - g->sg_cterm_bold = g->sg_cterm & HL_BOLD; - g->sg_blend = attrs.hl_blend; - - g->sg_script_ctx = current_sctx; - g->sg_script_ctx.sc_lnum += sourcing_lnum; - - // 'Normal' is special - if (STRCMP(g->sg_name_u, "NORMAL") == 0) { - cterm_normal_fg_color = g->sg_cterm_fg; - cterm_normal_bg_color = g->sg_cterm_bg; - normal_fg = g->sg_rgb_fg; - normal_bg = g->sg_rgb_bg; - normal_sp = g->sg_rgb_sp; - ui_default_colors_set(); - } else { - g->sg_attr = hl_get_syn_attr(0, id, attrs); - - // a cursor style uses this syn_id, make sure its attribute is updated. - if (cursor_mode_uses_syn_id(id)) { - ui_mode_info_set(); - } - } -} - - -/// 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; - int attr; - int id; - int idx; - struct hl_group item_before; - bool did_change = false; - bool dodefault = false; - bool doclear = false; - bool dolink = false; - bool error = false; - int color; - bool is_normal_group = false; // "Normal" group - bool did_highlight_changed = false; - - // If no argument, list current highlighting. - if (ends_excmd((uint8_t)(*line))) { - for (i = 1; i <= highlight_ga.ga_len && !got_int; i++) { - // TODO(brammool): only call when the group has attributes set - highlight_list_one(i); - } - return; - } - - // 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; - line = linep; - 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; - } else if (strncmp(line, "link", name_end - line) == 0) { - dolink = true; - } - - // ":highlight {group-name}": list highlighting for one group. - if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) { - id = syn_name2id_len((const char_u *)line, (int)(name_end - line)); - if (id == 0) { - semsg(_("E411: highlight group not found: %s"), line); - } else { - highlight_list_one(id); - } - return; - } - - // Handle ":highlight link {from} {to}" command. - if (dolink) { - const char *from_start = linep; - const char *from_end; - const char *to_start; - const char *to_end; - int from_id; - int to_id; - struct hl_group *hlgroup = NULL; - - 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((uint8_t)(*from_start)) - || ends_excmd((uint8_t)(*to_start))) { - semsg(_("E412: Not enough arguments: \":highlight link %s\""), - from_start); - return; - } - - if (!ends_excmd(*skipwhite((const char_u *)to_end))) { - semsg(_("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) { - to_id = 0; - } else { - to_id = syn_check_group(to_start, (int)(to_end - to_start)); - } - - if (from_id > 0) { - hlgroup = &HL_TABLE()[from_id - 1]; - if (dodefault && (forceit || hlgroup->sg_deflink == 0)) { - hlgroup->sg_deflink = to_id; - hlgroup->sg_deflink_sctx = current_sctx; - hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&hlgroup->sg_deflink_sctx); - } - } - - if (from_id > 0 && (!init || hlgroup->sg_set == 0)) { - // 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) { - emsg(_("E414: group has settings, highlight link ignored")); - } - } else if (hlgroup->sg_link != to_id - || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid - || hlgroup->sg_cleared) { - if (!init) { - hlgroup->sg_set |= SG_LINK; - } - hlgroup->sg_link = to_id; - hlgroup->sg_script_ctx = current_sctx; - hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&hlgroup->sg_script_ctx); - hlgroup->sg_cleared = false; - redraw_all_later(SOME_VALID); - - // Only call highlight changed() once after multiple changes - need_highlight_changed = true; - } - } - - return; - } - - if (doclear) { - // ":highlight clear [group]" command. - line = linep; - 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 j = 0; j < highlight_ga.ga_len; j++) { - highlight_clear(j); - } - init_highlight(true, true); - highlight_changed(); - redraw_all_later(NOT_VALID); - return; - } - 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). - return; - } - 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)) { - return; - } - - // Make a copy so we can check if any attribute actually changed - item_before = HL_TABLE()[idx]; - is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); - - // 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((uint8_t)(*linep))) { - key_start = linep; - if (*linep == '=') { - semsg(_("E415: unexpected equal sign: %s"), key_start); - error = true; - break; - } - - // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg", - // "guibg" or "guisp"). - while (*linep && !ascii_iswhite(*linep) && *linep != '=') { - linep++; - } - xfree(key); - key = (char *)vim_strnsave_up((const char_u *)key_start, - linep - key_start); - linep = (const char *)skipwhite((const char_u *)linep); - - if (strcmp(key, "NONE") == 0) { - if (!init || HL_TABLE()[idx].sg_set == 0) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI; - } - highlight_clear(idx); - } - continue; - } - - // Check for the equal sign. - if (*linep != '=') { - semsg(_("E416: missing equal sign: %s"), key_start); - error = true; - break; - } - linep++; - - // Isolate the argument. - linep = (const char *)skipwhite((const char_u *)linep); - if (*linep == '\'') { // guifg='color name' - arg_start = ++linep; - linep = strchr(linep, '\''); - if (linep == NULL) { - semsg(_(e_invarg2), key_start); - error = true; - break; - } - } else { - arg_start = linep; - linep = (const char *)skiptowhite((const char_u *)linep); - } - if (linep == arg_start) { - semsg(_("E417: missing argument: %s"), key_start); - error = true; - break; - } - xfree(arg); - arg = xstrndup(arg_start, (size_t)(linep - arg_start)); - - if (*linep == '\'') { - linep++; - } - - // Store the argument. - if (strcmp(key, "TERM") == 0 - || strcmp(key, "CTERM") == 0 - || strcmp(key, "GUI") == 0) { - attr = 0; - off = 0; - while (arg[off] != NUL) { - for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) { - len = (int)STRLEN(hl_name_table[i]); - if (STRNICMP(arg + off, hl_name_table[i], len) == 0) { - attr |= hl_attr_table[i]; - off += len; - break; - } - } - if (i < 0) { - semsg(_("E418: Illegal value: %s"), arg); - error = true; - break; - } - if (arg[off] == ',') { // Another one follows. - off++; - } - } - if (error) { - break; - } - if (*key == 'C') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { - 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) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - HL_TABLE()[idx].sg_gui = attr; - } - } - } else if (STRCMP(key, "FONT") == 0) { - // in non-GUI fonts are simply ignored - } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; - } - - /* When setting the foreground color, and previously the "bold" - * flag was set for a light color, reset it now */ - if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = false; - } - - if (ascii_isdigit(*arg)) { - color = atoi(arg); - } else if (STRICMP(arg, "fg") == 0) { - if (cterm_normal_fg_color) { - color = cterm_normal_fg_color - 1; - } else { - emsg(_("E419: FG color unknown")); - error = true; - break; - } - } else if (STRICMP(arg, "bg") == 0) { - if (cterm_normal_bg_color > 0) { - color = cterm_normal_bg_color - 1; - } else { - emsg(_("E420: BG color unknown")); - error = true; - break; - } - } else { - // Reduce calls to STRICMP a bit, it can be slow. - off = TOUPPER_ASC(*arg); - for (i = ARRAY_SIZE(color_names); --i >= 0;) { - if (off == color_names[i][0] - && STRICMP(arg + 1, color_names[i] + 1) == 0) { - break; - } - } - if (i < 0) { - semsg(_("E421: Color name or number not recognized: %s"), - key_start); - error = true; - break; - } - - TriState bold = kNone; - color = lookup_color(i, key[5] == 'F', &bold); - - // set/reset bold attribute to get light foreground - // colors (on some terminals, e.g. "linux") - if (bold == kTrue) { - HL_TABLE()[idx].sg_cterm |= HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = true; - } else if (bold == kFalse) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - } - } - // 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; - } - } else { - HL_TABLE()[idx].sg_cterm_bg = color + 1; - if (is_normal_group) { - cterm_normal_bg_color = color + 1; - if (!ui_rgb_attached()) { - if (color >= 0) { - int dark = -1; - - if (t_colors < 16) { - dark = (color == 0 || color == 4); - } else if (color < 16) { - // Limit the heuristic to the standard 16 colors - dark = (color < 7 || color == 8); - } - // Set the 'background' option if the value is - // wrong. - if (dark != -1 - && dark != (*p_bg == 'd') - && !option_was_set("bg")) { - set_option_value("bg", 0L, (dark ? "dark" : "light"), 0); - reset_option_was_set("bg"); - } - } - } - } - } - } - } else if (strcmp(key, "GUIFG") == 0) { - char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; - - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_fg = -1; - } - did_change = true; - } - } - - if (is_normal_group) { - normal_fg = HL_TABLE()[idx].sg_rgb_fg; - } - } else if (STRCMP(key, "GUIBG") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; - - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (STRCMP(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_bg = -1; - } - did_change = true; - } - } - - if (is_normal_group) { - normal_bg = HL_TABLE()[idx].sg_rgb_bg; - } - } else if (strcmp(key, "GUISP") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; - - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_sp = -1; - } - did_change = true; - } - } - - if (is_normal_group) { - normal_sp = HL_TABLE()[idx].sg_rgb_sp; - } - } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { - // Ignored for now - } else if (strcmp(key, "BLEND") == 0) { - if (strcmp(arg, "NONE") != 0) { - HL_TABLE()[idx].sg_blend = strtol(arg, NULL, 10); - } else { - HL_TABLE()[idx].sg_blend = -1; - } - } else { - semsg(_("E423: Illegal argument: %s"), key_start); - error = true; - break; - } - HL_TABLE()[idx].sg_cleared = false; - - // 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 = (const char *)skipwhite((const char_u *)linep); - } - } - - // 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 { - if (!error && is_normal_group) { - // Need to update all groups, because they might be using "bg" and/or - // "fg", which have been changed now. - highlight_attr_set_all(); - - if (!ui_has(kUILinegrid) && starting == 0) { - // Older UIs assume that we clear the screen after normal group is - // changed - ui_refresh(); - } else { - // TUI and newer UIs will repaint the screen themselves. NOT_VALID - // redraw below will still handle usages of guibg=fg etc. - ui_default_colors_set(); - } - did_highlight_changed = true; - redraw_all_later(NOT_VALID); - } else { - set_hl_attr(idx); - } - HL_TABLE()[idx].sg_script_ctx = current_sctx; - HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); - } - xfree(key); - xfree(arg); - - // Only call highlight_changed() once, after a sequence of highlight - // commands, and only if an attribute actually changed - if ((did_change - || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) - && !did_highlight_changed) { - // Do not trigger a redraw when highlighting is changed while - // redrawing. This may happen when evaluating 'statusline' changes the - // StatusLine group. - if (!updating_screen) { - redraw_all_later(NOT_VALID); - } - need_highlight_changed = true; - } -} - -#if defined(EXITFREE) -void free_highlight(void) -{ - for (int i = 0; i < highlight_ga.ga_len; ++i) { - highlight_clear(i); - xfree(HL_TABLE()[i].sg_name); - xfree(HL_TABLE()[i].sg_name_u); - } - ga_clear(&highlight_ga); - map_destroy(cstr_t, int)(&highlight_unames); -} - -#endif - -/* - * Reset the cterm colors to what they were before Vim was started, if - * possible. Otherwise reset them to zero. - */ -void restore_cterm_colors(void) -{ - normal_fg = -1; - normal_bg = -1; - normal_sp = -1; - cterm_normal_fg_color = 0; - cterm_normal_bg_color = 0; -} - -/// @param check_link if true also check for an existing link. -/// -/// @return TRUE if highlight group "idx" has any settings. -static int hl_has_settings(int idx, bool check_link) -{ - return HL_TABLE()[idx].sg_cleared == 0 - && (HL_TABLE()[idx].sg_attr != 0 - || HL_TABLE()[idx].sg_cterm_fg != 0 - || HL_TABLE()[idx].sg_cterm_bg != 0 - || HL_TABLE()[idx].sg_rgb_fg_name != NULL - || HL_TABLE()[idx].sg_rgb_bg_name != NULL - || HL_TABLE()[idx].sg_rgb_sp_name != NULL - || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); -} - -/* - * Clear highlighting for one group. - */ -static void highlight_clear(int idx) -{ - HL_TABLE()[idx].sg_cleared = true; - - HL_TABLE()[idx].sg_attr = 0; - HL_TABLE()[idx].sg_cterm = 0; - HL_TABLE()[idx].sg_cterm_bold = false; - HL_TABLE()[idx].sg_cterm_fg = 0; - HL_TABLE()[idx].sg_cterm_bg = 0; - HL_TABLE()[idx].sg_gui = 0; - HL_TABLE()[idx].sg_rgb_fg = -1; - HL_TABLE()[idx].sg_rgb_bg = -1; - HL_TABLE()[idx].sg_rgb_sp = -1; - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name); - HL_TABLE()[idx].sg_blend = -1; - // Restore default link and context if they exist. Otherwise clears. - HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink; - // Since we set the default link, set the location to where the default - // link was set. - HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx; -} - - -/// \addtogroup LIST_XXX -/// @{ -#define LIST_ATTR 1 -#define LIST_STRING 2 -#define LIST_INT 3 -/// @} - -static void highlight_list_one(const int id) -{ - struct hl_group *const sgp = &HL_TABLE()[id - 1]; // index is ID minus one - bool didh = false; - - if (message_filtered(sgp->sg_name)) { - return; - } - - didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_cterm, NULL, "cterm"); - didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_cterm_fg, NULL, "ctermfg"); - didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_cterm_bg, NULL, "ctermbg"); - - didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_gui, NULL, "gui"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_fg_name, "guifg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_bg_name, "guibg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_sp_name, "guisp"); - - didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_blend+1, NULL, "blend"); - - if (sgp->sg_link && !got_int) { - (void)syn_list_header(didh, 0, id, true); - didh = true; - msg_puts_attr("links to", HL_ATTR(HLF_D)); - msg_putchar(' '); - msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); - } - - if (!didh) { - highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", ""); - } - if (p_verbose > 0) { - last_set_msg(sgp->sg_script_ctx); - } -} - -Dictionary get_global_hl_defs(void) -{ - Dictionary rv = ARRAY_DICT_INIT; - for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { - Dictionary attrs = ARRAY_DICT_INIT; - struct hl_group *h = &HL_TABLE()[i - 1]; - if (h->sg_attr > 0) { - attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); - } else if (h->sg_link > 0) { - const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; - PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); - } - PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); - } - - return rv; -} - -/// Outputs a highlight when doing ":hi MyHighlight" -/// -/// @param type one of \ref LIST_XXX -/// @param iarg integer argument used if \p type == LIST_INT -/// @param sarg string used if \p type == LIST_STRING -static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, - const char *const name) -{ - char buf[100]; - - if (got_int) { - return false; - } - if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { - char *ts = buf; - if (type == LIST_INT) { - snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); - } else if (type == LIST_STRING) { - ts = sarg; - } else { // type == LIST_ATTR - buf[0] = NUL; - for (int i = 0; hl_attr_table[i] != 0; i++) { - if (iarg & hl_attr_table[i]) { - if (buf[0] != NUL) { - xstrlcat(buf, ",", 100); - } - xstrlcat(buf, hl_name_table[i], 100); - iarg &= ~hl_attr_table[i]; // don't want "inverse" - } - } - } - - (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + STRLEN(name) - + 1), id, false); - didh = true; - if (!got_int) { - if (*name != NUL) { - msg_puts_attr(name, HL_ATTR(HLF_D)); - msg_puts_attr("=", HL_ATTR(HLF_D)); - } - msg_outtrans((char_u *)ts); - } - } - return didh; -} - -/// Check whether highlight group has attribute -/// -/// @param[in] id Highlight group to check. -/// @param[in] flag Attribute to check. -/// @param[in] modec 'g' for GUI, 'c' for term. -/// -/// @return "1" if highlight group has attribute, NULL otherwise. -const char *highlight_has_attr(const int id, const int flag, const int modec) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE -{ - int attr; - - if (id <= 0 || id > highlight_ga.ga_len) { - return NULL; - } - - if (modec == 'g') { - attr = HL_TABLE()[id - 1].sg_gui; - } else { - attr = HL_TABLE()[id - 1].sg_cterm; - } - - return (attr & flag) ? "1" : NULL; -} - -/// Return color name of the given highlight group -/// -/// @param[in] id Highlight group to work with. -/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", -/// "bg#" or "sp#". -/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. -/// -/// @return color name, possibly in a static buffer. Buffer will be overwritten -/// on next highlight_color() call. May return NULL. -const char *highlight_color(const int id, const char *const what, const int modec) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - static char name[20]; - int n; - bool fg = false; - bool sp = false; - bool font = false; - - if (id <= 0 || id > highlight_ga.ga_len) { - return NULL; - } - - if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { - fg = true; - } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' - && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { - font = true; - } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { - sp = true; - } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { - return NULL; - } - if (modec == 'g') { - if (what[2] == '#' && ui_rgb_attached()) { - if (fg) { - n = HL_TABLE()[id - 1].sg_rgb_fg; - } else if (sp) { - n = HL_TABLE()[id - 1].sg_rgb_sp; - } else { - n = HL_TABLE()[id - 1].sg_rgb_bg; - } - if (n < 0 || n > 0xffffff) { - return NULL; - } - snprintf(name, sizeof(name), "#%06x", n); - return name; - } - if (fg) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; - } - if (sp) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; - } - return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; - } - if (font || sp) { - return NULL; - } - if (modec == 'c') { - if (fg) { - n = HL_TABLE()[id - 1].sg_cterm_fg - 1; - } else { - n = HL_TABLE()[id - 1].sg_cterm_bg - 1; - } - if (n < 0) { - return NULL; - } - snprintf(name, sizeof(name), "%d", n); - return name; - } - // term doesn't have color. - return NULL; -} - -/// Output the syntax list header. -/// -/// @param did_header did header already -/// @param outlen length of string that comes -/// @param id highlight group id -/// @param force_newline always start a new line -/// @return true when started a new line. -static bool syn_list_header(const bool did_header, const int outlen, const int id, - bool force_newline) -{ - int endcol = 19; - bool newline = true; - int name_col = 0; - bool adjust = true; - - if (!did_header) { - msg_putchar('\n'); - if (got_int) { - return true; - } - msg_outtrans(HL_TABLE()[id - 1].sg_name); - name_col = msg_col; - endcol = 15; - } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { - msg_putchar(' '); - adjust = false; - } else if (msg_col + outlen + 1 >= Columns || force_newline) { - msg_putchar('\n'); - if (got_int) { - return true; - } - } else { - if (msg_col >= endcol) { // wrap around is like starting a new line - newline = false; - } - } - - if (adjust) { - if (msg_col >= endcol) { - // output at least one space - endcol = msg_col + 1; - } - - msg_advance(endcol); - } - - // Show "xxx" with the attributes. - if (!did_header) { - if (endcol == Columns - 1 && endcol <= name_col) { - msg_putchar(' '); - } - msg_puts_attr("xxx", syn_id2attr(id)); - msg_putchar(' '); - } - - return newline; -} - -/// Set the attribute numbers for a highlight group. -/// Called after one of the attributes has changed. -/// @param idx corrected highlight index -static void set_hl_attr(int idx) -{ - HlAttrs at_en = HLATTRS_INIT; - struct hl_group *sgp = HL_TABLE() + idx; - - at_en.cterm_ae_attr = sgp->sg_cterm; - at_en.cterm_fg_color = sgp->sg_cterm_fg; - at_en.cterm_bg_color = sgp->sg_cterm_bg; - at_en.rgb_ae_attr = sgp->sg_gui; - // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is - // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name - // before setting attr_entry->{f,g}g_color to a other than -1 - at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; - at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; - at_en.hl_blend = sgp->sg_blend; - - sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en); - - // a cursor style uses this syn_id, make sure its attribute is updated. - if (cursor_mode_uses_syn_id(idx+1)) { - ui_mode_info_set(); - } -} - -int syn_name2id(const char *name) - FUNC_ATTR_NONNULL_ALL -{ - return syn_name2id_len((char_u *)name, STRLEN(name)); -} - -/// Lookup a highlight group name and return its ID. -/// -/// @param highlight name e.g. 'Cursor', 'Normal' -/// @return the highlight id, else 0 if \p name does not exist -int syn_name2id_len(const char_u *name, size_t len) - FUNC_ATTR_NONNULL_ALL -{ - char name_u[MAX_SYN_NAME + 1]; - - if (len == 0 || len > MAX_SYN_NAME) { - return 0; - } - - // Avoid using stricmp() too much, it's slow on some systems */ - // Avoid alloc()/free(), these are slow too. - memcpy(name_u, name, len); - name_u[len] = '\0'; - vim_strup((char_u *)name_u); - - // map_get(..., int) returns 0 when no key is present, which is - // the expected value for missing highlight group. - return map_get(cstr_t, int)(&highlight_unames, name_u); -} - -/// Lookup a highlight group name and return its attributes. -/// Return zero if not found. -int syn_name2attr(const char_u *name) - FUNC_ATTR_NONNULL_ALL -{ - int id = syn_name2id((char *)name); - - if (id != 0) { - return syn_id2attr(id); - } - return 0; -} - -/* - * Return TRUE if highlight group "name" exists. - */ -int highlight_exists(const char *name) -{ - return syn_name2id(name) > 0; -} - -/* - * Return the name of highlight group "id". - * When not a valid ID return an empty string. - */ -char_u *syn_id2name(int id) -{ - if (id <= 0 || id > highlight_ga.ga_len) { - return (char_u *)""; - } - return HL_TABLE()[id - 1].sg_name; -} - - -/// Find highlight group name in the table and return its ID. -/// If it doesn't exist yet, a new entry is created. -/// -/// @param pp Highlight group name -/// @param len length of \p pp -/// -/// @return 0 for failure else the id of the group -int syn_check_group(const char *name, int len) -{ - if (len > MAX_SYN_NAME) { - emsg(_(e_highlight_group_name_too_long)); - return 0; - } - int id = syn_name2id_len((char_u *)name, len); - if (id == 0) { // doesn't exist yet - return syn_add_group(vim_strnsave((char_u *)name, len)); - } - return id; -} - -/// Add new highlight group and return its ID. -/// -/// @param name must be an allocated string, it will be consumed. -/// @return 0 for failure, else the allocated group id -/// @see syn_check_group syn_unadd_group -static int syn_add_group(char_u *name) -{ - char_u *p; - - // Check that the name is ASCII letters, digits and underscore. - for (p = name; *p != NUL; ++p) { - if (!vim_isprintc(*p)) { - emsg(_("E669: Unprintable character in group name")); - xfree(name); - return 0; - } else if (!ASCII_ISALNUM(*p) && *p != '_') { - /* This is an error, but since there previously was no check only - * give a warning. */ - msg_source(HL_ATTR(HLF_W)); - msg(_("W18: Invalid character in group name")); - break; - } - } - - /* - * First call for this growarray: init growing array. - */ - if (highlight_ga.ga_data == NULL) { - highlight_ga.ga_itemsize = sizeof(struct hl_group); - ga_set_growsize(&highlight_ga, 10); - } - - if (highlight_ga.ga_len >= MAX_HL_ID) { - emsg(_("E849: Too many highlight and syntax groups")); - xfree(name); - return 0; - } - - char *const name_up = (char *)vim_strsave_up(name); - - // Append another syntax_highlight entry. - struct hl_group *hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga); - memset(hlgp, 0, sizeof(*hlgp)); - hlgp->sg_name = name; - hlgp->sg_rgb_bg = -1; - hlgp->sg_rgb_fg = -1; - hlgp->sg_rgb_sp = -1; - hlgp->sg_blend = -1; - hlgp->sg_name_u = name_up; - - int id = highlight_ga.ga_len; // ID is index plus one - - map_put(cstr_t, int)(&highlight_unames, name_up, id); - - return id; -} - -/// When, just after calling syn_add_group(), an error is discovered, this -/// function deletes the new name. -static void syn_unadd_group(void) -{ - highlight_ga.ga_len--; - HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; - map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); - xfree(item->sg_name); - xfree(item->sg_name_u); -} - - -/// Translate a group ID to highlight attributes. -/// @see syn_attr2entry -int syn_id2attr(int hl_id) -{ - hl_id = syn_get_final_id(hl_id); - struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one - - int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set); - if (attr >= 0) { - return attr; - } - return sgp->sg_attr; -} - - -/* - * Translate a group ID to the final group ID (following links). - */ -int syn_get_final_id(int hl_id) -{ - int count; - - if (hl_id > highlight_ga.ga_len || hl_id < 1) { - return 0; // Can be called from eval!! - } - /* - * Follow links until there is no more. - * Look out for loops! Break after 100 links. - */ - for (count = 100; --count >= 0;) { - struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one - - // ACHTUNG: when using "tmp" attribute (no link) the function might be - // called twice. it needs be smart enough to remember attr only to - // syn_id2attr time - int check = ns_get_hl(-1, hl_id, true, sgp->sg_set); - if (check == 0) { - return hl_id; // how dare! it broke the link! - } else if (check > 0) { - hl_id = check; - continue; - } - - - if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) { - break; - } - hl_id = sgp->sg_link; - } - - return hl_id; -} - -/// Refresh the color attributes of all highlight groups. -void highlight_attr_set_all(void) -{ - for (int idx = 0; idx < highlight_ga.ga_len; idx++) { - struct hl_group *sgp = &HL_TABLE()[idx]; - if (sgp->sg_rgb_bg_name != NULL) { - sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name); - } - if (sgp->sg_rgb_fg_name != NULL) { - sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name); - } - if (sgp->sg_rgb_sp_name != NULL) { - sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name); - } - set_hl_attr(idx); - } -} - -// Apply difference between User[1-9] and HLF_S to HLF_SNC. -static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) - FUNC_ATTR_NONNULL_ALL -{ - struct hl_group *const hlt = HL_TABLE(); - - if (id_alt == 0) { - memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group)); - hlt[hlcnt + i].sg_cterm = highlight_attr[hlf]; - hlt[hlcnt + i].sg_gui = highlight_attr[hlf]; - } else { - memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(struct hl_group)); - } - hlt[hlcnt + i].sg_link = 0; - - hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; - if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) { - hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; - } - if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) { - hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; - } - hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; - if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) { - hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg; - } - if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) { - hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; - } - if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) { - hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp; - } - highlight_ga.ga_len = hlcnt + i + 1; - set_hl_attr(hlcnt + i); // At long last we can apply - table[i] = syn_id2attr(hlcnt + i + 1); -} - -/// Translate highlight groups into attributes in highlight_attr[] and set up -/// the user highlights User1..9. A set of corresponding highlights to use on -/// top of HLF_SNC is computed. Called only when nvim starts and upon first -/// screen redraw after any :highlight command. -void highlight_changed(void) -{ - int id; - char userhl[30]; // use 30 to avoid compiler warning - int id_S = -1; - int id_SNC = 0; - int hlcnt; - - need_highlight_changed = false; - - /// Translate builtin highlight groups into attributes for quick lookup. - for (int hlf = 0; hlf < HLF_COUNT; hlf++) { - id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf])); - if (id == 0) { - abort(); - } - int final_id = syn_get_final_id(id); - if (hlf == HLF_SNC) { - id_SNC = final_id; - } else if (hlf == HLF_S) { - id_S = final_id; - } - - highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id, - hlf == HLF_INACTIVE); - - if (highlight_attr[hlf] != highlight_attr_last[hlf]) { - if (hlf == HLF_MSG) { - clear_cmdline = true; - } - ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), - highlight_attr[hlf]); - highlight_attr_last[hlf] = highlight_attr[hlf]; - } - } - - // - // Setup the user highlights - // - // Temporarily utilize 10 more hl entries: - // 9 for User1-User9 combined with StatusLineNC - // 1 for StatusLine default - // Must to be in there simultaneously in case of table overflows in - // get_attr_entry() - ga_grow(&highlight_ga, 10); - hlcnt = highlight_ga.ga_len; - if (id_S == -1) { - // Make sure id_S is always valid to simplify code below. Use the last entry - memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group)); - id_S = hlcnt + 10; - } - for (int i = 0; i < 9; i++) { - snprintf(userhl, sizeof(userhl), "User%d", i + 1); - id = syn_name2id(userhl); - if (id == 0) { - highlight_user[i] = 0; - highlight_stlnc[i] = 0; - } else { - highlight_user[i] = syn_id2attr(id); - combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc); - } - } - highlight_ga.ga_len = hlcnt; -} - - -/* - * Handle command line completion for :highlight command. - */ -void set_context_in_highlight_cmd(expand_T *xp, const char *arg) -{ - // Default: expand group names. - xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = (char_u *)arg; - include_link = 2; - include_default = 1; - - // (part of) subcommand already typed - if (*arg != NUL) { - const char *p = (const char *)skiptowhite((const char_u *)arg); - if (*p != NUL) { // Past "default" or group name. - include_default = 0; - if (strncmp("default", arg, p - arg) == 0) { - arg = (const char *)skipwhite((const char_u *)p); - xp->xp_pattern = (char_u *)arg; - p = (const char *)skiptowhite((const char_u *)arg); - } - if (*p != NUL) { // past group name - include_link = 0; - if (arg[1] == 'i' && arg[0] == 'N') { - highlight_list(); - } - if (strncmp("link", arg, p - arg) == 0 - || strncmp("clear", arg, p - arg) == 0) { - xp->xp_pattern = skipwhite((const char_u *)p); - p = (const char *)skiptowhite(xp->xp_pattern); - if (*p != NUL) { // Past first group name. - xp->xp_pattern = skipwhite((const char_u *)p); - p = (const char *)skiptowhite(xp->xp_pattern); - } - } - if (*p != NUL) { // Past group name(s). - xp->xp_context = EXPAND_NOTHING; - } - } - } - } -} - -/* - * List highlighting matches in a nice way. - */ -static void highlight_list(void) -{ - int i; - - for (i = 10; --i >= 0;) { - highlight_list_two(i, HL_ATTR(HLF_D)); - } - for (i = 40; --i >= 0;) { - highlight_list_two(99, 0); - } -} - -static void highlight_list_two(int cnt, int attr) -{ - msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr); - msg_clr_eos(); - ui_flush(); - os_delay(cnt == 99 ? 40L : (long)cnt * 50L, false); -} - - -/// Function given to ExpandGeneric() to obtain the list of group names. -const char *get_highlight_name(expand_T *const xp, int idx) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - return get_highlight_name_ext(xp, idx, true); -} - - -/// Obtain a highlight group name. -/// -/// @param skip_cleared if true don't return a cleared entry. -const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (idx < 0) { - return NULL; - } - - // Items are never removed from the table, skip the ones that were cleared. - if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { - return ""; - } - - if (idx == highlight_ga.ga_len && include_none != 0) { - return "none"; - } else if (idx == highlight_ga.ga_len + include_none - && include_default != 0) { - return "default"; - } else if (idx == highlight_ga.ga_len + include_none + include_default - && include_link != 0) { - return "link"; - } else if (idx == highlight_ga.ga_len + include_none + include_default + 1 - && include_link != 0) { - return "clear"; - } else if (idx >= highlight_ga.ga_len) { - return NULL; - } - return (const char *)HL_TABLE()[idx].sg_name; -} - -color_name_table_T color_name_table[] = { - // Colors from rgb.txt - { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) }, - { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) }, - { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) }, - { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) }, - { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) }, - { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) }, - { "Aqua", RGB_(0x00, 0xff, 0xff) }, - { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) }, - { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) }, - { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) }, - { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) }, - { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) }, - { "Azure", RGB_(0xf0, 0xff, 0xff) }, - { "Azure1", RGB_(0xf0, 0xff, 0xff) }, - { "Azure2", RGB_(0xe0, 0xee, 0xee) }, - { "Azure3", RGB_(0xc1, 0xcd, 0xcd) }, - { "Azure4", RGB_(0x83, 0x8b, 0x8b) }, - { "Beige", RGB_(0xf5, 0xf5, 0xdc) }, - { "Bisque", RGB_(0xff, 0xe4, 0xc4) }, - { "Bisque1", RGB_(0xff, 0xe4, 0xc4) }, - { "Bisque2", RGB_(0xee, 0xd5, 0xb7) }, - { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) }, - { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) }, - { "Black", RGB_(0x00, 0x00, 0x00) }, - { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) }, - { "Blue", RGB_(0x00, 0x00, 0xff) }, - { "Blue1", RGB_(0x0, 0x0, 0xff) }, - { "Blue2", RGB_(0x0, 0x0, 0xee) }, - { "Blue3", RGB_(0x0, 0x0, 0xcd) }, - { "Blue4", RGB_(0x0, 0x0, 0x8b) }, - { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) }, - { "Brown", RGB_(0xa5, 0x2a, 0x2a) }, - { "Brown1", RGB_(0xff, 0x40, 0x40) }, - { "Brown2", RGB_(0xee, 0x3b, 0x3b) }, - { "Brown3", RGB_(0xcd, 0x33, 0x33) }, - { "Brown4", RGB_(0x8b, 0x23, 0x23) }, - { "BurlyWood", RGB_(0xde, 0xb8, 0x87) }, - { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) }, - { "Burlywood2", RGB_(0xee, 0xc5, 0x91) }, - { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) }, - { "Burlywood4", RGB_(0x8b, 0x73, 0x55) }, - { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) }, - { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) }, - { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) }, - { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) }, - { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) }, - { "ChartReuse", RGB_(0x7f, 0xff, 0x00) }, - { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) }, - { "Chartreuse2", RGB_(0x76, 0xee, 0x0) }, - { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) }, - { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) }, - { "Chocolate", RGB_(0xd2, 0x69, 0x1e) }, - { "Chocolate1", RGB_(0xff, 0x7f, 0x24) }, - { "Chocolate2", RGB_(0xee, 0x76, 0x21) }, - { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) }, - { "Chocolate4", RGB_(0x8b, 0x45, 0x13) }, - { "Coral", RGB_(0xff, 0x7f, 0x50) }, - { "Coral1", RGB_(0xff, 0x72, 0x56) }, - { "Coral2", RGB_(0xee, 0x6a, 0x50) }, - { "Coral3", RGB_(0xcd, 0x5b, 0x45) }, - { "Coral4", RGB_(0x8b, 0x3e, 0x2f) }, - { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) }, - { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) }, - { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) }, - { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) }, - { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) }, - { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) }, - { "Crimson", RGB_(0xdc, 0x14, 0x3c) }, - { "Cyan", RGB_(0x00, 0xff, 0xff) }, - { "Cyan1", RGB_(0x0, 0xff, 0xff) }, - { "Cyan2", RGB_(0x0, 0xee, 0xee) }, - { "Cyan3", RGB_(0x0, 0xcd, 0xcd) }, - { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, - { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, - { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, - { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, - { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, - { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, - { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, - { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) }, - { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) }, - { "DarkGreen", RGB_(0x00, 0x64, 0x00) }, - { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) }, - { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) }, - { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) }, - { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) }, - { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) }, - { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) }, - { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) }, - { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) }, - { "DarkOrange", RGB_(0xff, 0x8c, 0x00) }, - { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) }, - { "DarkOrange2", RGB_(0xee, 0x76, 0x0) }, - { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) }, - { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) }, - { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) }, - { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) }, - { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) }, - { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) }, - { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) }, - { "DarkRed", RGB_(0x8b, 0x00, 0x00) }, - { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) }, - { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) }, - { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) }, - { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) }, - { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) }, - { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) }, - { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) }, - { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) }, - { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) }, - { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) }, - { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) }, - { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) }, - { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) }, - { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) }, - { "DarkViolet", RGB_(0x94, 0x00, 0xd3) }, - { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) }, - { "DeepPink", RGB_(0xff, 0x14, 0x93) }, - { "DeepPink1", RGB_(0xff, 0x14, 0x93) }, - { "DeepPink2", RGB_(0xee, 0x12, 0x89) }, - { "DeepPink3", RGB_(0xcd, 0x10, 0x76) }, - { "DeepPink4", RGB_(0x8b, 0xa, 0x50) }, - { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) }, - { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) }, - { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) }, - { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) }, - { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) }, - { "DimGray", RGB_(0x69, 0x69, 0x69) }, - { "DimGrey", RGB_(0x69, 0x69, 0x69) }, - { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) }, - { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) }, - { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) }, - { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) }, - { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) }, - { "Firebrick", RGB_(0xb2, 0x22, 0x22) }, - { "Firebrick1", RGB_(0xff, 0x30, 0x30) }, - { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) }, - { "Firebrick3", RGB_(0xcd, 0x26, 0x26) }, - { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) }, - { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) }, - { "ForestGreen", RGB_(0x22, 0x8b, 0x22) }, - { "Fuchsia", RGB_(0xff, 0x00, 0xff) }, - { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) }, - { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) }, - { "Gold", RGB_(0xff, 0xd7, 0x00) }, - { "Gold1", RGB_(0xff, 0xd7, 0x0) }, - { "Gold2", RGB_(0xee, 0xc9, 0x0) }, - { "Gold3", RGB_(0xcd, 0xad, 0x0) }, - { "Gold4", RGB_(0x8b, 0x75, 0x0) }, - { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, - { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, - { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, - { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, - { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) }, - { "Gray", RGB_(0x80, 0x80, 0x80) }, - { "Gray0", RGB_(0x0, 0x0, 0x0) }, - { "Gray1", RGB_(0x3, 0x3, 0x3) }, - { "Gray10", RGB_(0x1a, 0x1a, 0x1a) }, - { "Gray100", RGB_(0xff, 0xff, 0xff) }, - { "Gray11", RGB_(0x1c, 0x1c, 0x1c) }, - { "Gray12", RGB_(0x1f, 0x1f, 0x1f) }, - { "Gray13", RGB_(0x21, 0x21, 0x21) }, - { "Gray14", RGB_(0x24, 0x24, 0x24) }, - { "Gray15", RGB_(0x26, 0x26, 0x26) }, - { "Gray16", RGB_(0x29, 0x29, 0x29) }, - { "Gray17", RGB_(0x2b, 0x2b, 0x2b) }, - { "Gray18", RGB_(0x2e, 0x2e, 0x2e) }, - { "Gray19", RGB_(0x30, 0x30, 0x30) }, - { "Gray2", RGB_(0x5, 0x5, 0x5) }, - { "Gray20", RGB_(0x33, 0x33, 0x33) }, - { "Gray21", RGB_(0x36, 0x36, 0x36) }, - { "Gray22", RGB_(0x38, 0x38, 0x38) }, - { "Gray23", RGB_(0x3b, 0x3b, 0x3b) }, - { "Gray24", RGB_(0x3d, 0x3d, 0x3d) }, - { "Gray25", RGB_(0x40, 0x40, 0x40) }, - { "Gray26", RGB_(0x42, 0x42, 0x42) }, - { "Gray27", RGB_(0x45, 0x45, 0x45) }, - { "Gray28", RGB_(0x47, 0x47, 0x47) }, - { "Gray29", RGB_(0x4a, 0x4a, 0x4a) }, - { "Gray3", RGB_(0x8, 0x8, 0x8) }, - { "Gray30", RGB_(0x4d, 0x4d, 0x4d) }, - { "Gray31", RGB_(0x4f, 0x4f, 0x4f) }, - { "Gray32", RGB_(0x52, 0x52, 0x52) }, - { "Gray33", RGB_(0x54, 0x54, 0x54) }, - { "Gray34", RGB_(0x57, 0x57, 0x57) }, - { "Gray35", RGB_(0x59, 0x59, 0x59) }, - { "Gray36", RGB_(0x5c, 0x5c, 0x5c) }, - { "Gray37", RGB_(0x5e, 0x5e, 0x5e) }, - { "Gray38", RGB_(0x61, 0x61, 0x61) }, - { "Gray39", RGB_(0x63, 0x63, 0x63) }, - { "Gray4", RGB_(0xa, 0xa, 0xa) }, - { "Gray40", RGB_(0x66, 0x66, 0x66) }, - { "Gray41", RGB_(0x69, 0x69, 0x69) }, - { "Gray42", RGB_(0x6b, 0x6b, 0x6b) }, - { "Gray43", RGB_(0x6e, 0x6e, 0x6e) }, - { "Gray44", RGB_(0x70, 0x70, 0x70) }, - { "Gray45", RGB_(0x73, 0x73, 0x73) }, - { "Gray46", RGB_(0x75, 0x75, 0x75) }, - { "Gray47", RGB_(0x78, 0x78, 0x78) }, - { "Gray48", RGB_(0x7a, 0x7a, 0x7a) }, - { "Gray49", RGB_(0x7d, 0x7d, 0x7d) }, - { "Gray5", RGB_(0xd, 0xd, 0xd) }, - { "Gray50", RGB_(0x7f, 0x7f, 0x7f) }, - { "Gray51", RGB_(0x82, 0x82, 0x82) }, - { "Gray52", RGB_(0x85, 0x85, 0x85) }, - { "Gray53", RGB_(0x87, 0x87, 0x87) }, - { "Gray54", RGB_(0x8a, 0x8a, 0x8a) }, - { "Gray55", RGB_(0x8c, 0x8c, 0x8c) }, - { "Gray56", RGB_(0x8f, 0x8f, 0x8f) }, - { "Gray57", RGB_(0x91, 0x91, 0x91) }, - { "Gray58", RGB_(0x94, 0x94, 0x94) }, - { "Gray59", RGB_(0x96, 0x96, 0x96) }, - { "Gray6", RGB_(0xf, 0xf, 0xf) }, - { "Gray60", RGB_(0x99, 0x99, 0x99) }, - { "Gray61", RGB_(0x9c, 0x9c, 0x9c) }, - { "Gray62", RGB_(0x9e, 0x9e, 0x9e) }, - { "Gray63", RGB_(0xa1, 0xa1, 0xa1) }, - { "Gray64", RGB_(0xa3, 0xa3, 0xa3) }, - { "Gray65", RGB_(0xa6, 0xa6, 0xa6) }, - { "Gray66", RGB_(0xa8, 0xa8, 0xa8) }, - { "Gray67", RGB_(0xab, 0xab, 0xab) }, - { "Gray68", RGB_(0xad, 0xad, 0xad) }, - { "Gray69", RGB_(0xb0, 0xb0, 0xb0) }, - { "Gray7", RGB_(0x12, 0x12, 0x12) }, - { "Gray70", RGB_(0xb3, 0xb3, 0xb3) }, - { "Gray71", RGB_(0xb5, 0xb5, 0xb5) }, - { "Gray72", RGB_(0xb8, 0xb8, 0xb8) }, - { "Gray73", RGB_(0xba, 0xba, 0xba) }, - { "Gray74", RGB_(0xbd, 0xbd, 0xbd) }, - { "Gray75", RGB_(0xbf, 0xbf, 0xbf) }, - { "Gray76", RGB_(0xc2, 0xc2, 0xc2) }, - { "Gray77", RGB_(0xc4, 0xc4, 0xc4) }, - { "Gray78", RGB_(0xc7, 0xc7, 0xc7) }, - { "Gray79", RGB_(0xc9, 0xc9, 0xc9) }, - { "Gray8", RGB_(0x14, 0x14, 0x14) }, - { "Gray80", RGB_(0xcc, 0xcc, 0xcc) }, - { "Gray81", RGB_(0xcf, 0xcf, 0xcf) }, - { "Gray82", RGB_(0xd1, 0xd1, 0xd1) }, - { "Gray83", RGB_(0xd4, 0xd4, 0xd4) }, - { "Gray84", RGB_(0xd6, 0xd6, 0xd6) }, - { "Gray85", RGB_(0xd9, 0xd9, 0xd9) }, - { "Gray86", RGB_(0xdb, 0xdb, 0xdb) }, - { "Gray87", RGB_(0xde, 0xde, 0xde) }, - { "Gray88", RGB_(0xe0, 0xe0, 0xe0) }, - { "Gray89", RGB_(0xe3, 0xe3, 0xe3) }, - { "Gray9", RGB_(0x17, 0x17, 0x17) }, - { "Gray90", RGB_(0xe5, 0xe5, 0xe5) }, - { "Gray91", RGB_(0xe8, 0xe8, 0xe8) }, - { "Gray92", RGB_(0xeb, 0xeb, 0xeb) }, - { "Gray93", RGB_(0xed, 0xed, 0xed) }, - { "Gray94", RGB_(0xf0, 0xf0, 0xf0) }, - { "Gray95", RGB_(0xf2, 0xf2, 0xf2) }, - { "Gray96", RGB_(0xf5, 0xf5, 0xf5) }, - { "Gray97", RGB_(0xf7, 0xf7, 0xf7) }, - { "Gray98", RGB_(0xfa, 0xfa, 0xfa) }, - { "Gray99", RGB_(0xfc, 0xfc, 0xfc) }, - { "Green", RGB_(0x00, 0x80, 0x00) }, - { "Green1", RGB_(0x0, 0xff, 0x0) }, - { "Green2", RGB_(0x0, 0xee, 0x0) }, - { "Green3", RGB_(0x0, 0xcd, 0x0) }, - { "Green4", RGB_(0x0, 0x8b, 0x0) }, - { "GreenYellow", RGB_(0xad, 0xff, 0x2f) }, - { "Grey", RGB_(0x80, 0x80, 0x80) }, - { "Grey0", RGB_(0x0, 0x0, 0x0) }, - { "Grey1", RGB_(0x3, 0x3, 0x3) }, - { "Grey10", RGB_(0x1a, 0x1a, 0x1a) }, - { "Grey100", RGB_(0xff, 0xff, 0xff) }, - { "Grey11", RGB_(0x1c, 0x1c, 0x1c) }, - { "Grey12", RGB_(0x1f, 0x1f, 0x1f) }, - { "Grey13", RGB_(0x21, 0x21, 0x21) }, - { "Grey14", RGB_(0x24, 0x24, 0x24) }, - { "Grey15", RGB_(0x26, 0x26, 0x26) }, - { "Grey16", RGB_(0x29, 0x29, 0x29) }, - { "Grey17", RGB_(0x2b, 0x2b, 0x2b) }, - { "Grey18", RGB_(0x2e, 0x2e, 0x2e) }, - { "Grey19", RGB_(0x30, 0x30, 0x30) }, - { "Grey2", RGB_(0x5, 0x5, 0x5) }, - { "Grey20", RGB_(0x33, 0x33, 0x33) }, - { "Grey21", RGB_(0x36, 0x36, 0x36) }, - { "Grey22", RGB_(0x38, 0x38, 0x38) }, - { "Grey23", RGB_(0x3b, 0x3b, 0x3b) }, - { "Grey24", RGB_(0x3d, 0x3d, 0x3d) }, - { "Grey25", RGB_(0x40, 0x40, 0x40) }, - { "Grey26", RGB_(0x42, 0x42, 0x42) }, - { "Grey27", RGB_(0x45, 0x45, 0x45) }, - { "Grey28", RGB_(0x47, 0x47, 0x47) }, - { "Grey29", RGB_(0x4a, 0x4a, 0x4a) }, - { "Grey3", RGB_(0x8, 0x8, 0x8) }, - { "Grey30", RGB_(0x4d, 0x4d, 0x4d) }, - { "Grey31", RGB_(0x4f, 0x4f, 0x4f) }, - { "Grey32", RGB_(0x52, 0x52, 0x52) }, - { "Grey33", RGB_(0x54, 0x54, 0x54) }, - { "Grey34", RGB_(0x57, 0x57, 0x57) }, - { "Grey35", RGB_(0x59, 0x59, 0x59) }, - { "Grey36", RGB_(0x5c, 0x5c, 0x5c) }, - { "Grey37", RGB_(0x5e, 0x5e, 0x5e) }, - { "Grey38", RGB_(0x61, 0x61, 0x61) }, - { "Grey39", RGB_(0x63, 0x63, 0x63) }, - { "Grey4", RGB_(0xa, 0xa, 0xa) }, - { "Grey40", RGB_(0x66, 0x66, 0x66) }, - { "Grey41", RGB_(0x69, 0x69, 0x69) }, - { "Grey42", RGB_(0x6b, 0x6b, 0x6b) }, - { "Grey43", RGB_(0x6e, 0x6e, 0x6e) }, - { "Grey44", RGB_(0x70, 0x70, 0x70) }, - { "Grey45", RGB_(0x73, 0x73, 0x73) }, - { "Grey46", RGB_(0x75, 0x75, 0x75) }, - { "Grey47", RGB_(0x78, 0x78, 0x78) }, - { "Grey48", RGB_(0x7a, 0x7a, 0x7a) }, - { "Grey49", RGB_(0x7d, 0x7d, 0x7d) }, - { "Grey5", RGB_(0xd, 0xd, 0xd) }, - { "Grey50", RGB_(0x7f, 0x7f, 0x7f) }, - { "Grey51", RGB_(0x82, 0x82, 0x82) }, - { "Grey52", RGB_(0x85, 0x85, 0x85) }, - { "Grey53", RGB_(0x87, 0x87, 0x87) }, - { "Grey54", RGB_(0x8a, 0x8a, 0x8a) }, - { "Grey55", RGB_(0x8c, 0x8c, 0x8c) }, - { "Grey56", RGB_(0x8f, 0x8f, 0x8f) }, - { "Grey57", RGB_(0x91, 0x91, 0x91) }, - { "Grey58", RGB_(0x94, 0x94, 0x94) }, - { "Grey59", RGB_(0x96, 0x96, 0x96) }, - { "Grey6", RGB_(0xf, 0xf, 0xf) }, - { "Grey60", RGB_(0x99, 0x99, 0x99) }, - { "Grey61", RGB_(0x9c, 0x9c, 0x9c) }, - { "Grey62", RGB_(0x9e, 0x9e, 0x9e) }, - { "Grey63", RGB_(0xa1, 0xa1, 0xa1) }, - { "Grey64", RGB_(0xa3, 0xa3, 0xa3) }, - { "Grey65", RGB_(0xa6, 0xa6, 0xa6) }, - { "Grey66", RGB_(0xa8, 0xa8, 0xa8) }, - { "Grey67", RGB_(0xab, 0xab, 0xab) }, - { "Grey68", RGB_(0xad, 0xad, 0xad) }, - { "Grey69", RGB_(0xb0, 0xb0, 0xb0) }, - { "Grey7", RGB_(0x12, 0x12, 0x12) }, - { "Grey70", RGB_(0xb3, 0xb3, 0xb3) }, - { "Grey71", RGB_(0xb5, 0xb5, 0xb5) }, - { "Grey72", RGB_(0xb8, 0xb8, 0xb8) }, - { "Grey73", RGB_(0xba, 0xba, 0xba) }, - { "Grey74", RGB_(0xbd, 0xbd, 0xbd) }, - { "Grey75", RGB_(0xbf, 0xbf, 0xbf) }, - { "Grey76", RGB_(0xc2, 0xc2, 0xc2) }, - { "Grey77", RGB_(0xc4, 0xc4, 0xc4) }, - { "Grey78", RGB_(0xc7, 0xc7, 0xc7) }, - { "Grey79", RGB_(0xc9, 0xc9, 0xc9) }, - { "Grey8", RGB_(0x14, 0x14, 0x14) }, - { "Grey80", RGB_(0xcc, 0xcc, 0xcc) }, - { "Grey81", RGB_(0xcf, 0xcf, 0xcf) }, - { "Grey82", RGB_(0xd1, 0xd1, 0xd1) }, - { "Grey83", RGB_(0xd4, 0xd4, 0xd4) }, - { "Grey84", RGB_(0xd6, 0xd6, 0xd6) }, - { "Grey85", RGB_(0xd9, 0xd9, 0xd9) }, - { "Grey86", RGB_(0xdb, 0xdb, 0xdb) }, - { "Grey87", RGB_(0xde, 0xde, 0xde) }, - { "Grey88", RGB_(0xe0, 0xe0, 0xe0) }, - { "Grey89", RGB_(0xe3, 0xe3, 0xe3) }, - { "Grey9", RGB_(0x17, 0x17, 0x17) }, - { "Grey90", RGB_(0xe5, 0xe5, 0xe5) }, - { "Grey91", RGB_(0xe8, 0xe8, 0xe8) }, - { "Grey92", RGB_(0xeb, 0xeb, 0xeb) }, - { "Grey93", RGB_(0xed, 0xed, 0xed) }, - { "Grey94", RGB_(0xf0, 0xf0, 0xf0) }, - { "Grey95", RGB_(0xf2, 0xf2, 0xf2) }, - { "Grey96", RGB_(0xf5, 0xf5, 0xf5) }, - { "Grey97", RGB_(0xf7, 0xf7, 0xf7) }, - { "Grey98", RGB_(0xfa, 0xfa, 0xfa) }, - { "Grey99", RGB_(0xfc, 0xfc, 0xfc) }, - { "Honeydew", RGB_(0xf0, 0xff, 0xf0) }, - { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) }, - { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) }, - { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) }, - { "Honeydew4", RGB_(0x83, 0x8b, 0x83) }, - { "HotPink", RGB_(0xff, 0x69, 0xb4) }, - { "HotPink1", RGB_(0xff, 0x6e, 0xb4) }, - { "HotPink2", RGB_(0xee, 0x6a, 0xa7) }, - { "HotPink3", RGB_(0xcd, 0x60, 0x90) }, - { "HotPink4", RGB_(0x8b, 0x3a, 0x62) }, - { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) }, - { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) }, - { "IndianRed2", RGB_(0xee, 0x63, 0x63) }, - { "IndianRed3", RGB_(0xcd, 0x55, 0x55) }, - { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) }, - { "Indigo", RGB_(0x4b, 0x00, 0x82) }, - { "Ivory", RGB_(0xff, 0xff, 0xf0) }, - { "Ivory1", RGB_(0xff, 0xff, 0xf0) }, - { "Ivory2", RGB_(0xee, 0xee, 0xe0) }, - { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) }, - { "Ivory4", RGB_(0x8b, 0x8b, 0x83) }, - { "Khaki", RGB_(0xf0, 0xe6, 0x8c) }, - { "Khaki1", RGB_(0xff, 0xf6, 0x8f) }, - { "Khaki2", RGB_(0xee, 0xe6, 0x85) }, - { "Khaki3", RGB_(0xcd, 0xc6, 0x73) }, - { "Khaki4", RGB_(0x8b, 0x86, 0x4e) }, - { "Lavender", RGB_(0xe6, 0xe6, 0xfa) }, - { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) }, - { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) }, - { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) }, - { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) }, - { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) }, - { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) }, - { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) }, - { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) }, - { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) }, - { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) }, - { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) }, - { "LightBlue", RGB_(0xad, 0xd8, 0xe6) }, - { "LightBlue1", RGB_(0xbf, 0xef, 0xff) }, - { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) }, - { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) }, - { "LightBlue4", RGB_(0x68, 0x83, 0x8b) }, - { "LightCoral", RGB_(0xf0, 0x80, 0x80) }, - { "LightCyan", RGB_(0xe0, 0xff, 0xff) }, - { "LightCyan1", RGB_(0xe0, 0xff, 0xff) }, - { "LightCyan2", RGB_(0xd1, 0xee, 0xee) }, - { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) }, - { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) }, - { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) }, - { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) }, - { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, - { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, - { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, - { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, - { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, - { "LightGreen", RGB_(0x90, 0xee, 0x90) }, - { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, - { "LightMagenta", RGB_(0xff, 0xbb, 0xff) }, - { "LightPink", RGB_(0xff, 0xb6, 0xc1) }, - { "LightPink1", RGB_(0xff, 0xae, 0xb9) }, - { "LightPink2", RGB_(0xee, 0xa2, 0xad) }, - { "LightPink3", RGB_(0xcd, 0x8c, 0x95) }, - { "LightPink4", RGB_(0x8b, 0x5f, 0x65) }, - { "LightRed", RGB_(0xff, 0xbb, 0xbb) }, - { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) }, - { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) }, - { "LightSalmon2", RGB_(0xee, 0x95, 0x72) }, - { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) }, - { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) }, - { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) }, - { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) }, - { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) }, - { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) }, - { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) }, - { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) }, - { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) }, - { "LightSlateGray", RGB_(0x77, 0x88, 0x99) }, - { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) }, - { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) }, - { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) }, - { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) }, - { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) }, - { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) }, - { "LightYellow", RGB_(0xff, 0xff, 0xe0) }, - { "LightYellow1", RGB_(0xff, 0xff, 0xe0) }, - { "LightYellow2", RGB_(0xee, 0xee, 0xd1) }, - { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) }, - { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) }, - { "Lime", RGB_(0x00, 0xff, 0x00) }, - { "LimeGreen", RGB_(0x32, 0xcd, 0x32) }, - { "Linen", RGB_(0xfa, 0xf0, 0xe6) }, - { "Magenta", RGB_(0xff, 0x00, 0xff) }, - { "Magenta1", RGB_(0xff, 0x0, 0xff) }, - { "Magenta2", RGB_(0xee, 0x0, 0xee) }, - { "Magenta3", RGB_(0xcd, 0x0, 0xcd) }, - { "Magenta4", RGB_(0x8b, 0x0, 0x8b) }, - { "Maroon", RGB_(0x80, 0x00, 0x00) }, - { "Maroon1", RGB_(0xff, 0x34, 0xb3) }, - { "Maroon2", RGB_(0xee, 0x30, 0xa7) }, - { "Maroon3", RGB_(0xcd, 0x29, 0x90) }, - { "Maroon4", RGB_(0x8b, 0x1c, 0x62) }, - { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) }, - { "MediumBlue", RGB_(0x00, 0x00, 0xcd) }, - { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) }, - { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) }, - { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) }, - { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) }, - { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) }, - { "MediumPurple", RGB_(0x93, 0x70, 0xdb) }, - { "MediumPurple1", RGB_(0xab, 0x82, 0xff) }, - { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) }, - { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) }, - { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) }, - { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) }, - { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) }, - { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) }, - { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) }, - { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) }, - { "MidnightBlue", RGB_(0x19, 0x19, 0x70) }, - { "MintCream", RGB_(0xf5, 0xff, 0xfa) }, - { "MistyRose", RGB_(0xff, 0xe4, 0xe1) }, - { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) }, - { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) }, - { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) }, - { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) }, - { "Moccasin", RGB_(0xff, 0xe4, 0xb5) }, - { "NavajoWhite", RGB_(0xff, 0xde, 0xad) }, - { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) }, - { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) }, - { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) }, - { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) }, - { "Navy", RGB_(0x00, 0x00, 0x80) }, - { "NavyBlue", RGB_(0x0, 0x0, 0x80) }, - { "OldLace", RGB_(0xfd, 0xf5, 0xe6) }, - { "Olive", RGB_(0x80, 0x80, 0x00) }, - { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) }, - { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) }, - { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) }, - { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) }, - { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) }, - { "Orange", RGB_(0xff, 0xa5, 0x00) }, - { "Orange1", RGB_(0xff, 0xa5, 0x0) }, - { "Orange2", RGB_(0xee, 0x9a, 0x0) }, - { "Orange3", RGB_(0xcd, 0x85, 0x0) }, - { "Orange4", RGB_(0x8b, 0x5a, 0x0) }, - { "OrangeRed", RGB_(0xff, 0x45, 0x00) }, - { "OrangeRed1", RGB_(0xff, 0x45, 0x0) }, - { "OrangeRed2", RGB_(0xee, 0x40, 0x0) }, - { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) }, - { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) }, - { "Orchid", RGB_(0xda, 0x70, 0xd6) }, - { "Orchid1", RGB_(0xff, 0x83, 0xfa) }, - { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, - { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, - { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, - { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, - { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, - { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, - { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, - { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) }, - { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) }, - { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) }, - { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) }, - { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) }, - { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) }, - { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) }, - { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) }, - { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) }, - { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) }, - { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) }, - { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) }, - { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) }, - { "PeachPuff", RGB_(0xff, 0xda, 0xb9) }, - { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) }, - { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) }, - { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) }, - { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) }, - { "Peru", RGB_(0xcd, 0x85, 0x3f) }, - { "Pink", RGB_(0xff, 0xc0, 0xcb) }, - { "Pink1", RGB_(0xff, 0xb5, 0xc5) }, - { "Pink2", RGB_(0xee, 0xa9, 0xb8) }, - { "Pink3", RGB_(0xcd, 0x91, 0x9e) }, - { "Pink4", RGB_(0x8b, 0x63, 0x6c) }, - { "Plum", RGB_(0xdd, 0xa0, 0xdd) }, - { "Plum1", RGB_(0xff, 0xbb, 0xff) }, - { "Plum2", RGB_(0xee, 0xae, 0xee) }, - { "Plum3", RGB_(0xcd, 0x96, 0xcd) }, - { "Plum4", RGB_(0x8b, 0x66, 0x8b) }, - { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) }, - { "Purple", RGB_(0x80, 0x00, 0x80) }, - { "Purple1", RGB_(0x9b, 0x30, 0xff) }, - { "Purple2", RGB_(0x91, 0x2c, 0xee) }, - { "Purple3", RGB_(0x7d, 0x26, 0xcd) }, - { "Purple4", RGB_(0x55, 0x1a, 0x8b) }, - { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) }, - { "Red", RGB_(0xff, 0x00, 0x00) }, - { "Red1", RGB_(0xff, 0x0, 0x0) }, - { "Red2", RGB_(0xee, 0x0, 0x0) }, - { "Red3", RGB_(0xcd, 0x0, 0x0) }, - { "Red4", RGB_(0x8b, 0x0, 0x0) }, - { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) }, - { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) }, - { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) }, - { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) }, - { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) }, - { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) }, - { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) }, - { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) }, - { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) }, - { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) }, - { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) }, - { "Salmon", RGB_(0xfa, 0x80, 0x72) }, - { "Salmon1", RGB_(0xff, 0x8c, 0x69) }, - { "Salmon2", RGB_(0xee, 0x82, 0x62) }, - { "Salmon3", RGB_(0xcd, 0x70, 0x54) }, - { "Salmon4", RGB_(0x8b, 0x4c, 0x39) }, - { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) }, - { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) }, - { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) }, - { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) }, - { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) }, - { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) }, - { "SeaShell", RGB_(0xff, 0xf5, 0xee) }, - { "Seashell1", RGB_(0xff, 0xf5, 0xee) }, - { "Seashell2", RGB_(0xee, 0xe5, 0xde) }, - { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) }, - { "Seashell4", RGB_(0x8b, 0x86, 0x82) }, - { "Sienna", RGB_(0xa0, 0x52, 0x2d) }, - { "Sienna1", RGB_(0xff, 0x82, 0x47) }, - { "Sienna2", RGB_(0xee, 0x79, 0x42) }, - { "Sienna3", RGB_(0xcd, 0x68, 0x39) }, - { "Sienna4", RGB_(0x8b, 0x47, 0x26) }, - { "Silver", RGB_(0xc0, 0xc0, 0xc0) }, - { "SkyBlue", RGB_(0x87, 0xce, 0xeb) }, - { "SkyBlue1", RGB_(0x87, 0xce, 0xff) }, - { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) }, - { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) }, - { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) }, - { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) }, - { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) }, - { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) }, - { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) }, - { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) }, - { "SlateGray", RGB_(0x70, 0x80, 0x90) }, - { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) }, - { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) }, - { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) }, - { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) }, - { "SlateGrey", RGB_(0x70, 0x80, 0x90) }, - { "Snow", RGB_(0xff, 0xfa, 0xfa) }, - { "Snow1", RGB_(0xff, 0xfa, 0xfa) }, - { "Snow2", RGB_(0xee, 0xe9, 0xe9) }, - { "Snow3", RGB_(0xcd, 0xc9, 0xc9) }, - { "Snow4", RGB_(0x8b, 0x89, 0x89) }, - { "SpringGreen", RGB_(0x00, 0xff, 0x7f) }, - { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) }, - { "SpringGreen2", RGB_(0x0, 0xee, 0x76) }, - { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) }, - { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) }, - { "SteelBlue", RGB_(0x46, 0x82, 0xb4) }, - { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) }, - { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) }, - { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) }, - { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) }, - { "Tan", RGB_(0xd2, 0xb4, 0x8c) }, - { "Tan1", RGB_(0xff, 0xa5, 0x4f) }, - { "Tan2", RGB_(0xee, 0x9a, 0x49) }, - { "Tan3", RGB_(0xcd, 0x85, 0x3f) }, - { "Tan4", RGB_(0x8b, 0x5a, 0x2b) }, - { "Teal", RGB_(0x00, 0x80, 0x80) }, - { "Thistle", RGB_(0xd8, 0xbf, 0xd8) }, - { "Thistle1", RGB_(0xff, 0xe1, 0xff) }, - { "Thistle2", RGB_(0xee, 0xd2, 0xee) }, - { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) }, - { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) }, - { "Tomato", RGB_(0xff, 0x63, 0x47) }, - { "Tomato1", RGB_(0xff, 0x63, 0x47) }, - { "Tomato2", RGB_(0xee, 0x5c, 0x42) }, - { "Tomato3", RGB_(0xcd, 0x4f, 0x39) }, - { "Tomato4", RGB_(0x8b, 0x36, 0x26) }, - { "Turquoise", RGB_(0x40, 0xe0, 0xd0) }, - { "Turquoise1", RGB_(0x0, 0xf5, 0xff) }, - { "Turquoise2", RGB_(0x0, 0xe5, 0xee) }, - { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) }, - { "Turquoise4", RGB_(0x0, 0x86, 0x8b) }, - { "Violet", RGB_(0xee, 0x82, 0xee) }, - { "VioletRed", RGB_(0xd0, 0x20, 0x90) }, - { "VioletRed1", RGB_(0xff, 0x3e, 0x96) }, - { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) }, - { "VioletRed3", RGB_(0xcd, 0x32, 0x78) }, - { "VioletRed4", RGB_(0x8b, 0x22, 0x52) }, - { "WebGray", RGB_(0x80, 0x80, 0x80) }, - { "WebGreen", RGB_(0x0, 0x80, 0x0) }, - { "WebGrey", RGB_(0x80, 0x80, 0x80) }, - { "WebMaroon", RGB_(0x80, 0x0, 0x0) }, - { "WebPurple", RGB_(0x80, 0x0, 0x80) }, - { "Wheat", RGB_(0xf5, 0xde, 0xb3) }, - { "Wheat1", RGB_(0xff, 0xe7, 0xba) }, - { "Wheat2", RGB_(0xee, 0xd8, 0xae) }, - { "Wheat3", RGB_(0xcd, 0xba, 0x96) }, - { "Wheat4", RGB_(0x8b, 0x7e, 0x66) }, - { "White", RGB_(0xff, 0xff, 0xff) }, - { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) }, - { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) }, - { "X11Green", RGB_(0x0, 0xff, 0x0) }, - { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) }, - { "X11Maroon", RGB_(0xb0, 0x30, 0x60) }, - { "X11Purple", RGB_(0xa0, 0x20, 0xf0) }, - { "Yellow", RGB_(0xff, 0xff, 0x00) }, - { "Yellow1", RGB_(0xff, 0xff, 0x0) }, - { "Yellow2", RGB_(0xee, 0xee, 0x0) }, - { "Yellow3", RGB_(0xcd, 0xcd, 0x0) }, - { "Yellow4", RGB_(0x8b, 0x8b, 0x0) }, - { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) }, - { NULL, 0 }, -}; - - -/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), -/// else look into color_name_table to translate a color name to its -/// hex value -/// -/// @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 char *name) -{ - if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) - && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) - && isxdigit(name[6]) && name[7] == NUL) { - // rgb hex string - return strtol((char *)(name + 1), NULL, 16); - } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) { - return normal_bg; - } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) { - return normal_fg; - } - - for (int i = 0; color_name_table[i].name != NULL; i++) { - if (!STRICMP(name, color_name_table[i].name)) { - return color_name_table[i].color; - } - } - - return -1; -} - -int name_to_ctermcolor(const char *name) -{ - int i; - int off = TOUPPER_ASC(*name); - for (i = ARRAY_SIZE(color_names); --i >= 0;) { - if (off == color_names[i][0] - && STRICMP(name+1, color_names[i]+1) == 0) { - break; - } - } - if (i < 0) { - return -1; - } - TriState bold = kNone; - return lookup_color(i, false, &bold); -} - -/************************************** -* End of Highlighting stuff * -**************************************/ diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index 15fc084a0a..0d890314c5 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -29,12 +29,6 @@ #define SYN_GROUP_STATIC(s) syn_check_group(S_LEN(s)) -typedef struct { - char *name; - RgbValue color; -} 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[]; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index a76a806b80..5c8789ec37 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -55,6 +55,7 @@ #include "nvim/fileio.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/keymap.h" #include "nvim/log.h" #include "nvim/macros.h" @@ -70,7 +71,6 @@ #include "nvim/os/input.h" #include "nvim/screen.h" #include "nvim/state.h" -#include "nvim/syntax.h" #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -1288,7 +1288,7 @@ static bool send_mouse_event(Terminal *term, int c) return mouse_win == curwin; } - // ignore left release action if it was not proccessed above + // ignore left release action if it was not processed above // to prevent leaving Terminal mode after entering to it using a mouse if (c == K_LEFTRELEASE && mouse_win->w_buffer->terminal == term) { return false; diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 6971ecd357..aa7b3a225b 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -146,7 +146,7 @@ func Test_highlight_eol_with_cursorline_vertsplit() " 'abcd |abcd ' " ^^^^ ^^^^^^^^^ no highlight " ^ 'Search' highlight - " ^ 'VertSplit' highlight + " ^ 'WinSeparator' highlight let attrs0 = ScreenAttrs(1, 15)[0] call assert_equal(repeat([attrs0[0]], 4), attrs0[0:3]) call assert_equal(repeat([attrs0[0]], 9), attrs0[6:14]) @@ -160,7 +160,7 @@ func Test_highlight_eol_with_cursorline_vertsplit() " 'abcd |abcd ' " ^^^^ underline " ^ 'Search' highlight with underline - " ^ 'VertSplit' highlight + " ^ 'WinSeparator' highlight " ^^^^^^^^^ no highlight " underline diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b262fc6c54..917847608a 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -19,6 +19,7 @@ # include "nvim/os/os_win_console.h" #endif #include "nvim/event/rstream.h" +#include "nvim/msgpack_rpc/channel.h" #define KEY_BUFFER_SIZE 0xfff @@ -115,6 +116,7 @@ static void tinput_wait_enqueue(void **argv) { TermInput *input = argv[0]; if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) { + // End streamed paste with an empty string. const String keys = { .data = "", .size = 0 }; String copy = copy_string(keys); multiqueue_put(main_loop.events, tinput_paste_event, 3, @@ -124,8 +126,16 @@ static void tinput_wait_enqueue(void **argv) const String keys = { .data = buf, .size = len }; if (input->paste) { String copy = copy_string(keys); - multiqueue_put(main_loop.events, tinput_paste_event, 3, - copy.data, copy.size, (intptr_t)input->paste); + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(copy_string(keys))); // 'data' + ADD(args, BOOLEAN_OBJ(true)); // 'crlf' + ADD(args, INTEGER_OBJ(input->paste)); // 'phase' + rpc_send_event(ui_client_channel_id, "nvim_paste", args); + } else { + multiqueue_put(main_loop.events, tinput_paste_event, 3, + copy.data, copy.size, (intptr_t)input->paste); + } if (input->paste == 1) { // Paste phase: "continue" input->paste = 2; @@ -133,7 +143,17 @@ static void tinput_wait_enqueue(void **argv) rbuffer_consumed(input->key_buffer, len); rbuffer_reset(input->key_buffer); } else { - const size_t consumed = input_enqueue(keys); + size_t consumed; + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + Error err = ERROR_INIT; + ADD(args, STRING_OBJ(copy_string(keys))); + // TODO(bfredl): could be non-blocking now with paste? + Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &err); + consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; + } else { + consumed = input_enqueue(keys); + } if (consumed) { rbuffer_consumed(input->key_buffer, consumed); } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 7c67c058b0..da50f068b7 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -12,6 +12,7 @@ #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/diff.h" #include "nvim/event/loop.h" #include "nvim/ex_cmds2.h" @@ -224,7 +225,21 @@ void ui_refresh(void) int save_p_lz = p_lz; p_lz = false; // convince redrawing() to return true ... - screen_resize(width, height); + if (!ui_client_channel_id) { + screen_resize(width, height); + } else { + Array args = ARRAY_DICT_INIT; + Error err = ERROR_INIT; + ADD(args, INTEGER_OBJ((int)width)); + ADD(args, INTEGER_OBJ((int)height)); + rpc_send_call(ui_client_channel_id, "nvim_ui_try_resize", args, &err); + + if (ERROR_SET(&err)) { + ELOG("ui_client resize: %s", err.msg); + } + api_clear_error(&err); + } + p_lz = save_p_lz; if (ext_widgets[kUIMessages]) { diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 4a435aac4d..6e45a28e89 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -6,40 +6,55 @@ #include <assert.h> #include "nvim/vim.h" +#include "nvim/log.h" +#include "nvim/map.h" #include "nvim/ui_client.h" #include "nvim/api/private/helpers.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/api/private/dispatch.h" #include "nvim/ui.h" +#include "nvim/highlight.h" +#include "nvim/screen.h" + +static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; + +// Temporary buffer for converting a single grid_line event +static size_t buf_size = 0; +static schar_T *buf_char = NULL; +static sattr_T *buf_attr = NULL; + +static void add_ui_client_event_handler(String method, UIClientHandler handler) +{ + map_put(String, UIClientHandler)(&ui_client_handlers, method, handler); +} void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; - int width = 80; - int height = 25; + int width = Columns; + int height = Rows; Dictionary opts = ARRAY_DICT_INIT; PUT(opts, "rgb", BOOLEAN_OBJ(true)); PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true)); - // TODO(bfredl): use the size of the client UI ADD(args, INTEGER_OBJ((int)width)); ADD(args, INTEGER_OBJ((int)height)); ADD(args, DICTIONARY_OBJ(opts)); rpc_send_event(chan, "nvim_ui_attach", args); msgpack_rpc_add_redraw(); // GAME! + // TODO(bfredl): use a keyset instead + ui_client_methods_table_init(); ui_client_channel_id = chan; } /// Handler for "redraw" events sent by the NVIM server /// -/// This is just a stub. The mentioned functionality will be implemented. -/// -/// This function will be called by handle_request (in msgpack_rpc/channle.c) +/// This function will be called by handle_request (in msgpack_rpc/channel.c) /// The individual ui_events sent by the server are individually handled -/// by their respective handlers defined in ui_events_redraw.generated.h +/// by their respective handlers defined in ui_events_client.generated.h /// /// @note The "flush" event is called only once and only after handling all /// the other events @@ -50,10 +65,21 @@ Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) { for (size_t i = 0; i < args.size; i++) { Array call = args.items[i].data.array; - char *method_name = call.items[0].data.string.data; + String name = call.items[0].data.string; + + UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name); + if (!handler) { + ELOG("No ui client handler for %s", name.size ? name.data : "<empty>"); + continue; + } - fprintf(stderr, "%s: %zu\n", method_name, call.size-1); + // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); + DLOG("Invoke ui client handler for %s", name.data); + for (size_t j = 1; j < call.size; j++) { + handler(call.items[j].data.array); + } } + return NIL; } @@ -64,7 +90,126 @@ void ui_client_execute(uint64_t chan) { while (true) { loop_poll_events(&main_loop, -1); + multiqueue_process_events(resize_events); } getout(0); } + +static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) +{ + Error err = ERROR_INIT; + Dict(highlight) dict = { 0 }; + if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, d, &err)) { + // TODO(bfredl): log "err" + return HLATTRS_INIT; + } + return dict2hlattrs(&dict, true, NULL, &err); +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "ui_events_client.generated.h" +#endif + +void ui_client_event_grid_resize(Array args) +{ + if (args.size < 3 + || args.items[0].type != kObjectTypeInteger + || args.items[1].type != kObjectTypeInteger + || args.items[2].type != kObjectTypeInteger) { + ELOG("Error handling ui event 'grid_resize'"); + return; + } + + Integer grid = args.items[0].data.integer; + Integer width = args.items[1].data.integer; + Integer height = args.items[2].data.integer; + ui_call_grid_resize(grid, width, height); + + if (buf_size < (size_t)width) { + xfree(buf_char); + xfree(buf_attr); + buf_size = (size_t)width; + buf_char = xmalloc(buf_size * sizeof(schar_T)); + buf_attr = xmalloc(buf_size * sizeof(sattr_T)); + } +} + +void ui_client_event_grid_line(Array args) +{ + if (args.size < 4 + || args.items[0].type != kObjectTypeInteger + || args.items[1].type != kObjectTypeInteger + || args.items[2].type != kObjectTypeInteger + || args.items[3].type != kObjectTypeArray) { + goto error; + } + + Integer grid = args.items[0].data.integer; + Integer row = args.items[1].data.integer; + Integer startcol = args.items[2].data.integer; + Array cells = args.items[3].data.array; + + Integer endcol, clearcol; + // TODO(hlpr98): Accomodate other LineFlags when included in grid_line + LineFlags lineflags = 0; + endcol = startcol; + + size_t j = 0; + int cur_attr = 0; + int clear_attr = 0; + int clear_width = 0; + for (size_t i = 0; i < cells.size; i++) { + if (cells.items[i].type != kObjectTypeArray) { + goto error; + } + Array cell = cells.items[i].data.array; + + if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { + goto error; + } + String sstring = cell.items[0].data.string; + + char *schar = sstring.data; + int repeat = 1; + if (cell.size >= 2) { + if (cell.items[1].type != kObjectTypeInteger + || cell.items[1].data.integer < 0) { + goto error; + } + cur_attr = (int)cell.items[1].data.integer; + } + + if (cell.size >= 3) { + if (cell.items[2].type != kObjectTypeInteger + || cell.items[2].data.integer < 0) { + goto error; + } + repeat = (int)cell.items[2].data.integer; + } + + if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { + clear_width = repeat; + break; + } + + for (int r = 0; r < repeat; r++) { + if (j >= buf_size) { + goto error; // _YIKES_ + } + STRLCPY(buf_char[j], schar, sizeof(schar_T)); + buf_attr[j++] = cur_attr; + } + } + + endcol = startcol + (int)j; + clearcol = endcol + clear_width; + clear_attr = cur_attr; + + ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, + (const schar_T *)buf_char, (const sattr_T *)buf_attr); + return; + +error: + ELOG("Error handling ui event 'grid_line'"); +} diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 067f78d5c5..253deecc52 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -3,7 +3,11 @@ #include "nvim/api/private/defs.h" +typedef void (*UIClientHandler)(Array args); + #ifdef INCLUDE_GENERATED_DECLARATIONS #include "ui_client.h.generated.h" +#include "ui_events_client.h.generated.h" #endif + #endif // NVIM_UI_CLIENT_H diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index d7becb4fd4..e356960cc8 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -14,6 +14,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/lua/executor.h" @@ -23,7 +24,6 @@ #include "nvim/os/os.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/ugrid.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index a235b07b47..8b9f1e024d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -25,6 +25,7 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/highlight_group.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/memline.h" @@ -60,7 +61,7 @@ #define NOWIN (win_T *)-1 // non-existing window -#define ROWS_AVAIL (Rows - p_ch - tabline_height()) +#define ROWS_AVAIL (Rows - p_ch - tabline_height() - global_stl_height()) /// flags for win_enter_ext() typedef enum { @@ -658,6 +659,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) } wp->w_floating = 1; wp->w_status_height = 0; + wp->w_hsep_height = 0; wp->w_vsep_width = 0; win_config_float(wp, fconfig); @@ -967,6 +969,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int before; int minheight; int wmh1; + int hsep_height; bool did_set_fraction = false; // aucmd_win should always remain floating @@ -1079,6 +1082,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } } else { + hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT; layout = FR_COL; /* @@ -1087,7 +1091,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) */ // Current window requires at least 1 space. wmh1 = p_wmh == 0 ? 1 : p_wmh; - needed = wmh1 + STATUS_HEIGHT; + needed = wmh1 + hsep_height; if (flags & WSP_ROOM) { needed += p_wh - wmh1; } @@ -1129,15 +1133,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) new_size = oldwin_height / 2; } - if (new_size > available - minheight - STATUS_HEIGHT) { - new_size = available - minheight - STATUS_HEIGHT; + if (new_size > available - minheight - hsep_height) { + new_size = available - minheight - hsep_height; } if (new_size < wmh1) { new_size = wmh1; } // if it doesn't fit in the current window, need win_equal() - if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) { + if (oldwin_height - new_size - hsep_height < p_wmh) { do_equal = true; } @@ -1150,7 +1154,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) set_fraction(oldwin); did_set_fraction = true; - win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, + win_setheight_win(oldwin->w_height + new_size + hsep_height, oldwin); oldwin_height = oldwin->w_height; if (need_status) { @@ -1167,8 +1171,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_height > new_size - || frp->fr_win->w_height > oldwin_height - new_size - - STATUS_HEIGHT)) { + || frp->fr_win->w_height > oldwin_height - new_size - hsep_height)) { do_equal = true; break; } @@ -1294,13 +1297,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (flags & (WSP_TOP | WSP_BOT)) { // set height and row of new window to full height wp->w_winrow = tabline_height(); - win_new_height(wp, curfrp->fr_height - (p_ls > 0)); - wp->w_status_height = (p_ls > 0); + win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2)); + wp->w_status_height = (p_ls == 1 || p_ls == 2); + wp->w_hsep_height = 0; } else { // height and row of new window is same as current window wp->w_winrow = oldwin->w_winrow; win_new_height(wp, oldwin->w_height); wp->w_status_height = oldwin->w_status_height; + wp->w_hsep_height = oldwin->w_hsep_height; } frp->fr_height = curfrp->fr_height; @@ -1333,6 +1338,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) frame_fix_width(oldwin); frame_fix_width(wp); } else { + bool is_stl_global = global_stl_height() > 0; // width and column of new window is same as current window if (flags & (WSP_TOP | WSP_BOT)) { wp->w_wincol = 0; @@ -1348,28 +1354,53 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // "new_size" of the current window goes to the new window, use // one row for the status line win_new_height(wp, new_size); + if (before) { + wp->w_hsep_height = is_stl_global ? 1 : 0; + } else { + wp->w_hsep_height = oldwin->w_hsep_height; + oldwin->w_hsep_height = is_stl_global ? 1 : 0; + } if (flags & (WSP_TOP | WSP_BOT)) { int new_fr_height = curfrp->fr_height - new_size; - if (!((flags & WSP_BOT) && p_ls == 0)) { + if (!((flags & WSP_BOT) && p_ls == 0) && global_stl_height() == 0) { new_fr_height -= STATUS_HEIGHT; + } else if (global_stl_height() > 0) { + if (flags & WSP_BOT) { + frame_add_hsep(curfrp); + } else { + new_fr_height -= 1; + } } frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false); } else { - win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT)); + win_new_height(oldwin, oldwin_height - (new_size + + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT))); } + if (before) { // new window above current one wp->w_winrow = oldwin->w_winrow; - wp->w_status_height = STATUS_HEIGHT; - oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; + + if (is_stl_global) { + wp->w_status_height = 0; + oldwin->w_winrow += wp->w_height + 1; + } else { + wp->w_status_height = STATUS_HEIGHT; + oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; + } } else { // new window below current one - wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; - wp->w_status_height = oldwin->w_status_height; - if (!(flags & WSP_BOT)) { - oldwin->w_status_height = STATUS_HEIGHT; + if (is_stl_global) { + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + 1; + wp->w_status_height = 0; + } else { + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; + wp->w_status_height = oldwin->w_status_height; + if (!(flags & WSP_BOT)) { + oldwin->w_status_height = STATUS_HEIGHT; + } } } - if (flags & WSP_BOT) { + if ((flags & WSP_BOT) && global_stl_height() == 0) { frame_add_statusline(curfrp); } frame_fix_height(wp); @@ -1609,10 +1640,10 @@ int make_windows(int count, bool vertical) maxcount = (curwin->w_width + curwin->w_vsep_width - (p_wiw - p_wmw)) / (p_wmw + 1); } else { - // Each window needs at least 'winminheight' lines and a status line. - maxcount = (curwin->w_height - + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); + // Each window needs at least 'winminheight' lines + // If statusline isn't global, each window also needs a statusline + maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height + - (p_wh - p_wmh)) / (p_wmh + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT)); } if (maxcount < 2) { @@ -1707,7 +1738,7 @@ static void win_exchange(long Prenum) * if wp != wp2 * 3. remove wp from the list * 4. insert wp after wp2 - * 5. exchange the status line height and vsep width. + * 5. exchange the status line height, hsep height and vsep width. */ wp2 = curwin->w_prev; frp2 = curwin->w_frame->fr_prev; @@ -1733,6 +1764,9 @@ static void win_exchange(long Prenum) temp = curwin->w_vsep_width; curwin->w_vsep_width = wp->w_vsep_width; wp->w_vsep_width = temp; + temp = curwin->w_hsep_height; + curwin->w_hsep_height = wp->w_hsep_height; + wp->w_hsep_height = temp; frame_fix_height(curwin); frame_fix_height(wp); @@ -1813,10 +1847,13 @@ static void win_rotate(bool upwards, int count) frame_insert(frp->fr_parent->fr_child, frp); } - // exchange status height and vsep width of old and new last window + // exchange status height, hsep height and vsep width of old and new last window n = wp2->w_status_height; wp2->w_status_height = wp1->w_status_height; wp1->w_status_height = n; + n = wp2->w_hsep_height; + wp2->w_hsep_height = wp1->w_hsep_height; + wp1->w_hsep_height = n; frame_fix_height(wp1); frame_fix_height(wp2); n = wp2->w_vsep_width; @@ -1893,11 +1930,16 @@ void win_move_after(win_T *win1, win_T *win2) // check if there is something to do if (win2->w_next != win1) { - // may need move the status line/vertical separator of the last window + // may need move the status line, horizontal or vertical separator of the last window if (win1 == lastwin) { height = win1->w_prev->w_status_height; win1->w_prev->w_status_height = win1->w_status_height; win1->w_status_height = height; + + height = win1->w_prev->w_hsep_height; + win1->w_prev->w_hsep_height = win1->w_hsep_height; + win1->w_hsep_height = height; + if (win1->w_prev->w_vsep_width == 1) { // Remove the vertical separator from the last-but-one window, // add it to the last window. Adjust the frame widths. @@ -1910,6 +1952,11 @@ void win_move_after(win_T *win1, win_T *win2) height = win1->w_status_height; win1->w_status_height = win2->w_status_height; win2->w_status_height = height; + + height = win1->w_hsep_height; + win1->w_hsep_height = win2->w_hsep_height; + win2->w_hsep_height = height; + if (win1->w_vsep_width == 1) { // Remove the vertical separator from win1, add it to the last // window, win2. Adjust the frame widths. @@ -1973,6 +2020,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int int room = 0; int new_size; int has_next_curwin = 0; + int hsep_height; bool hnc; if (topfr->fr_layout == FR_LEAF) { @@ -2118,19 +2166,22 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int totwincount -= wincount; } } else { // topfr->fr_layout == FR_COL + hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT; topfr->fr_width = width; topfr->fr_height = height; if (dir != 'h') { // equalize frame heights // Compute maximum number of windows vertically in this frame. n = frame_minheight(topfr, NOWIN); - // add one for the bottom window if it doesn't have a statusline + // add one for the bottom window if it doesn't have a statusline or separator if (row + height == cmdline_row && p_ls == 0) { + extra_sep = STATUS_HEIGHT; + } else if (global_stl_height() > 0) { extra_sep = 1; } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmh + 1); + totwincount = (n + extra_sep) / (p_wmh + hsep_height); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2165,7 +2216,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // These windows don't use up room. totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmh + 1); + ? extra_sep : 0)) / (p_wmh + hsep_height); } room -= new_size - n; if (room < 0) { @@ -2211,7 +2262,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Compute the maximum number of windows vert. in "fr". n = frame_minheight(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmh + 1); + / (p_wmh + hsep_height); m = frame_minheight(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -3167,7 +3218,7 @@ static tabpage_T *alt_tabpage(void) /* * Find the left-upper window in frame "frp". */ -static win_T *frame2win(frame_T *frp) +win_T *frame2win(frame_T *frp) { while (frp->fr_win == NULL) { frp = frp->fr_child; @@ -3194,23 +3245,40 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp) return false; } +/// Check if current window is at the bottom +/// Returns true if there are no windows below current window +static bool is_bottom_win(win_T *wp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + frame_T *frp; + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_next != NULL) { + return false; + } + } + return true; +} /// Set a new height for a frame. Recursively sets the height for contained /// frames and windows. Caller must take care of positions. /// /// @param topfirst resize topmost contained frame first. /// @param wfh obey 'winfixheight' when there is a choice; /// may cause the height not to be set. -static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) +void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) FUNC_ATTR_NONNULL_ALL { frame_T *frp; int extra_lines; int h; + win_T *wp; if (topfrp->fr_win != NULL) { // Simple case: just one window. - win_new_height(topfrp->fr_win, - height - topfrp->fr_win->w_status_height); + wp = topfrp->fr_win; + if (is_bottom_win(wp)) { + wp->w_hsep_height = 0; + } + win_new_height(wp, height - wp->w_hsep_height - wp->w_status_height); } else if (topfrp->fr_layout == FR_ROW) { do { // All frames in this row get the same new height. @@ -3366,8 +3434,8 @@ static void frame_add_statusline(frame_T *frp) if (frp->fr_layout == FR_LEAF) { wp = frp->fr_win; if (wp->w_status_height == 0) { - if (wp->w_height > 0) { // don't make it negative - --wp->w_height; + if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative + wp->w_height -= STATUS_HEIGHT; } wp->w_status_height = STATUS_HEIGHT; } @@ -3487,10 +3555,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw topfrp->fr_width = width; } -/* - * Add the vertical separator to windows at the right side of "frp". - * Note: Does not check if there is room! - */ +/// Add the vertical separator to windows at the right side of "frp". +/// Note: Does not check if there is room! static void frame_add_vsep(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { @@ -3520,6 +3586,37 @@ static void frame_add_vsep(const frame_T *frp) } } +/// Add the horizontal separator to windows at the bottom of "frp". +/// Note: Does not check if there is room or whether the windows have a statusline! +static void frame_add_hsep(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) +{ + win_T *wp; + + if (frp->fr_layout == FR_LEAF) { + wp = frp->fr_win; + if (wp->w_hsep_height == 0) { + if (wp->w_height > 0) { // don't make it negative + wp->w_height++; + } + wp->w_hsep_height = 1; + } + } else if (frp->fr_layout == FR_ROW) { + // Handle all the frames in the row. + FOR_ALL_FRAMES(frp, frp->fr_child) { + frame_add_hsep(frp); + } + } else { + assert(frp->fr_layout == FR_COL); + // Only need to handle the last frame in the column. + frp = frp->fr_child; + while (frp->fr_next != NULL) { + frp = frp->fr_next; + } + frame_add_hsep(frp); + } +} + /* * Set frame width from the window it contains. */ @@ -3534,7 +3631,7 @@ static void frame_fix_width(win_T *wp) static void frame_fix_height(win_T *wp) FUNC_ATTR_NONNULL_ALL { - wp->w_frame->fr_height = wp->w_height + wp->w_status_height; + wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height; } /* @@ -3552,10 +3649,11 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) if (topfrp->fr_win != NULL) { if (topfrp->fr_win == next_curwin) { - m = p_wh + topfrp->fr_win->w_status_height; + m = p_wh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height; } else { - // window: minimal height of the window plus status line - m = p_wmh + topfrp->fr_win->w_status_height; + // window: minimal height of the window plus separator column or status line + // depending on whether global statusline is enabled + m = p_wmh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height; if (topfrp->fr_win == curwin && next_curwin == NULL) { // Current window is minimal one line high. if (p_wmh == 0) { @@ -3784,7 +3882,7 @@ static int win_alloc_firstwin(win_T *oldwin) new_frame(curwin); topframe = curwin->w_frame; topframe->fr_width = Columns; - topframe->fr_height = Rows - p_ch; + topframe->fr_height = Rows - p_ch - global_stl_height(); return OK; } @@ -5200,11 +5298,8 @@ void win_size_restore(garray_T *gap) } } -/* - * Update the position for all windows, using the width and height of the - * frames. - * Returns the row just after the last window. - */ +// Update the position for all windows, using the width and height of the frames. +// Returns the row just after the last window and global statusline (if there is one). int win_comp_pos(void) { int row = tabline_height(); @@ -5219,7 +5314,7 @@ int win_comp_pos(void) } } - return row; + return row + global_stl_height(); } void win_reconfig_floats(void) @@ -5253,7 +5348,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_redr_status = true; wp->w_pos_changed = true; } - const int h = wp->w_height + wp->w_status_height; + const int h = wp->w_height + wp->w_hsep_height + wp->w_status_height; *row += h > topfrp->fr_height ? topfrp->fr_height : h; *col += wp->w_width + wp->w_vsep_width; } else { @@ -5302,7 +5397,7 @@ void win_setheight_win(int height, win_T *win) win_config_float(win, win->w_float_config); redraw_later(win, NOT_VALID); } else { - frame_setheight(win->w_frame, height + win->w_status_height); + frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height); // recompute the window positions int row = win_comp_pos(); @@ -5393,8 +5488,8 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } else { win_T *wp = lastwin_nofloating(); - room_cmdline = Rows - p_ch - - (wp->w_winrow + wp->w_height + wp->w_status_height); + room_cmdline = Rows - p_ch - global_stl_height() + - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; } @@ -5756,7 +5851,7 @@ void win_drag_status_line(win_T *dragwin, int offset) } else { // drag down up = false; // Only dragging the last status line can reduce p_ch. - room = Rows - cmdline_row; + room = Rows - cmdline_row - global_stl_height(); if (curfr->fr_next == NULL) { room -= 1; } else { @@ -6397,72 +6492,104 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u return find_file_name_in_path(ptr, len, options, count, rel_fname); } -/// Add or remove a status line for the bottom window(s), according to the +/// Add or remove a status line from window(s), according to the /// value of 'laststatus'. /// /// @param morewin pretend there are two or more windows if true. void last_status(bool morewin) { // Don't make a difference between horizontal or vertical split. - last_status_rec(topframe, (p_ls == 2 - || (p_ls == 1 && (morewin || !one_window())))); + last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window()))), + global_stl_height() > 0); } -static void last_status_rec(frame_T *fr, bool statusline) +// Look for resizable frames and take lines from them to make room for the statusline +static void resize_frame_for_status(frame_T *fr, int resize_amount) +{ + // Find a frame to take a line from. + frame_T *fp = fr; + win_T *wp = fr->fr_win; + int n; + + while (resize_amount > 0) { + while (fp->fr_height <= frame_minheight(fp, NULL)) { + if (fp == topframe) { + emsg(_(e_noroom)); + return; + } + // In a column of frames: go to frame above. If already at + // the top or in a row of frames: go to parent. + if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { + fp = fp->fr_prev; + } else { + fp = fp->fr_parent; + } + } + n = MIN(fp->fr_height - frame_minheight(fp, NULL), resize_amount); + resize_amount -= n; + + if (fp != fr) { + frame_new_height(fp, fp->fr_height - n, false, false); + frame_fix_height(wp); + (void)win_comp_pos(); + } else { + win_new_height(wp, wp->w_height - n); + } + } +} + +static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) { frame_T *fp; win_T *wp; if (fr->fr_layout == FR_LEAF) { wp = fr->fr_win; - if (wp->w_status_height != 0 && !statusline) { - // remove status line - win_new_height(wp, wp->w_height + 1); + bool is_last = is_bottom_win(wp); + + if (is_last) { + if (wp->w_status_height != 0 && (!statusline || is_stl_global)) { + // Remove status line + wp->w_status_height = 0; + win_new_height(wp, wp->w_height + STATUS_HEIGHT); + comp_col(); + } else if (wp->w_status_height == 0 && !is_stl_global && statusline) { + // Add statusline to window if needed + wp->w_status_height = STATUS_HEIGHT; + resize_frame_for_status(fr, STATUS_HEIGHT); + comp_col(); + } + } else if (wp->w_status_height != 0 && is_stl_global) { + // If statusline is global and the window has a statusline, replace it with a horizontal + // separator + if (STATUS_HEIGHT - 1 != 0) { + win_new_height(wp, wp->w_height + STATUS_HEIGHT - 1); + } wp->w_status_height = 0; + wp->w_hsep_height = 1; comp_col(); - } else if (wp->w_status_height == 0 && statusline) { - // Find a frame to take a line from. - fp = fr; - while (fp->fr_height <= frame_minheight(fp, NULL)) { - if (fp == topframe) { - emsg(_(e_noroom)); - return; - } - // In a column of frames: go to frame above. If already at - // the top or in a row of frames: go to parent. - if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { - fp = fp->fr_prev; - } else { - fp = fp->fr_parent; - } - } - wp->w_status_height = 1; - if (fp != fr) { - frame_new_height(fp, fp->fr_height - 1, false, false); - frame_fix_height(wp); - (void)win_comp_pos(); - } else { - win_new_height(wp, wp->w_height - 1); - } + } else if (wp->w_status_height == 0 && !is_stl_global) { + // If statusline isn't global and the window doesn't have a statusline, re-add it + wp->w_status_height = STATUS_HEIGHT; + wp->w_hsep_height = 0; + resize_frame_for_status(fr, STATUS_HEIGHT - 1); comp_col(); - redraw_all_later(SOME_VALID); } - } else if (fr->fr_layout == FR_ROW) { - // vertically split windows, set status line for each one + redraw_all_later(SOME_VALID); + } else if (fr->fr_layout == FR_COL) { + // For a column frame, recursively call this function for all child frames FOR_ALL_FRAMES(fp, fr->fr_child) { - last_status_rec(fp, statusline); + last_status_rec(fp, statusline, is_stl_global); } } else { - // horizontally split window, set status line for last one - for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) { + // For a row frame, recursively call this function for all child frames + FOR_ALL_FRAMES(fp, fr->fr_child) { + last_status_rec(fp, statusline, is_stl_global); } - last_status_rec(fp, statusline); } } -/* - * Return the number of lines used by the tab page line. - */ +/// Return the number of lines used by the tab page line. int tabline_height(void) { if (ui_has(kUITabline)) { @@ -6478,10 +6605,14 @@ int tabline_height(void) return 1; } -/* - * Return the minimal number of rows that is needed on the screen to display - * the current number of windows. - */ +/// Return the number of lines used by the global statusline +int global_stl_height(void) +{ + return (p_ls == 3) ? STATUS_HEIGHT : 0; +} + +/// Return the minimal number of rows that is needed on the screen to display +/// the current number of windows. int min_rows(void) { if (firstwin == NULL) { // not initialized yet @@ -6495,7 +6626,7 @@ int min_rows(void) total = n; } } - total += tabline_height(); + total += tabline_height() + global_stl_height(); total += 1; // count the room for the command line return total; } diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index b0712ff366..9b51af1eec 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -61,6 +61,44 @@ describe('print', function() eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>', pcall_err(command, 'lua print("foo", v_tblout, "bar")')) end) + it('coerces error values into strings', function() + write_file(fname, [[ + function string_error() error("my mistake") end + function number_error() error(1234) end + function nil_error() error(nil) end + function table_error() error({message = "my mistake"}) end + function custom_error() + local err = {message = "my mistake", code = 11234} + setmetatable(err, { + __tostring = function(t) + return "Internal Error [" .. t.code .. "] " .. t.message + end + }) + error(err) + end + function bad_custom_error() + local err = {message = "my mistake", code = 11234} + setmetatable(err, { + -- intentionally not a function, downstream programmer has made an mistake + __tostring = "Internal Error [" .. err.code .. "] " .. err.message + }) + error(err) + end + ]]) + eq('', exec_capture('luafile ' .. fname)) + eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: my mistake', + pcall_err(command, 'lua string_error()')) + eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: 1234', + pcall_err(command, 'lua number_error()')) + eq('Vim(lua):E5108: Error executing lua [NULL]', + pcall_err(command, 'lua nil_error()')) + eq('Vim(lua):E5108: Error executing lua [NULL]', + pcall_err(command, 'lua table_error()')) + eq('Vim(lua):E5108: Error executing lua Internal Error [11234] my mistake', + pcall_err(command, 'lua custom_error()')) + eq('Vim(lua):E5108: Error executing lua [NULL]', + pcall_err(command, 'lua bad_custom_error()')) + end) it('prints strings with NULs and NLs correctly', function() meths.set_option('more', true) eq('abc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n', diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index e66e08d9d0..5b4daf02ea 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -14,7 +14,7 @@ local feed = helpers.feed local pcall_err = helpers.pcall_err local exec_lua = helpers.exec_lua local matches = helpers.matches -local source = helpers.source +local exec = helpers.exec local NIL = helpers.NIL local retry = helpers.retry local next_msg = helpers.next_msg @@ -743,7 +743,7 @@ describe('lua stdlib', function() -- compat: nvim_call_function uses "special" value for vimL float eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]])) - source([[ + exec([[ func! FooFunc(test) let g:test = a:test return {} @@ -771,6 +771,12 @@ describe('lua stdlib', function() -- error handling eq({false, 'Vim:E897: List or Blob required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]])) + + -- conversion between LuaRef and Vim Funcref + eq(true, exec_lua([[ + local x = vim.fn.VarArg(function() return 'foo' end, function() return 'bar' end) + return #x == 2 and x[1]() == 'foo' and x[2]() == 'bar' + ]])) end) it('vim.fn should error when calling API function', function() @@ -993,8 +999,11 @@ describe('lua stdlib', function() exec_lua [[ local counter = 0 - vim.g.AddCounter = function() counter = counter + 1 end - vim.g.GetCounter = function() return counter end + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.g.AddCounter = add_counter + vim.g.GetCounter = get_counter + vim.g.funcs = {add = add_counter, get = get_counter} ]] eq(0, eval('g:GetCounter()')) @@ -1006,11 +1015,18 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.g.GetCounter()]])) exec_lua([[vim.api.nvim_get_var('AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_get_var('GetCounter')()]])) + exec_lua([[vim.g.funcs.add()]]) + eq(5, exec_lua([[return vim.g.funcs.get()]])) + exec_lua([[vim.api.nvim_get_var('funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_get_var('funcs').get()]])) exec_lua [[ local counter = 0 - vim.api.nvim_set_var('AddCounter', function() counter = counter + 1 end) - vim.api.nvim_set_var('GetCounter', function() return counter end) + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.api.nvim_set_var('AddCounter', add_counter) + vim.api.nvim_set_var('GetCounter', get_counter) + vim.api.nvim_set_var('funcs', {add = add_counter, get = get_counter}) ]] eq(0, eval('g:GetCounter()')) @@ -1022,6 +1038,10 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.g.GetCounter()]])) exec_lua([[vim.api.nvim_get_var('AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_get_var('GetCounter')()]])) + exec_lua([[vim.g.funcs.add()]]) + eq(5, exec_lua([[return vim.g.funcs.get()]])) + exec_lua([[vim.api.nvim_get_var('funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_get_var('funcs').get()]])) -- Check if autoload works properly local pathsep = helpers.get_pathsep() @@ -1072,8 +1092,11 @@ describe('lua stdlib', function() exec_lua [[ local counter = 0 - vim.b.AddCounter = function() counter = counter + 1 end - vim.b.GetCounter = function() return counter end + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.b.AddCounter = add_counter + vim.b.GetCounter = get_counter + vim.b.funcs = {add = add_counter, get = get_counter} ]] eq(0, eval('b:GetCounter()')) @@ -1085,11 +1108,18 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.b.GetCounter()]])) exec_lua([[vim.api.nvim_buf_get_var(0, 'AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_buf_get_var(0, 'GetCounter')()]])) + exec_lua([[vim.b.funcs.add()]]) + eq(5, exec_lua([[return vim.b.funcs.get()]])) + exec_lua([[vim.api.nvim_buf_get_var(0, 'funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_buf_get_var(0, 'funcs').get()]])) exec_lua [[ local counter = 0 - vim.api.nvim_buf_set_var(0, 'AddCounter', function() counter = counter + 1 end) - vim.api.nvim_buf_set_var(0, 'GetCounter', function() return counter end) + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.api.nvim_buf_set_var(0, 'AddCounter', add_counter) + vim.api.nvim_buf_set_var(0, 'GetCounter', get_counter) + vim.api.nvim_buf_set_var(0, 'funcs', {add = add_counter, get = get_counter}) ]] eq(0, eval('b:GetCounter()')) @@ -1101,6 +1131,10 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.b.GetCounter()]])) exec_lua([[vim.api.nvim_buf_get_var(0, 'AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_buf_get_var(0, 'GetCounter')()]])) + exec_lua([[vim.b.funcs.add()]]) + eq(5, exec_lua([[return vim.b.funcs.get()]])) + exec_lua([[vim.api.nvim_buf_get_var(0, 'funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_buf_get_var(0, 'funcs').get()]])) exec_lua [[ vim.cmd "vnew" @@ -1141,8 +1175,11 @@ describe('lua stdlib', function() exec_lua [[ local counter = 0 - vim.w.AddCounter = function() counter = counter + 1 end - vim.w.GetCounter = function() return counter end + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.w.AddCounter = add_counter + vim.w.GetCounter = get_counter + vim.w.funcs = {add = add_counter, get = get_counter} ]] eq(0, eval('w:GetCounter()')) @@ -1154,11 +1191,18 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.w.GetCounter()]])) exec_lua([[vim.api.nvim_win_get_var(0, 'AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_win_get_var(0, 'GetCounter')()]])) + exec_lua([[vim.w.funcs.add()]]) + eq(5, exec_lua([[return vim.w.funcs.get()]])) + exec_lua([[vim.api.nvim_win_get_var(0, 'funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_win_get_var(0, 'funcs').get()]])) exec_lua [[ local counter = 0 - vim.api.nvim_win_set_var(0, 'AddCounter', function() counter = counter + 1 end) - vim.api.nvim_win_set_var(0, 'GetCounter', function() return counter end) + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.api.nvim_win_set_var(0, 'AddCounter', add_counter) + vim.api.nvim_win_set_var(0, 'GetCounter', get_counter) + vim.api.nvim_win_set_var(0, 'funcs', {add = add_counter, get = get_counter}) ]] eq(0, eval('w:GetCounter()')) @@ -1170,6 +1214,10 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.w.GetCounter()]])) exec_lua([[vim.api.nvim_win_get_var(0, 'AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_win_get_var(0, 'GetCounter')()]])) + exec_lua([[vim.w.funcs.add()]]) + eq(5, exec_lua([[return vim.w.funcs.get()]])) + exec_lua([[vim.api.nvim_win_get_var(0, 'funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_win_get_var(0, 'funcs').get()]])) exec_lua [[ vim.cmd "vnew" @@ -1205,8 +1253,11 @@ describe('lua stdlib', function() exec_lua [[ local counter = 0 - vim.t.AddCounter = function() counter = counter + 1 end - vim.t.GetCounter = function() return counter end + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.t.AddCounter = add_counter + vim.t.GetCounter = get_counter + vim.t.funcs = {add = add_counter, get = get_counter} ]] eq(0, eval('t:GetCounter()')) @@ -1218,11 +1269,18 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.t.GetCounter()]])) exec_lua([[vim.api.nvim_tabpage_get_var(0, 'AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'GetCounter')()]])) + exec_lua([[vim.t.funcs.add()]]) + eq(5, exec_lua([[return vim.t.funcs.get()]])) + exec_lua([[vim.api.nvim_tabpage_get_var(0, 'funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'funcs').get()]])) exec_lua [[ local counter = 0 - vim.api.nvim_tabpage_set_var(0, 'AddCounter', function() counter = counter + 1 end) - vim.api.nvim_tabpage_set_var(0, 'GetCounter', function() return counter end) + local function add_counter() counter = counter + 1 end + local function get_counter() return counter end + vim.api.nvim_tabpage_set_var(0, 'AddCounter', add_counter) + vim.api.nvim_tabpage_set_var(0, 'GetCounter', get_counter) + vim.api.nvim_tabpage_set_var(0, 'funcs', {add = add_counter, get = get_counter}) ]] eq(0, eval('t:GetCounter()')) @@ -1234,6 +1292,10 @@ describe('lua stdlib', function() eq(3, exec_lua([[return vim.t.GetCounter()]])) exec_lua([[vim.api.nvim_tabpage_get_var(0, 'AddCounter')()]]) eq(4, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'GetCounter')()]])) + exec_lua([[vim.t.funcs.add()]]) + eq(5, exec_lua([[return vim.t.funcs.get()]])) + exec_lua([[vim.api.nvim_tabpage_get_var(0, 'funcs').add()]]) + eq(6, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'funcs').get()]])) exec_lua [[ vim.cmd "tabnew" diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua index 9059441fd9..b5f3c2bd31 100644 --- a/test/functional/terminal/channel_spec.lua +++ b/test/functional/terminal/channel_spec.lua @@ -33,7 +33,7 @@ describe('terminal channel is closed and later released if', function() -- channel has been closed but not released eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[call chanclose(id) | call chansend(id, 'test')]])) - screen:expect({any='%[Terminal closed]'}) + screen:expect({any='%[Terminal closed%]'}) eq(chans, eval('len(nvim_list_chans())')) -- delete terminal feed('i<CR>') @@ -49,7 +49,7 @@ describe('terminal channel is closed and later released if', function() -- channel has been closed but not released eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[call chanclose(id) | call chansend(id, 'test')]])) - screen:expect({any='%[Terminal closed]'}) + screen:expect({any='%[Terminal closed%]'}) eq(chans, eval('len(nvim_list_chans())')) -- channel still hasn't been released yet eq("Vim(call):Can't send data to closed stream", diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 03cd4bfd06..4c51547e2c 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -212,10 +212,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 60 + m.hl_id = 61 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 61 end + if m.id_lm then m.id_lm = 62 end end -- Assert the new expectation. diff --git a/test/functional/ui/global_statusline_spec.lua b/test/functional/ui/global_statusline_spec.lua new file mode 100644 index 0000000000..6b37e5e2f1 --- /dev/null +++ b/test/functional/ui/global_statusline_spec.lua @@ -0,0 +1,233 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, command, feed = helpers.clear, helpers.command, helpers.feed + +describe('global statusline', function() + local screen + + before_each(function() + clear() + screen = Screen.new(60, 16) + screen:attach() + command('set laststatus=3') + command('set ruler') + end) + + it('works', function() + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] 0,0-1 All}| + | + ]], attr_ids={ + [1] = {bold = true, foreground = Screen.colors.Blue1}; + [2] = {bold = true, reverse = true}; + }} + + feed('i<CR><CR>') + screen:expect{grid=[[ + | + | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] [+] 3,1 All}| + {3:-- INSERT --} | + ]], attr_ids={ + [1] = {bold = true, foreground = Screen.colors.Blue}; + [2] = {bold = true, reverse = true}; + [3] = {bold = true}; + }} + end) + + it('works with splits', function() + command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split') + screen:expect{grid=[[ + {1:│} {1:│} {1:│}^ | + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:├────────────────┤}{2:~}{1:│}{2:~ }| + {2:~ }{1:│} {1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:├────────────────────}| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│} | + {1:────────────────────┴────────────────┴─┤}{2:~ }| + {1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {3:[No Name] 0,0-1 All}| + | + ]], attr_ids={ + [1] = {reverse = true}; + [2] = {bold = true, foreground = Screen.colors.Blue1}; + [3] = {bold = true, reverse = true}; + }} + end) + + it('works when switching between values of laststatus', function() + command('set laststatus=1') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + 0,0-1 All | + ]], attr_ids={ + [1] = {foreground = Screen.colors.Blue, bold = true}; + }} + + command('set laststatus=3') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] 0,0-1 All}| + | + ]], attr_ids={ + [1] = {foreground = Screen.colors.Blue, bold = true}; + [2] = {reverse = true, bold = true}; + }} + + command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split') + command('set laststatus=2') + screen:expect{grid=[[ + {1:│} {1:│} {1:│}^ | + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│< Name] 0,0-1 │}{2:~}{1:│}{2:~ }| + {2:~ }{1:│} {1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{3:<No Name] 0,0-1 All}| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│} | + {1:<No Name] 0,0-1 All < Name] 0,0-1 <│}{2:~ }| + {1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {1:[No Name] 0,0-1 All <No Name] 0,0-1 All}| + | + ]], attr_ids={ + [1] = {reverse = true}; + [2] = {foreground = Screen.colors.Blue, bold = true}; + [3] = {reverse = true, bold = true}; + }} + + command('set laststatus=3') + screen:expect{grid=[[ + {1:│} {1:│} {1:│}^ | + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:├────────────────┤}{2:~}{1:│}{2:~ }| + {2:~ }{1:│} {1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:├────────────────────}| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│} | + {1:────────────────────┴────────────────┴─┤}{2:~ }| + {1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {3:[No Name] 0,0-1 All}| + | + ]], attr_ids={ + [1] = {reverse = true}; + [2] = {foreground = Screen.colors.Blue, bold = true}; + [3] = {reverse = true, bold = true}; + }} + + command('set laststatus=0') + screen:expect{grid=[[ + {1:│} {1:│} {1:│}^ | + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│< Name] 0,0-1 │}{2:~}{1:│}{2:~ }| + {2:~ }{1:│} {1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{3:<No Name] 0,0-1 All}| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│} | + {1:<No Name] 0,0-1 All < Name] 0,0-1 <│}{2:~ }| + {1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + 0,0-1 All | + ]], attr_ids={ + [1] = {reverse = true}; + [2] = {foreground = Screen.colors.Blue, bold = true}; + [3] = {reverse = true, bold = true}; + }} + + command('set laststatus=3') + screen:expect{grid=[[ + {1:│} {1:│} {1:│}^ | + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:├────────────────┤}{2:~}{1:│}{2:~ }| + {2:~ }{1:│} {1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:├────────────────────}| + {2:~ }{1:│}{2:~ }{1:│}{2:~}{1:│} | + {1:────────────────────┴────────────────┴─┤}{2:~ }| + {1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {2:~ }{1:│}{2:~ }| + {3:[No Name] 0,0-1 All}| + | + ]], attr_ids={ + [1] = {reverse = true}; + [2] = {foreground = Screen.colors.Blue, bold = true}; + [3] = {reverse = true, bold = true}; + }} + end) +end) diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index 295b70f265..dcea2c76dd 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -59,7 +59,7 @@ describe('ext_hlstate detailed highlights', function() it('work with cleared UI highlights', function() screen:set_default_attr_ids({ - [1] = {{}, {{hi_name = "VertSplit", ui_name = "VertSplit", kind = "ui"}}}, + [1] = {{}, {{hi_name = "VertSplit", ui_name = "WinSeparator", kind = "ui"}}}, [2] = {{bold = true, foreground = Screen.colors.Blue1}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}}, [3] = {{bold = true, reverse = true}, diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 8342044b5e..51a703b593 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -48,6 +48,7 @@ local predefined_hl_defs = { TermCursor=true, VertSplit=true, WildMenu=true, + WinSeparator=true, EndOfBuffer=true, QuickFixLine=true, Substitute=true, |