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 <limits.h>
7 #include <stdbool.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "nvim/api/buffer.h"
12 #include "nvim/api/deprecated.h"
13 #include "nvim/api/private/converter.h"
14 #include "nvim/api/private/defs.h"
15 #include "nvim/api/private/dispatch.h"
16 #include "nvim/api/private/helpers.h"
17 #include "nvim/api/vim.h"
18 #include "nvim/api/window.h"
19 #include "nvim/ascii.h"
20 #include "nvim/buffer.h"
21 #include "nvim/buffer_defs.h"
22 #include "nvim/context.h"
23 #include "nvim/decoration.h"
24 #include "nvim/edit.h"
25 #include "nvim/eval.h"
26 #include "nvim/eval/typval.h"
27 #include "nvim/eval/userfunc.h"
28 #include "nvim/ex_cmds2.h"
29 #include "nvim/ex_docmd.h"
30 #include "nvim/file_search.h"
31 #include "nvim/fileio.h"
32 #include "nvim/getchar.h"
33 #include "nvim/highlight.h"
34 #include "nvim/lua/executor.h"
35 #include "nvim/mark.h"
36 #include "nvim/memline.h"
37 #include "nvim/memory.h"
38 #include "nvim/message.h"
39 #include "nvim/move.h"
40 #include "nvim/msgpack_rpc/channel.h"
41 #include "nvim/msgpack_rpc/helpers.h"
42 #include "nvim/ops.h"
43 #include "nvim/option.h"
44 #include "nvim/os/input.h"
45 #include "nvim/os/process.h"
46 #include "nvim/popupmnu.h"
47 #include "nvim/screen.h"
48 #include "nvim/state.h"
49 #include "nvim/syntax.h"
50 #include "nvim/types.h"
51 #include "nvim/ui.h"
52 #include "nvim/vim.h"
53 #include "nvim/viml/parser/expressions.h"
54 #include "nvim/viml/parser/parser.h"
55 #include "nvim/window.h"
56
57 #define LINE_BUFFER_SIZE 4096
58
59 #ifdef INCLUDE_GENERATED_DECLARATIONS
60 # include "api/vim.c.generated.h"
61 #endif
62
63 /// Gets a highlight definition by name.
64 ///
65 /// @param name Highlight group name
66 /// @param rgb Export RGB colors
67 /// @param[out] err Error details, if any
68 /// @return Highlight definition map
69 /// @see nvim_get_hl_by_id
nvim_get_hl_by_name(String name,Boolean rgb,Error * err)70 Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
71 FUNC_API_SINCE(3)
72 {
73 Dictionary result = ARRAY_DICT_INIT;
74 int id = syn_name2id(name.data);
75
76 if (id == 0) {
77 api_set_error(err, kErrorTypeException, "Invalid highlight name: %s",
78 name.data);
79 return result;
80 }
81 result = nvim_get_hl_by_id(id, rgb, err);
82 return result;
83 }
84
85 /// Gets a highlight definition by id. |hlID()|
86 ///
87 /// @param hl_id Highlight id as returned by |hlID()|
88 /// @param rgb Export RGB colors
89 /// @param[out] err Error details, if any
90 /// @return Highlight definition map
91 /// @see nvim_get_hl_by_name
nvim_get_hl_by_id(Integer hl_id,Boolean rgb,Error * err)92 Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
93 FUNC_API_SINCE(3)
94 {
95 Dictionary dic = ARRAY_DICT_INIT;
96 if (syn_get_final_id((int)hl_id) == 0) {
97 api_set_error(err, kErrorTypeException,
98 "Invalid highlight id: %" PRId64, hl_id);
99 return dic;
100 }
101 int attrcode = syn_id2attr((int)hl_id);
102 return hl_get_attr_by_id(attrcode, rgb, err);
103 }
104
105 /// Gets a highlight group by name
106 ///
107 /// similar to |hlID()|, but allocates a new ID if not present.
nvim_get_hl_id_by_name(String name)108 Integer nvim_get_hl_id_by_name(String name)
109 FUNC_API_SINCE(7)
110 {
111 return syn_check_group(name.data, (int)name.size);
112 }
113
nvim__get_hl_defs(Integer ns_id,Error * err)114 Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
115 {
116 if (ns_id == 0) {
117 return get_global_hl_defs();
118 }
119 abort();
120 }
121
122 /// Set a highlight group.
123 ///
124 /// @param ns_id number of namespace for this highlight
125 /// @param name highlight group name, like ErrorMsg
126 /// @param val highlight definition map, like |nvim_get_hl_by_name|.
127 /// in addition the following keys are also recognized:
128 /// `default`: don't override existing definition,
129 /// like `hi default`
130 /// `ctermfg`: sets foreground of cterm color
131 /// `ctermbg`: sets background of cterm color
132 /// `cterm` : cterm attribute map. sets attributed for
133 /// cterm colors. similer to `hi cterm`
134 /// Note: by default cterm attributes are
135 /// same as attributes of gui color
136 /// @param[out] err Error details, if any
137 ///
138 /// TODO: ns_id = 0, should modify :highlight namespace
139 /// TODO val should take update vs reset flag
nvim_set_hl(Integer ns_id,String name,Dictionary val,Error * err)140 void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err)
141 FUNC_API_SINCE(7)
142 {
143 int hl_id = syn_check_group(name.data, (int)name.size);
144 int link_id = -1;
145
146 HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
147 if (!ERROR_SET(err)) {
148 ns_hl_def((NS)ns_id, hl_id, attrs, link_id);
149 }
150 }
151
152 /// Set active namespace for highlights.
153 ///
154 /// NB: this function can be called from async contexts, but the
155 /// semantics are not yet well-defined. To start with
156 /// |nvim_set_decoration_provider| on_win and on_line callbacks
157 /// are explicitly allowed to change the namespace during a redraw cycle.
158 ///
159 /// @param ns_id the namespace to activate
160 /// @param[out] err Error details, if any
nvim__set_hl_ns(Integer ns_id,Error * err)161 void nvim__set_hl_ns(Integer ns_id, Error *err)
162 FUNC_API_FAST
163 {
164 if (ns_id >= 0) {
165 ns_hl_active = (NS)ns_id;
166 }
167
168 // TODO(bfredl): this is a little bit hackish. Eventually we want a standard
169 // event path for redraws caused by "fast" events. This could tie in with
170 // better throttling of async events causing redraws, such as non-batched
171 // nvim_buf_set_extmark calls from async contexts.
172 if (!provider_active && !ns_hl_changed) {
173 multiqueue_put(main_loop.events, on_redraw_event, 0);
174 }
175 ns_hl_changed = true;
176 }
177
on_redraw_event(void ** argv)178 static void on_redraw_event(void **argv)
179 {
180 redraw_all_later(NOT_VALID);
181 }
182
183
184 /// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
185 /// flags. This is a blocking call, unlike |nvim_input()|.
186 ///
187 /// On execution error: does not fail, but updates v:errmsg.
188 ///
189 /// To input sequences like <C-o> use |nvim_replace_termcodes()| (typically
190 /// with escape_csi=true) to replace |keycodes|, then pass the result to
191 /// nvim_feedkeys().
192 ///
193 /// Example:
194 /// <pre>
195 /// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
196 /// :call nvim_feedkeys(key, 'n', v:true)
197 /// </pre>
198 ///
199 /// @param keys to be typed
200 /// @param mode behavior flags, see |feedkeys()|
201 /// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys`
202 /// @see feedkeys()
203 /// @see vim_strsave_escape_csi
nvim_feedkeys(String keys,String mode,Boolean escape_csi)204 void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
205 FUNC_API_SINCE(1)
206 {
207 bool remap = true;
208 bool insert = false;
209 bool typed = false;
210 bool execute = false;
211 bool dangerous = false;
212
213 for (size_t i = 0; i < mode.size; ++i) {
214 switch (mode.data[i]) {
215 case 'n':
216 remap = false; break;
217 case 'm':
218 remap = true; break;
219 case 't':
220 typed = true; break;
221 case 'i':
222 insert = true; break;
223 case 'x':
224 execute = true; break;
225 case '!':
226 dangerous = true; break;
227 }
228 }
229
230 if (keys.size == 0 && !execute) {
231 return;
232 }
233
234 char *keys_esc;
235 if (escape_csi) {
236 // Need to escape K_SPECIAL and CSI before putting the string in the
237 // typeahead buffer.
238 keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data);
239 } else {
240 keys_esc = keys.data;
241 }
242 ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE),
243 insert ? 0 : typebuf.tb_len, !typed, false);
244 if (vgetc_busy) {
245 typebuf_was_filled = true;
246 }
247
248 if (escape_csi) {
249 xfree(keys_esc);
250 }
251
252 if (execute) {
253 int save_msg_scroll = msg_scroll;
254
255 // Avoid a 1 second delay when the keys start Insert mode.
256 msg_scroll = false;
257 if (!dangerous) {
258 ex_normal_busy++;
259 }
260 exec_normal(true);
261 if (!dangerous) {
262 ex_normal_busy--;
263 }
264 msg_scroll |= save_msg_scroll;
265 }
266 }
267
268 /// Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a low-level
269 /// input buffer and the call is non-blocking (input is processed
270 /// asynchronously by the eventloop).
271 ///
272 /// On execution error: does not fail, but updates v:errmsg.
273 ///
274 /// @note |keycodes| like <CR> are translated, so "<" is special.
275 /// To input a literal "<", send <LT>.
276 ///
277 /// @note For mouse events use |nvim_input_mouse()|. The pseudokey form
278 /// "<LeftMouse><col,row>" is deprecated since |api-level| 6.
279 ///
280 /// @param keys to be typed
281 /// @return Number of bytes actually written (can be fewer than
282 /// requested if the buffer becomes full).
nvim_input(String keys)283 Integer nvim_input(String keys)
284 FUNC_API_SINCE(1) FUNC_API_FAST
285 {
286 return (Integer)input_enqueue(keys);
287 }
288
289 /// Send mouse event from GUI.
290 ///
291 /// Non-blocking: does not wait on any result, but queues the event to be
292 /// processed soon by the event loop.
293 ///
294 /// @note Currently this doesn't support "scripting" multiple mouse events
295 /// by calling it multiple times in a loop: the intermediate mouse
296 /// positions will be ignored. It should be used to implement real-time
297 /// mouse input in a GUI. The deprecated pseudokey form
298 /// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation.
299 ///
300 /// @param button Mouse button: one of "left", "right", "middle", "wheel".
301 /// @param action For ordinary buttons, one of "press", "drag", "release".
302 /// For the wheel, one of "up", "down", "left", "right".
303 /// @param modifier String of modifiers each represented by a single char.
304 /// The same specifiers are used as for a key press, except
305 /// that the "-" separator is optional, so "C-A-", "c-a"
306 /// and "CA" can all be used to specify Ctrl+Alt+click.
307 /// @param grid Grid number if the client uses |ui-multigrid|, else 0.
308 /// @param row Mouse row-position (zero-based, like redraw events)
309 /// @param col Mouse column-position (zero-based, like redraw events)
310 /// @param[out] err Error details, if any
nvim_input_mouse(String button,String action,String modifier,Integer grid,Integer row,Integer col,Error * err)311 void nvim_input_mouse(String button, String action, String modifier, Integer grid, Integer row,
312 Integer col, Error *err)
313 FUNC_API_SINCE(6) FUNC_API_FAST
314 {
315 if (button.data == NULL || action.data == NULL) {
316 goto error;
317 }
318
319 int code = 0;
320
321 if (strequal(button.data, "left")) {
322 code = KE_LEFTMOUSE;
323 } else if (strequal(button.data, "middle")) {
324 code = KE_MIDDLEMOUSE;
325 } else if (strequal(button.data, "right")) {
326 code = KE_RIGHTMOUSE;
327 } else if (strequal(button.data, "wheel")) {
328 code = KE_MOUSEDOWN;
329 } else {
330 goto error;
331 }
332
333 if (code == KE_MOUSEDOWN) {
334 if (strequal(action.data, "down")) {
335 code = KE_MOUSEUP;
336 } else if (strequal(action.data, "up")) {
337 // code = KE_MOUSEDOWN
338 } else if (strequal(action.data, "left")) {
339 code = KE_MOUSERIGHT;
340 } else if (strequal(action.data, "right")) {
341 code = KE_MOUSELEFT;
342 } else {
343 goto error;
344 }
345 } else {
346 if (strequal(action.data, "press")) {
347 // pass
348 } else if (strequal(action.data, "drag")) {
349 code += KE_LEFTDRAG - KE_LEFTMOUSE;
350 } else if (strequal(action.data, "release")) {
351 code += KE_LEFTRELEASE - KE_LEFTMOUSE;
352 } else {
353 goto error;
354 }
355 }
356
357 int modmask = 0;
358 for (size_t i = 0; i < modifier.size; i++) {
359 char byte = modifier.data[i];
360 if (byte == '-') {
361 continue;
362 }
363 int mod = name_to_mod_mask(byte);
364 if (mod == 0) {
365 api_set_error(err, kErrorTypeValidation,
366 "invalid modifier %c", byte);
367 return;
368 }
369 modmask |= mod;
370 }
371
372 input_enqueue_mouse(code, (uint8_t)modmask, (int)grid, (int)row, (int)col);
373 return;
374
375 error:
376 api_set_error(err, kErrorTypeValidation,
377 "invalid button or action");
378 }
379
380 /// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
381 /// the internal representation.
382 ///
383 /// @param str String to be converted.
384 /// @param from_part Legacy Vim parameter. Usually true.
385 /// @param do_lt Also translate <lt>. Ignored if `special` is false.
386 /// @param special Replace |keycodes|, e.g. <CR> becomes a "\n" char.
387 /// @see replace_termcodes
388 /// @see cpoptions
nvim_replace_termcodes(String str,Boolean from_part,Boolean do_lt,Boolean special)389 String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special)
390 FUNC_API_SINCE(1)
391 {
392 if (str.size == 0) {
393 // Empty string
394 return (String) { .data = NULL, .size = 0 };
395 }
396
397 char *ptr = NULL;
398 replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
399 from_part, do_lt, special, CPO_TO_CPO_FLAGS);
400 return cstr_as_string(ptr);
401 }
402
403
404 /// Execute Lua code. Parameters (if any) are available as `...` inside the
405 /// chunk. The chunk can return a value.
406 ///
407 /// Only statements are executed. To evaluate an expression, prefix it
408 /// with `return`: return my_function(...)
409 ///
410 /// @param code Lua code to execute
411 /// @param args Arguments to the code
412 /// @param[out] err Details of an error encountered while parsing
413 /// or executing the Lua code.
414 ///
415 /// @return Return value of Lua code if present or NIL.
nvim_exec_lua(String code,Array args,Error * err)416 Object nvim_exec_lua(String code, Array args, Error *err)
417 FUNC_API_SINCE(7)
418 FUNC_API_REMOTE_ONLY
419 {
420 return nlua_exec(code, args, err);
421 }
422
423 /// Notify the user with a message
424 ///
425 /// Relays the call to vim.notify . By default forwards your message in the
426 /// echo area but can be overridden to trigger desktop notifications.
427 ///
428 /// @param msg Message to display to the user
429 /// @param log_level The log level
430 /// @param opts Reserved for future use.
431 /// @param[out] err Error details, if any
nvim_notify(String msg,Integer log_level,Dictionary opts,Error * err)432 Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
433 FUNC_API_SINCE(7)
434 {
435 FIXED_TEMP_ARRAY(args, 3);
436 args.items[0] = STRING_OBJ(msg);
437 args.items[1] = INTEGER_OBJ(log_level);
438 args.items[2] = DICTIONARY_OBJ(opts);
439
440 return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err);
441 }
442
443 /// Calculates the number of display cells occupied by `text`.
444 /// <Tab> counts as one cell.
445 ///
446 /// @param text Some text
447 /// @param[out] err Error details, if any
448 /// @return Number of cells
nvim_strwidth(String text,Error * err)449 Integer nvim_strwidth(String text, Error *err)
450 FUNC_API_SINCE(1)
451 {
452 if (text.size > INT_MAX) {
453 api_set_error(err, kErrorTypeValidation, "String is too long");
454 return 0;
455 }
456
457 return (Integer)mb_string2cells((char_u *)text.data);
458 }
459
460 /// Gets the paths contained in 'runtimepath'.
461 ///
462 /// @return List of paths
nvim_list_runtime_paths(Error * err)463 ArrayOf(String) nvim_list_runtime_paths(Error *err)
464 FUNC_API_SINCE(1)
465 {
466 return nvim_get_runtime_file(NULL_STRING, true, err);
467 }
468
nvim__runtime_inspect(void)469 Array nvim__runtime_inspect(void)
470 {
471 return runtime_inspect();
472 }
473
474 /// Find files in runtime directories
475 ///
476 /// 'name' can contain wildcards. For example
477 /// nvim_get_runtime_file("colors/*.vim", true) will return all color
478 /// scheme files. Always use forward slashes (/) in the search pattern for
479 /// subdirectories regardless of platform.
480 ///
481 /// It is not an error to not find any files. An empty array is returned then.
482 ///
483 /// @param name pattern of files to search for
484 /// @param all whether to return all matches or only the first
485 /// @return list of absolute paths to the found files
nvim_get_runtime_file(String name,Boolean all,Error * err)486 ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err)
487 FUNC_API_SINCE(7)
488 FUNC_API_FAST
489 {
490 Array rv = ARRAY_DICT_INIT;
491
492 int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
493
494 do_in_runtimepath((char_u *)(name.size ? name.data : ""),
495 flags, find_runtime_cb, &rv);
496 return rv;
497 }
498
find_runtime_cb(char_u * fname,void * cookie)499 static void find_runtime_cb(char_u *fname, void *cookie)
500 {
501 Array *rv = (Array *)cookie;
502 if (fname != NULL) {
503 ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname)));
504 }
505 }
506
nvim__get_lib_dir(void)507 String nvim__get_lib_dir(void)
508 {
509 return cstr_as_string(get_lib_dir());
510 }
511
512 /// Find files in runtime directories
513 ///
514 /// @param pat pattern of files to search for
515 /// @param all whether to return all matches or only the first
516 /// @param options
517 /// is_lua: only search lua subdirs
518 /// @return list of absolute paths to the found files
nvim__get_runtime(Array pat,Boolean all,Dict (runtime)* opts,Error * err)519 ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err)
520 FUNC_API_SINCE(8)
521 FUNC_API_FAST
522 {
523 bool is_lua = api_object_to_bool(opts->is_lua, "is_lua", false, err);
524 if (ERROR_SET(err)) {
525 return (Array)ARRAY_DICT_INIT;
526 }
527 return runtime_get_named(is_lua, pat, all);
528 }
529
530
531 /// Changes the global working directory.
532 ///
533 /// @param dir Directory path
534 /// @param[out] err Error details, if any
nvim_set_current_dir(String dir,Error * err)535 void nvim_set_current_dir(String dir, Error *err)
536 FUNC_API_SINCE(1)
537 {
538 if (dir.size >= MAXPATHL) {
539 api_set_error(err, kErrorTypeValidation, "Directory name is too long");
540 return;
541 }
542
543 char string[MAXPATHL];
544 memcpy(string, dir.data, dir.size);
545 string[dir.size] = NUL;
546
547 try_start();
548
549 if (vim_chdir((char_u *)string)) {
550 if (!try_end(err)) {
551 api_set_error(err, kErrorTypeException, "Failed to change directory");
552 }
553 return;
554 }
555
556 post_chdir(kCdScopeGlobal, true);
557 try_end(err);
558 }
559
560 /// Gets the current line.
561 ///
562 /// @param[out] err Error details, if any
563 /// @return Current line string
nvim_get_current_line(Error * err)564 String nvim_get_current_line(Error *err)
565 FUNC_API_SINCE(1)
566 {
567 return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
568 }
569
570 /// Sets the current line.
571 ///
572 /// @param line Line contents
573 /// @param[out] err Error details, if any
nvim_set_current_line(String line,Error * err)574 void nvim_set_current_line(String line, Error *err)
575 FUNC_API_SINCE(1)
576 FUNC_API_CHECK_TEXTLOCK
577 {
578 buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
579 }
580
581 /// Deletes the current line.
582 ///
583 /// @param[out] err Error details, if any
nvim_del_current_line(Error * err)584 void nvim_del_current_line(Error *err)
585 FUNC_API_SINCE(1)
586 FUNC_API_CHECK_TEXTLOCK
587 {
588 buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
589 }
590
591 /// Gets a global (g:) variable.
592 ///
593 /// @param name Variable name
594 /// @param[out] err Error details, if any
595 /// @return Variable value
nvim_get_var(String name,Error * err)596 Object nvim_get_var(String name, Error *err)
597 FUNC_API_SINCE(1)
598 {
599 return dict_get_value(&globvardict, name, err);
600 }
601
602 /// Sets a global (g:) variable.
603 ///
604 /// @param name Variable name
605 /// @param value Variable value
606 /// @param[out] err Error details, if any
nvim_set_var(String name,Object value,Error * err)607 void nvim_set_var(String name, Object value, Error *err)
608 FUNC_API_SINCE(1)
609 {
610 dict_set_var(&globvardict, name, value, false, false, err);
611 }
612
613 /// Removes a global (g:) variable.
614 ///
615 /// @param name Variable name
616 /// @param[out] err Error details, if any
nvim_del_var(String name,Error * err)617 void nvim_del_var(String name, Error *err)
618 FUNC_API_SINCE(1)
619 {
620 dict_set_var(&globvardict, name, NIL, true, false, err);
621 }
622
623 /// Gets a v: variable.
624 ///
625 /// @param name Variable name
626 /// @param[out] err Error details, if any
627 /// @return Variable value
nvim_get_vvar(String name,Error * err)628 Object nvim_get_vvar(String name, Error *err)
629 FUNC_API_SINCE(1)
630 {
631 return dict_get_value(&vimvardict, name, err);
632 }
633
634 /// Sets a v: variable, if it is not readonly.
635 ///
636 /// @param name Variable name
637 /// @param value Variable value
638 /// @param[out] err Error details, if any
nvim_set_vvar(String name,Object value,Error * err)639 void nvim_set_vvar(String name, Object value, Error *err)
640 FUNC_API_SINCE(6)
641 {
642 dict_set_var(&vimvardict, name, value, false, false, err);
643 }
644
645 /// Gets an option value string.
646 ///
647 /// @param name Option name
648 /// @param[out] err Error details, if any
649 /// @return Option value (global)
nvim_get_option(String name,Error * err)650 Object nvim_get_option(String name, Error *err)
651 FUNC_API_SINCE(1)
652 {
653 return get_option_from(NULL, SREQ_GLOBAL, name, err);
654 }
655
656 /// Gets the option information for all options.
657 ///
658 /// The dictionary has the full option names as keys and option metadata
659 /// dictionaries as detailed at |nvim_get_option_info|.
660 ///
661 /// @return dictionary of all options
nvim_get_all_options_info(Error * err)662 Dictionary nvim_get_all_options_info(Error *err)
663 FUNC_API_SINCE(7)
664 {
665 return get_all_vimoptions();
666 }
667
668 /// Gets the option information for one option
669 ///
670 /// Resulting dictionary has keys:
671 /// - name: Name of the option (like 'filetype')
672 /// - shortname: Shortened name of the option (like 'ft')
673 /// - type: type of option ("string", "number" or "boolean")
674 /// - default: The default value for the option
675 /// - was_set: Whether the option was set.
676 ///
677 /// - last_set_sid: Last set script id (if any)
678 /// - last_set_linenr: line number where option was set
679 /// - last_set_chan: Channel where option was set (0 for local)
680 ///
681 /// - scope: one of "global", "win", or "buf"
682 /// - global_local: whether win or buf option has a global value
683 ///
684 /// - commalist: List of comma separated values
685 /// - flaglist: List of single char flags
686 ///
687 ///
688 /// @param name Option name
689 /// @param[out] err Error details, if any
690 /// @return Option Information
nvim_get_option_info(String name,Error * err)691 Dictionary nvim_get_option_info(String name, Error *err)
692 FUNC_API_SINCE(7)
693 {
694 return get_vimoption(name, err);
695 }
696
697 /// Sets an option value.
698 ///
699 /// @param channel_id
700 /// @param name Option name
701 /// @param value New option value
702 /// @param[out] err Error details, if any
nvim_set_option(uint64_t channel_id,String name,Object value,Error * err)703 void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
704 FUNC_API_SINCE(1)
705 {
706 set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
707 }
708
709 /// Echo a message.
710 ///
711 /// @param chunks A list of [text, hl_group] arrays, each representing a
712 /// text chunk with specified highlight. `hl_group` element
713 /// can be omitted for no highlight.
714 /// @param history if true, add to |message-history|.
715 /// @param opts Optional parameters. Reserved for future use.
nvim_echo(Array chunks,Boolean history,Dictionary opts,Error * err)716 void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err)
717 FUNC_API_SINCE(7)
718 {
719 HlMessage hl_msg = parse_hl_msg(chunks, err);
720 if (ERROR_SET(err)) {
721 goto error;
722 }
723
724 if (opts.size > 0) {
725 api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
726 goto error;
727 }
728
729 no_wait_return++;
730 msg_start();
731 msg_clr_eos();
732 bool need_clear = false;
733 for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
734 HlMessageChunk chunk = kv_A(hl_msg, i);
735 msg_multiline_attr((const char *)chunk.text.data, chunk.attr,
736 false, &need_clear);
737 }
738 if (history) {
739 msg_ext_set_kind("echomsg");
740 add_hl_msg_hist(hl_msg);
741 } else {
742 msg_ext_set_kind("echo");
743 }
744 no_wait_return--;
745 msg_end();
746
747 error:
748 clear_hl_msg(&hl_msg);
749 }
750
751 /// Writes a message to the Vim output buffer. Does not append "\n", the
752 /// message is buffered (won't display) until a linefeed is written.
753 ///
754 /// @param str Message
nvim_out_write(String str)755 void nvim_out_write(String str)
756 FUNC_API_SINCE(1)
757 {
758 write_msg(str, false);
759 }
760
761 /// Writes a message to the Vim error buffer. Does not append "\n", the
762 /// message is buffered (won't display) until a linefeed is written.
763 ///
764 /// @param str Message
nvim_err_write(String str)765 void nvim_err_write(String str)
766 FUNC_API_SINCE(1)
767 {
768 write_msg(str, true);
769 }
770
771 /// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
772 /// flushed (and displayed).
773 ///
774 /// @param str Message
775 /// @see nvim_err_write()
nvim_err_writeln(String str)776 void nvim_err_writeln(String str)
777 FUNC_API_SINCE(1)
778 {
779 nvim_err_write(str);
780 nvim_err_write((String) { .data = "\n", .size = 1 });
781 }
782
783 /// Gets the current list of buffer handles
784 ///
785 /// Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
786 /// Use |nvim_buf_is_loaded()| to check if a buffer is loaded.
787 ///
788 /// @return List of buffer handles
nvim_list_bufs(void)789 ArrayOf(Buffer) nvim_list_bufs(void)
790 FUNC_API_SINCE(1)
791 {
792 Array rv = ARRAY_DICT_INIT;
793
794 FOR_ALL_BUFFERS(b) {
795 rv.size++;
796 }
797
798 rv.items = xmalloc(sizeof(Object) * rv.size);
799 size_t i = 0;
800
801 FOR_ALL_BUFFERS(b) {
802 rv.items[i++] = BUFFER_OBJ(b->handle);
803 }
804
805 return rv;
806 }
807
808 /// Gets the current buffer.
809 ///
810 /// @return Buffer handle
nvim_get_current_buf(void)811 Buffer nvim_get_current_buf(void)
812 FUNC_API_SINCE(1)
813 {
814 return curbuf->handle;
815 }
816
817 /// Sets the current buffer.
818 ///
819 /// @param buffer Buffer handle
820 /// @param[out] err Error details, if any
nvim_set_current_buf(Buffer buffer,Error * err)821 void nvim_set_current_buf(Buffer buffer, Error *err)
822 FUNC_API_SINCE(1)
823 FUNC_API_CHECK_TEXTLOCK
824 {
825 buf_T *buf = find_buffer_by_handle(buffer, err);
826
827 if (!buf) {
828 return;
829 }
830
831 try_start();
832 int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
833 if (!try_end(err) && result == FAIL) {
834 api_set_error(err,
835 kErrorTypeException,
836 "Failed to switch to buffer %d",
837 buffer);
838 }
839 }
840
841 /// Gets the current list of window handles.
842 ///
843 /// @return List of window handles
nvim_list_wins(void)844 ArrayOf(Window) nvim_list_wins(void)
845 FUNC_API_SINCE(1)
846 {
847 Array rv = ARRAY_DICT_INIT;
848
849 FOR_ALL_TAB_WINDOWS(tp, wp) {
850 rv.size++;
851 }
852
853 rv.items = xmalloc(sizeof(Object) * rv.size);
854 size_t i = 0;
855
856 FOR_ALL_TAB_WINDOWS(tp, wp) {
857 rv.items[i++] = WINDOW_OBJ(wp->handle);
858 }
859
860 return rv;
861 }
862
863 /// Gets the current window.
864 ///
865 /// @return Window handle
nvim_get_current_win(void)866 Window nvim_get_current_win(void)
867 FUNC_API_SINCE(1)
868 {
869 return curwin->handle;
870 }
871
872 /// Sets the current window.
873 ///
874 /// @param window Window handle
875 /// @param[out] err Error details, if any
nvim_set_current_win(Window window,Error * err)876 void nvim_set_current_win(Window window, Error *err)
877 FUNC_API_SINCE(1)
878 FUNC_API_CHECK_TEXTLOCK
879 {
880 win_T *win = find_window_by_handle(window, err);
881
882 if (!win) {
883 return;
884 }
885
886 try_start();
887 goto_tabpage_win(win_find_tabpage(win), win);
888 if (!try_end(err) && win != curwin) {
889 api_set_error(err,
890 kErrorTypeException,
891 "Failed to switch to window %d",
892 window);
893 }
894 }
895
896 /// Creates a new, empty, unnamed buffer.
897 ///
898 /// @param listed Sets 'buflisted'
899 /// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
900 /// (always 'nomodified'). Also sets 'nomodeline' on the buffer.
901 /// @param[out] err Error details, if any
902 /// @return Buffer handle, or 0 on error
903 ///
904 /// @see buf_open_scratch
nvim_create_buf(Boolean listed,Boolean scratch,Error * err)905 Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
906 FUNC_API_SINCE(6)
907 {
908 try_start();
909 buf_T *buf = buflist_new(NULL, NULL, (linenr_T)0,
910 BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
911 try_end(err);
912 if (buf == NULL) {
913 goto fail;
914 }
915
916 // Open the memline for the buffer. This will avoid spurious autocmds when
917 // a later nvim_buf_set_lines call would have needed to "open" the buffer.
918 try_start();
919 block_autocmds();
920 int status = ml_open(buf);
921 unblock_autocmds();
922 try_end(err);
923 if (status == FAIL) {
924 goto fail;
925 }
926
927 if (scratch) {
928 aco_save_T aco;
929 aucmd_prepbuf(&aco, buf);
930 set_option_value("bufhidden", 0L, "hide", OPT_LOCAL);
931 set_option_value("buftype", 0L, "nofile", OPT_LOCAL);
932 set_option_value("swapfile", 0L, NULL, OPT_LOCAL);
933 set_option_value("modeline", 0L, NULL, OPT_LOCAL); // 'nomodeline'
934 aucmd_restbuf(&aco);
935 }
936 return buf->b_fnum;
937
938 fail:
939 if (!ERROR_SET(err)) {
940 api_set_error(err, kErrorTypeException, "Failed to create buffer");
941 }
942 return 0;
943 }
944
945 /// Open a terminal instance in a buffer
946 ///
947 /// By default (and currently the only option) the terminal will not be
948 /// connected to an external process. Instead, input send on the channel
949 /// will be echoed directly by the terminal. This is useful to display
950 /// ANSI terminal sequences returned as part of a rpc message, or similar.
951 ///
952 /// Note: to directly initiate the terminal using the right size, display the
953 /// buffer in a configured window before calling this. For instance, for a
954 /// floating display, first create an empty buffer using |nvim_create_buf()|,
955 /// then display it using |nvim_open_win()|, and then call this function.
956 /// Then |nvim_chan_send()| can be called immediately to process sequences
957 /// in a virtual terminal having the intended size.
958 ///
959 /// @param buffer the buffer to use (expected to be empty)
960 /// @param opts Optional parameters.
961 /// - on_input: lua callback for input sent, i e keypresses in terminal
962 /// mode. Note: keypresses are sent raw as they would be to the pty
963 /// master end. For instance, a carriage return is sent
964 /// as a "\r", not as a "\n". |textlock| applies. It is possible
965 /// to call |nvim_chan_send| directly in the callback however.
966 /// ["input", term, bufnr, data]
967 /// @param[out] err Error details, if any
968 /// @return Channel id, or 0 on error
nvim_open_term(Buffer buffer,DictionaryOf (LuaRef)opts,Error * err)969 Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
970 FUNC_API_SINCE(7)
971 {
972 buf_T *buf = find_buffer_by_handle(buffer, err);
973 if (!buf) {
974 return 0;
975 }
976
977 LuaRef cb = LUA_NOREF;
978 for (size_t i = 0; i < opts.size; i++) {
979 String k = opts.items[i].key;
980 Object *v = &opts.items[i].value;
981 if (strequal("on_input", k.data)) {
982 if (v->type != kObjectTypeLuaRef) {
983 api_set_error(err, kErrorTypeValidation,
984 "%s is not a function", "on_input");
985 return 0;
986 }
987 cb = v->data.luaref;
988 v->data.luaref = LUA_NOREF;
989 break;
990 } else {
991 api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
992 }
993 }
994
995 TerminalOptions topts;
996 Channel *chan = channel_alloc(kChannelStreamInternal);
997 chan->stream.internal.cb = cb;
998 topts.data = chan;
999 // NB: overridden in terminal_check_size if a window is already
1000 // displaying the buffer
1001 topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0);
1002 topts.height = (uint16_t)curwin->w_height_inner;
1003 topts.write_cb = term_write;
1004 topts.resize_cb = term_resize;
1005 topts.close_cb = term_close;
1006 Terminal *term = terminal_open(buf, topts);
1007 terminal_check_size(term);
1008 chan->term = term;
1009 return (Integer)chan->id;
1010 }
1011
term_write(char * buf,size_t size,void * data)1012 static void term_write(char *buf, size_t size, void *data)
1013 {
1014 Channel *chan = data;
1015 LuaRef cb = chan->stream.internal.cb;
1016 if (cb == LUA_NOREF) {
1017 return;
1018 }
1019 FIXED_TEMP_ARRAY(args, 3);
1020 args.items[0] = INTEGER_OBJ((Integer)chan->id);
1021 args.items[1] = BUFFER_OBJ(terminal_buf(chan->term));
1022 args.items[2] = STRING_OBJ(((String){ .data = buf, .size = size }));
1023 textlock++;
1024 nlua_call_ref(cb, "input", args, false, NULL);
1025 textlock--;
1026 }
1027
term_resize(uint16_t width,uint16_t height,void * data)1028 static void term_resize(uint16_t width, uint16_t height, void *data)
1029 {
1030 // TODO(bfredl): lua callback
1031 }
1032
term_close(void * data)1033 static void term_close(void *data)
1034 {
1035 Channel *chan = data;
1036 terminal_destroy(chan->term);
1037 chan->term = NULL;
1038 api_free_luaref(chan->stream.internal.cb);
1039 chan->stream.internal.cb = LUA_NOREF;
1040 channel_decref(chan);
1041 }
1042
1043
1044 /// Send data to channel `id`. For a job, it writes it to the
1045 /// stdin of the process. For the stdio channel |channel-stdio|,
1046 /// it writes to Nvim's stdout. For an internal terminal instance
1047 /// (|nvim_open_term()|) it writes directly to terimal output.
1048 /// See |channel-bytes| for more information.
1049 ///
1050 /// This function writes raw data, not RPC messages. If the channel
1051 /// was created with `rpc=true` then the channel expects RPC
1052 /// messages, use |vim.rpcnotify()| and |vim.rpcrequest()| instead.
1053 ///
1054 /// @param chan id of the channel
1055 /// @param data data to write. 8-bit clean: can contain NUL bytes.
1056 /// @param[out] err Error details, if any
nvim_chan_send(Integer chan,String data,Error * err)1057 void nvim_chan_send(Integer chan, String data, Error *err)
1058 FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY FUNC_API_LUA_ONLY
1059 {
1060 const char *error = NULL;
1061 if (!data.size) {
1062 return;
1063 }
1064
1065 channel_send((uint64_t)chan, data.data, data.size,
1066 false, &error);
1067 if (error) {
1068 api_set_error(err, kErrorTypeValidation, "%s", error);
1069 }
1070 }
1071
1072 /// Gets the current list of tabpage handles.
1073 ///
1074 /// @return List of tabpage handles
nvim_list_tabpages(void)1075 ArrayOf(Tabpage) nvim_list_tabpages(void)
1076 FUNC_API_SINCE(1)
1077 {
1078 Array rv = ARRAY_DICT_INIT;
1079
1080 FOR_ALL_TABS(tp) {
1081 rv.size++;
1082 }
1083
1084 rv.items = xmalloc(sizeof(Object) * rv.size);
1085 size_t i = 0;
1086
1087 FOR_ALL_TABS(tp) {
1088 rv.items[i++] = TABPAGE_OBJ(tp->handle);
1089 }
1090
1091 return rv;
1092 }
1093
1094 /// Gets the current tabpage.
1095 ///
1096 /// @return Tabpage handle
nvim_get_current_tabpage(void)1097 Tabpage nvim_get_current_tabpage(void)
1098 FUNC_API_SINCE(1)
1099 {
1100 return curtab->handle;
1101 }
1102
1103 /// Sets the current tabpage.
1104 ///
1105 /// @param tabpage Tabpage handle
1106 /// @param[out] err Error details, if any
nvim_set_current_tabpage(Tabpage tabpage,Error * err)1107 void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
1108 FUNC_API_SINCE(1)
1109 FUNC_API_CHECK_TEXTLOCK
1110 {
1111 tabpage_T *tp = find_tab_by_handle(tabpage, err);
1112
1113 if (!tp) {
1114 return;
1115 }
1116
1117 try_start();
1118 goto_tabpage_tp(tp, true, true);
1119 if (!try_end(err) && tp != curtab) {
1120 api_set_error(err,
1121 kErrorTypeException,
1122 "Failed to switch to tabpage %d",
1123 tabpage);
1124 }
1125 }
1126
1127 /// Pastes at cursor, in any mode.
1128 ///
1129 /// Invokes the `vim.paste` handler, which handles each mode appropriately.
1130 /// Sets redo/undo. Faster than |nvim_input()|. Lines break at LF ("\n").
1131 ///
1132 /// Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err`
1133 /// but do not affect the return value (which is strictly decided by
1134 /// `vim.paste()`). On error, subsequent calls are ignored ("drained") until
1135 /// the next paste is initiated (phase 1 or -1).
1136 ///
1137 /// @param data Multiline input. May be binary (containing NUL bytes).
1138 /// @param crlf Also break lines at CR and CRLF.
1139 /// @param phase -1: paste in a single call (i.e. without streaming).
1140 /// To "stream" a paste, call `nvim_paste` sequentially with
1141 /// these `phase` values:
1142 /// - 1: starts the paste (exactly once)
1143 /// - 2: continues the paste (zero or more times)
1144 /// - 3: ends the paste (exactly once)
1145 /// @param[out] err Error details, if any
1146 /// @return
1147 /// - true: Client may continue pasting.
1148 /// - false: Client must cancel the paste.
nvim_paste(String data,Boolean crlf,Integer phase,Error * err)1149 Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
1150 FUNC_API_SINCE(6)
1151 FUNC_API_CHECK_TEXTLOCK
1152 {
1153 static bool draining = false;
1154 bool cancel = false;
1155
1156 if (phase < -1 || phase > 3) {
1157 api_set_error(err, kErrorTypeValidation, "Invalid phase: %" PRId64, phase);
1158 return false;
1159 }
1160 Array args = ARRAY_DICT_INIT;
1161 Object rv = OBJECT_INIT;
1162 if (phase == -1 || phase == 1) { // Start of paste-stream.
1163 draining = false;
1164 } else if (draining) {
1165 // Skip remaining chunks. Report error only once per "stream".
1166 goto theend;
1167 }
1168 Array lines = string_to_array(data, crlf);
1169 ADD(args, ARRAY_OBJ(lines));
1170 ADD(args, INTEGER_OBJ(phase));
1171 rv = nvim_exec_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args,
1172 err);
1173 if (ERROR_SET(err)) {
1174 draining = true;
1175 goto theend;
1176 }
1177 if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 1)) {
1178 ResetRedobuff();
1179 AppendCharToRedobuff('a'); // Dot-repeat.
1180 }
1181 // vim.paste() decides if client should cancel. Errors do NOT cancel: we
1182 // want to drain remaining chunks (rather than divert them to main input).
1183 cancel = (rv.type == kObjectTypeBoolean && !rv.data.boolean);
1184 if (!cancel && !(State & CMDLINE)) { // Dot-repeat.
1185 for (size_t i = 0; i < lines.size; i++) {
1186 String s = lines.items[i].data.string;
1187 assert(data.size <= INT_MAX);
1188 AppendToRedobuffLit((char_u *)s.data, (int)s.size);
1189 // readfile()-style: "\n" is indicated by presence of N+1 item.
1190 if (i + 1 < lines.size) {
1191 AppendCharToRedobuff(NL);
1192 }
1193 }
1194 }
1195 if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 3)) {
1196 AppendCharToRedobuff(ESC); // Dot-repeat.
1197 }
1198 theend:
1199 api_free_object(rv);
1200 api_free_array(args);
1201 if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
1202 draining = false;
1203 }
1204
1205 return !cancel;
1206 }
1207
1208 /// Puts text at cursor, in any mode.
1209 ///
1210 /// Compare |:put| and |p| which are always linewise.
1211 ///
1212 /// @param lines |readfile()|-style list of lines. |channel-lines|
1213 /// @param type Edit behavior: any |getregtype()| result, or:
1214 /// - "b" |blockwise-visual| mode (may include width, e.g. "b3")
1215 /// - "c" |charwise| mode
1216 /// - "l" |linewise| mode
1217 /// - "" guess by contents, see |setreg()|
1218 /// @param after If true insert after cursor (like |p|), or before (like |P|).
1219 /// @param follow If true place cursor at end of inserted text.
1220 /// @param[out] err Error details, if any
nvim_put(ArrayOf (String)lines,String type,Boolean after,Boolean follow,Error * err)1221 void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Error *err)
1222 FUNC_API_SINCE(6)
1223 FUNC_API_CHECK_TEXTLOCK
1224 {
1225 yankreg_T *reg = xcalloc(1, sizeof(yankreg_T));
1226 if (!prepare_yankreg_from_object(reg, type, lines.size)) {
1227 api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data);
1228 goto cleanup;
1229 }
1230 if (lines.size == 0) {
1231 goto cleanup; // Nothing to do.
1232 }
1233
1234 for (size_t i = 0; i < lines.size; i++) {
1235 if (lines.items[i].type != kObjectTypeString) {
1236 api_set_error(err, kErrorTypeValidation,
1237 "Invalid lines (expected array of strings)");
1238 goto cleanup;
1239 }
1240 String line = lines.items[i].data.string;
1241 reg->y_array[i] = (char_u *)xmemdupz(line.data, line.size);
1242 memchrsub(reg->y_array[i], NUL, NL, line.size);
1243 }
1244
1245 finish_yankreg_from_object(reg, false);
1246
1247 TRY_WRAP({
1248 try_start();
1249 bool VIsual_was_active = VIsual_active;
1250 msg_silent++; // Avoid "N more lines" message.
1251 do_put(0, reg, after ? FORWARD : BACKWARD, 1, follow ? PUT_CURSEND : 0);
1252 msg_silent--;
1253 VIsual_active = VIsual_was_active;
1254 try_end(err);
1255 });
1256
1257 cleanup:
1258 free_register(reg);
1259 xfree(reg);
1260 }
1261
1262 /// Subscribes to event broadcasts.
1263 ///
1264 /// @param channel_id Channel id (passed automatically by the dispatcher)
1265 /// @param event Event type string
nvim_subscribe(uint64_t channel_id,String event)1266 void nvim_subscribe(uint64_t channel_id, String event)
1267 FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
1268 {
1269 size_t length = (event.size < METHOD_MAXLEN ? event.size : METHOD_MAXLEN);
1270 char e[METHOD_MAXLEN + 1];
1271 memcpy(e, event.data, length);
1272 e[length] = NUL;
1273 rpc_subscribe(channel_id, e);
1274 }
1275
1276 /// Unsubscribes to event broadcasts.
1277 ///
1278 /// @param channel_id Channel id (passed automatically by the dispatcher)
1279 /// @param event Event type string
nvim_unsubscribe(uint64_t channel_id,String event)1280 void nvim_unsubscribe(uint64_t channel_id, String event)
1281 FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
1282 {
1283 size_t length = (event.size < METHOD_MAXLEN ?
1284 event.size :
1285 METHOD_MAXLEN);
1286 char e[METHOD_MAXLEN + 1];
1287 memcpy(e, event.data, length);
1288 e[length] = NUL;
1289 rpc_unsubscribe(channel_id, e);
1290 }
1291
1292 /// Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or
1293 /// "#rrggbb" hexadecimal string.
1294 ///
1295 /// Example:
1296 /// <pre>
1297 /// :echo nvim_get_color_by_name("Pink")
1298 /// :echo nvim_get_color_by_name("#cbcbcb")
1299 /// </pre>
1300 ///
1301 /// @param name Color name or "#rrggbb" string
1302 /// @return 24-bit RGB value, or -1 for invalid argument.
nvim_get_color_by_name(String name)1303 Integer nvim_get_color_by_name(String name)
1304 FUNC_API_SINCE(1)
1305 {
1306 return name_to_color(name.data);
1307 }
1308
1309 /// Returns a map of color names and RGB values.
1310 ///
1311 /// Keys are color names (e.g. "Aqua") and values are 24-bit RGB color values
1312 /// (e.g. 65535).
1313 ///
1314 /// @return Map of color names and RGB values.
nvim_get_color_map(void)1315 Dictionary nvim_get_color_map(void)
1316 FUNC_API_SINCE(1)
1317 {
1318 Dictionary colors = ARRAY_DICT_INIT;
1319
1320 for (int i = 0; color_name_table[i].name != NULL; i++) {
1321 PUT(colors, color_name_table[i].name,
1322 INTEGER_OBJ(color_name_table[i].color));
1323 }
1324 return colors;
1325 }
1326
1327 /// Gets a map of the current editor state.
1328 ///
1329 /// @param opts Optional parameters.
1330 /// - types: List of |context-types| ("regs", "jumps", "bufs",
1331 /// "gvars", …) to gather, or empty for "all".
1332 /// @param[out] err Error details, if any
1333 ///
1334 /// @return map of global |context|.
nvim_get_context(Dict (context)* opts,Error * err)1335 Dictionary nvim_get_context(Dict(context) *opts, Error *err)
1336 FUNC_API_SINCE(6)
1337 {
1338 Array types = ARRAY_DICT_INIT;
1339 if (opts->types.type == kObjectTypeArray) {
1340 types = opts->types.data.array;
1341 } else if (opts->types.type != kObjectTypeNil) {
1342 api_set_error(err, kErrorTypeValidation, "invalid value for key: types");
1343 return (Dictionary)ARRAY_DICT_INIT;
1344 }
1345
1346 int int_types = types.size > 0 ? 0 : kCtxAll;
1347 if (types.size > 0) {
1348 for (size_t i = 0; i < types.size; i++) {
1349 if (types.items[i].type == kObjectTypeString) {
1350 const char *const s = types.items[i].data.string.data;
1351 if (strequal(s, "regs")) {
1352 int_types |= kCtxRegs;
1353 } else if (strequal(s, "jumps")) {
1354 int_types |= kCtxJumps;
1355 } else if (strequal(s, "bufs")) {
1356 int_types |= kCtxBufs;
1357 } else if (strequal(s, "gvars")) {
1358 int_types |= kCtxGVars;
1359 } else if (strequal(s, "sfuncs")) {
1360 int_types |= kCtxSFuncs;
1361 } else if (strequal(s, "funcs")) {
1362 int_types |= kCtxFuncs;
1363 } else {
1364 api_set_error(err, kErrorTypeValidation, "unexpected type: %s", s);
1365 return (Dictionary)ARRAY_DICT_INIT;
1366 }
1367 }
1368 }
1369 }
1370
1371 Context ctx = CONTEXT_INIT;
1372 ctx_save(&ctx, int_types);
1373 Dictionary dict = ctx_to_dict(&ctx);
1374 ctx_free(&ctx);
1375 return dict;
1376 }
1377
1378 /// Sets the current editor state from the given |context| map.
1379 ///
1380 /// @param dict |Context| map.
nvim_load_context(Dictionary dict)1381 Object nvim_load_context(Dictionary dict)
1382 FUNC_API_SINCE(6)
1383 {
1384 Context ctx = CONTEXT_INIT;
1385
1386 int save_did_emsg = did_emsg;
1387 did_emsg = false;
1388
1389 ctx_from_dict(dict, &ctx);
1390 if (!did_emsg) {
1391 ctx_restore(&ctx, kCtxAll);
1392 }
1393
1394 ctx_free(&ctx);
1395
1396 did_emsg = save_did_emsg;
1397 return (Object)OBJECT_INIT;
1398 }
1399
1400 /// Gets the current mode. |mode()|
1401 /// "blocking" is true if Nvim is waiting for input.
1402 ///
1403 /// @returns Dictionary { "mode": String, "blocking": Boolean }
nvim_get_mode(void)1404 Dictionary nvim_get_mode(void)
1405 FUNC_API_SINCE(2) FUNC_API_FAST
1406 {
1407 Dictionary rv = ARRAY_DICT_INIT;
1408 char *modestr = get_mode();
1409 bool blocked = input_blocking();
1410
1411 PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr)));
1412 PUT(rv, "blocking", BOOLEAN_OBJ(blocked));
1413
1414 return rv;
1415 }
1416
1417 /// Gets a list of global (non-buffer-local) |mapping| definitions.
1418 ///
1419 /// @param mode Mode short-name ("n", "i", "v", ...)
1420 /// @returns Array of maparg()-like dictionaries describing mappings.
1421 /// The "buffer" key is always zero.
nvim_get_keymap(String mode)1422 ArrayOf(Dictionary) nvim_get_keymap(String mode)
1423 FUNC_API_SINCE(3)
1424 {
1425 return keymap_array(mode, NULL);
1426 }
1427
1428 /// Sets a global |mapping| for the given mode.
1429 ///
1430 /// To set a buffer-local mapping, use |nvim_buf_set_keymap()|.
1431 ///
1432 /// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs}
1433 /// or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
1434 ///
1435 /// Example:
1436 /// <pre>
1437 /// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
1438 /// </pre>
1439 ///
1440 /// is equivalent to:
1441 /// <pre>
1442 /// nmap <nowait> <Space><NL> <Nop>
1443 /// </pre>
1444 ///
1445 /// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
1446 /// or "!" for |:map!|, or empty string for |:map|.
1447 /// @param lhs Left-hand-side |{lhs}| of the mapping.
1448 /// @param rhs Right-hand-side |{rhs}| of the mapping.
1449 /// @param opts Optional parameters map. Accepts all |:map-arguments|
1450 /// as keys excluding |<buffer>| but including |noremap|.
1451 /// Values are Booleans. Unknown key is an error.
1452 /// @param[out] err Error details, if any.
nvim_set_keymap(String mode,String lhs,String rhs,Dict (keymap)* opts,Error * err)1453 void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err)
1454 FUNC_API_SINCE(6)
1455 {
1456 modify_keymap(-1, false, mode, lhs, rhs, opts, err);
1457 }
1458
1459 /// Unmaps a global |mapping| for the given mode.
1460 ///
1461 /// To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|.
1462 ///
1463 /// @see |nvim_set_keymap()|
nvim_del_keymap(String mode,String lhs,Error * err)1464 void nvim_del_keymap(String mode, String lhs, Error *err)
1465 FUNC_API_SINCE(6)
1466 {
1467 nvim_buf_del_keymap(-1, mode, lhs, err);
1468 }
1469
1470 /// Gets a map of global (non-buffer-local) Ex commands.
1471 ///
1472 /// Currently only |user-commands| are supported, not builtin Ex commands.
1473 ///
1474 /// @param opts Optional parameters. Currently only supports
1475 /// {"builtin":false}
1476 /// @param[out] err Error details, if any.
1477 ///
1478 /// @returns Map of maps describing commands.
nvim_get_commands(Dict (get_commands)* opts,Error * err)1479 Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
1480 FUNC_API_SINCE(4)
1481 {
1482 return nvim_buf_get_commands(-1, opts, err);
1483 }
1484
1485 /// Returns a 2-tuple (Array), where item 0 is the current channel id and item
1486 /// 1 is the |api-metadata| map (Dictionary).
1487 ///
1488 /// @returns 2-tuple [{channel-id}, {api-metadata}]
nvim_get_api_info(uint64_t channel_id)1489 Array nvim_get_api_info(uint64_t channel_id)
1490 FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY
1491 {
1492 Array rv = ARRAY_DICT_INIT;
1493
1494 assert(channel_id <= INT64_MAX);
1495 ADD(rv, INTEGER_OBJ((int64_t)channel_id));
1496 ADD(rv, DICTIONARY_OBJ(api_metadata()));
1497
1498 return rv;
1499 }
1500
1501 /// Self-identifies the client.
1502 ///
1503 /// The client/plugin/application should call this after connecting, to provide
1504 /// hints about its identity and purpose, for debugging and orchestration.
1505 ///
1506 /// Can be called more than once; the caller should merge old info if
1507 /// appropriate. Example: library first identifies the channel, then a plugin
1508 /// using that library later identifies itself.
1509 ///
1510 /// @note "Something is better than nothing". You don't need to include all the
1511 /// fields.
1512 ///
1513 /// @param channel_id
1514 /// @param name Short name for the connected client
1515 /// @param version Dictionary describing the version, with these
1516 /// (optional) keys:
1517 /// - "major" major version (defaults to 0 if not set, for no release yet)
1518 /// - "minor" minor version
1519 /// - "patch" patch number
1520 /// - "prerelease" string describing a prerelease, like "dev" or "beta1"
1521 /// - "commit" hash or similar identifier of commit
1522 /// @param type Must be one of the following values. Client libraries should
1523 /// default to "remote" unless overridden by the user.
1524 /// - "remote" remote client connected to Nvim.
1525 /// - "ui" gui frontend
1526 /// - "embedder" application using Nvim as a component (for example,
1527 /// IDE/editor implementing a vim mode).
1528 /// - "host" plugin host, typically started by nvim
1529 /// - "plugin" single plugin, started by nvim
1530 /// @param methods Builtin methods in the client. For a host, this does not
1531 /// include plugin methods which will be discovered later.
1532 /// The key should be the method name, the values are dicts with
1533 /// these (optional) keys (more keys may be added in future
1534 /// versions of Nvim, thus unknown keys are ignored. Clients
1535 /// must only use keys defined in this or later versions of
1536 /// Nvim):
1537 /// - "async" if true, send as a notification. If false or unspecified,
1538 /// use a blocking request
1539 /// - "nargs" Number of arguments. Could be a single integer or an array
1540 /// of two integers, minimum and maximum inclusive.
1541 ///
1542 /// @param attributes Arbitrary string:string map of informal client properties.
1543 /// Suggested keys:
1544 /// - "website": Client homepage URL (e.g. GitHub repository)
1545 /// - "license": License description ("Apache 2", "GPLv3", "MIT", …)
1546 /// - "logo": URI or path to image, preferably small logo or icon.
1547 /// .png or .svg format is preferred.
1548 ///
1549 /// @param[out] err Error details, if any
nvim_set_client_info(uint64_t channel_id,String name,Dictionary version,String type,Dictionary methods,Dictionary attributes,Error * err)1550 void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, String type,
1551 Dictionary methods, Dictionary attributes, Error *err)
1552 FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
1553 {
1554 Dictionary info = ARRAY_DICT_INIT;
1555 PUT(info, "name", copy_object(STRING_OBJ(name)));
1556
1557 version = copy_dictionary(version);
1558 bool has_major = false;
1559 for (size_t i = 0; i < version.size; i++) {
1560 if (strequal(version.items[i].key.data, "major")) {
1561 has_major = true;
1562 break;
1563 }
1564 }
1565 if (!has_major) {
1566 PUT(version, "major", INTEGER_OBJ(0));
1567 }
1568 PUT(info, "version", DICTIONARY_OBJ(version));
1569
1570 PUT(info, "type", copy_object(STRING_OBJ(type)));
1571 PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods)));
1572 PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes)));
1573
1574 rpc_set_client_info(channel_id, info);
1575 }
1576
1577 /// Gets information about a channel.
1578 ///
1579 /// @returns Dictionary describing a channel, with these keys:
1580 /// - "id" Channel id.
1581 /// - "argv" (optional) Job arguments list.
1582 /// - "stream" Stream underlying the channel.
1583 /// - "stdio" stdin and stdout of this Nvim instance
1584 /// - "stderr" stderr of this Nvim instance
1585 /// - "socket" TCP/IP socket or named pipe
1586 /// - "job" Job with communication over its stdio.
1587 /// - "mode" How data received on the channel is interpreted.
1588 /// - "bytes" Send and receive raw bytes.
1589 /// - "terminal" |terminal| instance interprets ASCII sequences.
1590 /// - "rpc" |RPC| communication on the channel is active.
1591 /// - "pty" (optional) Name of pseudoterminal. On a POSIX system this
1592 /// is a device path like "/dev/pts/1". If the name is unknown,
1593 /// the key will still be present if a pty is used (e.g. for
1594 /// winpty on Windows).
1595 /// - "buffer" (optional) Buffer with connected |terminal| instance.
1596 /// - "client" (optional) Info about the peer (client on the other end of
1597 /// the RPC channel), if provided by it via
1598 /// |nvim_set_client_info()|.
1599 ///
nvim_get_chan_info(Integer chan,Error * err)1600 Dictionary nvim_get_chan_info(Integer chan, Error *err)
1601 FUNC_API_SINCE(4)
1602 {
1603 if (chan < 0) {
1604 return (Dictionary)ARRAY_DICT_INIT;
1605 }
1606 return channel_info((uint64_t)chan);
1607 }
1608
1609 /// Get information about all open channels.
1610 ///
1611 /// @returns Array of Dictionaries, each describing a channel with
1612 /// the format specified at |nvim_get_chan_info()|.
nvim_list_chans(void)1613 Array nvim_list_chans(void)
1614 FUNC_API_SINCE(4)
1615 {
1616 return channel_all_info();
1617 }
1618
1619 /// Calls many API methods atomically.
1620 ///
1621 /// This has two main usages:
1622 /// 1. To perform several requests from an async context atomically, i.e.
1623 /// without interleaving redraws, RPC requests from other clients, or user
1624 /// interactions (however API methods may trigger autocommands or event
1625 /// processing which have such side-effects, e.g. |:sleep| may wake timers).
1626 /// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
1627 ///
1628 /// @param channel_id
1629 /// @param calls an array of calls, where each call is described by an array
1630 /// with two elements: the request name, and an array of arguments.
1631 /// @param[out] err Validation error details (malformed `calls` parameter),
1632 /// if any. Errors from batched calls are given in the return value.
1633 ///
1634 /// @return Array of two elements. The first is an array of return
1635 /// values. The second is NIL if all calls succeeded. If a call resulted in
1636 /// an error, it is a three-element array with the zero-based index of the call
1637 /// which resulted in an error, the error type and the error message. If an
1638 /// error occurred, the values from all preceding calls will still be returned.
nvim_call_atomic(uint64_t channel_id,Array calls,Error * err)1639 Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
1640 FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
1641 {
1642 Array rv = ARRAY_DICT_INIT;
1643 Array results = ARRAY_DICT_INIT;
1644 Error nested_error = ERROR_INIT;
1645
1646 size_t i; // also used for freeing the variables
1647 for (i = 0; i < calls.size; i++) {
1648 if (calls.items[i].type != kObjectTypeArray) {
1649 api_set_error(err,
1650 kErrorTypeValidation,
1651 "Items in calls array must be arrays");
1652 goto validation_error;
1653 }
1654 Array call = calls.items[i].data.array;
1655 if (call.size != 2) {
1656 api_set_error(err,
1657 kErrorTypeValidation,
1658 "Items in calls array must be arrays of size 2");
1659 goto validation_error;
1660 }
1661
1662 if (call.items[0].type != kObjectTypeString) {
1663 api_set_error(err,
1664 kErrorTypeValidation,
1665 "Name must be String");
1666 goto validation_error;
1667 }
1668 String name = call.items[0].data.string;
1669
1670 if (call.items[1].type != kObjectTypeArray) {
1671 api_set_error(err,
1672 kErrorTypeValidation,
1673 "Args must be Array");
1674 goto validation_error;
1675 }
1676 Array args = call.items[1].data.array;
1677
1678 MsgpackRpcRequestHandler handler =
1679 msgpack_rpc_get_handler_for(name.data,
1680 name.size,
1681 &nested_error);
1682
1683 if (ERROR_SET(&nested_error)) {
1684 break;
1685 }
1686 Object result = handler.fn(channel_id, args, &nested_error);
1687 if (ERROR_SET(&nested_error)) {
1688 // error handled after loop
1689 break;
1690 }
1691
1692 ADD(results, result);
1693 }
1694
1695 ADD(rv, ARRAY_OBJ(results));
1696 if (ERROR_SET(&nested_error)) {
1697 Array errval = ARRAY_DICT_INIT;
1698 ADD(errval, INTEGER_OBJ((Integer)i));
1699 ADD(errval, INTEGER_OBJ(nested_error.type));
1700 ADD(errval, STRING_OBJ(cstr_to_string(nested_error.msg)));
1701 ADD(rv, ARRAY_OBJ(errval));
1702 } else {
1703 ADD(rv, NIL);
1704 }
1705 goto theend;
1706
1707 validation_error:
1708 api_free_array(results);
1709 theend:
1710 api_clear_error(&nested_error);
1711 return rv;
1712 }
1713
1714 /// Writes a message to vim output or error buffer. The string is split
1715 /// and flushed after each newline. Incomplete lines are kept for writing
1716 /// later.
1717 ///
1718 /// @param message Message to write
1719 /// @param to_err true: message is an error (uses `emsg` instead of `msg`)
write_msg(String message,bool to_err)1720 static void write_msg(String message, bool to_err)
1721 {
1722 static size_t out_pos = 0, err_pos = 0;
1723 static char out_line_buf[LINE_BUFFER_SIZE], err_line_buf[LINE_BUFFER_SIZE];
1724
1725 #define PUSH_CHAR(i, pos, line_buf, msg) \
1726 if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \
1727 line_buf[pos] = NUL; \
1728 msg(line_buf); \
1729 pos = 0; \
1730 continue; \
1731 } \
1732 line_buf[pos++] = message.data[i];
1733
1734 ++no_wait_return;
1735 for (uint32_t i = 0; i < message.size; i++) {
1736 if (to_err) {
1737 PUSH_CHAR(i, err_pos, err_line_buf, emsg);
1738 } else {
1739 PUSH_CHAR(i, out_pos, out_line_buf, msg);
1740 }
1741 }
1742 --no_wait_return;
1743 msg_end();
1744 }
1745
1746 // Functions used for testing purposes
1747
1748 /// Returns object given as argument.
1749 ///
1750 /// This API function is used for testing. One should not rely on its presence
1751 /// in plugins.
1752 ///
1753 /// @param[in] obj Object to return.
1754 ///
1755 /// @return its argument.
nvim__id(Object obj)1756 Object nvim__id(Object obj)
1757 {
1758 return copy_object(obj);
1759 }
1760
1761 /// Returns array given as argument.
1762 ///
1763 /// This API function is used for testing. One should not rely on its presence
1764 /// in plugins.
1765 ///
1766 /// @param[in] arr Array to return.
1767 ///
1768 /// @return its argument.
nvim__id_array(Array arr)1769 Array nvim__id_array(Array arr)
1770 {
1771 return copy_object(ARRAY_OBJ(arr)).data.array;
1772 }
1773
1774 /// Returns dictionary given as argument.
1775 ///
1776 /// This API function is used for testing. One should not rely on its presence
1777 /// in plugins.
1778 ///
1779 /// @param[in] dct Dictionary to return.
1780 ///
1781 /// @return its argument.
nvim__id_dictionary(Dictionary dct)1782 Dictionary nvim__id_dictionary(Dictionary dct)
1783 {
1784 return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
1785 }
1786
1787 /// Returns floating-point value given as argument.
1788 ///
1789 /// This API function is used for testing. One should not rely on its presence
1790 /// in plugins.
1791 ///
1792 /// @param[in] flt Value to return.
1793 ///
1794 /// @return its argument.
nvim__id_float(Float flt)1795 Float nvim__id_float(Float flt)
1796 {
1797 return flt;
1798 }
1799
1800 /// Gets internal stats.
1801 ///
1802 /// @return Map of various internal stats.
nvim__stats(void)1803 Dictionary nvim__stats(void)
1804 {
1805 Dictionary rv = ARRAY_DICT_INIT;
1806 PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync));
1807 PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
1808 PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_refcount));
1809 return rv;
1810 }
1811
1812 /// Gets a list of dictionaries representing attached UIs.
1813 ///
1814 /// @return Array of UI dictionaries, each with these keys:
1815 /// - "height" Requested height of the UI
1816 /// - "width" Requested width of the UI
1817 /// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
1818 /// - "ext_..." Requested UI extensions, see |ui-option|
1819 /// - "chan" Channel id of remote UI (not present for TUI)
nvim_list_uis(void)1820 Array nvim_list_uis(void)
1821 FUNC_API_SINCE(4)
1822 {
1823 return ui_array();
1824 }
1825
1826 /// Gets the immediate children of process `pid`.
1827 ///
1828 /// @return Array of child process ids, empty if process not found.
nvim_get_proc_children(Integer pid,Error * err)1829 Array nvim_get_proc_children(Integer pid, Error *err)
1830 FUNC_API_SINCE(4)
1831 {
1832 Array rvobj = ARRAY_DICT_INIT;
1833 int *proc_list = NULL;
1834
1835 if (pid <= 0 || pid > INT_MAX) {
1836 api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
1837 goto end;
1838 }
1839
1840 size_t proc_count;
1841 int rv = os_proc_children((int)pid, &proc_list, &proc_count);
1842 if (rv != 0) {
1843 // syscall failed (possibly because of kernel options), try shelling out.
1844 DLOG("fallback to vim._os_proc_children()");
1845 Array a = ARRAY_DICT_INIT;
1846 ADD(a, INTEGER_OBJ(pid));
1847 String s = cstr_to_string("return vim._os_proc_children(select(1, ...))");
1848 Object o = nlua_exec(s, a, err);
1849 api_free_string(s);
1850 api_free_array(a);
1851 if (o.type == kObjectTypeArray) {
1852 rvobj = o.data.array;
1853 } else if (!ERROR_SET(err)) {
1854 api_set_error(err, kErrorTypeException,
1855 "Failed to get process children. pid=%" PRId64 " error=%d",
1856 pid, rv);
1857 }
1858 goto end;
1859 }
1860
1861 for (size_t i = 0; i < proc_count; i++) {
1862 ADD(rvobj, INTEGER_OBJ(proc_list[i]));
1863 }
1864
1865 end:
1866 xfree(proc_list);
1867 return rvobj;
1868 }
1869
1870 /// Gets info describing process `pid`.
1871 ///
1872 /// @return Map of process properties, or NIL if process not found.
nvim_get_proc(Integer pid,Error * err)1873 Object nvim_get_proc(Integer pid, Error *err)
1874 FUNC_API_SINCE(4)
1875 {
1876 Object rvobj = OBJECT_INIT;
1877 rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT;
1878 rvobj.type = kObjectTypeDictionary;
1879
1880 if (pid <= 0 || pid > INT_MAX) {
1881 api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
1882 return NIL;
1883 }
1884 #ifdef WIN32
1885 rvobj.data.dictionary = os_proc_info((int)pid);
1886 if (rvobj.data.dictionary.size == 0) { // Process not found.
1887 return NIL;
1888 }
1889 #else
1890 // Cross-platform process info APIs are miserable, so use `ps` instead.
1891 Array a = ARRAY_DICT_INIT;
1892 ADD(a, INTEGER_OBJ(pid));
1893 String s = cstr_to_string("return vim._os_proc_info(select(1, ...))");
1894 Object o = nlua_exec(s, a, err);
1895 api_free_string(s);
1896 api_free_array(a);
1897 if (o.type == kObjectTypeArray && o.data.array.size == 0) {
1898 return NIL; // Process not found.
1899 } else if (o.type == kObjectTypeDictionary) {
1900 rvobj.data.dictionary = o.data.dictionary;
1901 } else if (!ERROR_SET(err)) {
1902 api_set_error(err, kErrorTypeException,
1903 "Failed to get process info. pid=%" PRId64, pid);
1904 }
1905 #endif
1906 return rvobj;
1907 }
1908
1909 /// Selects an item in the completion popupmenu.
1910 ///
1911 /// If |ins-completion| is not active this API call is silently ignored.
1912 /// Useful for an external UI using |ui-popupmenu| to control the popupmenu
1913 /// with the mouse. Can also be used in a mapping; use <cmd> |:map-cmd| to
1914 /// ensure the mapping doesn't end completion mode.
1915 ///
1916 /// @param item Index (zero-based) of the item to select. Value of -1 selects
1917 /// nothing and restores the original text.
1918 /// @param insert Whether the selection should be inserted in the buffer.
1919 /// @param finish Finish the completion and dismiss the popupmenu. Implies
1920 /// `insert`.
1921 /// @param opts Optional parameters. Reserved for future use.
1922 /// @param[out] err Error details, if any
nvim_select_popupmenu_item(Integer item,Boolean insert,Boolean finish,Dictionary opts,Error * err)1923 void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dictionary opts,
1924 Error *err)
1925 FUNC_API_SINCE(6)
1926 {
1927 if (opts.size > 0) {
1928 api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
1929 return;
1930 }
1931
1932 if (finish) {
1933 insert = true;
1934 }
1935
1936 pum_ext_select_item((int)item, insert, finish);
1937 }
1938
1939 /// NB: if your UI doesn't use hlstate, this will not return hlstate first time
nvim__inspect_cell(Integer grid,Integer row,Integer col,Error * err)1940 Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
1941 {
1942 Array ret = ARRAY_DICT_INIT;
1943
1944 // TODO(bfredl): if grid == 0 we should read from the compositor's buffer.
1945 // The only problem is that it does not yet exist.
1946 ScreenGrid *g = &default_grid;
1947 if (grid == pum_grid.handle) {
1948 g = &pum_grid;
1949 } else if (grid > 1) {
1950 win_T *wp = get_win_by_grid_handle((handle_T)grid);
1951 if (wp != NULL && wp->w_grid_alloc.chars != NULL) {
1952 g = &wp->w_grid_alloc;
1953 } else {
1954 api_set_error(err, kErrorTypeValidation,
1955 "No grid with the given handle");
1956 return ret;
1957 }
1958 }
1959
1960 if (row < 0 || row >= g->Rows
1961 || col < 0 || col >= g->Columns) {
1962 return ret;
1963 }
1964 size_t off = g->line_offset[(size_t)row] + (size_t)col;
1965 ADD(ret, STRING_OBJ(cstr_to_string((char *)g->chars[off])));
1966 int attr = g->attrs[off];
1967 ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err)));
1968 // will not work first time
1969 if (!highlight_use_hlstate()) {
1970 ADD(ret, ARRAY_OBJ(hl_inspect(attr)));
1971 }
1972 return ret;
1973 }
1974
nvim__screenshot(String path)1975 void nvim__screenshot(String path)
1976 FUNC_API_FAST
1977 {
1978 ui_call_screenshot(path);
1979 }
1980
1981
1982 /// Deletes a uppercase/file named mark. See |mark-motions|.
1983 ///
1984 /// @note fails with error if a lowercase or buffer local named mark is used.
1985 /// @param name Mark name
1986 /// @return true if the mark was deleted, else false.
1987 /// @see |nvim_buf_del_mark()|
1988 /// @see |nvim_get_mark()|
nvim_del_mark(String name,Error * err)1989 Boolean nvim_del_mark(String name, Error *err)
1990 FUNC_API_SINCE(8)
1991 {
1992 bool res = false;
1993 if (name.size != 1) {
1994 api_set_error(err, kErrorTypeValidation,
1995 "Mark name must be a single character");
1996 return res;
1997 }
1998 // Only allow file/uppercase marks
1999 // TODO(muniter): Refactor this ASCII_ISUPPER macro to a proper function
2000 if (ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data)) {
2001 res = set_mark(NULL, name, 0, 0, err);
2002 } else {
2003 api_set_error(err, kErrorTypeValidation,
2004 "Only file/uppercase marks allowed, invalid mark name: '%c'",
2005 *name.data);
2006 }
2007 return res;
2008 }
2009
2010 /// Return a tuple (row, col, buffer, buffername) representing the position of
2011 /// the uppercase/file named mark. See |mark-motions|.
2012 ///
2013 /// Marks are (1,0)-indexed. |api-indexing|
2014 ///
2015 /// @note fails with error if a lowercase or buffer local named mark is used.
2016 /// @param name Mark name
2017 /// @param opts Optional parameters. Reserved for future use.
2018 /// @return 4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if the mark is
2019 /// not set.
2020 /// @see |nvim_buf_set_mark()|
2021 /// @see |nvim_del_mark()|
nvim_get_mark(String name,Dictionary opts,Error * err)2022 Array nvim_get_mark(String name, Dictionary opts, Error *err)
2023 FUNC_API_SINCE(8)
2024 {
2025 Array rv = ARRAY_DICT_INIT;
2026
2027 if (name.size != 1) {
2028 api_set_error(err, kErrorTypeValidation,
2029 "Mark name must be a single character");
2030 return rv;
2031 } else if (!(ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data))) {
2032 api_set_error(err, kErrorTypeValidation,
2033 "Only file/uppercase marks allowed, invalid mark name: '%c'",
2034 *name.data);
2035 return rv;
2036 }
2037
2038 xfmark_T mark = get_global_mark(*name.data);
2039 pos_T pos = mark.fmark.mark;
2040 bool allocated = false;
2041 int bufnr;
2042 char *filename;
2043
2044 // Marks are from an open buffer it fnum is non zero
2045 if (mark.fmark.fnum != 0) {
2046 bufnr = mark.fmark.fnum;
2047 filename = (char *)buflist_nr2name(bufnr, true, true);
2048 allocated = true;
2049 // Marks comes from shada
2050 } else {
2051 filename = (char *)mark.fname;
2052 bufnr = 0;
2053 }
2054
2055 bool exists = filename != NULL;
2056 Integer row;
2057 Integer col;
2058
2059 if (!exists || pos.lnum <= 0) {
2060 if (allocated) {
2061 xfree(filename);
2062 allocated = false;
2063 }
2064 filename = "";
2065 bufnr = 0;
2066 row = 0;
2067 col = 0;
2068 } else {
2069 row = pos.lnum;
2070 col = pos.col;
2071 }
2072
2073 ADD(rv, INTEGER_OBJ(row));
2074 ADD(rv, INTEGER_OBJ(col));
2075 ADD(rv, INTEGER_OBJ(bufnr));
2076 ADD(rv, STRING_OBJ(cstr_to_string(filename)));
2077
2078 if (allocated) {
2079 xfree(filename);
2080 }
2081
2082 return rv;
2083 }
2084
2085 /// Evaluates statusline string.
2086 ///
2087 /// @param str Statusline string (see 'statusline').
2088 /// @param opts Optional parameters.
2089 /// - winid: (number) |window-ID| of the window to use as context for statusline.
2090 /// - maxwidth: (number) Maximum width of statusline.
2091 /// - fillchar: (string) Character to fill blank spaces in the statusline (see
2092 /// 'fillchars').
2093 /// - highlights: (boolean) Return highlight information.
2094 /// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid}
2095 /// is ignored.
2096 ///
2097 /// @param[out] err Error details, if any.
2098 /// @return Dictionary containing statusline information, with these keys:
2099 /// - str: (string) Characters that will be displayed on the statusline.
2100 /// - width: (number) Display width of the statusline.
2101 /// - highlights: Array containing highlight information of the statusline. Only included when
2102 /// the "highlights" key in {opts} is |TRUE|. Each element of the array is a
2103 /// |Dictionary| with these keys:
2104 /// - start: (number) Byte index (0-based) of first character that uses the highlight.
2105 /// - group: (string) Name of highlight group.
nvim_eval_statusline(String str,Dict (eval_statusline)* opts,Error * err)2106 Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *err)
2107 FUNC_API_SINCE(8) FUNC_API_FAST
2108 {
2109 Dictionary result = ARRAY_DICT_INIT;
2110
2111 int maxwidth;
2112 char fillchar = 0;
2113 Window window = 0;
2114 bool use_tabline = false;
2115 bool highlights = false;
2116
2117 if (HAS_KEY(opts->winid)) {
2118 if (opts->winid.type != kObjectTypeInteger) {
2119 api_set_error(err, kErrorTypeValidation, "winid must be an integer");
2120 return result;
2121 }
2122
2123 window = (Window)opts->winid.data.integer;
2124 }
2125
2126 if (HAS_KEY(opts->fillchar)) {
2127 if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size > 1) {
2128 api_set_error(err, kErrorTypeValidation, "fillchar must be an ASCII character");
2129 return result;
2130 }
2131
2132 fillchar = opts->fillchar.data.string.data[0];
2133 }
2134
2135 if (HAS_KEY(opts->highlights)) {
2136 highlights = api_object_to_bool(opts->highlights, "highlights", false, err);
2137
2138 if (ERROR_SET(err)) {
2139 return result;
2140 }
2141 }
2142
2143 if (HAS_KEY(opts->use_tabline)) {
2144 use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err);
2145
2146 if (ERROR_SET(err)) {
2147 return result;
2148 }
2149 }
2150
2151 win_T *wp, *ewp;
2152
2153 if (use_tabline) {
2154 wp = NULL;
2155 ewp = curwin;
2156 fillchar = ' ';
2157 } else {
2158 wp = find_window_by_handle(window, err);
2159 ewp = wp;
2160
2161 if (fillchar == 0) {
2162 int attr;
2163 fillchar = (char)fillchar_status(&attr, wp);
2164 }
2165 }
2166
2167 if (HAS_KEY(opts->maxwidth)) {
2168 if (opts->maxwidth.type != kObjectTypeInteger) {
2169 api_set_error(err, kErrorTypeValidation, "maxwidth must be an integer");
2170 return result;
2171 }
2172
2173 maxwidth = (int)opts->maxwidth.data.integer;
2174 } else {
2175 maxwidth = use_tabline ? Columns : wp->w_width;
2176 }
2177
2178 char buf[MAXPATHL];
2179 stl_hlrec_t *hltab;
2180 stl_hlrec_t **hltab_ptr = highlights ? &hltab : NULL;
2181
2182 // Temporarily reset 'cursorbind' to prevent side effects from moving the cursor away and back.
2183 int p_crb_save = ewp->w_p_crb;
2184 ewp->w_p_crb = false;
2185
2186 int width = build_stl_str_hl(ewp,
2187 (char_u *)buf,
2188 sizeof(buf),
2189 (char_u *)str.data,
2190 false,
2191 (char_u)fillchar,
2192 maxwidth,
2193 hltab_ptr,
2194 NULL);
2195
2196 PUT(result, "width", INTEGER_OBJ(width));
2197
2198 // Restore original value of 'cursorbind'
2199 ewp->w_p_crb = p_crb_save;
2200
2201 if (highlights) {
2202 Array hl_values = ARRAY_DICT_INIT;
2203 const char *grpname;
2204 char user_group[6];
2205
2206 // If first character doesn't have a defined highlight,
2207 // add the default highlight at the beginning of the highlight list
2208 if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) {
2209 Dictionary hl_info = ARRAY_DICT_INIT;
2210 grpname = get_default_stl_hl(wp);
2211
2212 PUT(hl_info, "start", INTEGER_OBJ(0));
2213 PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
2214
2215 ADD(hl_values, DICTIONARY_OBJ(hl_info));
2216 }
2217
2218 for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) {
2219 Dictionary hl_info = ARRAY_DICT_INIT;
2220
2221 PUT(hl_info, "start", INTEGER_OBJ((char *)sp->start - buf));
2222
2223 if (sp->userhl == 0) {
2224 grpname = get_default_stl_hl(wp);
2225 } else if (sp->userhl < 0) {
2226 grpname = (char *)syn_id2name(-sp->userhl);
2227 } else {
2228 snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
2229 grpname = user_group;
2230 }
2231
2232 PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
2233
2234 ADD(hl_values, DICTIONARY_OBJ(hl_info));
2235 }
2236
2237 PUT(result, "highlights", ARRAY_OBJ(hl_values));
2238 }
2239
2240 PUT(result, "str", CSTR_TO_OBJ((char *)buf));
2241
2242 return result;
2243 }
2244