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 <stdbool.h>
5#include <stdint.h>
6#include <stdlib.h>
7#include <limits.h>
8
9#include "nvim/ascii.h"
10#include "nvim/globals.h"
11#include "nvim/api/window.h"
12#include "nvim/api/private/defs.h"
13#include "nvim/api/private/helpers.h"
14#include "nvim/ex_docmd.h"
15#include "nvim/vim.h"
16#include "nvim/buffer.h"
17#include "nvim/cursor.h"
18#include "nvim/option.h"
19#include "nvim/window.h"
20#include "nvim/screen.h"
21#include "nvim/move.h"
22
23/// Gets the current buffer in a window
24///
25/// @param window Window handle, or 0 for current window
26/// @param[out] err Error details, if any
27/// @return Buffer handle
28Buffer nvim_win_get_buf(Window window, Error *err)
29 FUNC_API_SINCE(1)
30{
31 win_T *win = find_window_by_handle(window, err);
32
33 if (!win) {
34 return 0;
35 }
36
37 return win->w_buffer->handle;
38}
39
40/// Sets the current buffer in a window, without side-effects
41///
42/// @param window Window handle, or 0 for current window
43/// @param buffer Buffer handle
44/// @param[out] err Error details, if any
45void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
46 FUNC_API_SINCE(5)
47{
48 win_T *win = find_window_by_handle(window, err), *save_curwin = curwin;
49 buf_T *buf = find_buffer_by_handle(buffer, err);
50 tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab;
51
52 if (!win || !buf) {
53 return;
54 }
55
56 if (switch_win(&save_curwin, &save_curtab, win, tab, false) == FAIL) {
57 api_set_error(err,
58 kErrorTypeException,
59 "Failed to switch to window %d",
60 window);
61 }
62
63 try_start();
64 int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
65 if (!try_end(err) && result == FAIL) {
66 api_set_error(err,
67 kErrorTypeException,
68 "Failed to set buffer %d",
69 buffer);
70 }
71
72 // If window is not current, state logic will not validate its cursor.
73 // So do it now.
74 validate_cursor();
75
76 restore_win(save_curwin, save_curtab, false);
77}
78
79/// Gets the (1,0)-indexed cursor position in the window. |api-indexing|
80///
81/// @param window Window handle, or 0 for current window
82/// @param[out] err Error details, if any
83/// @return (row, col) tuple
84ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err)
85 FUNC_API_SINCE(1)
86{
87 Array rv = ARRAY_DICT_INIT;
88 win_T *win = find_window_by_handle(window, err);
89
90 if (win) {
91 ADD(rv, INTEGER_OBJ(win->w_cursor.lnum));
92 ADD(rv, INTEGER_OBJ(win->w_cursor.col));
93 }
94
95 return rv;
96}
97
98/// Sets the (1,0)-indexed cursor position in the window. |api-indexing|
99///
100/// @param window Window handle, or 0 for current window
101/// @param pos (row, col) tuple representing the new position
102/// @param[out] err Error details, if any
103void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
104 FUNC_API_SINCE(1)
105{
106 win_T *win = find_window_by_handle(window, err);
107
108 if (!win) {
109 return;
110 }
111
112 if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger
113 || pos.items[1].type != kObjectTypeInteger) {
114 api_set_error(err,
115 kErrorTypeValidation,
116 "Argument \"pos\" must be a [row, col] array");
117 return;
118 }
119
120 int64_t row = pos.items[0].data.integer;
121 int64_t col = pos.items[1].data.integer;
122
123 if (row <= 0 || row > win->w_buffer->b_ml.ml_line_count) {
124 api_set_error(err, kErrorTypeValidation, "Cursor position outside buffer");
125 return;
126 }
127
128 if (col > MAXCOL || col < 0) {
129 api_set_error(err, kErrorTypeValidation, "Column value outside range");
130 return;
131 }
132
133 win->w_cursor.lnum = (linenr_T)row;
134 win->w_cursor.col = (colnr_T)col;
135 win->w_cursor.coladd = 0;
136 // When column is out of range silently correct it.
137 check_cursor_col_win(win);
138
139 // Make sure we stick in this column.
140 win->w_set_curswant = true;
141
142 // make sure cursor is in visible range even if win != curwin
143 update_topline_win(win);
144
145 redraw_win_later(win, VALID);
146}
147
148/// Gets the window height
149///
150/// @param window Window handle, or 0 for current window
151/// @param[out] err Error details, if any
152/// @return Height as a count of rows
153Integer nvim_win_get_height(Window window, Error *err)
154 FUNC_API_SINCE(1)
155{
156 win_T *win = find_window_by_handle(window, err);
157
158 if (!win) {
159 return 0;
160 }
161
162 return win->w_height;
163}
164
165/// Sets the window height. This will only succeed if the screen is split
166/// horizontally.
167///
168/// @param window Window handle, or 0 for current window
169/// @param height Height as a count of rows
170/// @param[out] err Error details, if any
171void nvim_win_set_height(Window window, Integer height, Error *err)
172 FUNC_API_SINCE(1)
173{
174 win_T *win = find_window_by_handle(window, err);
175
176 if (!win) {
177 return;
178 }
179
180 if (height > INT_MAX || height < INT_MIN) {
181 api_set_error(err, kErrorTypeValidation, "Height value outside range");
182 return;
183 }
184
185 win_T *savewin = curwin;
186 curwin = win;
187 try_start();
188 win_setheight((int)height);
189 curwin = savewin;
190 try_end(err);
191}
192
193/// Gets the window width
194///
195/// @param window Window handle, or 0 for current window
196/// @param[out] err Error details, if any
197/// @return Width as a count of columns
198Integer nvim_win_get_width(Window window, Error *err)
199 FUNC_API_SINCE(1)
200{
201 win_T *win = find_window_by_handle(window, err);
202
203 if (!win) {
204 return 0;
205 }
206
207 return win->w_width;
208}
209
210/// Sets the window width. This will only succeed if the screen is split
211/// vertically.
212///
213/// @param window Window handle, or 0 for current window
214/// @param width Width as a count of columns
215/// @param[out] err Error details, if any
216void nvim_win_set_width(Window window, Integer width, Error *err)
217 FUNC_API_SINCE(1)
218{
219 win_T *win = find_window_by_handle(window, err);
220
221 if (!win) {
222 return;
223 }
224
225 if (width > INT_MAX || width < INT_MIN) {
226 api_set_error(err, kErrorTypeValidation, "Width value outside range");
227 return;
228 }
229
230 win_T *savewin = curwin;
231 curwin = win;
232 try_start();
233 win_setwidth((int)width);
234 curwin = savewin;
235 try_end(err);
236}
237
238/// Gets a window-scoped (w:) variable
239///
240/// @param window Window handle, or 0 for current window
241/// @param name Variable name
242/// @param[out] err Error details, if any
243/// @return Variable value
244Object nvim_win_get_var(Window window, String name, Error *err)
245 FUNC_API_SINCE(1)
246{
247 win_T *win = find_window_by_handle(window, err);
248
249 if (!win) {
250 return (Object) OBJECT_INIT;
251 }
252
253 return dict_get_value(win->w_vars, name, err);
254}
255
256/// Sets a window-scoped (w:) variable
257///
258/// @param window Window handle, or 0 for current window
259/// @param name Variable name
260/// @param value Variable value
261/// @param[out] err Error details, if any
262void nvim_win_set_var(Window window, String name, Object value, Error *err)
263 FUNC_API_SINCE(1)
264{
265 win_T *win = find_window_by_handle(window, err);
266
267 if (!win) {
268 return;
269 }
270
271 dict_set_var(win->w_vars, name, value, false, false, err);
272}
273
274/// Removes a window-scoped (w:) variable
275///
276/// @param window Window handle, or 0 for current window
277/// @param name Variable name
278/// @param[out] err Error details, if any
279void nvim_win_del_var(Window window, String name, Error *err)
280 FUNC_API_SINCE(1)
281{
282 win_T *win = find_window_by_handle(window, err);
283
284 if (!win) {
285 return;
286 }
287
288 dict_set_var(win->w_vars, name, NIL, true, false, err);
289}
290
291/// Sets a window-scoped (w:) variable
292///
293/// @deprecated
294///
295/// @param window Window handle, or 0 for current window
296/// @param name Variable name
297/// @param value Variable value
298/// @param[out] err Error details, if any
299/// @return Old value or nil if there was no previous value.
300///
301/// @warning It may return nil if there was no previous value
302/// or if previous value was `v:null`.
303Object window_set_var(Window window, String name, Object value, Error *err)
304{
305 win_T *win = find_window_by_handle(window, err);
306
307 if (!win) {
308 return (Object) OBJECT_INIT;
309 }
310
311 return dict_set_var(win->w_vars, name, value, false, true, err);
312}
313
314/// Removes a window-scoped (w:) variable
315///
316/// @deprecated
317///
318/// @param window Window handle, or 0 for current window
319/// @param name variable name
320/// @param[out] err Error details, if any
321/// @return Old value
322Object window_del_var(Window window, String name, Error *err)
323{
324 win_T *win = find_window_by_handle(window, err);
325
326 if (!win) {
327 return (Object) OBJECT_INIT;
328 }
329
330 return dict_set_var(win->w_vars, name, NIL, true, true, err);
331}
332
333/// Gets a window option value
334///
335/// @param window Window handle, or 0 for current window
336/// @param name Option name
337/// @param[out] err Error details, if any
338/// @return Option value
339Object nvim_win_get_option(Window window, String name, Error *err)
340 FUNC_API_SINCE(1)
341{
342 win_T *win = find_window_by_handle(window, err);
343
344 if (!win) {
345 return (Object) OBJECT_INIT;
346 }
347
348 return get_option_from(win, SREQ_WIN, name, err);
349}
350
351/// Sets a window option value. Passing 'nil' as value deletes the option(only
352/// works if there's a global fallback)
353///
354/// @param channel_id
355/// @param window Window handle, or 0 for current window
356/// @param name Option name
357/// @param value Option value
358/// @param[out] err Error details, if any
359void nvim_win_set_option(uint64_t channel_id, Window window,
360 String name, Object value, Error *err)
361 FUNC_API_SINCE(1)
362{
363 win_T *win = find_window_by_handle(window, err);
364
365 if (!win) {
366 return;
367 }
368
369 set_option_to(channel_id, win, SREQ_WIN, name, value, err);
370}
371
372/// Gets the window position in display cells. First position is zero.
373///
374/// @param window Window handle, or 0 for current window
375/// @param[out] err Error details, if any
376/// @return (row, col) tuple with the window position
377ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err)
378 FUNC_API_SINCE(1)
379{
380 Array rv = ARRAY_DICT_INIT;
381 win_T *win = find_window_by_handle(window, err);
382
383 if (win) {
384 ADD(rv, INTEGER_OBJ(win->w_winrow));
385 ADD(rv, INTEGER_OBJ(win->w_wincol));
386 }
387
388 return rv;
389}
390
391/// Gets the window tabpage
392///
393/// @param window Window handle, or 0 for current window
394/// @param[out] err Error details, if any
395/// @return Tabpage that contains the window
396Tabpage nvim_win_get_tabpage(Window window, Error *err)
397 FUNC_API_SINCE(1)
398{
399 Tabpage rv = 0;
400 win_T *win = find_window_by_handle(window, err);
401
402 if (win) {
403 rv = win_find_tabpage(win)->handle;
404 }
405
406 return rv;
407}
408
409/// Gets the window number
410///
411/// @param window Window handle, or 0 for current window
412/// @param[out] err Error details, if any
413/// @return Window number
414Integer nvim_win_get_number(Window window, Error *err)
415 FUNC_API_SINCE(1)
416{
417 int rv = 0;
418 win_T *win = find_window_by_handle(window, err);
419
420 if (!win) {
421 return rv;
422 }
423
424 int tabnr;
425 win_get_tabwin(window, &tabnr, &rv);
426
427 return rv;
428}
429
430/// Checks if a window is valid
431///
432/// @param window Window handle, or 0 for current window
433/// @return true if the window is valid, false otherwise
434Boolean nvim_win_is_valid(Window window)
435 FUNC_API_SINCE(1)
436{
437 Error stub = ERROR_INIT;
438 Boolean ret = find_window_by_handle(window, &stub) != NULL;
439 api_clear_error(&stub);
440 return ret;
441}
442
443
444/// Configures window layout. Currently only for floating and external windows
445/// (including changing a split window to those layouts).
446///
447/// When reconfiguring a floating window, absent option keys will not be
448/// changed. `row`/`col` and `relative` must be reconfigured together.
449///
450/// @see |nvim_open_win()|
451///
452/// @param window Window handle, or 0 for current window
453/// @param config Map defining the window configuration,
454/// see |nvim_open_win()|
455/// @param[out] err Error details, if any
456void nvim_win_set_config(Window window, Dictionary config, Error *err)
457 FUNC_API_SINCE(6)
458{
459 win_T *win = find_window_by_handle(window, err);
460 if (!win) {
461 return;
462 }
463 bool new_float = !win->w_floating;
464 // reuse old values, if not overriden
465 FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
466
467 if (!parse_float_config(config, &fconfig, !new_float, err)) {
468 return;
469 }
470 if (new_float) {
471 if (!win_new_float(win, fconfig, err)) {
472 return;
473 }
474 redraw_later(NOT_VALID);
475 } else {
476 win_config_float(win, fconfig);
477 win->w_pos_changed = true;
478 }
479 if (fconfig.style == kWinStyleMinimal) {
480 win_set_minimal_style(win);
481 didset_window_options(win);
482 }
483}
484
485/// Gets window configuration.
486///
487/// The returned value may be given to |nvim_open_win()|.
488///
489/// `relative` is empty for normal windows.
490///
491/// @param window Window handle, or 0 for current window
492/// @param[out] err Error details, if any
493/// @return Map defining the window configuration, see |nvim_open_win()|
494Dictionary nvim_win_get_config(Window window, Error *err)
495 FUNC_API_SINCE(6)
496{
497 Dictionary rv = ARRAY_DICT_INIT;
498
499 win_T *wp = find_window_by_handle(window, err);
500 if (!wp) {
501 return rv;
502 }
503
504 FloatConfig *config = &wp->w_float_config;
505
506 PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable));
507 PUT(rv, "external", BOOLEAN_OBJ(config->external));
508
509 if (wp->w_floating) {
510 PUT(rv, "width", INTEGER_OBJ(config->width));
511 PUT(rv, "height", INTEGER_OBJ(config->height));
512 if (!config->external) {
513 if (config->relative == kFloatRelativeWindow) {
514 PUT(rv, "win", INTEGER_OBJ(config->window));
515 if (config->bufpos.lnum >= 0) {
516 Array pos = ARRAY_DICT_INIT;
517 ADD(pos, INTEGER_OBJ(config->bufpos.lnum));
518 ADD(pos, INTEGER_OBJ(config->bufpos.col));
519 PUT(rv, "bufpos", ARRAY_OBJ(pos));
520 }
521 }
522 PUT(rv, "anchor", STRING_OBJ(cstr_to_string(
523 float_anchor_str[config->anchor])));
524 PUT(rv, "row", FLOAT_OBJ(config->row));
525 PUT(rv, "col", FLOAT_OBJ(config->col));
526 }
527 }
528
529 const char *rel = (wp->w_floating && !config->external
530 ? float_relative_str[config->relative] : "");
531 PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel)));
532
533 return rv;
534}
535
536/// Closes the window (like |:close| with a |window-ID|).
537///
538/// @param window Window handle, or 0 for current window
539/// @param force Behave like `:close!` The last window of a buffer with
540/// unwritten changes can be closed. The buffer will become
541/// hidden, even if 'hidden' is not set.
542/// @param[out] err Error details, if any
543void nvim_win_close(Window window, Boolean force, Error *err)
544 FUNC_API_SINCE(6)
545{
546 win_T *win = find_window_by_handle(window, err);
547 if (!win) {
548 return;
549 }
550
551 if (cmdwin_type != 0) {
552 if (win == curwin) {
553 cmdwin_result = Ctrl_C;
554 } else {
555 api_set_error(err, kErrorTypeException, "%s", _(e_cmdwin));
556 }
557 return;
558 }
559
560 tabpage_T *tabpage = win_find_tabpage(win);
561 TryState tstate;
562 try_enter(&tstate);
563 ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
564 vim_ignored = try_leave(&tstate, err);
565}
566