1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4#include <assert.h>
5#include <inttypes.h>
6#include <stdbool.h>
7#include <stdlib.h>
8#include <string.h>
9#include <limits.h>
10
11#include "nvim/api/vim.h"
12#include "nvim/ascii.h"
13#include "nvim/api/private/helpers.h"
14#include "nvim/api/private/defs.h"
15#include "nvim/api/private/dispatch.h"
16#include "nvim/api/buffer.h"
17#include "nvim/api/window.h"
18#include "nvim/msgpack_rpc/channel.h"
19#include "nvim/msgpack_rpc/helpers.h"
20#include "nvim/lua/executor.h"
21#include "nvim/vim.h"
22#include "nvim/buffer.h"
23#include "nvim/context.h"
24#include "nvim/file_search.h"
25#include "nvim/highlight.h"
26#include "nvim/window.h"
27#include "nvim/types.h"
28#include "nvim/ex_docmd.h"
29#include "nvim/screen.h"
30#include "nvim/memline.h"
31#include "nvim/mark.h"
32#include "nvim/memory.h"
33#include "nvim/message.h"
34#include "nvim/popupmnu.h"
35#include "nvim/edit.h"
36#include "nvim/eval.h"
37#include "nvim/eval/typval.h"
38#include "nvim/fileio.h"
39#include "nvim/ops.h"
40#include "nvim/option.h"
41#include "nvim/state.h"
42#include "nvim/syntax.h"
43#include "nvim/getchar.h"
44#include "nvim/os/input.h"
45#include "nvim/os/process.h"
46#include "nvim/viml/parser/expressions.h"
47#include "nvim/viml/parser/parser.h"
48#include "nvim/ui.h"
49
50#define LINE_BUFFER_SIZE 4096
51
52#ifdef INCLUDE_GENERATED_DECLARATIONS
53# include "api/vim.c.generated.h"
54#endif
55
56// `msg_list` controls the collection of abort-causing non-exception errors,
57// which would otherwise be ignored. This pattern is from do_cmdline().
58//
59// TODO(bfredl): prepare error-handling at "top level" (nv_event).
60#define TRY_WRAP(code) \
61 do { \
62 struct msglist **saved_msg_list = msg_list; \
63 struct msglist *private_msg_list; \
64 msg_list = &private_msg_list; \
65 private_msg_list = NULL; \
66 code \
67 msg_list = saved_msg_list; /* Restore the exception context. */ \
68 } while (0)
69
70void api_vim_init(void)
71 FUNC_API_NOEXPORT
72{
73 namespace_ids = map_new(String, handle_T)();
74}
75
76void api_vim_free_all_mem(void)
77 FUNC_API_NOEXPORT
78{
79 String name;
80 handle_T id;
81 map_foreach(namespace_ids, name, id, {
82 (void)id;
83 xfree(name.data);
84 })
85 map_free(String, handle_T)(namespace_ids);
86}
87
88/// Executes an ex-command.
89///
90/// On execution error: fails with VimL error, does not update v:errmsg.
91///
92/// @param command Ex-command string
93/// @param[out] err Error details (Vim error), if any
94void nvim_command(String command, Error *err)
95 FUNC_API_SINCE(1)
96{
97 try_start();
98 do_cmdline_cmd(command.data);
99 try_end(err);
100}
101
102/// Gets a highlight definition by name.
103///
104/// @param name Highlight group name
105/// @param rgb Export RGB colors
106/// @param[out] err Error details, if any
107/// @return Highlight definition map
108/// @see nvim_get_hl_by_id
109Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
110 FUNC_API_SINCE(3)
111{
112 Dictionary result = ARRAY_DICT_INIT;
113 int id = syn_name2id((const char_u *)name.data);
114
115 if (id == 0) {
116 api_set_error(err, kErrorTypeException, "Invalid highlight name: %s",
117 name.data);
118 return result;
119 }
120 result = nvim_get_hl_by_id(id, rgb, err);
121 return result;
122}
123
124/// Gets a highlight definition by id. |hlID()|
125///
126/// @param hl_id Highlight id as returned by |hlID()|
127/// @param rgb Export RGB colors
128/// @param[out] err Error details, if any
129/// @return Highlight definition map
130/// @see nvim_get_hl_by_name
131Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
132 FUNC_API_SINCE(3)
133{
134 Dictionary dic = ARRAY_DICT_INIT;
135 if (syn_get_final_id((int)hl_id) == 0) {
136 api_set_error(err, kErrorTypeException,
137 "Invalid highlight id: %" PRId64, hl_id);
138 return dic;
139 }
140 int attrcode = syn_id2attr((int)hl_id);
141 return hl_get_attr_by_id(attrcode, rgb, err);
142}
143
144/// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
145/// flags. This is a blocking call, unlike |nvim_input()|.
146///
147/// On execution error: does not fail, but updates v:errmsg.
148///
149/// @param keys to be typed
150/// @param mode behavior flags, see |feedkeys()|
151/// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys`
152/// @see feedkeys()
153/// @see vim_strsave_escape_csi
154void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
155 FUNC_API_SINCE(1)
156{
157 bool remap = true;
158 bool insert = false;
159 bool typed = false;
160 bool execute = false;
161 bool dangerous = false;
162
163 for (size_t i = 0; i < mode.size; ++i) {
164 switch (mode.data[i]) {
165 case 'n': remap = false; break;
166 case 'm': remap = true; break;
167 case 't': typed = true; break;
168 case 'i': insert = true; break;
169 case 'x': execute = true; break;
170 case '!': dangerous = true; break;
171 }
172 }
173
174 if (keys.size == 0 && !execute) {
175 return;
176 }
177
178 char *keys_esc;
179 if (escape_csi) {
180 // Need to escape K_SPECIAL and CSI before putting the string in the
181 // typeahead buffer.
182 keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data);
183 } else {
184 keys_esc = keys.data;
185 }
186 ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE),
187 insert ? 0 : typebuf.tb_len, !typed, false);
188
189 if (escape_csi) {
190 xfree(keys_esc);
191 }
192
193 if (vgetc_busy) {
194 typebuf_was_filled = true;
195 }
196 if (execute) {
197 int save_msg_scroll = msg_scroll;
198
199 /* Avoid a 1 second delay when the keys start Insert mode. */
200 msg_scroll = false;
201 if (!dangerous) {
202 ex_normal_busy++;
203 }
204 exec_normal(true);
205 if (!dangerous) {
206 ex_normal_busy--;
207 }
208 msg_scroll |= save_msg_scroll;
209 }
210}
211
212/// Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a low-level
213/// input buffer and the call is non-blocking (input is processed
214/// asynchronously by the eventloop).
215///
216/// On execution error: does not fail, but updates v:errmsg.
217///
218/// @note |keycodes| like <CR> are translated, so "<" is special.
219/// To input a literal "<", send <LT>.
220///
221/// @note For mouse events use |nvim_input_mouse()|. The pseudokey form
222/// "<LeftMouse><col,row>" is deprecated since |api-level| 6.
223///
224/// @param keys to be typed
225/// @return Number of bytes actually written (can be fewer than
226/// requested if the buffer becomes full).
227Integer nvim_input(String keys)
228 FUNC_API_SINCE(1) FUNC_API_FAST
229{
230 return (Integer)input_enqueue(keys);
231}
232
233/// Send mouse event from GUI.
234///
235/// Non-blocking: does not wait on any result, but queues the event to be
236/// processed soon by the event loop.
237///
238/// @note Currently this doesn't support "scripting" multiple mouse events
239/// by calling it multiple times in a loop: the intermediate mouse
240/// positions will be ignored. It should be used to implement real-time
241/// mouse input in a GUI. The deprecated pseudokey form
242/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitiation.
243///
244/// @param button Mouse button: one of "left", "right", "middle", "wheel".
245/// @param action For ordinary buttons, one of "press", "drag", "release".
246/// For the wheel, one of "up", "down", "left", "right".
247/// @param modifier String of modifiers each represented by a single char.
248/// The same specifiers are used as for a key press, except
249/// that the "-" separator is optional, so "C-A-", "c-a"
250/// and "CA" can all be used to specify Ctrl+Alt+click.
251/// @param grid Grid number if the client uses |ui-multigrid|, else 0.
252/// @param row Mouse row-position (zero-based, like redraw events)
253/// @param col Mouse column-position (zero-based, like redraw events)
254/// @param[out] err Error details, if any
255void nvim_input_mouse(String button, String action, String modifier,
256 Integer grid, Integer row, Integer col, Error *err)
257 FUNC_API_SINCE(6) FUNC_API_FAST
258{
259 if (button.data == NULL || action.data == NULL) {
260 goto error;
261 }
262
263 int code = 0;
264
265 if (strequal(button.data, "left")) {
266 code = KE_LEFTMOUSE;
267 } else if (strequal(button.data, "middle")) {
268 code = KE_MIDDLEMOUSE;
269 } else if (strequal(button.data, "right")) {
270 code = KE_RIGHTMOUSE;
271 } else if (strequal(button.data, "wheel")) {
272 code = KE_MOUSEDOWN;
273 } else {
274 goto error;
275 }
276
277 if (code == KE_MOUSEDOWN) {
278 if (strequal(action.data, "down")) {
279 code = KE_MOUSEUP;
280 } else if (strequal(action.data, "up")) {
281 code = KE_MOUSEDOWN;
282 } else if (strequal(action.data, "left")) {
283 code = KE_MOUSERIGHT;
284 } else if (strequal(action.data, "right")) {
285 code = KE_MOUSELEFT;
286 } else {
287 goto error;
288 }
289 } else {
290 if (strequal(action.data, "press")) {
291 // pass
292 } else if (strequal(action.data, "drag")) {
293 code += KE_LEFTDRAG - KE_LEFTMOUSE;
294 } else if (strequal(action.data, "release")) {
295 code += KE_LEFTRELEASE - KE_LEFTMOUSE;
296 } else {
297 goto error;
298 }
299 }
300
301 int modmask = 0;
302 for (size_t i = 0; i < modifier.size; i++) {
303 char byte = modifier.data[i];
304 if (byte == '-') {
305 continue;
306 }
307 int mod = name_to_mod_mask(byte);
308 if (mod == 0) {
309 api_set_error(err, kErrorTypeValidation,
310 "invalid modifier %c", byte);
311 return;
312 }
313 modmask |= mod;
314 }
315
316 input_enqueue_mouse(code, (uint8_t)modmask, (int)grid, (int)row, (int)col);
317 return;
318
319error:
320 api_set_error(err, kErrorTypeValidation,
321 "invalid button or action");
322}
323
324/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
325/// the internal representation.
326///
327/// @param str String to be converted.
328/// @param from_part Legacy Vim parameter. Usually true.
329/// @param do_lt Also translate <lt>. Ignored if `special` is false.
330/// @param special Replace |keycodes|, e.g. <CR> becomes a "\n" char.
331/// @see replace_termcodes
332/// @see cpoptions
333String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
334 Boolean special)
335 FUNC_API_SINCE(1)
336{
337 if (str.size == 0) {
338 // Empty string
339 return (String) { .data = NULL, .size = 0 };
340 }
341
342 char *ptr = NULL;
343 replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
344 from_part, do_lt, special, CPO_TO_CPO_FLAGS);
345 return cstr_as_string(ptr);
346}
347
348/// Executes an ex-command and returns its (non-error) output.
349/// Shell |:!| output is not captured.
350///
351/// On execution error: fails with VimL error, does not update v:errmsg.
352///
353/// @param command Ex-command string
354/// @param[out] err Error details (Vim error), if any
355String nvim_command_output(String command, Error *err)
356 FUNC_API_SINCE(1)
357{
358 const int save_msg_silent = msg_silent;
359 garray_T *const save_capture_ga = capture_ga;
360 garray_T capture_local;
361 ga_init(&capture_local, 1, 80);
362
363 try_start();
364 msg_silent++;
365 capture_ga = &capture_local;
366 do_cmdline_cmd(command.data);
367 capture_ga = save_capture_ga;
368 msg_silent = save_msg_silent;
369 try_end(err);
370
371 if (ERROR_SET(err)) {
372 goto theend;
373 }
374
375 if (capture_local.ga_len > 1) {
376 String s = (String){
377 .data = capture_local.ga_data,
378 .size = (size_t)capture_local.ga_len,
379 };
380 // redir usually (except :echon) prepends a newline.
381 if (s.data[0] == '\n') {
382 memmove(s.data, s.data + 1, s.size - 1);
383 s.data[s.size - 1] = '\0';
384 s.size = s.size - 1;
385 }
386 return s; // Caller will free the memory.
387 }
388
389theend:
390 ga_clear(&capture_local);
391 return (String)STRING_INIT;
392}
393
394/// Evaluates a VimL expression (:help expression).
395/// Dictionaries and Lists are recursively expanded.
396///
397/// On execution error: fails with VimL error, does not update v:errmsg.
398///
399/// @param expr VimL expression string
400/// @param[out] err Error details, if any
401/// @return Evaluation result or expanded object
402Object nvim_eval(String expr, Error *err)
403 FUNC_API_SINCE(1)
404{
405 static int recursive = 0; // recursion depth
406 Object rv = OBJECT_INIT;
407
408 TRY_WRAP({
409 // Initialize `force_abort` and `suppress_errthrow` at the top level.
410 if (!recursive) {
411 force_abort = false;
412 suppress_errthrow = false;
413 current_exception = NULL;
414 // `did_emsg` is set by emsg(), which cancels execution.
415 did_emsg = false;
416 }
417 recursive++;
418 try_start();
419
420 typval_T rettv;
421 int ok = eval0((char_u *)expr.data, &rettv, NULL, true);
422
423 if (!try_end(err)) {
424 if (ok == FAIL) {
425 // Should never happen, try_end() should get the error. #8371
426 api_set_error(err, kErrorTypeException, "Failed to evaluate expression");
427 } else {
428 rv = vim_to_object(&rettv);
429 }
430 }
431
432 tv_clear(&rettv);
433 recursive--;
434 });
435
436 return rv;
437}
438
439/// Execute Lua code. Parameters (if any) are available as `...` inside the
440/// chunk. The chunk can return a value.
441///
442/// Only statements are executed. To evaluate an expression, prefix it
443/// with `return`: return my_function(...)
444///
445/// @param code Lua code to execute
446/// @param args Arguments to the code
447/// @param[out] err Details of an error encountered while parsing
448/// or executing the Lua code.
449///
450/// @return Return value of Lua code if present or NIL.
451Object nvim_execute_lua(String code, Array args, Error *err)
452 FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY
453{
454 return executor_exec_lua_api(code, args, err);
455}
456
457/// Calls a VimL function.
458///
459/// @param fn Function name
460/// @param args Function arguments
461/// @param self `self` dict, or NULL for non-dict functions
462/// @param[out] err Error details, if any
463/// @return Result of the function call
464static Object _call_function(String fn, Array args, dict_T *self, Error *err)
465{
466 static int recursive = 0; // recursion depth
467 Object rv = OBJECT_INIT;
468
469 if (args.size > MAX_FUNC_ARGS) {
470 api_set_error(err, kErrorTypeValidation,
471 "Function called with too many arguments");
472 return rv;
473 }
474
475 // Convert the arguments in args from Object to typval_T values
476 typval_T vim_args[MAX_FUNC_ARGS + 1];
477 size_t i = 0; // also used for freeing the variables
478 for (; i < args.size; i++) {
479 if (!object_to_vim(args.items[i], &vim_args[i], err)) {
480 goto free_vim_args;
481 }
482 }
483
484 TRY_WRAP({
485 // Initialize `force_abort` and `suppress_errthrow` at the top level.
486 if (!recursive) {
487 force_abort = false;
488 suppress_errthrow = false;
489 current_exception = NULL;
490 // `did_emsg` is set by emsg(), which cancels execution.
491 did_emsg = false;
492 }
493 recursive++;
494 try_start();
495 typval_T rettv;
496 int dummy;
497 // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
498 // (see above) to capture abort-causing non-exception errors.
499 (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
500 vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
501 &dummy, true, NULL, self);
502 if (!try_end(err)) {
503 rv = vim_to_object(&rettv);
504 }
505 tv_clear(&rettv);
506 recursive--;
507 });
508
509free_vim_args:
510 while (i > 0) {
511 tv_clear(&vim_args[--i]);
512 }
513
514 return rv;
515}
516
517/// Calls a VimL function with the given arguments.
518///
519/// On execution error: fails with VimL error, does not update v:errmsg.
520///
521/// @param fn Function to call
522/// @param args Function arguments packed in an Array
523/// @param[out] err Error details, if any
524/// @return Result of the function call
525Object nvim_call_function(String fn, Array args, Error *err)
526 FUNC_API_SINCE(1)
527{
528 return _call_function(fn, args, NULL, err);
529}
530
531/// Calls a VimL |Dictionary-function| with the given arguments.
532///
533/// On execution error: fails with VimL error, does not update v:errmsg.
534///
535/// @param dict Dictionary, or String evaluating to a VimL |self| dict
536/// @param fn Name of the function defined on the VimL dict
537/// @param args Function arguments packed in an Array
538/// @param[out] err Error details, if any
539/// @return Result of the function call
540Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
541 FUNC_API_SINCE(4)
542{
543 Object rv = OBJECT_INIT;
544
545 typval_T rettv;
546 bool mustfree = false;
547 switch (dict.type) {
548 case kObjectTypeString: {
549 try_start();
550 if (eval0((char_u *)dict.data.string.data, &rettv, NULL, true) == FAIL) {
551 api_set_error(err, kErrorTypeException,
552 "Failed to evaluate dict expression");
553 }
554 if (try_end(err)) {
555 return rv;
556 }
557 // Evaluation of the string arg created a new dict or increased the
558 // refcount of a dict. Not necessary for a RPC dict.
559 mustfree = true;
560 break;
561 }
562 case kObjectTypeDictionary: {
563 if (!object_to_vim(dict, &rettv, err)) {
564 goto end;
565 }
566 break;
567 }
568 default: {
569 api_set_error(err, kErrorTypeValidation,
570 "dict argument type must be String or Dictionary");
571 return rv;
572 }
573 }
574 dict_T *self_dict = rettv.vval.v_dict;
575 if (rettv.v_type != VAR_DICT || !self_dict) {
576 api_set_error(err, kErrorTypeValidation, "dict not found");
577 goto end;
578 }
579
580 if (fn.data && fn.size > 0 && dict.type != kObjectTypeDictionary) {
581 dictitem_T *const di = tv_dict_find(self_dict, fn.data, (ptrdiff_t)fn.size);
582 if (di == NULL) {
583 api_set_error(err, kErrorTypeValidation, "Not found: %s", fn.data);
584 goto end;
585 }
586 if (di->di_tv.v_type == VAR_PARTIAL) {
587 api_set_error(err, kErrorTypeValidation,
588 "partial function not supported");
589 goto end;
590 }
591 if (di->di_tv.v_type != VAR_FUNC) {
592 api_set_error(err, kErrorTypeValidation, "Not a function: %s", fn.data);
593 goto end;
594 }
595 fn = (String) {
596 .data = (char *)di->di_tv.vval.v_string,
597 .size = strlen((char *)di->di_tv.vval.v_string),
598 };
599 }
600
601 if (!fn.data || fn.size < 1) {
602 api_set_error(err, kErrorTypeValidation, "Invalid (empty) function name");
603 goto end;
604 }
605
606 rv = _call_function(fn, args, self_dict, err);
607end:
608 if (mustfree) {
609 tv_clear(&rettv);
610 }
611
612 return rv;
613}
614
615/// Calculates the number of display cells occupied by `text`.
616/// <Tab> counts as one cell.
617///
618/// @param text Some text
619/// @param[out] err Error details, if any
620/// @return Number of cells
621Integer nvim_strwidth(String text, Error *err)
622 FUNC_API_SINCE(1)
623{
624 if (text.size > INT_MAX) {
625 api_set_error(err, kErrorTypeValidation, "String is too long");
626 return 0;
627 }
628
629 return (Integer)mb_string2cells((char_u *)text.data);
630}
631
632/// Gets the paths contained in 'runtimepath'.
633///
634/// @return List of paths
635ArrayOf(String) nvim_list_runtime_paths(void)
636 FUNC_API_SINCE(1)
637{
638 Array rv = ARRAY_DICT_INIT;
639 char_u *rtp = p_rtp;
640
641 if (*rtp == NUL) {
642 // No paths
643 return rv;
644 }
645
646 // Count the number of paths in rtp
647 while (*rtp != NUL) {
648 if (*rtp == ',') {
649 rv.size++;
650 }
651 rtp++;
652 }
653 rv.size++;
654
655 // Allocate memory for the copies
656 rv.items = xmalloc(sizeof(*rv.items) * rv.size);
657 // Reset the position
658 rtp = p_rtp;
659 // Start copying
660 for (size_t i = 0; i < rv.size; i++) {
661 rv.items[i].type = kObjectTypeString;
662 rv.items[i].data.string.data = xmalloc(MAXPATHL);
663 // Copy the path from 'runtimepath' to rv.items[i]
664 size_t length = copy_option_part(&rtp,
665 (char_u *)rv.items[i].data.string.data,
666 MAXPATHL,
667 ",");
668 rv.items[i].data.string.size = length;
669 }
670
671 return rv;
672}
673
674/// Changes the global working directory.
675///
676/// @param dir Directory path
677/// @param[out] err Error details, if any
678void nvim_set_current_dir(String dir, Error *err)
679 FUNC_API_SINCE(1)
680{
681 if (dir.size >= MAXPATHL) {
682 api_set_error(err, kErrorTypeValidation, "Directory name is too long");
683 return;
684 }
685
686 char string[MAXPATHL];
687 memcpy(string, dir.data, dir.size);
688 string[dir.size] = NUL;
689
690 try_start();
691
692 if (vim_chdir((char_u *)string)) {
693 if (!try_end(err)) {
694 api_set_error(err, kErrorTypeException, "Failed to change directory");
695 }
696 return;
697 }
698
699 post_chdir(kCdScopeGlobal, true);
700 try_end(err);
701}
702
703/// Gets the current line.
704///
705/// @param[out] err Error details, if any
706/// @return Current line string
707String nvim_get_current_line(Error *err)
708 FUNC_API_SINCE(1)
709{
710 return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
711}
712
713/// Sets the current line.
714///
715/// @param line Line contents
716/// @param[out] err Error details, if any
717void nvim_set_current_line(String line, Error *err)
718 FUNC_API_SINCE(1)
719{
720 buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
721}
722
723/// Deletes the current line.
724///
725/// @param[out] err Error details, if any
726void nvim_del_current_line(Error *err)
727 FUNC_API_SINCE(1)
728{
729 buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
730}
731
732/// Gets a global (g:) variable.
733///
734/// @param name Variable name
735/// @param[out] err Error details, if any
736/// @return Variable value
737Object nvim_get_var(String name, Error *err)
738 FUNC_API_SINCE(1)
739{
740 return dict_get_value(&globvardict, name, err);
741}
742
743/// Sets a global (g:) variable.
744///
745/// @param name Variable name
746/// @param value Variable value
747/// @param[out] err Error details, if any
748void nvim_set_var(String name, Object value, Error *err)
749 FUNC_API_SINCE(1)
750{
751 dict_set_var(&globvardict, name, value, false, false, err);
752}
753
754/// Removes a global (g:) variable.
755///
756/// @param name Variable name
757/// @param[out] err Error details, if any
758void nvim_del_var(String name, Error *err)
759 FUNC_API_SINCE(1)
760{
761 dict_set_var(&globvardict, name, NIL, true, false, err);
762}
763
764/// @deprecated
765/// @see nvim_set_var
766/// @warning May return nil if there was no previous value
767/// OR if previous value was `v:null`.
768/// @return Old value or nil if there was no previous value.
769Object vim_set_var(String name, Object value, Error *err)
770{
771 return dict_set_var(&globvardict, name, value, false, true, err);
772}
773
774/// @deprecated
775/// @see nvim_del_var
776Object vim_del_var(String name, Error *err)
777{
778 return dict_set_var(&globvardict, name, NIL, true, true, err);
779}
780
781/// Gets a v: variable.
782///
783/// @param name Variable name
784/// @param[out] err Error details, if any
785/// @return Variable value
786Object nvim_get_vvar(String name, Error *err)
787 FUNC_API_SINCE(1)
788{
789 return dict_get_value(&vimvardict, name, err);
790}
791
792/// Sets a v: variable, if it is not readonly.
793///
794/// @param name Variable name
795/// @param value Variable value
796/// @param[out] err Error details, if any
797void nvim_set_vvar(String name, Object value, Error *err)
798 FUNC_API_SINCE(6)
799{
800 dict_set_var(&vimvardict, name, value, false, false, err);
801}
802
803/// Gets an option value string.
804///
805/// @param name Option name
806/// @param[out] err Error details, if any
807/// @return Option value (global)
808Object nvim_get_option(String name, Error *err)
809 FUNC_API_SINCE(1)
810{
811 return get_option_from(NULL, SREQ_GLOBAL, name, err);
812}
813
814/// Sets an option value.
815///
816/// @param channel_id
817/// @param name Option name
818/// @param value New option value
819/// @param[out] err Error details, if any
820void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
821 FUNC_API_SINCE(1)
822{
823 set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
824}
825
826/// Writes a message to the Vim output buffer. Does not append "\n", the
827/// message is buffered (won't display) until a linefeed is written.
828///
829/// @param str Message
830void nvim_out_write(String str)
831 FUNC_API_SINCE(1)
832{
833 write_msg(str, false);
834}
835
836/// Writes a message to the Vim error buffer. Does not append "\n", the
837/// message is buffered (won't display) until a linefeed is written.
838///
839/// @param str Message
840void nvim_err_write(String str)
841 FUNC_API_SINCE(1)
842{
843 write_msg(str, true);
844}
845
846/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
847/// flushed (and displayed).
848///
849/// @param str Message
850/// @see nvim_err_write()
851void nvim_err_writeln(String str)
852 FUNC_API_SINCE(1)
853{
854 nvim_err_write(str);
855 nvim_err_write((String) { .data = "\n", .size = 1 });
856}
857
858/// Gets the current list of buffer handles
859///
860/// Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
861/// Use |nvim_buf_is_loaded()| to check if a buffer is loaded.
862///
863/// @return List of buffer handles
864ArrayOf(Buffer) nvim_list_bufs(void)
865 FUNC_API_SINCE(1)
866{
867 Array rv = ARRAY_DICT_INIT;
868
869 FOR_ALL_BUFFERS(b) {
870 rv.size++;
871 }
872
873 rv.items = xmalloc(sizeof(Object) * rv.size);
874 size_t i = 0;
875
876 FOR_ALL_BUFFERS(b) {
877 rv.items[i++] = BUFFER_OBJ(b->handle);
878 }
879
880 return rv;
881}
882
883/// Gets the current buffer.
884///
885/// @return Buffer handle
886Buffer nvim_get_current_buf(void)
887 FUNC_API_SINCE(1)
888{
889 return curbuf->handle;
890}
891
892/// Sets the current buffer.
893///
894/// @param buffer Buffer handle
895/// @param[out] err Error details, if any
896void nvim_set_current_buf(Buffer buffer, Error *err)
897 FUNC_API_SINCE(1)
898{
899 buf_T *buf = find_buffer_by_handle(buffer, err);
900
901 if (!buf) {
902 return;
903 }
904
905 try_start();
906 int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
907 if (!try_end(err) && result == FAIL) {
908 api_set_error(err,
909 kErrorTypeException,
910 "Failed to switch to buffer %d",
911 buffer);
912 }
913}
914
915/// Gets the current list of window handles.
916///
917/// @return List of window handles
918ArrayOf(Window) nvim_list_wins(void)
919 FUNC_API_SINCE(1)
920{
921 Array rv = ARRAY_DICT_INIT;
922
923 FOR_ALL_TAB_WINDOWS(tp, wp) {
924 rv.size++;
925 }
926
927 rv.items = xmalloc(sizeof(Object) * rv.size);
928 size_t i = 0;
929
930 FOR_ALL_TAB_WINDOWS(tp, wp) {
931 rv.items[i++] = WINDOW_OBJ(wp->handle);
932 }
933
934 return rv;
935}
936
937/// Gets the current window.
938///
939/// @return Window handle
940Window nvim_get_current_win(void)
941 FUNC_API_SINCE(1)
942{
943 return curwin->handle;
944}
945
946/// Sets the current window.
947///
948/// @param window Window handle
949/// @param[out] err Error details, if any
950void nvim_set_current_win(Window window, Error *err)
951 FUNC_API_SINCE(1)
952{
953 win_T *win = find_window_by_handle(window, err);
954
955 if (!win) {
956 return;
957 }
958
959 try_start();
960 goto_tabpage_win(win_find_tabpage(win), win);
961 if (!try_end(err) && win != curwin) {
962 api_set_error(err,
963 kErrorTypeException,
964 "Failed to switch to window %d",
965 window);
966 }
967}
968
969/// Creates a new, empty, unnamed buffer.
970///
971/// @param listed Sets 'buflisted'
972/// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
973/// (always 'nomodified')
974/// @param[out] err Error details, if any
975/// @return Buffer handle, or 0 on error
976///
977/// @see buf_open_scratch
978Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
979 FUNC_API_SINCE(6)
980{
981 try_start();
982 buf_T *buf = buflist_new(NULL, NULL, (linenr_T)0,
983 BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
984 try_end(err);
985 if (buf == NULL) {
986 goto fail;
987 }
988
989 // Open the memline for the buffer. This will avoid spurious autocmds when
990 // a later nvim_buf_set_lines call would have needed to "open" the buffer.
991 try_start();
992 block_autocmds();
993 int status = ml_open(buf);
994 unblock_autocmds();
995 try_end(err);
996 if (status == FAIL) {
997 goto fail;
998 }
999
1000 if (scratch) {
1001 aco_save_T aco;
1002 aucmd_prepbuf(&aco, buf);
1003 set_option_value("bh", 0L, "hide", OPT_LOCAL);
1004 set_option_value("bt", 0L, "nofile", OPT_LOCAL);
1005 set_option_value("swf", 0L, NULL, OPT_LOCAL);
1006 aucmd_restbuf(&aco);
1007 }
1008 return buf->b_fnum;
1009
1010fail:
1011 if (!ERROR_SET(err)) {
1012 api_set_error(err, kErrorTypeException, "Failed to create buffer");
1013 }
1014 return 0;
1015}
1016
1017/// Open a new window.
1018///
1019/// Currently this is used to open floating and external windows.
1020/// Floats are windows that are drawn above the split layout, at some anchor
1021/// position in some other window. Floats can be drawn internally or by external
1022/// GUI with the |ui-multigrid| extension. External windows are only supported
1023/// with multigrid GUIs, and are displayed as separate top-level windows.
1024///
1025/// For a general overview of floats, see |api-floatwin|.
1026///
1027/// Exactly one of `external` and `relative` must be specified. The `width` and
1028/// `height` of the new window must be specified.
1029///
1030/// With relative=editor (row=0,col=0) refers to the top-left corner of the
1031/// screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right
1032/// corner. Fractional values are allowed, but the builtin implementation
1033/// (used by non-multigrid UIs) will always round down to nearest integer.
1034///
1035/// Out-of-bounds values, and configurations that make the float not fit inside
1036/// the main editor, are allowed. The builtin implementation truncates values
1037/// so floats are fully within the main screen grid. External GUIs
1038/// could let floats hover outside of the main window like a tooltip, but
1039/// this should not be used to specify arbitrary WM screen positions.
1040///
1041/// Example (Lua): window-relative float
1042/// <pre>
1043/// vim.api.nvim_open_win(0, false,
1044/// {relative='win', row=3, col=3, width=12, height=3})
1045/// </pre>
1046///
1047/// Example (Lua): buffer-relative float (travels as buffer is scrolled)
1048/// <pre>
1049/// vim.api.nvim_open_win(0, false,
1050/// {relative='win', width=12, height=3, bufpos={100,10}})
1051/// </pre>
1052///
1053/// @param buffer Buffer to display, or 0 for current buffer
1054/// @param enter Enter the window (make it the current window)
1055/// @param config Map defining the window configuration. Keys:
1056/// - `relative`: Sets the window layout to "floating", placed at (row,col)
1057/// coordinates relative to one of:
1058/// - "editor" The global editor grid
1059/// - "win" Window given by the `win` field, or current window by
1060/// default.
1061/// - "cursor" Cursor position in current window.
1062/// - `win`: |window-ID| for relative="win".
1063/// - `anchor`: Decides which corner of the float to place at (row,col):
1064/// - "NW" northwest (default)
1065/// - "NE" northeast
1066/// - "SW" southwest
1067/// - "SE" southeast
1068/// - `width`: Window width (in character cells). Minimum of 1.
1069/// - `height`: Window height (in character cells). Minimum of 1.
1070/// - `bufpos`: Places float relative to buffer text (only when
1071/// relative="win"). Takes a tuple of zero-indexed [line, column].
1072/// `row` and `col` if given are applied relative to this
1073/// position, else they default to `row=1` and `col=0`
1074/// (thus like a tooltip near the buffer text).
1075/// - `row`: Row position in units of "screen cell height", may be fractional.
1076/// - `col`: Column position in units of "screen cell width", may be
1077/// fractional.
1078/// - `focusable`: Enable focus by user actions (wincmds, mouse events).
1079/// Defaults to true. Non-focusable windows can be entered by
1080/// |nvim_set_current_win()|.
1081/// - `external`: GUI should display the window as an external
1082/// top-level window. Currently accepts no other positioning
1083/// configuration together with this.
1084/// - `style`: Configure the appearance of the window. Currently only takes
1085/// one non-empty value:
1086/// - "minimal" Nvim will display the window with many UI options
1087/// disabled. This is useful when displaying a temporary
1088/// float where the text should not be edited. Disables
1089/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn',
1090/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn'
1091/// is changed to `auto`. The end-of-buffer region is hidden
1092/// by setting `eob` flag of 'fillchars' to a space char,
1093/// and clearing the |EndOfBuffer| region in 'winhighlight'.
1094/// @param[out] err Error details, if any
1095///
1096/// @return Window handle, or 0 on error
1097Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
1098 Error *err)
1099 FUNC_API_SINCE(6)
1100{
1101 FloatConfig fconfig = FLOAT_CONFIG_INIT;
1102 if (!parse_float_config(config, &fconfig, false, err)) {
1103 return 0;
1104 }
1105 win_T *wp = win_new_float(NULL, fconfig, err);
1106 if (!wp) {
1107 return 0;
1108 }
1109 if (enter) {
1110 win_enter(wp, false);
1111 }
1112 if (buffer > 0) {
1113 nvim_win_set_buf(wp->handle, buffer, err);
1114 }
1115
1116 if (fconfig.style == kWinStyleMinimal) {
1117 win_set_minimal_style(wp);
1118 didset_window_options(wp);
1119 }
1120 return wp->handle;
1121}
1122
1123/// Gets the current list of tabpage handles.
1124///
1125/// @return List of tabpage handles
1126ArrayOf(Tabpage) nvim_list_tabpages(void)
1127 FUNC_API_SINCE(1)
1128{
1129 Array rv = ARRAY_DICT_INIT;
1130
1131 FOR_ALL_TABS(tp) {
1132 rv.size++;
1133 }
1134
1135 rv.items = xmalloc(sizeof(Object) * rv.size);
1136 size_t i = 0;
1137
1138 FOR_ALL_TABS(tp) {
1139 rv.items[i++] = TABPAGE_OBJ(tp->handle);
1140 }
1141
1142 return rv;
1143}
1144
1145/// Gets the current tabpage.
1146///
1147/// @return Tabpage handle
1148Tabpage nvim_get_current_tabpage(void)
1149 FUNC_API_SINCE(1)
1150{
1151 return curtab->handle;
1152}
1153
1154/// Sets the current tabpage.
1155///
1156/// @param tabpage Tabpage handle
1157/// @param[out] err Error details, if any
1158void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
1159 FUNC_API_SINCE(1)
1160{
1161 tabpage_T *tp = find_tab_by_handle(tabpage, err);
1162
1163 if (!tp) {
1164 return;
1165 }
1166
1167 try_start();
1168 goto_tabpage_tp(tp, true, true);
1169 if (!try_end(err) && tp != curtab) {
1170 api_set_error(err,
1171 kErrorTypeException,
1172 "Failed to switch to tabpage %d",
1173 tabpage);
1174 }
1175}
1176
1177/// Creates a new namespace, or gets an existing one.
1178///
1179/// Namespaces are used for buffer highlights and virtual text, see
1180/// |nvim_buf_add_highlight()| and |nvim_buf_set_virtual_text()|.
1181///
1182/// Namespaces can be named or anonymous. If `name` matches an existing
1183/// namespace, the associated id is returned. If `name` is an empty string
1184/// a new, anonymous namespace is created.
1185///
1186/// @param name Namespace name or empty string
1187/// @return Namespace id
1188Integer nvim_create_namespace(String name)
1189 FUNC_API_SINCE(5)
1190{
1191 handle_T id = map_get(String, handle_T)(namespace_ids, name);
1192 if (id > 0) {
1193 return id;
1194 }
1195 id = next_namespace_id++;
1196 if (name.size > 0) {
1197 String name_alloc = copy_string(name);
1198 map_put(String, handle_T)(namespace_ids, name_alloc, id);
1199 }
1200 return (Integer)id;
1201}
1202
1203/// Gets existing, non-anonymous namespaces.
1204///
1205/// @return dict that maps from names to namespace ids.
1206Dictionary nvim_get_namespaces(void)
1207 FUNC_API_SINCE(5)
1208{
1209 Dictionary retval = ARRAY_DICT_INIT;
1210 String name;
1211 handle_T id;
1212
1213 map_foreach(namespace_ids, name, id, {
1214 PUT(retval, name.data, INTEGER_OBJ(id));
1215 })
1216
1217 return retval;
1218}
1219
1220/// Pastes at cursor, in any mode.
1221///
1222/// Invokes the `vim.paste` handler, which handles each mode appropriately.
1223/// Sets redo/undo. Faster than |nvim_input()|. Lines break at LF ("\n").
1224///
1225/// Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err`
1226/// but do not affect the return value (which is strictly decided by
1227/// `vim.paste()`). On error, subsequent calls are ignored ("drained") until
1228/// the next paste is initiated (phase 1 or -1).
1229///
1230/// @param data Multiline input. May be binary (containing NUL bytes).
1231/// @param crlf Also break lines at CR and CRLF.
1232/// @param phase -1: paste in a single call (i.e. without streaming).
1233/// To "stream" a paste, call `nvim_paste` sequentially with
1234/// these `phase` values:
1235/// - 1: starts the paste (exactly once)
1236/// - 2: continues the paste (zero or more times)
1237/// - 3: ends the paste (exactly once)
1238/// @param[out] err Error details, if any
1239/// @return
1240/// - true: Client may continue pasting.
1241/// - false: Client must cancel the paste.
1242Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
1243 FUNC_API_SINCE(6)
1244{
1245 static bool draining = false;
1246 bool cancel = false;
1247
1248 if (phase < -1 || phase > 3) {
1249 api_set_error(err, kErrorTypeValidation, "Invalid phase: %"PRId64, phase);
1250 return false;
1251 }
1252 Array args = ARRAY_DICT_INIT;
1253 Object rv = OBJECT_INIT;
1254 if (phase == -1 || phase == 1) { // Start of paste-stream.
1255 draining = false;
1256 } else if (draining) {
1257 // Skip remaining chunks. Report error only once per "stream".
1258 goto theend;
1259 }
1260 Array lines = string_to_array(data, crlf);
1261 ADD(args, ARRAY_OBJ(lines));
1262 ADD(args, INTEGER_OBJ(phase));
1263 rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args,
1264 err);
1265 if (ERROR_SET(err)) {
1266 draining = true;
1267 goto theend;
1268 }
1269 if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 1)) {
1270 ResetRedobuff();
1271 AppendCharToRedobuff('a'); // Dot-repeat.
1272 }
1273 // vim.paste() decides if client should cancel. Errors do NOT cancel: we
1274 // want to drain remaining chunks (rather than divert them to main input).
1275 cancel = (rv.type == kObjectTypeBoolean && !rv.data.boolean);
1276 if (!cancel && !(State & CMDLINE)) { // Dot-repeat.
1277 for (size_t i = 0; i < lines.size; i++) {
1278 String s = lines.items[i].data.string;
1279 assert(data.size <= INT_MAX);
1280 AppendToRedobuffLit((char_u *)s.data, (int)s.size);
1281 // readfile()-style: "\n" is indicated by presence of N+1 item.
1282 if (i + 1 < lines.size) {
1283 AppendCharToRedobuff(NL);
1284 }
1285 }
1286 }
1287 if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 3)) {
1288 AppendCharToRedobuff(ESC); // Dot-repeat.
1289 }
1290theend:
1291 api_free_object(rv);
1292 api_free_array(args);
1293 if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
1294 draining = false;
1295 }
1296
1297 return !cancel;
1298}
1299
1300/// Puts text at cursor, in any mode.
1301///
1302/// Compare |:put| and |p| which are always linewise.
1303///
1304/// @param lines |readfile()|-style list of lines. |channel-lines|
1305/// @param type Edit behavior: any |getregtype()| result, or:
1306/// - "b" |blockwise-visual| mode (may include width, e.g. "b3")
1307/// - "c" |characterwise| mode
1308/// - "l" |linewise| mode
1309/// - "" guess by contents, see |setreg()|
1310/// @param after Insert after cursor (like |p|), or before (like |P|).
1311/// @param follow Place cursor at end of inserted text.
1312/// @param[out] err Error details, if any
1313void nvim_put(ArrayOf(String) lines, String type, Boolean after,
1314 Boolean follow, Error *err)
1315 FUNC_API_SINCE(6)
1316{
1317 yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1);
1318 if (!prepare_yankreg_from_object(reg, type, lines.size)) {
1319 api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data);
1320 goto cleanup;
1321 }
1322 if (lines.size == 0) {
1323 goto cleanup; // Nothing to do.
1324 }
1325
1326 for (size_t i = 0; i < lines.size; i++) {
1327 if (lines.items[i].type != kObjectTypeString) {
1328 api_set_error(err, kErrorTypeValidation,
1329 "Invalid lines (expected array of strings)");
1330 goto cleanup;
1331 }
1332 String line = lines.items[i].data.string;
1333 reg->y_array[i] = (char_u *)xmemdupz(line.data, line.size);
1334 memchrsub(reg->y_array[i], NUL, NL, line.size);
1335 }
1336
1337 finish_yankreg_from_object(reg, false);
1338
1339 TRY_WRAP({
1340 try_start();
1341 bool VIsual_was_active = VIsual_active;
1342 msg_silent++; // Avoid "N more lines" message.
1343 do_put(0, reg, after ? FORWARD : BACKWARD, 1, follow ? PUT_CURSEND : 0);
1344 msg_silent--;
1345 VIsual_active = VIsual_was_active;
1346 try_end(err);
1347 });
1348
1349cleanup:
1350 free_register(reg);
1351 xfree(reg);
1352}
1353
1354/// Subscribes to event broadcasts.
1355///
1356/// @param channel_id Channel id (passed automatically by the dispatcher)
1357/// @param event Event type string
1358void nvim_subscribe(uint64_t channel_id, String event)
1359 FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
1360{
1361 size_t length = (event.size < METHOD_MAXLEN ? event.size : METHOD_MAXLEN);
1362 char e[METHOD_MAXLEN + 1];
1363 memcpy(e, event.data, length);
1364 e[length] = NUL;
1365 rpc_subscribe(channel_id, e);
1366}
1367
1368/// Unsubscribes to event broadcasts.
1369///
1370/// @param channel_id Channel id (passed automatically by the dispatcher)
1371/// @param event Event type string
1372void nvim_unsubscribe(uint64_t channel_id, String event)
1373 FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
1374{
1375 size_t length = (event.size < METHOD_MAXLEN ?
1376 event.size :
1377 METHOD_MAXLEN);
1378 char e[METHOD_MAXLEN + 1];
1379 memcpy(e, event.data, length);
1380 e[length] = NUL;
1381 rpc_unsubscribe(channel_id, e);
1382}
1383
1384/// Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or
1385/// "#rrggbb" hexadecimal string.
1386///
1387/// Example:
1388/// <pre>
1389/// :echo nvim_get_color_by_name("Pink")
1390/// :echo nvim_get_color_by_name("#cbcbcb")
1391/// </pre>
1392///
1393/// @param name Color name or "#rrggbb" string
1394/// @return 24-bit RGB value, or -1 for invalid argument.
1395Integer nvim_get_color_by_name(String name)
1396 FUNC_API_SINCE(1)
1397{
1398 return name_to_color((char_u *)name.data);
1399}
1400
1401/// Returns a map of color names and RGB values.
1402///
1403/// Keys are color names (e.g. "Aqua") and values are 24-bit RGB color values
1404/// (e.g. 65535).
1405///
1406/// @return Map of color names and RGB values.
1407Dictionary nvim_get_color_map(void)
1408 FUNC_API_SINCE(1)
1409{
1410 Dictionary colors = ARRAY_DICT_INIT;
1411
1412 for (int i = 0; color_name_table[i].name != NULL; i++) {
1413 PUT(colors, color_name_table[i].name,
1414 INTEGER_OBJ(color_name_table[i].color));
1415 }
1416 return colors;
1417}
1418
1419/// Gets a map of the current editor state.
1420///
1421/// @param types Context types ("regs", "jumps", "buflist", "gvars", ...)
1422/// to gather, or NIL for all (see |context-types|).
1423///
1424/// @return map of global |context|.
1425Dictionary nvim_get_context(Array types)
1426 FUNC_API_SINCE(6)
1427{
1428 int int_types = 0;
1429 if (types.size == 1 && types.items[0].type == kObjectTypeNil) {
1430 int_types = kCtxAll;
1431 } else {
1432 for (size_t i = 0; i < types.size; i++) {
1433 if (types.items[i].type == kObjectTypeString) {
1434 const char *const current = types.items[i].data.string.data;
1435 if (strequal(current, "regs")) {
1436 int_types |= kCtxRegs;
1437 } else if (strequal(current, "jumps")) {
1438 int_types |= kCtxJumps;
1439 } else if (strequal(current, "buflist")) {
1440 int_types |= kCtxBuflist;
1441 } else if (strequal(current, "gvars")) {
1442 int_types |= kCtxGVars;
1443 } else if (strequal(current, "sfuncs")) {
1444 int_types |= kCtxSFuncs;
1445 } else if (strequal(current, "funcs")) {
1446 int_types |= kCtxFuncs;
1447 }
1448 }
1449 }
1450 }
1451
1452 Context ctx = CONTEXT_INIT;
1453 ctx_save(&ctx, int_types);
1454 Dictionary dict = ctx_to_dict(&ctx);
1455 ctx_free(&ctx);
1456 return dict;
1457}
1458
1459/// Sets the current editor state from the given |context| map.
1460///
1461/// @param dict |Context| map.
1462Object nvim_load_context(Dictionary dict)
1463 FUNC_API_SINCE(6)
1464{
1465 Context ctx = CONTEXT_INIT;
1466
1467 int save_did_emsg = did_emsg;
1468 did_emsg = false;
1469
1470 ctx_from_dict(dict, &ctx);
1471 if (!did_emsg) {
1472 ctx_restore(&ctx, kCtxAll);
1473 }
1474
1475 ctx_free(&ctx);
1476
1477 did_emsg = save_did_emsg;
1478 return (Object)OBJECT_INIT;
1479}
1480
1481/// Gets the current mode. |mode()|
1482/// "blocking" is true if Nvim is waiting for input.
1483///
1484/// @returns Dictionary { "mode": String, "blocking": Boolean }
1485Dictionary nvim_get_mode(void)
1486 FUNC_API_SINCE(2) FUNC_API_FAST
1487{
1488 Dictionary rv = ARRAY_DICT_INIT;
1489 char *modestr = get_mode();
1490 bool blocked = input_blocking();
1491
1492 PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr)));
1493 PUT(rv, "blocking", BOOLEAN_OBJ(blocked));
1494
1495 return rv;
1496}
1497
1498/// Gets a list of global (non-buffer-local) |mapping| definitions.
1499///
1500/// @param mode Mode short-name ("n", "i", "v", ...)
1501/// @returns Array of maparg()-like dictionaries describing mappings.
1502/// The "buffer" key is always zero.
1503ArrayOf(Dictionary) nvim_get_keymap(String mode)
1504 FUNC_API_SINCE(3)
1505{
1506 return keymap_array(mode, NULL);
1507}
1508
1509/// Sets a global |mapping| for the given mode.
1510///
1511/// To set a buffer-local mapping, use |nvim_buf_set_keymap()|.
1512///
1513/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs}
1514/// or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
1515///
1516/// Example:
1517/// <pre>
1518/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
1519/// </pre>
1520///
1521/// is equivalent to:
1522/// <pre>
1523/// nmap <nowait> <Space><NL> <Nop>
1524/// </pre>
1525///
1526/// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
1527/// or "!" for |:map!|, or empty string for |:map|.
1528/// @param lhs Left-hand-side |{lhs}| of the mapping.
1529/// @param rhs Right-hand-side |{rhs}| of the mapping.
1530/// @param opts Optional parameters map. Accepts all |:map-arguments|
1531/// as keys excluding |<buffer>| but including |noremap|.
1532/// Values are Booleans. Unknown key is an error.
1533/// @param[out] err Error details, if any.
1534void nvim_set_keymap(String mode, String lhs, String rhs,
1535 Dictionary opts, Error *err)
1536 FUNC_API_SINCE(6)
1537{
1538 modify_keymap(-1, false, mode, lhs, rhs, opts, err);
1539}
1540
1541/// Unmaps a global |mapping| for the given mode.
1542///
1543/// To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|.
1544///
1545/// @see |nvim_set_keymap()|
1546void nvim_del_keymap(String mode, String lhs, Error *err)
1547 FUNC_API_SINCE(6)
1548{
1549 nvim_buf_del_keymap(-1, mode, lhs, err);
1550}
1551
1552/// Gets a map of global (non-buffer-local) Ex commands.
1553///
1554/// Currently only |user-commands| are supported, not builtin Ex commands.
1555///
1556/// @param opts Optional parameters. Currently only supports
1557/// {"builtin":false}
1558/// @param[out] err Error details, if any.
1559///
1560/// @returns Map of maps describing commands.
1561Dictionary nvim_get_commands(Dictionary opts, Error *err)
1562 FUNC_API_SINCE(4)
1563{
1564 return nvim_buf_get_commands(-1, opts, err);
1565}
1566
1567/// Returns a 2-tuple (Array), where item 0 is the current channel id and item
1568/// 1 is the |api-metadata| map (Dictionary).
1569///
1570/// @returns 2-tuple [{channel-id}, {api-metadata}]
1571Array nvim_get_api_info(uint64_t channel_id)
1572 FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY
1573{
1574 Array rv = ARRAY_DICT_INIT;
1575
1576 assert(channel_id <= INT64_MAX);
1577 ADD(rv, INTEGER_OBJ((int64_t)channel_id));
1578 ADD(rv, DICTIONARY_OBJ(api_metadata()));
1579
1580 return rv;
1581}
1582
1583/// Self-identifies the client.
1584///
1585/// The client/plugin/application should call this after connecting, to provide
1586/// hints about its identity and purpose, for debugging and orchestration.
1587///
1588/// Can be called more than once; the caller should merge old info if
1589/// appropriate. Example: library first identifies the channel, then a plugin
1590/// using that library later identifies itself.
1591///
1592/// @note "Something is better than nothing". You don't need to include all the
1593/// fields.
1594///
1595/// @param channel_id
1596/// @param name Short name for the connected client
1597/// @param version Dictionary describing the version, with these
1598/// (optional) keys:
1599/// - "major" major version (defaults to 0 if not set, for no release yet)
1600/// - "minor" minor version
1601/// - "patch" patch number
1602/// - "prerelease" string describing a prerelease, like "dev" or "beta1"
1603/// - "commit" hash or similar identifier of commit
1604/// @param type Must be one of the following values. Client libraries should
1605/// default to "remote" unless overridden by the user.
1606/// - "remote" remote client connected to Nvim.
1607/// - "ui" gui frontend
1608/// - "embedder" application using Nvim as a component (for example,
1609/// IDE/editor implementing a vim mode).
1610/// - "host" plugin host, typically started by nvim
1611/// - "plugin" single plugin, started by nvim
1612/// @param methods Builtin methods in the client. For a host, this does not
1613/// include plugin methods which will be discovered later.
1614/// The key should be the method name, the values are dicts with
1615/// these (optional) keys (more keys may be added in future
1616/// versions of Nvim, thus unknown keys are ignored. Clients
1617/// must only use keys defined in this or later versions of
1618/// Nvim):
1619/// - "async" if true, send as a notification. If false or unspecified,
1620/// use a blocking request
1621/// - "nargs" Number of arguments. Could be a single integer or an array
1622/// of two integers, minimum and maximum inclusive.
1623///
1624/// @param attributes Arbitrary string:string map of informal client properties.
1625/// Suggested keys:
1626/// - "website": Client homepage URL (e.g. GitHub repository)
1627/// - "license": License description ("Apache 2", "GPLv3", "MIT", …)
1628/// - "logo": URI or path to image, preferably small logo or icon.
1629/// .png or .svg format is preferred.
1630///
1631/// @param[out] err Error details, if any
1632void nvim_set_client_info(uint64_t channel_id, String name,
1633 Dictionary version, String type,
1634 Dictionary methods, Dictionary attributes,
1635 Error *err)
1636 FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
1637{
1638 Dictionary info = ARRAY_DICT_INIT;
1639 PUT(info, "name", copy_object(STRING_OBJ(name)));
1640
1641 version = copy_dictionary(version);
1642 bool has_major = false;
1643 for (size_t i = 0; i < version.size; i++) {
1644 if (strequal(version.items[i].key.data, "major")) {
1645 has_major = true;
1646 break;
1647 }
1648 }
1649 if (!has_major) {
1650 PUT(version, "major", INTEGER_OBJ(0));
1651 }
1652 PUT(info, "version", DICTIONARY_OBJ(version));
1653
1654 PUT(info, "type", copy_object(STRING_OBJ(type)));
1655 PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods)));
1656 PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes)));
1657
1658 rpc_set_client_info(channel_id, info);
1659}
1660
1661/// Get information about a channel.
1662///
1663/// @returns Dictionary describing a channel, with these keys:
1664/// - "stream" the stream underlying the channel
1665/// - "stdio" stdin and stdout of this Nvim instance
1666/// - "stderr" stderr of this Nvim instance
1667/// - "socket" TCP/IP socket or named pipe
1668/// - "job" job with communication over its stdio
1669/// - "mode" how data received on the channel is interpreted
1670/// - "bytes" send and receive raw bytes
1671/// - "terminal" a |terminal| instance interprets ASCII sequences
1672/// - "rpc" |RPC| communication on the channel is active
1673/// - "pty" Name of pseudoterminal, if one is used (optional).
1674/// On a POSIX system, this will be a device path like
1675/// /dev/pts/1. Even if the name is unknown, the key will
1676/// still be present to indicate a pty is used. This is
1677/// currently the case when using winpty on windows.
1678/// - "buffer" buffer with connected |terminal| instance (optional)
1679/// - "client" information about the client on the other end of the
1680/// RPC channel, if it has added it using
1681/// |nvim_set_client_info()|. (optional)
1682///
1683Dictionary nvim_get_chan_info(Integer chan, Error *err)
1684 FUNC_API_SINCE(4)
1685{
1686 if (chan < 0) {
1687 return (Dictionary)ARRAY_DICT_INIT;
1688 }
1689 return channel_info((uint64_t)chan);
1690}
1691
1692/// Get information about all open channels.
1693///
1694/// @returns Array of Dictionaries, each describing a channel with
1695/// the format specified at |nvim_get_chan_info()|.
1696Array nvim_list_chans(void)
1697 FUNC_API_SINCE(4)
1698{
1699 return channel_all_info();
1700}
1701
1702/// Calls many API methods atomically.
1703///
1704/// This has two main usages:
1705/// 1. To perform several requests from an async context atomically, i.e.
1706/// without interleaving redraws, RPC requests from other clients, or user
1707/// interactions (however API methods may trigger autocommands or event
1708/// processing which have such side-effects, e.g. |:sleep| may wake timers).
1709/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
1710///
1711/// @param channel_id
1712/// @param calls an array of calls, where each call is described by an array
1713/// with two elements: the request name, and an array of arguments.
1714/// @param[out] err Validation error details (malformed `calls` parameter),
1715/// if any. Errors from batched calls are given in the return value.
1716///
1717/// @return Array of two elements. The first is an array of return
1718/// values. The second is NIL if all calls succeeded. If a call resulted in
1719/// an error, it is a three-element array with the zero-based index of the call
1720/// which resulted in an error, the error type and the error message. If an
1721/// error occurred, the values from all preceding calls will still be returned.
1722Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
1723 FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
1724{
1725 Array rv = ARRAY_DICT_INIT;
1726 Array results = ARRAY_DICT_INIT;
1727 Error nested_error = ERROR_INIT;
1728
1729 size_t i; // also used for freeing the variables
1730 for (i = 0; i < calls.size; i++) {
1731 if (calls.items[i].type != kObjectTypeArray) {
1732 api_set_error(err,
1733 kErrorTypeValidation,
1734 "Items in calls array must be arrays");
1735 goto validation_error;
1736 }
1737 Array call = calls.items[i].data.array;
1738 if (call.size != 2) {
1739 api_set_error(err,
1740 kErrorTypeValidation,
1741 "Items in calls array must be arrays of size 2");
1742 goto validation_error;
1743 }
1744
1745 if (call.items[0].type != kObjectTypeString) {
1746 api_set_error(err,
1747 kErrorTypeValidation,
1748 "Name must be String");
1749 goto validation_error;
1750 }
1751 String name = call.items[0].data.string;
1752
1753 if (call.items[1].type != kObjectTypeArray) {
1754 api_set_error(err,
1755 kErrorTypeValidation,
1756 "Args must be Array");
1757 goto validation_error;
1758 }
1759 Array args = call.items[1].data.array;
1760
1761 MsgpackRpcRequestHandler handler =
1762 msgpack_rpc_get_handler_for(name.data,
1763 name.size,
1764 &nested_error);
1765
1766 if (ERROR_SET(&nested_error)) {
1767 break;
1768 }
1769 Object result = handler.fn(channel_id, args, &nested_error);
1770 if (ERROR_SET(&nested_error)) {
1771 // error handled after loop
1772 break;
1773 }
1774
1775 ADD(results, result);
1776 }
1777
1778 ADD(rv, ARRAY_OBJ(results));
1779 if (ERROR_SET(&nested_error)) {
1780 Array errval = ARRAY_DICT_INIT;
1781 ADD(errval, INTEGER_OBJ((Integer)i));
1782 ADD(errval, INTEGER_OBJ(nested_error.type));
1783 ADD(errval, STRING_OBJ(cstr_to_string(nested_error.msg)));
1784 ADD(rv, ARRAY_OBJ(errval));
1785 } else {
1786 ADD(rv, NIL);
1787 }
1788 goto theend;
1789
1790validation_error:
1791 api_free_array(results);
1792theend:
1793 api_clear_error(&nested_error);
1794 return rv;
1795}
1796
1797typedef struct {
1798 ExprASTNode **node_p;
1799 Object *ret_node_p;
1800} ExprASTConvStackItem;
1801
1802/// @cond DOXYGEN_NOT_A_FUNCTION
1803typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
1804/// @endcond
1805
1806/// Parse a VimL expression.
1807///
1808/// @param[in] expr Expression to parse. Always treated as a single line.
1809/// @param[in] flags Flags:
1810/// - "m" if multiple expressions in a row are allowed (only
1811/// the first one will be parsed),
1812/// - "E" if EOC tokens are not allowed (determines whether
1813/// they will stop parsing process or be recognized as an
1814/// operator/space, though also yielding an error).
1815/// - "l" when needing to start parsing with lvalues for
1816/// ":let" or ":for".
1817/// Common flag sets:
1818/// - "m" to parse like for ":echo".
1819/// - "E" to parse like for "<C-r>=".
1820/// - empty string for ":call".
1821/// - "lm" to parse for ":let".
1822/// @param[in] highlight If true, return value will also include "highlight"
1823/// key containing array of 4-tuples (arrays) (Integer,
1824/// Integer, Integer, String), where first three numbers
1825/// define the highlighted region and represent line,
1826/// starting column and ending column (latter exclusive:
1827/// one should highlight region [start_col, end_col)).
1828///
1829/// @return
1830/// - AST: top-level dictionary with these keys:
1831/// - "error": Dictionary with error, present only if parser saw some
1832/// error. Contains the following keys:
1833/// - "message": String, error message in printf format, translated.
1834/// Must contain exactly one "%.*s".
1835/// - "arg": String, error message argument.
1836/// - "len": Amount of bytes successfully parsed. With flags equal to ""
1837/// that should be equal to the length of expr string.
1838/// (“Sucessfully parsed” here means “participated in AST
1839/// creation”, not “till the first error”.)
1840/// - "ast": AST, either nil or a dictionary with these keys:
1841/// - "type": node type, one of the value names from ExprASTNodeType
1842/// stringified without "kExprNode" prefix.
1843/// - "start": a pair [line, column] describing where node is "started"
1844/// where "line" is always 0 (will not be 0 if you will be
1845/// using nvim_parse_viml() on e.g. ":let", but that is not
1846/// present yet). Both elements are Integers.
1847/// - "len": “length” of the node. This and "start" are there for
1848/// debugging purposes primary (debugging parser and providing
1849/// debug information).
1850/// - "children": a list of nodes described in top/"ast". There always
1851/// is zero, one or two children, key will not be present
1852/// if node has no children. Maximum number of children
1853/// may be found in node_maxchildren array.
1854/// - Local values (present only for certain nodes):
1855/// - "scope": a single Integer, specifies scope for "Option" and
1856/// "PlainIdentifier" nodes. For "Option" it is one of
1857/// ExprOptScope values, for "PlainIdentifier" it is one of
1858/// ExprVarScope values.
1859/// - "ident": identifier (without scope, if any), present for "Option",
1860/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
1861/// - "name": Integer, register name (one character) or -1. Only present
1862/// for "Register" nodes.
1863/// - "cmp_type": String, comparison type, one of the value names from
1864/// ExprComparisonType, stringified without "kExprCmp"
1865/// prefix. Only present for "Comparison" nodes.
1866/// - "ccs_strategy": String, case comparison strategy, one of the
1867/// value names from ExprCaseCompareStrategy,
1868/// stringified without "kCCStrategy" prefix. Only
1869/// present for "Comparison" nodes.
1870/// - "augmentation": String, augmentation type for "Assignment" nodes.
1871/// Is either an empty string, "Add", "Subtract" or
1872/// "Concat" for "=", "+=", "-=" or ".=" respectively.
1873/// - "invert": Boolean, true if result of comparison needs to be
1874/// inverted. Only present for "Comparison" nodes.
1875/// - "ivalue": Integer, integer value for "Integer" nodes.
1876/// - "fvalue": Float, floating-point value for "Float" nodes.
1877/// - "svalue": String, value for "SingleQuotedString" and
1878/// "DoubleQuotedString" nodes.
1879/// @param[out] err Error details, if any
1880Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
1881 Error *err)
1882 FUNC_API_SINCE(4) FUNC_API_FAST
1883{
1884 int pflags = 0;
1885 for (size_t i = 0 ; i < flags.size ; i++) {
1886 switch (flags.data[i]) {
1887 case 'm': { pflags |= kExprFlagsMulti; break; }
1888 case 'E': { pflags |= kExprFlagsDisallowEOC; break; }
1889 case 'l': { pflags |= kExprFlagsParseLet; break; }
1890 case NUL: {
1891 api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
1892 (unsigned)flags.data[i]);
1893 return (Dictionary)ARRAY_DICT_INIT;
1894 }
1895 default: {
1896 api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)",
1897 flags.data[i], (unsigned)flags.data[i]);
1898 return (Dictionary)ARRAY_DICT_INIT;
1899 }
1900 }
1901 }
1902 ParserLine plines[] = {
1903 {
1904 .data = expr.data,
1905 .size = expr.size,
1906 .allocated = false,
1907 },
1908 { NULL, 0, false },
1909 };
1910 ParserLine *plines_p = plines;
1911 ParserHighlight colors;
1912 kvi_init(colors);
1913 ParserHighlight *const colors_p = (highlight ? &colors : NULL);
1914 ParserState pstate;
1915 viml_parser_init(
1916 &pstate, parser_simple_get_line, &plines_p, colors_p);
1917 ExprAST east = viml_pexpr_parse(&pstate, pflags);
1918
1919 const size_t ret_size = (
1920 2 // "ast", "len"
1921 + (size_t)(east.err.msg != NULL) // "error"
1922 + (size_t)highlight // "highlight"
1923 + 0);
1924 Dictionary ret = {
1925 .items = xmalloc(ret_size * sizeof(ret.items[0])),
1926 .size = 0,
1927 .capacity = ret_size,
1928 };
1929 ret.items[ret.size++] = (KeyValuePair) {
1930 .key = STATIC_CSTR_TO_STRING("ast"),
1931 .value = NIL,
1932 };
1933 ret.items[ret.size++] = (KeyValuePair) {
1934 .key = STATIC_CSTR_TO_STRING("len"),
1935 .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
1936 ? plines[0].size
1937 : pstate.pos.col)),
1938 };
1939 if (east.err.msg != NULL) {
1940 Dictionary err_dict = {
1941 .items = xmalloc(2 * sizeof(err_dict.items[0])),
1942 .size = 2,
1943 .capacity = 2,
1944 };
1945 err_dict.items[0] = (KeyValuePair) {
1946 .key = STATIC_CSTR_TO_STRING("message"),
1947 .value = STRING_OBJ(cstr_to_string(east.err.msg)),
1948 };
1949 if (east.err.arg == NULL) {
1950 err_dict.items[1] = (KeyValuePair) {
1951 .key = STATIC_CSTR_TO_STRING("arg"),
1952 .value = STRING_OBJ(STRING_INIT),
1953 };
1954 } else {
1955 err_dict.items[1] = (KeyValuePair) {
1956 .key = STATIC_CSTR_TO_STRING("arg"),
1957 .value = STRING_OBJ(((String) {
1958 .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len),
1959 .size = (size_t)east.err.arg_len,
1960 })),
1961 };
1962 }
1963 ret.items[ret.size++] = (KeyValuePair) {
1964 .key = STATIC_CSTR_TO_STRING("error"),
1965 .value = DICTIONARY_OBJ(err_dict),
1966 };
1967 }
1968 if (highlight) {
1969 Array hl = (Array) {
1970 .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])),
1971 .capacity = kv_size(colors),
1972 .size = kv_size(colors),
1973 };
1974 for (size_t i = 0 ; i < kv_size(colors) ; i++) {
1975 const ParserHighlightChunk chunk = kv_A(colors, i);
1976 Array chunk_arr = (Array) {
1977 .items = xmalloc(4 * sizeof(chunk_arr.items[0])),
1978 .capacity = 4,
1979 .size = 4,
1980 };
1981 chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line);
1982 chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col);
1983 chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col);
1984 chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group));
1985 hl.items[i] = ARRAY_OBJ(chunk_arr);
1986 }
1987 ret.items[ret.size++] = (KeyValuePair) {
1988 .key = STATIC_CSTR_TO_STRING("highlight"),
1989 .value = ARRAY_OBJ(hl),
1990 };
1991 }
1992 kvi_destroy(colors);
1993
1994 // Walk over the AST, freeing nodes in process.
1995 ExprASTConvStack ast_conv_stack;
1996 kvi_init(ast_conv_stack);
1997 kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
1998 .node_p = &east.root,
1999 .ret_node_p = &ret.items[0].value,
2000 }));
2001 while (kv_size(ast_conv_stack)) {
2002 ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
2003 ExprASTNode *const node = *cur_item.node_p;
2004 if (node == NULL) {
2005 assert(kv_size(ast_conv_stack) == 1);
2006 kv_drop(ast_conv_stack, 1);
2007 } else {
2008 if (cur_item.ret_node_p->type == kObjectTypeNil) {
2009 const size_t ret_node_items_size = (size_t)(
2010 3 // "type", "start" and "len"
2011 + (node->children != NULL) // "children"
2012 + (node->type == kExprNodeOption
2013 || node->type == kExprNodePlainIdentifier) // "scope"
2014 + (node->type == kExprNodeOption
2015 || node->type == kExprNodePlainIdentifier
2016 || node->type == kExprNodePlainKey
2017 || node->type == kExprNodeEnvironment) // "ident"
2018 + (node->type == kExprNodeRegister) // "name"
2019 + (3 // "cmp_type", "ccs_strategy", "invert"
2020 * (node->type == kExprNodeComparison))
2021 + (node->type == kExprNodeInteger) // "ivalue"
2022 + (node->type == kExprNodeFloat) // "fvalue"
2023 + (node->type == kExprNodeDoubleQuotedString
2024 || node->type == kExprNodeSingleQuotedString) // "svalue"
2025 + (node->type == kExprNodeAssignment) // "augmentation"
2026 + 0);
2027 Dictionary ret_node = {
2028 .items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])),
2029 .capacity = ret_node_items_size,
2030 .size = 0,
2031 };
2032 *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node);
2033 }
2034 Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary;
2035 if (node->children != NULL) {
2036 const size_t num_children = 1 + (node->children->next != NULL);
2037 Array children_array = {
2038 .items = xmalloc(num_children * sizeof(children_array.items[0])),
2039 .capacity = num_children,
2040 .size = num_children,
2041 };
2042 for (size_t i = 0; i < num_children; i++) {
2043 children_array.items[i] = NIL;
2044 }
2045 ret_node->items[ret_node->size++] = (KeyValuePair) {
2046 .key = STATIC_CSTR_TO_STRING("children"),
2047 .value = ARRAY_OBJ(children_array),
2048 };
2049 kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
2050 .node_p = &node->children,
2051 .ret_node_p = &children_array.items[0],
2052 }));
2053 } else if (node->next != NULL) {
2054 kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
2055 .node_p = &node->next,
2056 .ret_node_p = cur_item.ret_node_p + 1,
2057 }));
2058 } else {
2059 kv_drop(ast_conv_stack, 1);
2060 ret_node->items[ret_node->size++] = (KeyValuePair) {
2061 .key = STATIC_CSTR_TO_STRING("type"),
2062 .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])),
2063 };
2064 Array start_array = {
2065 .items = xmalloc(2 * sizeof(start_array.items[0])),
2066 .capacity = 2,
2067 .size = 2,
2068 };
2069 start_array.items[0] = INTEGER_OBJ((Integer)node->start.line);
2070 start_array.items[1] = INTEGER_OBJ((Integer)node->start.col);
2071 ret_node->items[ret_node->size++] = (KeyValuePair) {
2072 .key = STATIC_CSTR_TO_STRING("start"),
2073 .value = ARRAY_OBJ(start_array),
2074 };
2075 ret_node->items[ret_node->size++] = (KeyValuePair) {
2076 .key = STATIC_CSTR_TO_STRING("len"),
2077 .value = INTEGER_OBJ((Integer)node->len),
2078 };
2079 switch (node->type) {
2080 case kExprNodeDoubleQuotedString:
2081 case kExprNodeSingleQuotedString: {
2082 ret_node->items[ret_node->size++] = (KeyValuePair) {
2083 .key = STATIC_CSTR_TO_STRING("svalue"),
2084 .value = STRING_OBJ(((String) {
2085 .data = node->data.str.value,
2086 .size = node->data.str.size,
2087 })),
2088 };
2089 break;
2090 }
2091 case kExprNodeOption: {
2092 ret_node->items[ret_node->size++] = (KeyValuePair) {
2093 .key = STATIC_CSTR_TO_STRING("scope"),
2094 .value = INTEGER_OBJ(node->data.opt.scope),
2095 };
2096 ret_node->items[ret_node->size++] = (KeyValuePair) {
2097 .key = STATIC_CSTR_TO_STRING("ident"),
2098 .value = STRING_OBJ(((String) {
2099 .data = xmemdupz(node->data.opt.ident,
2100 node->data.opt.ident_len),
2101 .size = node->data.opt.ident_len,
2102 })),
2103 };
2104 break;
2105 }
2106 case kExprNodePlainIdentifier: {
2107 ret_node->items[ret_node->size++] = (KeyValuePair) {
2108 .key = STATIC_CSTR_TO_STRING("scope"),
2109 .value = INTEGER_OBJ(node->data.var.scope),
2110 };
2111 ret_node->items[ret_node->size++] = (KeyValuePair) {
2112 .key = STATIC_CSTR_TO_STRING("ident"),
2113 .value = STRING_OBJ(((String) {
2114 .data = xmemdupz(node->data.var.ident,
2115 node->data.var.ident_len),
2116 .size = node->data.var.ident_len,
2117 })),
2118 };
2119 break;
2120 }
2121 case kExprNodePlainKey: {
2122 ret_node->items[ret_node->size++] = (KeyValuePair) {
2123 .key = STATIC_CSTR_TO_STRING("ident"),
2124 .value = STRING_OBJ(((String) {
2125 .data = xmemdupz(node->data.var.ident,
2126 node->data.var.ident_len),
2127 .size = node->data.var.ident_len,
2128 })),
2129 };
2130 break;
2131 }
2132 case kExprNodeEnvironment: {
2133 ret_node->items[ret_node->size++] = (KeyValuePair) {
2134 .key = STATIC_CSTR_TO_STRING("ident"),
2135 .value = STRING_OBJ(((String) {
2136 .data = xmemdupz(node->data.env.ident,
2137 node->data.env.ident_len),
2138 .size = node->data.env.ident_len,
2139 })),
2140 };
2141 break;
2142 }
2143 case kExprNodeRegister: {
2144 ret_node->items[ret_node->size++] = (KeyValuePair) {
2145 .key = STATIC_CSTR_TO_STRING("name"),
2146 .value = INTEGER_OBJ(node->data.reg.name),
2147 };
2148 break;
2149 }
2150 case kExprNodeComparison: {
2151 ret_node->items[ret_node->size++] = (KeyValuePair) {
2152 .key = STATIC_CSTR_TO_STRING("cmp_type"),
2153 .value = STRING_OBJ(cstr_to_string(
2154 eltkn_cmp_type_tab[node->data.cmp.type])),
2155 };
2156 ret_node->items[ret_node->size++] = (KeyValuePair) {
2157 .key = STATIC_CSTR_TO_STRING("ccs_strategy"),
2158 .value = STRING_OBJ(cstr_to_string(
2159 ccs_tab[node->data.cmp.ccs])),
2160 };
2161 ret_node->items[ret_node->size++] = (KeyValuePair) {
2162 .key = STATIC_CSTR_TO_STRING("invert"),
2163 .value = BOOLEAN_OBJ(node->data.cmp.inv),
2164 };
2165 break;
2166 }
2167 case kExprNodeFloat: {
2168 ret_node->items[ret_node->size++] = (KeyValuePair) {
2169 .key = STATIC_CSTR_TO_STRING("fvalue"),
2170 .value = FLOAT_OBJ(node->data.flt.value),
2171 };
2172 break;
2173 }
2174 case kExprNodeInteger: {
2175 ret_node->items[ret_node->size++] = (KeyValuePair) {
2176 .key = STATIC_CSTR_TO_STRING("ivalue"),
2177 .value = INTEGER_OBJ((Integer)(
2178 node->data.num.value > API_INTEGER_MAX
2179 ? API_INTEGER_MAX
2180 : (Integer)node->data.num.value)),
2181 };
2182 break;
2183 }
2184 case kExprNodeAssignment: {
2185 const ExprAssignmentType asgn_type = node->data.ass.type;
2186 ret_node->items[ret_node->size++] = (KeyValuePair) {
2187 .key = STATIC_CSTR_TO_STRING("augmentation"),
2188 .value = STRING_OBJ(
2189 asgn_type == kExprAsgnPlain
2190 ? (String)STRING_INIT
2191 : cstr_to_string(expr_asgn_type_tab[asgn_type])),
2192 };
2193 break;
2194 }
2195 case kExprNodeMissing:
2196 case kExprNodeOpMissing:
2197 case kExprNodeTernary:
2198 case kExprNodeTernaryValue:
2199 case kExprNodeSubscript:
2200 case kExprNodeListLiteral:
2201 case kExprNodeUnaryPlus:
2202 case kExprNodeBinaryPlus:
2203 case kExprNodeNested:
2204 case kExprNodeCall:
2205 case kExprNodeComplexIdentifier:
2206 case kExprNodeUnknownFigure:
2207 case kExprNodeLambda:
2208 case kExprNodeDictLiteral:
2209 case kExprNodeCurlyBracesIdentifier:
2210 case kExprNodeComma:
2211 case kExprNodeColon:
2212 case kExprNodeArrow:
2213 case kExprNodeConcat:
2214 case kExprNodeConcatOrSubscript:
2215 case kExprNodeOr:
2216 case kExprNodeAnd:
2217 case kExprNodeUnaryMinus:
2218 case kExprNodeBinaryMinus:
2219 case kExprNodeNot:
2220 case kExprNodeMultiplication:
2221 case kExprNodeDivision:
2222 case kExprNodeMod: {
2223 break;
2224 }
2225 }
2226 assert(cur_item.ret_node_p->data.dictionary.size
2227 == cur_item.ret_node_p->data.dictionary.capacity);
2228 xfree(*cur_item.node_p);
2229 *cur_item.node_p = NULL;
2230 }
2231 }
2232 }
2233 kvi_destroy(ast_conv_stack);
2234
2235 assert(ret.size == ret.capacity);
2236 // Should be a no-op actually, leaving it in case non-nodes will need to be
2237 // freed later.
2238 viml_pexpr_free_ast(east);
2239 viml_parser_destroy(&pstate);
2240 return ret;
2241}
2242
2243
2244/// Writes a message to vim output or error buffer. The string is split
2245/// and flushed after each newline. Incomplete lines are kept for writing
2246/// later.
2247///
2248/// @param message Message to write
2249/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
2250static void write_msg(String message, bool to_err)
2251{
2252 static size_t out_pos = 0, err_pos = 0;
2253 static char out_line_buf[LINE_BUFFER_SIZE], err_line_buf[LINE_BUFFER_SIZE];
2254
2255#define PUSH_CHAR(i, pos, line_buf, msg) \
2256 if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \
2257 line_buf[pos] = NUL; \
2258 msg((char_u *)line_buf); \
2259 pos = 0; \
2260 continue; \
2261 } \
2262 \
2263 line_buf[pos++] = message.data[i];
2264
2265 ++no_wait_return;
2266 for (uint32_t i = 0; i < message.size; i++) {
2267 if (to_err) {
2268 PUSH_CHAR(i, err_pos, err_line_buf, emsg);
2269 } else {
2270 PUSH_CHAR(i, out_pos, out_line_buf, msg);
2271 }
2272 }
2273 --no_wait_return;
2274 msg_end();
2275}
2276
2277// Functions used for testing purposes
2278
2279/// Returns object given as argument.
2280///
2281/// This API function is used for testing. One should not rely on its presence
2282/// in plugins.
2283///
2284/// @param[in] obj Object to return.
2285///
2286/// @return its argument.
2287Object nvim__id(Object obj)
2288{
2289 return copy_object(obj);
2290}
2291
2292/// Returns array given as argument.
2293///
2294/// This API function is used for testing. One should not rely on its presence
2295/// in plugins.
2296///
2297/// @param[in] arr Array to return.
2298///
2299/// @return its argument.
2300Array nvim__id_array(Array arr)
2301{
2302 return copy_object(ARRAY_OBJ(arr)).data.array;
2303}
2304
2305/// Returns dictionary given as argument.
2306///
2307/// This API function is used for testing. One should not rely on its presence
2308/// in plugins.
2309///
2310/// @param[in] dct Dictionary to return.
2311///
2312/// @return its argument.
2313Dictionary nvim__id_dictionary(Dictionary dct)
2314{
2315 return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
2316}
2317
2318/// Returns floating-point value given as argument.
2319///
2320/// This API function is used for testing. One should not rely on its presence
2321/// in plugins.
2322///
2323/// @param[in] flt Value to return.
2324///
2325/// @return its argument.
2326Float nvim__id_float(Float flt)
2327{
2328 return flt;
2329}
2330
2331/// Gets internal stats.
2332///
2333/// @return Map of various internal stats.
2334Dictionary nvim__stats(void)
2335{
2336 Dictionary rv = ARRAY_DICT_INIT;
2337 PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync));
2338 PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
2339 return rv;
2340}
2341
2342/// Gets a list of dictionaries representing attached UIs.
2343///
2344/// @return Array of UI dictionaries, each with these keys:
2345/// - "height" Requested height of the UI
2346/// - "width" Requested width of the UI
2347/// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
2348/// - "ext_..." Requested UI extensions, see |ui-option|
2349/// - "chan" Channel id of remote UI (not present for TUI)
2350Array nvim_list_uis(void)
2351 FUNC_API_SINCE(4)
2352{
2353 return ui_array();
2354}
2355
2356/// Gets the immediate children of process `pid`.
2357///
2358/// @return Array of child process ids, empty if process not found.
2359Array nvim_get_proc_children(Integer pid, Error *err)
2360 FUNC_API_SINCE(4)
2361{
2362 Array rvobj = ARRAY_DICT_INIT;
2363 int *proc_list = NULL;
2364
2365 if (pid <= 0 || pid > INT_MAX) {
2366 api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
2367 goto end;
2368 }
2369
2370 size_t proc_count;
2371 int rv = os_proc_children((int)pid, &proc_list, &proc_count);
2372 if (rv != 0) {
2373 // syscall failed (possibly because of kernel options), try shelling out.
2374 DLOG("fallback to vim._os_proc_children()");
2375 Array a = ARRAY_DICT_INIT;
2376 ADD(a, INTEGER_OBJ(pid));
2377 String s = cstr_to_string("return vim._os_proc_children(select(1, ...))");
2378 Object o = nvim_execute_lua(s, a, err);
2379 api_free_string(s);
2380 api_free_array(a);
2381 if (o.type == kObjectTypeArray) {
2382 rvobj = o.data.array;
2383 } else if (!ERROR_SET(err)) {
2384 api_set_error(err, kErrorTypeException,
2385 "Failed to get process children. pid=%" PRId64 " error=%d",
2386 pid, rv);
2387 }
2388 goto end;
2389 }
2390
2391 for (size_t i = 0; i < proc_count; i++) {
2392 ADD(rvobj, INTEGER_OBJ(proc_list[i]));
2393 }
2394
2395end:
2396 xfree(proc_list);
2397 return rvobj;
2398}
2399
2400/// Gets info describing process `pid`.
2401///
2402/// @return Map of process properties, or NIL if process not found.
2403Object nvim_get_proc(Integer pid, Error *err)
2404 FUNC_API_SINCE(4)
2405{
2406 Object rvobj = OBJECT_INIT;
2407 rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT;
2408 rvobj.type = kObjectTypeDictionary;
2409
2410 if (pid <= 0 || pid > INT_MAX) {
2411 api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
2412 return NIL;
2413 }
2414#ifdef WIN32
2415 rvobj.data.dictionary = os_proc_info((int)pid);
2416 if (rvobj.data.dictionary.size == 0) { // Process not found.
2417 return NIL;
2418 }
2419#else
2420 // Cross-platform process info APIs are miserable, so use `ps` instead.
2421 Array a = ARRAY_DICT_INIT;
2422 ADD(a, INTEGER_OBJ(pid));
2423 String s = cstr_to_string("return vim._os_proc_info(select(1, ...))");
2424 Object o = nvim_execute_lua(s, a, err);
2425 api_free_string(s);
2426 api_free_array(a);
2427 if (o.type == kObjectTypeArray && o.data.array.size == 0) {
2428 return NIL; // Process not found.
2429 } else if (o.type == kObjectTypeDictionary) {
2430 rvobj.data.dictionary = o.data.dictionary;
2431 } else if (!ERROR_SET(err)) {
2432 api_set_error(err, kErrorTypeException,
2433 "Failed to get process info. pid=%" PRId64, pid);
2434 }
2435#endif
2436 return rvobj;
2437}
2438
2439/// Selects an item in the completion popupmenu.
2440///
2441/// If |ins-completion| is not active this API call is silently ignored.
2442/// Useful for an external UI using |ui-popupmenu| to control the popupmenu
2443/// with the mouse. Can also be used in a mapping; use <cmd> |:map-cmd| to
2444/// ensure the mapping doesn't end completion mode.
2445///
2446/// @param item Index (zero-based) of the item to select. Value of -1 selects
2447/// nothing and restores the original text.
2448/// @param insert Whether the selection should be inserted in the buffer.
2449/// @param finish Finish the completion and dismiss the popupmenu. Implies
2450/// `insert`.
2451/// @param opts Optional parameters. Reserved for future use.
2452/// @param[out] err Error details, if any
2453void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish,
2454 Dictionary opts, Error *err)
2455 FUNC_API_SINCE(6)
2456{
2457 if (opts.size > 0) {
2458 api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
2459 return;
2460 }
2461
2462 if (finish) {
2463 insert = true;
2464 }
2465
2466 pum_ext_select_item((int)item, insert, finish);
2467}
2468
2469/// NB: if your UI doesn't use hlstate, this will not return hlstate first time
2470Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
2471{
2472 Array ret = ARRAY_DICT_INIT;
2473
2474 // TODO(bfredl): if grid == 0 we should read from the compositor's buffer.
2475 // The only problem is that it does not yet exist.
2476 ScreenGrid *g = &default_grid;
2477 if (grid == pum_grid.handle) {
2478 g = &pum_grid;
2479 } else if (grid > 1) {
2480 win_T *wp = get_win_by_grid_handle((handle_T)grid);
2481 if (wp != NULL && wp->w_grid.chars != NULL) {
2482 g = &wp->w_grid;
2483 } else {
2484 api_set_error(err, kErrorTypeValidation,
2485 "No grid with the given handle");
2486 return ret;
2487 }
2488 }
2489
2490 if (row < 0 || row >= g->Rows
2491 || col < 0 || col >= g->Columns) {
2492 return ret;
2493 }
2494 size_t off = g->line_offset[(size_t)row] + (size_t)col;
2495 ADD(ret, STRING_OBJ(cstr_to_string((char *)g->chars[off])));
2496 int attr = g->attrs[off];
2497 ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err)));
2498 // will not work first time
2499 if (!highlight_use_hlstate()) {
2500 ADD(ret, ARRAY_OBJ(hl_inspect(attr)));
2501 }
2502 return ret;
2503}
2504