blob: e611c52e4ccb27ea6f99ddf95c30cfbddb75c659 [file] [log] [blame]
Bram Moolenaar261f3462019-09-07 15:45:32 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * evalbuffer.c: Buffer related builtin functions
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17/*
18 * Mark references in functions of buffers.
19 */
20 int
21set_ref_in_buffers(int copyID)
22{
23 int abort = FALSE;
24 buf_T *bp;
25
26 FOR_ALL_BUFFERS(bp)
27 {
28 listener_T *lnr;
Bram Moolenaar261f3462019-09-07 15:45:32 +020029
30 for (lnr = bp->b_listener; !abort && lnr != NULL; lnr = lnr->lr_next)
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +000031 abort = abort || set_ref_in_callback(&lnr->lr_callback, copyID);
Bram Moolenaar261f3462019-09-07 15:45:32 +020032# ifdef FEAT_JOB_CHANNEL
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +000033 if (!abort)
34 abort = abort || set_ref_in_callback(&bp->b_prompt_callback, copyID);
35 if (!abort)
36 abort = abort || set_ref_in_callback(&bp->b_prompt_interrupt, copyID);
Bram Moolenaar261f3462019-09-07 15:45:32 +020037# endif
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +000038#ifdef FEAT_COMPL_FUNC
39 if (!abort)
40 abort = abort || set_ref_in_callback(&bp->b_cfu_cb, copyID);
41 if (!abort)
42 abort = abort || set_ref_in_callback(&bp->b_ofu_cb, copyID);
43 if (!abort)
44 abort = abort || set_ref_in_callback(&bp->b_tsrfu_cb, copyID);
45#endif
46 if (!abort)
47 abort = abort || set_ref_in_callback(&bp->b_tfu_cb, copyID);
Bram Moolenaar261f3462019-09-07 15:45:32 +020048 if (abort)
49 break;
50 }
51 return abort;
52}
53
54 buf_T *
55buflist_find_by_name(char_u *name, int curtab_only)
56{
57 int save_magic;
58 char_u *save_cpo;
59 buf_T *buf;
60
61 // Ignore 'magic' and 'cpoptions' here to make scripts portable
62 save_magic = p_magic;
63 p_magic = TRUE;
64 save_cpo = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +010065 p_cpo = empty_option;
Bram Moolenaar261f3462019-09-07 15:45:32 +020066
67 buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name),
68 TRUE, FALSE, curtab_only));
69
70 p_magic = save_magic;
71 p_cpo = save_cpo;
72 return buf;
73}
74
75/*
76 * Find a buffer by number or exact name.
77 */
78 buf_T *
79find_buffer(typval_T *avar)
80{
81 buf_T *buf = NULL;
82
83 if (avar->v_type == VAR_NUMBER)
84 buf = buflist_findnr((int)avar->vval.v_number);
Bram Moolenaar7b45d462021-03-27 19:09:02 +010085 else if (in_vim9script() && check_for_string_arg(avar, 0) == FAIL)
86 return NULL;
Bram Moolenaar261f3462019-09-07 15:45:32 +020087 else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL)
88 {
89 buf = buflist_findname_exp(avar->vval.v_string);
90 if (buf == NULL)
91 {
92 // No full path name match, try a match with a URL or a "nofile"
93 // buffer, these don't use the full path.
94 FOR_ALL_BUFFERS(buf)
95 if (buf->b_fname != NULL
Bram Moolenaar6d4b2f52022-08-25 15:11:15 +010096 && (path_with_url(buf->b_fname) || bt_nofilename(buf))
Bram Moolenaar261f3462019-09-07 15:45:32 +020097 && STRCMP(buf->b_fname, avar->vval.v_string) == 0)
98 break;
99 }
100 }
101 return buf;
102}
103
104/*
105 * If there is a window for "curbuf", make it the current window.
106 */
107 static void
108find_win_for_curbuf(void)
109{
110 wininfo_T *wip;
111
Bram Moolenaar3da85972022-11-27 19:45:49 +0000112 // The b_wininfo list should have the windows that recently contained the
113 // buffer, going over this is faster than going over all the windows.
114 // Do check the buffer is still there.
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200115 FOR_ALL_BUF_WININFO(curbuf, wip)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200116 {
Bram Moolenaar3da85972022-11-27 19:45:49 +0000117 if (wip->wi_win != NULL && wip->wi_win->w_buffer == curbuf)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200118 {
119 curwin = wip->wi_win;
120 break;
121 }
122 }
123}
124
zeertzjq228e4222022-11-20 11:13:17 +0000125typedef struct {
126 win_T *cob_curwin_save;
127 aco_save_T cob_aco;
128 int cob_using_aco;
129 int cob_save_VIsual_active;
130} cob_T;
131
132/*
133 * Used before making a change in "buf", which is not the current one: Make
134 * "buf" the current buffer and find a window for this buffer, so that side
135 * effects are done correctly (e.g., adjusting marks).
136 *
137 * Information is saved in "cob" and MUST be restored by calling
138 * change_other_buffer_restore().
Bram Moolenaare76062c2022-11-28 18:51:43 +0000139 *
140 * If this fails then "curbuf" will not be equal to "buf".
zeertzjq228e4222022-11-20 11:13:17 +0000141 */
142 static void
143change_other_buffer_prepare(cob_T *cob, buf_T *buf)
144{
145 CLEAR_POINTER(cob);
146
147 // Set "curbuf" to the buffer being changed. Then make sure there is a
148 // window for it to handle any side effects.
149 cob->cob_save_VIsual_active = VIsual_active;
150 VIsual_active = FALSE;
151 cob->cob_curwin_save = curwin;
152 curbuf = buf;
153 find_win_for_curbuf(); // simplest: find existing window for "buf"
154
155 if (curwin->w_buffer != buf)
156 {
157 // No existing window for this buffer. It is dangerous to have
158 // curwin->w_buffer differ from "curbuf", use the autocmd window.
159 curbuf = curwin->w_buffer;
160 aucmd_prepbuf(&cob->cob_aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +0000161 if (curbuf == buf)
162 cob->cob_using_aco = TRUE;
zeertzjq228e4222022-11-20 11:13:17 +0000163 }
164}
165
166 static void
167change_other_buffer_restore(cob_T *cob)
168{
169 if (cob->cob_using_aco)
170 {
171 aucmd_restbuf(&cob->cob_aco);
172 }
173 else
174 {
175 curwin = cob->cob_curwin_save;
176 curbuf = curwin->w_buffer;
177 }
178 VIsual_active = cob->cob_save_VIsual_active;
179}
180
Bram Moolenaar261f3462019-09-07 15:45:32 +0200181/*
Bram Moolenaar34453202021-01-31 13:08:38 +0100182 * Set line or list of lines in buffer "buf" to "lines".
183 * Any type is allowed and converted to a string.
Bram Moolenaar261f3462019-09-07 15:45:32 +0200184 */
185 static void
186set_buffer_lines(
187 buf_T *buf,
188 linenr_T lnum_arg,
189 int append,
190 typval_T *lines,
191 typval_T *rettv)
192{
193 linenr_T lnum = lnum_arg + (append ? 1 : 0);
194 char_u *line = NULL;
195 list_T *l = NULL;
196 listitem_T *li = NULL;
197 long added = 0;
198 linenr_T append_lnum;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200199
200 // When using the current buffer ml_mfp will be set if needed. Useful when
201 // setline() is used on startup. For other buffers the buffer must be
202 // loaded.
Bram Moolenaarc934bfa2022-11-19 13:59:43 +0000203 int is_curbuf = buf == curbuf;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200204 if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1)
205 {
206 rettv->vval.v_number = 1; // FAIL
Bram Moolenaar8dac2ac2021-12-27 20:57:06 +0000207 if (in_vim9script() && lnum < 1)
208 semsg(_(e_invalid_line_number_nr), lnum_arg);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200209 return;
210 }
211
zeertzjq228e4222022-11-20 11:13:17 +0000212 // After this don't use "return", goto "cleanup"!
213 cob_T cob;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200214 if (!is_curbuf)
zeertzjq228e4222022-11-20 11:13:17 +0000215 // set "curbuf" to "buf" and find a window for this buffer
216 change_other_buffer_prepare(&cob, buf);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200217
218 if (append)
219 // appendbufline() uses the line number below which we insert
220 append_lnum = lnum - 1;
221 else
222 // setbufline() uses the line number above which we insert, we only
223 // append if it's below the last line
224 append_lnum = curbuf->b_ml.ml_line_count;
225
226 if (lines->v_type == VAR_LIST)
227 {
228 l = lines->vval.v_list;
Bram Moolenaarad48e6c2020-04-21 22:19:45 +0200229 if (l == NULL || list_len(l) == 0)
230 {
Bram Moolenaarcd9c8d42022-11-05 23:46:43 +0000231 // not appending anything always succeeds
Bram Moolenaarad48e6c2020-04-21 22:19:45 +0200232 goto done;
233 }
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200234 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200235 li = l->lv_first;
236 }
237 else
Bram Moolenaar34453202021-01-31 13:08:38 +0100238 line = typval_tostring(lines, FALSE);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200239
240 // default result is zero == OK
241 for (;;)
242 {
243 if (l != NULL)
244 {
245 // list argument, get next string
246 if (li == NULL)
247 break;
Bram Moolenaar34453202021-01-31 13:08:38 +0100248 vim_free(line);
249 line = typval_tostring(&li->li_tv, FALSE);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200250 li = li->li_next;
251 }
252
253 rettv->vval.v_number = 1; // FAIL
254 if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1)
255 break;
256
257 // When coming here from Insert mode, sync undo, so that this can be
258 // undone separately from what was previously inserted.
259 if (u_sync_once == 2)
260 {
261 u_sync_once = 1; // notify that u_sync() was called
262 u_sync(TRUE);
263 }
264
265 if (!append && lnum <= curbuf->b_ml.ml_line_count)
266 {
267 // Existing line, replace it.
268 // Removes any existing text properties.
269 if (u_savesub(lnum) == OK && ml_replace_len(
270 lnum, line, (colnr_T)STRLEN(line) + 1, TRUE, TRUE) == OK)
271 {
272 changed_bytes(lnum, 0);
273 if (is_curbuf && lnum == curwin->w_cursor.lnum)
274 check_cursor_col();
275 rettv->vval.v_number = 0; // OK
276 }
277 }
278 else if (added > 0 || u_save(lnum - 1, lnum) == OK)
279 {
280 // append the line
281 ++added;
282 if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK)
283 rettv->vval.v_number = 0; // OK
284 }
285
286 if (l == NULL) // only one string argument
287 break;
288 ++lnum;
289 }
Bram Moolenaar34453202021-01-31 13:08:38 +0100290 vim_free(line);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200291
292 if (added > 0)
293 {
294 win_T *wp;
295 tabpage_T *tp;
296
297 appended_lines_mark(append_lnum, added);
298
299 // Only adjust the cursor for buffers other than the current, unless it
300 // is the current window. For curbuf and other windows it has been
301 // done in mark_adjust_internal().
302 FOR_ALL_TAB_WINDOWS(tp, wp)
303 if (wp->w_buffer == buf
304 && (wp->w_buffer != curbuf || wp == curwin)
305 && wp->w_cursor.lnum > append_lnum)
306 wp->w_cursor.lnum += added;
307 check_cursor_col();
Bram Moolenaar58a3cae2022-09-08 13:43:10 +0100308
309 // Only update the window view if w_buffer matches curbuf, otherwise
310 // the computations will be wrong.
311 if (curwin->w_buffer == curbuf)
312 update_topline();
Bram Moolenaar261f3462019-09-07 15:45:32 +0200313 }
314
Bram Moolenaarad48e6c2020-04-21 22:19:45 +0200315done:
Bram Moolenaar261f3462019-09-07 15:45:32 +0200316 if (!is_curbuf)
zeertzjq228e4222022-11-20 11:13:17 +0000317 change_other_buffer_restore(&cob);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200318}
319
320/*
321 * "append(lnum, string/list)" function
322 */
323 void
324f_append(typval_T *argvars, typval_T *rettv)
325{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200326 linenr_T lnum;
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000327 int did_emsg_before = did_emsg;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200328
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200329 if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
330 return;
331
332 lnum = tv_get_lnum(&argvars[0]);
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000333 if (did_emsg == did_emsg_before)
334 set_buffer_lines(curbuf, lnum, TRUE, &argvars[1], rettv);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200335}
336
337/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +0200338 * Set or append lines to a buffer.
Bram Moolenaar261f3462019-09-07 15:45:32 +0200339 */
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +0200340 static void
341buf_set_append_line(typval_T *argvars, typval_T *rettv, int append)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200342{
343 linenr_T lnum;
344 buf_T *buf;
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000345 int did_emsg_before = did_emsg;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200346
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200347 if (in_vim9script()
348 && (check_for_buffer_arg(argvars, 0) == FAIL
349 || check_for_lnum_arg(argvars, 1) == FAIL
350 || check_for_string_or_number_or_list_arg(argvars, 2) == FAIL))
351 return;
352
Bram Moolenaar261f3462019-09-07 15:45:32 +0200353 buf = tv_get_buf(&argvars[0], FALSE);
354 if (buf == NULL)
355 rettv->vval.v_number = 1; // FAIL
356 else
357 {
358 lnum = tv_get_lnum_buf(&argvars[1], buf);
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000359 if (did_emsg == did_emsg_before)
360 set_buffer_lines(buf, lnum, append, &argvars[2], rettv);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200361 }
362}
363
364/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +0200365 * "appendbufline(buf, lnum, string/list)" function
366 */
367 void
368f_appendbufline(typval_T *argvars, typval_T *rettv)
369{
370 buf_set_append_line(argvars, rettv, TRUE);
371}
372
373/*
Bram Moolenaar261f3462019-09-07 15:45:32 +0200374 * "bufadd(expr)" function
375 */
376 void
377f_bufadd(typval_T *argvars, typval_T *rettv)
378{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200379 char_u *name;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200380
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200381 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
382 return;
383
384 name = tv_get_string(&argvars[0]);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200385 rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0);
386}
387
388/*
389 * "bufexists(expr)" function
390 */
391 void
392f_bufexists(typval_T *argvars, typval_T *rettv)
393{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200394 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
395 return;
396
Bram Moolenaar261f3462019-09-07 15:45:32 +0200397 rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL);
398}
399
400/*
401 * "buflisted(expr)" function
402 */
403 void
404f_buflisted(typval_T *argvars, typval_T *rettv)
405{
406 buf_T *buf;
407
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200408 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
409 return;
410
Bram Moolenaar261f3462019-09-07 15:45:32 +0200411 buf = find_buffer(&argvars[0]);
412 rettv->vval.v_number = (buf != NULL && buf->b_p_bl);
413}
414
415/*
416 * "bufload(expr)" function
417 */
418 void
419f_bufload(typval_T *argvars, typval_T *rettv UNUSED)
420{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200421 buf_T *buf;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200422
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200423 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
424 return;
425
426 buf = get_buf_arg(&argvars[0]);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200427 if (buf != NULL)
428 buffer_ensure_loaded(buf);
429}
430
431/*
432 * "bufloaded(expr)" function
433 */
434 void
435f_bufloaded(typval_T *argvars, typval_T *rettv)
436{
437 buf_T *buf;
438
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200439 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
440 return;
441
Bram Moolenaar261f3462019-09-07 15:45:32 +0200442 buf = find_buffer(&argvars[0]);
443 rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
444}
445
446/*
447 * "bufname(expr)" function
448 */
449 void
450f_bufname(typval_T *argvars, typval_T *rettv)
451{
452 buf_T *buf;
Bram Moolenaar02aaad92020-08-30 21:26:57 +0200453 typval_T *tv = &argvars[0];
Bram Moolenaar261f3462019-09-07 15:45:32 +0200454
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200455 if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
456 return;
457
Bram Moolenaar02aaad92020-08-30 21:26:57 +0200458 if (tv->v_type == VAR_UNKNOWN)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200459 buf = curbuf;
460 else
Bram Moolenaar3767e3a2020-09-01 23:06:01 +0200461 buf = tv_get_buf_from_arg(tv);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200462 rettv->v_type = VAR_STRING;
463 if (buf != NULL && buf->b_fname != NULL)
464 rettv->vval.v_string = vim_strsave(buf->b_fname);
465 else
466 rettv->vval.v_string = NULL;
467}
468
469/*
470 * "bufnr(expr)" function
471 */
472 void
473f_bufnr(typval_T *argvars, typval_T *rettv)
474{
475 buf_T *buf;
476 int error = FALSE;
477 char_u *name;
478
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200479 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +0200480 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200481 || (argvars[0].v_type != VAR_UNKNOWN
482 && check_for_opt_bool_arg(argvars, 1) == FAIL)))
483 return;
484
Bram Moolenaar261f3462019-09-07 15:45:32 +0200485 if (argvars[0].v_type == VAR_UNKNOWN)
486 buf = curbuf;
487 else
Bram Moolenaar3767e3a2020-09-01 23:06:01 +0200488 buf = tv_get_buf_from_arg(&argvars[0]);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200489
490 // If the buffer isn't found and the second argument is not zero create a
491 // new buffer.
492 if (buf == NULL
493 && argvars[1].v_type != VAR_UNKNOWN
Bram Moolenaarfe136c92020-09-04 18:35:26 +0200494 && tv_get_bool_chk(&argvars[1], &error) != 0
Bram Moolenaar261f3462019-09-07 15:45:32 +0200495 && !error
496 && (name = tv_get_string_chk(&argvars[0])) != NULL
497 && !error)
498 buf = buflist_new(name, NULL, (linenr_T)1, 0);
499
500 if (buf != NULL)
501 rettv->vval.v_number = buf->b_fnum;
502 else
503 rettv->vval.v_number = -1;
504}
505
506 static void
507buf_win_common(typval_T *argvars, typval_T *rettv, int get_nr)
508{
509 win_T *wp;
510 int winnr = 0;
511 buf_T *buf;
512
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200513 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
514 return;
515
Bram Moolenaar3767e3a2020-09-01 23:06:01 +0200516 buf = tv_get_buf_from_arg(&argvars[0]);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200517 FOR_ALL_WINDOWS(wp)
518 {
519 ++winnr;
520 if (wp->w_buffer == buf)
521 break;
522 }
523 rettv->vval.v_number = (wp != NULL ? (get_nr ? winnr : wp->w_id) : -1);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200524}
525
526/*
527 * "bufwinid(nr)" function
528 */
529 void
530f_bufwinid(typval_T *argvars, typval_T *rettv)
531{
532 buf_win_common(argvars, rettv, FALSE);
533}
534
535/*
536 * "bufwinnr(nr)" function
537 */
538 void
539f_bufwinnr(typval_T *argvars, typval_T *rettv)
540{
541 buf_win_common(argvars, rettv, TRUE);
542}
543
544/*
545 * "deletebufline()" function
546 */
547 void
548f_deletebufline(typval_T *argvars, typval_T *rettv)
549{
550 buf_T *buf;
551 linenr_T first, last;
552 linenr_T lnum;
553 long count;
554 int is_curbuf;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200555 tabpage_T *tp;
556 win_T *wp;
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000557 int did_emsg_before = did_emsg;
558
559 rettv->vval.v_number = 1; // FAIL by default
Bram Moolenaar261f3462019-09-07 15:45:32 +0200560
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200561 if (in_vim9script()
562 && (check_for_buffer_arg(argvars, 0) == FAIL
563 || check_for_lnum_arg(argvars, 1) == FAIL
564 || check_for_opt_lnum_arg(argvars, 2) == FAIL))
565 return;
566
Bram Moolenaar261f3462019-09-07 15:45:32 +0200567 buf = tv_get_buf(&argvars[0], FALSE);
568 if (buf == NULL)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200569 return;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200570
571 first = tv_get_lnum_buf(&argvars[1], buf);
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000572 if (did_emsg > did_emsg_before)
573 return;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200574 if (argvars[2].v_type != VAR_UNKNOWN)
575 last = tv_get_lnum_buf(&argvars[2], buf);
576 else
577 last = first;
578
579 if (buf->b_ml.ml_mfp == NULL || first < 1
580 || first > buf->b_ml.ml_line_count || last < first)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200581 return;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200582
zeertzjq7af3ee22022-11-06 22:26:05 +0000583 // After this don't use "return", goto "cleanup"!
zeertzjq228e4222022-11-20 11:13:17 +0000584 is_curbuf = buf == curbuf;
585 cob_T cob;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200586 if (!is_curbuf)
zeertzjq228e4222022-11-20 11:13:17 +0000587 // set "curbuf" to "buf" and find a window for this buffer
588 change_other_buffer_prepare(&cob, buf);
589
Bram Moolenaar261f3462019-09-07 15:45:32 +0200590 if (last > curbuf->b_ml.ml_line_count)
591 last = curbuf->b_ml.ml_line_count;
592 count = last - first + 1;
593
594 // When coming here from Insert mode, sync undo, so that this can be
595 // undone separately from what was previously inserted.
596 if (u_sync_once == 2)
597 {
598 u_sync_once = 1; // notify that u_sync() was called
599 u_sync(TRUE);
600 }
601
602 if (u_save(first - 1, last + 1) == FAIL)
zeertzjq7af3ee22022-11-06 22:26:05 +0000603 goto cleanup;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200604
zeertzjq7af3ee22022-11-06 22:26:05 +0000605 for (lnum = first; lnum <= last; ++lnum)
606 ml_delete_flags(first, ML_DEL_MESSAGE);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200607
zeertzjq7af3ee22022-11-06 22:26:05 +0000608 FOR_ALL_TAB_WINDOWS(tp, wp)
609 if (wp->w_buffer == buf)
610 {
611 if (wp->w_cursor.lnum > last)
612 wp->w_cursor.lnum -= count;
613 else if (wp->w_cursor.lnum > first)
614 wp->w_cursor.lnum = first;
615 if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
616 wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
617 wp->w_valid = 0;
618 if (wp->w_cursor.lnum <= wp->w_topline)
619 wp->w_topline = 1;
620 }
621 check_cursor_col();
622 deleted_lines_mark(first, count);
623 rettv->vval.v_number = 0; // OK
624
625cleanup:
Bram Moolenaar261f3462019-09-07 15:45:32 +0200626 if (!is_curbuf)
zeertzjq228e4222022-11-20 11:13:17 +0000627 change_other_buffer_restore(&cob);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200628}
629
630/*
631 * Returns buffer options, variables and other attributes in a dictionary.
632 */
633 static dict_T *
634get_buffer_info(buf_T *buf)
635{
636 dict_T *dict;
637 tabpage_T *tp;
638 win_T *wp;
639 list_T *windows;
640
641 dict = dict_alloc();
642 if (dict == NULL)
643 return NULL;
644
645 dict_add_number(dict, "bufnr", buf->b_fnum);
646 dict_add_string(dict, "name", buf->b_ffname);
647 dict_add_number(dict, "lnum", buf == curbuf ? curwin->w_cursor.lnum
648 : buflist_findlnum(buf));
Bram Moolenaara9e96792019-12-17 22:40:15 +0100649 dict_add_number(dict, "linecount", buf->b_ml.ml_line_count);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200650 dict_add_number(dict, "loaded", buf->b_ml.ml_mfp != NULL);
651 dict_add_number(dict, "listed", buf->b_p_bl);
652 dict_add_number(dict, "changed", bufIsChanged(buf));
653 dict_add_number(dict, "changedtick", CHANGEDTICK(buf));
654 dict_add_number(dict, "hidden",
655 buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
656
657 // Get a reference to buffer variables
658 dict_add_dict(dict, "variables", buf->b_vars);
659
660 // List of windows displaying this buffer
661 windows = list_alloc();
662 if (windows != NULL)
663 {
664 FOR_ALL_TAB_WINDOWS(tp, wp)
665 if (wp->w_buffer == buf)
666 list_append_number(windows, (varnumber_T)wp->w_id);
667 dict_add_list(dict, "windows", windows);
668 }
669
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100670#ifdef FEAT_PROP_POPUP
Bram Moolenaar261f3462019-09-07 15:45:32 +0200671 // List of popup windows displaying this buffer
672 windows = list_alloc();
673 if (windows != NULL)
674 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200675 FOR_ALL_POPUPWINS(wp)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200676 if (wp->w_buffer == buf)
677 list_append_number(windows, (varnumber_T)wp->w_id);
678 FOR_ALL_TABPAGES(tp)
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200679 FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200680 if (wp->w_buffer == buf)
681 list_append_number(windows, (varnumber_T)wp->w_id);
682
683 dict_add_list(dict, "popups", windows);
684 }
685#endif
686
687#ifdef FEAT_SIGNS
688 if (buf->b_signlist != NULL)
689 {
690 // List of signs placed in this buffer
691 list_T *signs = list_alloc();
692 if (signs != NULL)
693 {
694 get_buffer_signs(buf, signs);
695 dict_add_list(dict, "signs", signs);
696 }
697 }
698#endif
699
Bram Moolenaar52410572019-10-27 05:12:45 +0100700#ifdef FEAT_VIMINFO
701 dict_add_number(dict, "lastused", buf->b_last_used);
702#endif
703
Bram Moolenaar261f3462019-09-07 15:45:32 +0200704 return dict;
705}
706
707/*
708 * "getbufinfo()" function
709 */
710 void
711f_getbufinfo(typval_T *argvars, typval_T *rettv)
712{
713 buf_T *buf = NULL;
714 buf_T *argbuf = NULL;
715 dict_T *d;
716 int filtered = FALSE;
717 int sel_buflisted = FALSE;
718 int sel_bufloaded = FALSE;
719 int sel_bufmodified = FALSE;
720
Bram Moolenaar93a10962022-06-16 11:42:09 +0100721 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200722 return;
723
Yegappan Lakshmanancd917202021-07-21 19:09:09 +0200724 if (in_vim9script()
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200725 && check_for_opt_buffer_or_dict_arg(argvars, 0) == FAIL)
Yegappan Lakshmanancd917202021-07-21 19:09:09 +0200726 return;
727
Bram Moolenaar261f3462019-09-07 15:45:32 +0200728 // List of all the buffers or selected buffers
729 if (argvars[0].v_type == VAR_DICT)
730 {
731 dict_T *sel_d = argvars[0].vval.v_dict;
732
733 if (sel_d != NULL)
734 {
Bram Moolenaar261f3462019-09-07 15:45:32 +0200735 filtered = TRUE;
Bram Moolenaard61efa52022-07-23 09:52:04 +0100736 sel_buflisted = dict_get_bool(sel_d, "buflisted", FALSE);
737 sel_bufloaded = dict_get_bool(sel_d, "bufloaded", FALSE);
738 sel_bufmodified = dict_get_bool(sel_d, "bufmodified",
Bram Moolenaar036c2cf2020-09-05 17:37:07 +0200739 FALSE);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200740 }
741 }
742 else if (argvars[0].v_type != VAR_UNKNOWN)
743 {
744 // Information about one buffer. Argument specifies the buffer
Bram Moolenaar3767e3a2020-09-01 23:06:01 +0200745 argbuf = tv_get_buf_from_arg(&argvars[0]);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200746 if (argbuf == NULL)
747 return;
748 }
749
750 // Return information about all the buffers or a specified buffer
751 FOR_ALL_BUFFERS(buf)
752 {
753 if (argbuf != NULL && argbuf != buf)
754 continue;
755 if (filtered && ((sel_bufloaded && buf->b_ml.ml_mfp == NULL)
756 || (sel_buflisted && !buf->b_p_bl)
757 || (sel_bufmodified && !buf->b_changed)))
758 continue;
759
760 d = get_buffer_info(buf);
761 if (d != NULL)
762 list_append_dict(rettv->vval.v_list, d);
763 if (argbuf != NULL)
764 return;
765 }
766}
767
768/*
769 * Get line or list of lines from buffer "buf" into "rettv".
770 * Return a range (from start to end) of lines in rettv from the specified
771 * buffer.
772 * If 'retlist' is TRUE, then the lines are returned as a Vim List.
773 */
774 static void
775get_buffer_lines(
776 buf_T *buf,
777 linenr_T start,
778 linenr_T end,
779 int retlist,
780 typval_T *rettv)
781{
782 char_u *p;
783
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100784 if (retlist)
785 {
786 if (rettv_list_alloc(rettv) == FAIL)
787 return;
788 }
789 else
790 {
791 rettv->v_type = VAR_STRING;
792 rettv->vval.v_string = NULL;
793 }
Bram Moolenaar261f3462019-09-07 15:45:32 +0200794
795 if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
796 return;
797
798 if (!retlist)
799 {
800 if (start >= 1 && start <= buf->b_ml.ml_line_count)
801 p = ml_get_buf(buf, start, FALSE);
802 else
803 p = (char_u *)"";
804 rettv->vval.v_string = vim_strsave(p);
805 }
806 else
807 {
808 if (end < start)
809 return;
810
811 if (start < 1)
812 start = 1;
813 if (end > buf->b_ml.ml_line_count)
814 end = buf->b_ml.ml_line_count;
815 while (start <= end)
816 if (list_append_string(rettv->vval.v_list,
817 ml_get_buf(buf, start++, FALSE), -1) == FAIL)
818 break;
819 }
820}
821
822/*
Bram Moolenaarce30ccc2022-11-21 19:57:04 +0000823 * "retlist" TRUE: "getbufline()" function
824 * "retlist" FALSE: "getbufoneline()" function
Bram Moolenaar261f3462019-09-07 15:45:32 +0200825 */
Bram Moolenaarce30ccc2022-11-21 19:57:04 +0000826 static void
827getbufline(typval_T *argvars, typval_T *rettv, int retlist)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200828{
Bram Moolenaare6e70a12020-10-22 18:23:38 +0200829 linenr_T lnum = 1;
830 linenr_T end = 1;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200831 buf_T *buf;
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000832 int did_emsg_before = did_emsg;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200833
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200834 if (in_vim9script()
835 && (check_for_buffer_arg(argvars, 0) == FAIL
836 || check_for_lnum_arg(argvars, 1) == FAIL
837 || check_for_opt_lnum_arg(argvars, 2) == FAIL))
838 return;
839
Bram Moolenaar3767e3a2020-09-01 23:06:01 +0200840 buf = tv_get_buf_from_arg(&argvars[0]);
Bram Moolenaare6e70a12020-10-22 18:23:38 +0200841 if (buf != NULL)
842 {
843 lnum = tv_get_lnum_buf(&argvars[1], buf);
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000844 if (did_emsg > did_emsg_before)
845 return;
Bram Moolenaare6e70a12020-10-22 18:23:38 +0200846 if (argvars[2].v_type == VAR_UNKNOWN)
847 end = lnum;
848 else
849 end = tv_get_lnum_buf(&argvars[2], buf);
850 }
Bram Moolenaar261f3462019-09-07 15:45:32 +0200851
Bram Moolenaarce30ccc2022-11-21 19:57:04 +0000852 get_buffer_lines(buf, lnum, end, retlist, rettv);
853}
854
855/*
856 * "getbufline()" function
857 */
858 void
859f_getbufline(typval_T *argvars, typval_T *rettv)
860{
861 getbufline(argvars, rettv, TRUE);
862}
863
864/*
865 * "getbufoneline()" function
866 */
867 void
868f_getbufoneline(typval_T *argvars, typval_T *rettv)
869{
870 getbufline(argvars, rettv, FALSE);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200871}
872
873/*
874 * "getline(lnum, [end])" function
875 */
876 void
877f_getline(typval_T *argvars, typval_T *rettv)
878{
879 linenr_T lnum;
880 linenr_T end;
881 int retlist;
882
Yegappan Lakshmanancd917202021-07-21 19:09:09 +0200883 if (in_vim9script()
884 && (check_for_lnum_arg(argvars, 0) == FAIL
885 || check_for_opt_lnum_arg(argvars, 1) == FAIL))
886 return;
887
Bram Moolenaar261f3462019-09-07 15:45:32 +0200888 lnum = tv_get_lnum(argvars);
889 if (argvars[1].v_type == VAR_UNKNOWN)
890 {
891 end = 0;
892 retlist = FALSE;
893 }
894 else
895 {
896 end = tv_get_lnum(&argvars[1]);
897 retlist = TRUE;
898 }
899
900 get_buffer_lines(curbuf, lnum, end, retlist, rettv);
901}
902
903/*
904 * "setbufline()" function
905 */
906 void
907f_setbufline(typval_T *argvars, typval_T *rettv)
908{
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +0200909 buf_set_append_line(argvars, rettv, FALSE);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200910}
911
912/*
913 * "setline()" function
914 */
915 void
916f_setline(typval_T *argvars, typval_T *rettv)
917{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200918 linenr_T lnum;
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000919 int did_emsg_before = did_emsg;
Bram Moolenaar261f3462019-09-07 15:45:32 +0200920
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200921 if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
922 return;
923
924 lnum = tv_get_lnum(&argvars[0]);
Bram Moolenaar8b6256f2021-12-28 11:24:49 +0000925 if (did_emsg == did_emsg_before)
926 set_buffer_lines(curbuf, lnum, FALSE, &argvars[1], rettv);
Bram Moolenaar261f3462019-09-07 15:45:32 +0200927}
928#endif // FEAT_EVAL
929
Dominique Pelle748b3082022-01-08 12:41:16 +0000930#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200931/*
932 * Make "buf" the current buffer. restore_buffer() MUST be called to undo.
933 * No autocommands will be executed. Use aucmd_prepbuf() if there are any.
934 */
935 void
936switch_buffer(bufref_T *save_curbuf, buf_T *buf)
937{
938 block_autocmds();
Bram Moolenaar3e0107e2021-01-02 13:53:59 +0100939#ifdef FEAT_FOLDING
940 ++disable_fold_update;
941#endif
Bram Moolenaar261f3462019-09-07 15:45:32 +0200942 set_bufref(save_curbuf, curbuf);
943 --curbuf->b_nwindows;
944 curbuf = buf;
945 curwin->w_buffer = buf;
946 ++curbuf->b_nwindows;
947}
948
949/*
950 * Restore the current buffer after using switch_buffer().
951 */
952 void
953restore_buffer(bufref_T *save_curbuf)
954{
955 unblock_autocmds();
Bram Moolenaar3e0107e2021-01-02 13:53:59 +0100956#ifdef FEAT_FOLDING
957 --disable_fold_update;
958#endif
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100959 // Check for valid buffer, just in case.
Bram Moolenaar261f3462019-09-07 15:45:32 +0200960 if (bufref_valid(save_curbuf))
961 {
962 --curbuf->b_nwindows;
963 curwin->w_buffer = save_curbuf->br_buf;
964 curbuf = save_curbuf->br_buf;
965 ++curbuf->b_nwindows;
966 }
967}
968
969/*
970 * Find a window for buffer "buf".
971 * If found OK is returned and "wp" and "tp" are set to the window and tabpage.
972 * If not found FAIL is returned.
973 */
974 static int
975find_win_for_buf(
976 buf_T *buf,
977 win_T **wp,
978 tabpage_T **tp)
979{
980 FOR_ALL_TAB_WINDOWS(*tp, *wp)
981 if ((*wp)->w_buffer == buf)
982 return OK;
983 return FAIL;
984}
985
986/*
987 * Find a window that contains "buf" and switch to it.
988 * If there is no such window, use the current window and change "curbuf".
989 * Caller must initialize save_curbuf to NULL.
990 * restore_win_for_buf() MUST be called later!
991 */
992 void
993switch_to_win_for_buf(
Bram Moolenaar18f47402022-01-06 13:24:51 +0000994 buf_T *buf,
995 switchwin_T *switchwin,
996 bufref_T *save_curbuf)
Bram Moolenaar261f3462019-09-07 15:45:32 +0200997{
998 win_T *wp;
999 tabpage_T *tp;
1000
1001 if (find_win_for_buf(buf, &wp, &tp) == FAIL)
1002 switch_buffer(save_curbuf, buf);
Bram Moolenaar18f47402022-01-06 13:24:51 +00001003 else if (switch_win(switchwin, wp, tp, TRUE) == FAIL)
Bram Moolenaar261f3462019-09-07 15:45:32 +02001004 {
Bram Moolenaar18f47402022-01-06 13:24:51 +00001005 restore_win(switchwin, TRUE);
Bram Moolenaar261f3462019-09-07 15:45:32 +02001006 switch_buffer(save_curbuf, buf);
1007 }
1008}
1009
1010 void
1011restore_win_for_buf(
Bram Moolenaar18f47402022-01-06 13:24:51 +00001012 switchwin_T *switchwin,
1013 bufref_T *save_curbuf)
Bram Moolenaar261f3462019-09-07 15:45:32 +02001014{
1015 if (save_curbuf->br_buf == NULL)
Bram Moolenaar18f47402022-01-06 13:24:51 +00001016 restore_win(switchwin, TRUE);
Bram Moolenaar261f3462019-09-07 15:45:32 +02001017 else
1018 restore_buffer(save_curbuf);
1019}
1020#endif