blob: 36ce494df86b4b1a20b95425ac9ad6bc372c2c64 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaarda861d62016-07-17 15:46:27 +02002 *
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/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +020011 * list.c: List support and container (List, Dict, Blob) functions.
Bram Moolenaarda861d62016-07-17 15:46:27 +020012 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010018// List heads for garbage collection.
19static list_T *first_list = NULL; // list of all lists
Bram Moolenaarda861d62016-07-17 15:46:27 +020020
Bram Moolenaar00d253e2020-04-06 22:13:01 +020021#define FOR_ALL_WATCHERS(l, lw) \
22 for ((lw) = (l)->lv_watch; (lw) != NULL; (lw) = (lw)->lw_next)
23
Bram Moolenaarbdff0122020-04-05 18:56:05 +020024static void list_free_item(list_T *l, listitem_T *item);
25
Bram Moolenaarda861d62016-07-17 15:46:27 +020026/*
27 * Add a watcher to a list.
28 */
29 void
30list_add_watch(list_T *l, listwatch_T *lw)
31{
32 lw->lw_next = l->lv_watch;
33 l->lv_watch = lw;
34}
35
36/*
37 * Remove a watcher from a list.
38 * No warning when it isn't found...
39 */
40 void
41list_rem_watch(list_T *l, listwatch_T *lwrem)
42{
43 listwatch_T *lw, **lwp;
44
45 lwp = &l->lv_watch;
Bram Moolenaar00d253e2020-04-06 22:13:01 +020046 FOR_ALL_WATCHERS(l, lw)
Bram Moolenaarda861d62016-07-17 15:46:27 +020047 {
48 if (lw == lwrem)
49 {
50 *lwp = lw->lw_next;
51 break;
52 }
53 lwp = &lw->lw_next;
54 }
55}
56
57/*
58 * Just before removing an item from a list: advance watchers to the next
59 * item.
60 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020061 static void
Bram Moolenaarda861d62016-07-17 15:46:27 +020062list_fix_watch(list_T *l, listitem_T *item)
63{
64 listwatch_T *lw;
65
Bram Moolenaar00d253e2020-04-06 22:13:01 +020066 FOR_ALL_WATCHERS(l, lw)
Bram Moolenaarda861d62016-07-17 15:46:27 +020067 if (lw->lw_item == item)
68 lw->lw_item = item->li_next;
69}
70
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010071 static void
72list_init(list_T *l)
73{
74 // Prepend the list to the list of lists for garbage collection.
75 if (first_list != NULL)
76 first_list->lv_used_prev = l;
77 l->lv_used_prev = NULL;
78 l->lv_used_next = first_list;
79 first_list = l;
80}
81
Bram Moolenaarda861d62016-07-17 15:46:27 +020082/*
83 * Allocate an empty header for a list.
84 * Caller should take care of the reference count.
85 */
86 list_T *
87list_alloc(void)
88{
89 list_T *l;
90
Bram Moolenaarc799fe22019-05-28 23:08:19 +020091 l = ALLOC_CLEAR_ONE(list_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +020092 if (l != NULL)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010093 list_init(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +020094 return l;
95}
96
97/*
Bram Moolenaarf49cc602018-11-11 15:21:05 +010098 * list_alloc() with an ID for alloc_fail().
99 */
100 list_T *
101list_alloc_id(alloc_id_T id UNUSED)
102{
103#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200104 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +0100105 return NULL;
106#endif
107 return (list_alloc());
108}
109
110/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100111 * Allocate space for a list, plus "count" items.
Bram Moolenaar24fe33a2022-11-24 00:09:02 +0000112 * This uses one allocation for efficiency.
113 * The reference count is not set.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100114 * Next list_set_item() must be called for each item.
115 */
116 list_T *
117list_alloc_with_items(int count)
118{
119 list_T *l;
120
121 l = (list_T *)alloc_clear(sizeof(list_T) + count * sizeof(listitem_T));
Bram Moolenaar24fe33a2022-11-24 00:09:02 +0000122 if (l == NULL)
123 return NULL;
124
125 list_init(l);
126
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000127 if (count <= 0)
128 return l;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100129
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000130 listitem_T *li = (listitem_T *)(l + 1);
131 int i;
132
133 l->lv_len = count;
134 l->lv_with_items = count;
135 l->lv_first = li;
136 l->lv_u.mat.lv_last = li + count - 1;
137 for (i = 0; i < count; ++i)
138 {
139 if (i == 0)
140 li->li_prev = NULL;
141 else
142 li->li_prev = li - 1;
143 if (i == count - 1)
144 li->li_next = NULL;
145 else
146 li->li_next = li + 1;
147 ++li;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100148 }
Bram Moolenaar24fe33a2022-11-24 00:09:02 +0000149
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100150 return l;
151}
152
153/*
154 * Set item "idx" for a list previously allocated with list_alloc_with_items().
155 * The contents of "tv" is moved into the list item.
156 * Each item must be set exactly once.
157 */
158 void
159list_set_item(list_T *l, int idx, typval_T *tv)
160{
161 listitem_T *li = (listitem_T *)(l + 1) + idx;
162
163 li->li_tv = *tv;
164}
165
166/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200167 * Allocate an empty list for a return value, with reference count set.
168 * Returns OK or FAIL.
169 */
170 int
171rettv_list_alloc(typval_T *rettv)
172{
173 list_T *l = list_alloc();
174
175 if (l == NULL)
176 return FAIL;
177
Bram Moolenaarda861d62016-07-17 15:46:27 +0200178 rettv->v_lock = 0;
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200179 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200180 return OK;
181}
182
183/*
Bram Moolenaar162b7142018-12-21 15:17:36 +0100184 * Same as rettv_list_alloc() but uses an allocation id for testing.
185 */
186 int
187rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED)
188{
189#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +0200190 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaar162b7142018-12-21 15:17:36 +0100191 return FAIL;
192#endif
193 return rettv_list_alloc(rettv);
194}
195
196
197/*
Bram Moolenaare809a4e2019-07-04 17:35:05 +0200198 * Set a list as the return value. Increments the reference count.
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200199 */
200 void
201rettv_list_set(typval_T *rettv, list_T *l)
202{
203 rettv->v_type = VAR_LIST;
204 rettv->vval.v_list = l;
205 if (l != NULL)
206 ++l->lv_refcount;
207}
208
209/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200210 * Unreference a list: decrement the reference count and free it when it
211 * becomes zero.
212 */
213 void
214list_unref(list_T *l)
215{
216 if (l != NULL && --l->lv_refcount <= 0)
217 list_free(l);
218}
219
220/*
221 * Free a list, including all non-container items it points to.
222 * Ignores the reference count.
223 */
224 static void
225list_free_contents(list_T *l)
226{
227 listitem_T *item;
228
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100229 if (l->lv_first != &range_list_item)
230 for (item = l->lv_first; item != NULL; item = l->lv_first)
231 {
232 // Remove the item before deleting it.
233 l->lv_first = item->li_next;
234 clear_tv(&item->li_tv);
235 list_free_item(l, item);
236 }
Bram Moolenaarda861d62016-07-17 15:46:27 +0200237}
238
239/*
240 * Go through the list of lists and free items without the copyID.
241 * But don't free a list that has a watcher (used in a for loop), these
242 * are not referenced anywhere.
243 */
244 int
245list_free_nonref(int copyID)
246{
247 list_T *ll;
248 int did_free = FALSE;
249
250 for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
251 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
252 && ll->lv_watch == NULL)
253 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100254 // Free the List and ordinary items it contains, but don't recurse
255 // into Lists and Dictionaries, they will be in the list of dicts
256 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200257 list_free_contents(ll);
258 did_free = TRUE;
259 }
260 return did_free;
261}
262
263 static void
264list_free_list(list_T *l)
265{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100266 // Remove the list from the list of lists for garbage collection.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200267 if (l->lv_used_prev == NULL)
268 first_list = l->lv_used_next;
269 else
270 l->lv_used_prev->lv_used_next = l->lv_used_next;
271 if (l->lv_used_next != NULL)
272 l->lv_used_next->lv_used_prev = l->lv_used_prev;
273
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100274 free_type(l->lv_type);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200275 vim_free(l);
276}
277
278 void
279list_free_items(int copyID)
280{
281 list_T *ll, *ll_next;
282
283 for (ll = first_list; ll != NULL; ll = ll_next)
284 {
285 ll_next = ll->lv_used_next;
286 if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
287 && ll->lv_watch == NULL)
288 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100289 // Free the List and ordinary items it contains, but don't recurse
290 // into Lists and Dictionaries, they will be in the list of dicts
291 // or list of lists.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200292 list_free_list(ll);
293 }
294 }
295}
296
297 void
298list_free(list_T *l)
299{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000300 if (in_free_unref_items)
301 return;
302
303 list_free_contents(l);
304 list_free_list(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200305}
306
307/*
308 * Allocate a list item.
309 * It is not initialized, don't forget to set v_lock.
310 */
311 listitem_T *
312listitem_alloc(void)
313{
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200314 return ALLOC_ONE(listitem_T);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200315}
316
317/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100318 * Free a list item, unless it was allocated together with the list itself.
319 * Does not clear the value. Does not notify watchers.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200320 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +0200321 static void
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100322list_free_item(list_T *l, listitem_T *item)
323{
324 if (l->lv_with_items == 0 || item < (listitem_T *)l
325 || item >= (listitem_T *)(l + 1) + l->lv_with_items)
326 vim_free(item);
327}
328
329/*
330 * Free a list item, unless it was allocated together with the list itself.
331 * Also clears the value. Does not notify watchers.
332 */
333 void
334listitem_free(list_T *l, listitem_T *item)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200335{
336 clear_tv(&item->li_tv);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100337 list_free_item(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200338}
339
340/*
341 * Remove a list item from a List and free it. Also clears the value.
342 */
343 void
344listitem_remove(list_T *l, listitem_T *item)
345{
346 vimlist_remove(l, item, item);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100347 listitem_free(l, item);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200348}
349
350/*
351 * Get the number of items in a list.
352 */
353 long
354list_len(list_T *l)
355{
356 if (l == NULL)
357 return 0L;
358 return l->lv_len;
359}
360
361/*
362 * Return TRUE when two lists have exactly the same values.
363 */
364 int
365list_equal(
366 list_T *l1,
367 list_T *l2,
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +0200368 int ic) // ignore case for strings
Bram Moolenaarda861d62016-07-17 15:46:27 +0200369{
370 listitem_T *item1, *item2;
371
Bram Moolenaarda861d62016-07-17 15:46:27 +0200372 if (l1 == l2)
373 return TRUE;
374 if (list_len(l1) != list_len(l2))
375 return FALSE;
Bram Moolenaar7b293c72020-04-09 21:33:22 +0200376 if (list_len(l1) == 0)
377 // empty and NULL list are considered equal
378 return TRUE;
379 if (l1 == NULL || l2 == NULL)
380 return FALSE;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200381
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200382 CHECK_LIST_MATERIALIZE(l1);
383 CHECK_LIST_MATERIALIZE(l2);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100384
Bram Moolenaarda861d62016-07-17 15:46:27 +0200385 for (item1 = l1->lv_first, item2 = l2->lv_first;
386 item1 != NULL && item2 != NULL;
387 item1 = item1->li_next, item2 = item2->li_next)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +0200388 if (!tv_equal(&item1->li_tv, &item2->li_tv, ic))
Bram Moolenaarda861d62016-07-17 15:46:27 +0200389 return FALSE;
390 return item1 == NULL && item2 == NULL;
391}
392
393/*
394 * Locate item with index "n" in list "l" and return it.
395 * A negative index is counted from the end; -1 is the last item.
396 * Returns NULL when "n" is out of range.
397 */
398 listitem_T *
399list_find(list_T *l, long n)
400{
401 listitem_T *item;
402 long idx;
403
404 if (l == NULL)
405 return NULL;
406
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100407 // Negative index is relative to the end.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200408 if (n < 0)
409 n = l->lv_len + n;
410
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100411 // Check for index out of range.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200412 if (n < 0 || n >= l->lv_len)
413 return NULL;
414
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200415 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100416
Christian Brabandtdf63da92023-11-23 20:14:28 +0100417 // range_list_materialize may reset l->lv_len
418 if (n >= l->lv_len)
419 return NULL;
420
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100421 // When there is a cached index may start search from there.
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100422 if (l->lv_u.mat.lv_idx_item != NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200423 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100424 if (n < l->lv_u.mat.lv_idx / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200425 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100426 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200427 item = l->lv_first;
428 idx = 0;
429 }
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100430 else if (n > (l->lv_u.mat.lv_idx + l->lv_len) / 2)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200431 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100432 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100433 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200434 idx = l->lv_len - 1;
435 }
436 else
437 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100438 // closest to the cached index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100439 item = l->lv_u.mat.lv_idx_item;
440 idx = l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200441 }
442 }
443 else
444 {
445 if (n < l->lv_len / 2)
446 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100447 // closest to the start of the list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200448 item = l->lv_first;
449 idx = 0;
450 }
451 else
452 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100453 // closest to the end of the list
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100454 item = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200455 idx = l->lv_len - 1;
456 }
457 }
458
459 while (n > idx)
460 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100461 // search forward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200462 item = item->li_next;
463 ++idx;
464 }
465 while (n < idx)
466 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100467 // search backward
Bram Moolenaarda861d62016-07-17 15:46:27 +0200468 item = item->li_prev;
469 --idx;
470 }
471
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100472 // cache the used index
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100473 l->lv_u.mat.lv_idx = idx;
474 l->lv_u.mat.lv_idx_item = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200475
476 return item;
477}
478
479/*
480 * Get list item "l[idx]" as a number.
481 */
482 long
483list_find_nr(
484 list_T *l,
485 long idx,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100486 int *errorp) // set to TRUE when something wrong
Bram Moolenaarda861d62016-07-17 15:46:27 +0200487{
488 listitem_T *li;
489
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100490 if (l != NULL && l->lv_first == &range_list_item)
491 {
492 long n = idx;
493
494 // not materialized range() list: compute the value.
495 // Negative index is relative to the end.
496 if (n < 0)
497 n = l->lv_len + n;
498
499 // Check for index out of range.
500 if (n < 0 || n >= l->lv_len)
501 {
502 if (errorp != NULL)
503 *errorp = TRUE;
504 return -1L;
505 }
506
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100507 return l->lv_u.nonmat.lv_start + n * l->lv_u.nonmat.lv_stride;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100508 }
509
Bram Moolenaarda861d62016-07-17 15:46:27 +0200510 li = list_find(l, idx);
511 if (li == NULL)
512 {
513 if (errorp != NULL)
514 *errorp = TRUE;
515 return -1L;
516 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100517 return (long)tv_get_number_chk(&li->li_tv, errorp);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200518}
519
520/*
521 * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
522 */
523 char_u *
524list_find_str(list_T *l, long idx)
525{
526 listitem_T *li;
527
528 li = list_find(l, idx - 1);
529 if (li == NULL)
530 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000531 semsg(_(e_list_index_out_of_range_nr), idx);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200532 return NULL;
533 }
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100534 return tv_get_string(&li->li_tv);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200535}
536
537/*
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100538 * Like list_find() but when a negative index is used that is not found use
539 * zero and set "idx" to zero. Used for first index of a range.
540 */
541 listitem_T *
542list_find_index(list_T *l, long *idx)
543{
544 listitem_T *li = list_find(l, *idx);
545
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000546 if (li != NULL)
547 return li;
548
549 if (*idx < 0)
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100550 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000551 *idx = 0;
552 li = list_find(l, *idx);
Bram Moolenaar5b5ae292021-02-20 17:04:02 +0100553 }
554 return li;
555}
556
557/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200558 * Locate "item" list "l" and return its index.
559 * Returns -1 when "item" is not in the list.
560 */
561 long
562list_idx_of_item(list_T *l, listitem_T *item)
563{
564 long idx = 0;
565 listitem_T *li;
566
567 if (l == NULL)
568 return -1;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200569 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200570 idx = 0;
571 for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
572 ++idx;
573 if (li == NULL)
574 return -1;
575 return idx;
576}
577
578/*
579 * Append item "item" to the end of list "l".
580 */
581 void
582list_append(list_T *l, listitem_T *item)
583{
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200584 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100585 if (l->lv_u.mat.lv_last == NULL)
Bram Moolenaarda861d62016-07-17 15:46:27 +0200586 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100587 // empty list
Bram Moolenaarda861d62016-07-17 15:46:27 +0200588 l->lv_first = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200589 item->li_prev = NULL;
590 }
591 else
592 {
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100593 l->lv_u.mat.lv_last->li_next = item;
594 item->li_prev = l->lv_u.mat.lv_last;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200595 }
Bram Moolenaar35c807d2022-01-27 16:36:29 +0000596 l->lv_u.mat.lv_last = item;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200597 ++l->lv_len;
598 item->li_next = NULL;
599}
600
601/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100602 * Append typval_T "tv" to the end of list "l". "tv" is copied.
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200603 * Return FAIL when out of memory or the type is wrong.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200604 */
605 int
606list_append_tv(list_T *l, typval_T *tv)
607{
Bram Moolenaar90fba562021-07-09 19:17:55 +0200608 listitem_T *li;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200609
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200610 if (l->lv_type != NULL && l->lv_type->tt_member != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +0200611 && check_typval_arg_type(l->lv_type->tt_member, tv,
612 NULL, 0) == FAIL)
Bram Moolenaarf32f0992021-07-08 20:53:40 +0200613 return FAIL;
Bram Moolenaar90fba562021-07-09 19:17:55 +0200614 li = listitem_alloc();
Bram Moolenaarda861d62016-07-17 15:46:27 +0200615 if (li == NULL)
616 return FAIL;
617 copy_tv(tv, &li->li_tv);
618 list_append(l, li);
619 return OK;
620}
621
622/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100623 * As list_append_tv() but move the value instead of copying it.
624 * Return FAIL when out of memory.
625 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200626 static int
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100627list_append_tv_move(list_T *l, typval_T *tv)
628{
629 listitem_T *li = listitem_alloc();
630
631 if (li == NULL)
632 return FAIL;
633 li->li_tv = *tv;
634 list_append(l, li);
635 return OK;
636}
637
638/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200639 * Add a dictionary to a list. Used by getqflist().
640 * Return FAIL when out of memory.
641 */
642 int
643list_append_dict(list_T *list, dict_T *dict)
644{
645 listitem_T *li = listitem_alloc();
646
647 if (li == NULL)
648 return FAIL;
649 li->li_tv.v_type = VAR_DICT;
650 li->li_tv.v_lock = 0;
651 li->li_tv.vval.v_dict = dict;
652 list_append(list, li);
653 ++dict->dv_refcount;
654 return OK;
655}
656
657/*
Bram Moolenaar4f505882018-02-10 21:06:32 +0100658 * Append list2 to list1.
659 * Return FAIL when out of memory.
660 */
661 int
Bram Moolenaar6f8d2ac2018-07-25 19:49:45 +0200662list_append_list(list_T *list1, list_T *list2)
Bram Moolenaar4f505882018-02-10 21:06:32 +0100663{
664 listitem_T *li = listitem_alloc();
665
666 if (li == NULL)
667 return FAIL;
668 li->li_tv.v_type = VAR_LIST;
669 li->li_tv.v_lock = 0;
670 li->li_tv.vval.v_list = list2;
671 list_append(list1, li);
672 ++list2->lv_refcount;
673 return OK;
674}
675
676/*
Bram Moolenaarda861d62016-07-17 15:46:27 +0200677 * Make a copy of "str" and append it as an item to list "l".
678 * When "len" >= 0 use "str[len]".
679 * Returns FAIL when out of memory.
680 */
681 int
682list_append_string(list_T *l, char_u *str, int len)
683{
684 listitem_T *li = listitem_alloc();
685
686 if (li == NULL)
687 return FAIL;
688 list_append(l, li);
689 li->li_tv.v_type = VAR_STRING;
690 li->li_tv.v_lock = 0;
691 if (str == NULL)
692 li->li_tv.vval.v_string = NULL;
693 else if ((li->li_tv.vval.v_string = (len >= 0 ? vim_strnsave(str, len)
694 : vim_strsave(str))) == NULL)
695 return FAIL;
696 return OK;
697}
698
699/*
700 * Append "n" to list "l".
701 * Returns FAIL when out of memory.
702 */
703 int
704list_append_number(list_T *l, varnumber_T n)
705{
706 listitem_T *li;
707
708 li = listitem_alloc();
709 if (li == NULL)
710 return FAIL;
711 li->li_tv.v_type = VAR_NUMBER;
712 li->li_tv.v_lock = 0;
713 li->li_tv.vval.v_number = n;
714 list_append(l, li);
715 return OK;
716}
717
718/*
719 * Insert typval_T "tv" in list "l" before "item".
720 * If "item" is NULL append at the end.
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100721 * Return FAIL when out of memory or the type is wrong.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200722 */
723 int
724list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
725{
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100726 listitem_T *ni;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200727
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100728 if (l->lv_type != NULL && l->lv_type->tt_member != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +0200729 && check_typval_arg_type(l->lv_type->tt_member, tv,
730 NULL, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100731 return FAIL;
732 ni = listitem_alloc();
Bram Moolenaarda861d62016-07-17 15:46:27 +0200733 if (ni == NULL)
734 return FAIL;
735 copy_tv(tv, &ni->li_tv);
736 list_insert(l, ni, item);
737 return OK;
738}
739
740 void
741list_insert(list_T *l, listitem_T *ni, listitem_T *item)
742{
Bram Moolenaar7e9f3512020-05-13 22:44:22 +0200743 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +0200744 if (item == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100745 // Append new item at end of list.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200746 list_append(l, ni);
747 else
748 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100749 // Insert new item before existing item.
Bram Moolenaarda861d62016-07-17 15:46:27 +0200750 ni->li_prev = item->li_prev;
751 ni->li_next = item;
752 if (item->li_prev == NULL)
753 {
754 l->lv_first = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100755 ++l->lv_u.mat.lv_idx;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200756 }
757 else
758 {
759 item->li_prev->li_next = ni;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +0100760 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +0200761 }
762 item->li_prev = ni;
763 ++l->lv_len;
764 }
765}
766
767/*
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200768 * Get the list item in "l" with index "n1". "n1" is adjusted if needed.
Bram Moolenaar22ebd172022-04-01 15:26:58 +0100769 * In Vim9, it is at the end of the list, add an item if "can_append" is TRUE.
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200770 * Return NULL if there is no such item.
771 */
772 listitem_T *
Bram Moolenaar22ebd172022-04-01 15:26:58 +0100773check_range_index_one(list_T *l, long *n1, int can_append, int quiet)
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200774{
Bram Moolenaar22ebd172022-04-01 15:26:58 +0100775 long orig_n1 = *n1;
776 listitem_T *li = list_find_index(l, n1);
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200777
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000778 if (li != NULL)
779 return li;
780
781 // Vim9: Allow for adding an item at the end.
782 if (can_append && in_vim9script()
783 && *n1 == l->lv_len && l->lv_lock == 0)
784 {
785 list_append_number(l, 0);
786 li = list_find_index(l, n1);
787 }
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200788 if (li == NULL)
789 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000790 if (!quiet)
791 semsg(_(e_list_index_out_of_range_nr), orig_n1);
792 return NULL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200793 }
794 return li;
795}
796
797/*
798 * Check that "n2" can be used as the second index in a range of list "l".
799 * If "n1" or "n2" is negative it is changed to the positive index.
800 * "li1" is the item for item "n1".
801 * Return OK or FAIL.
802 */
803 int
804check_range_index_two(
805 list_T *l,
806 long *n1,
807 listitem_T *li1,
808 long *n2,
809 int quiet)
810{
811 if (*n2 < 0)
812 {
813 listitem_T *ni = list_find(l, *n2);
814
815 if (ni == NULL)
816 {
817 if (!quiet)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000818 semsg(_(e_list_index_out_of_range_nr), *n2);
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200819 return FAIL;
820 }
821 *n2 = list_idx_of_item(l, ni);
822 }
823
824 // Check that n2 isn't before n1.
825 if (*n1 < 0)
826 *n1 = list_idx_of_item(l, li1);
827 if (*n2 < *n1)
828 {
829 if (!quiet)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000830 semsg(_(e_list_index_out_of_range_nr), *n2);
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200831 return FAIL;
832 }
833 return OK;
834}
835
836/*
837 * Assign values from list "src" into a range of "dest".
838 * "idx1_arg" is the index of the first item in "dest" to be replaced.
839 * "idx2" is the index of last item to be replaced, but when "empty_idx2" is
840 * TRUE then replace all items after "idx1".
841 * "op" is the operator, normally "=" but can be "+=" and the like.
842 * "varname" is used for error messages.
843 * Returns OK or FAIL.
844 */
845 int
846list_assign_range(
847 list_T *dest,
848 list_T *src,
849 long idx1_arg,
850 long idx2,
851 int empty_idx2,
852 char_u *op,
853 char_u *varname)
854{
855 listitem_T *src_li;
856 listitem_T *dest_li;
857 long idx1 = idx1_arg;
858 listitem_T *first_li = list_find_index(dest, &idx1);
859 long idx;
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200860 type_T *member_type = NULL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200861
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000862 // Check whether any of the list items is locked before making any changes.
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200863 idx = idx1;
864 dest_li = first_li;
865 for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
866 {
867 if (value_check_lock(dest_li->li_tv.v_lock, varname, FALSE))
868 return FAIL;
869 src_li = src_li->li_next;
870 if (src_li == NULL || (!empty_idx2 && idx2 == idx))
871 break;
872 dest_li = dest_li->li_next;
873 ++idx;
874 }
875
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200876 if (in_vim9script() && dest->lv_type != NULL
877 && dest->lv_type->tt_member != NULL)
878 member_type = dest->lv_type->tt_member;
879
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000880 // Assign the List values to the list items.
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200881 idx = idx1;
882 dest_li = first_li;
883 for (src_li = src->lv_first; src_li != NULL; )
884 {
885 if (op != NULL && *op != '=')
886 tv_op(&dest_li->li_tv, &src_li->li_tv, op);
887 else
888 {
Bram Moolenaar89071cb2021-08-13 18:20:09 +0200889 if (member_type != NULL
890 && check_typval_arg_type(member_type, &src_li->li_tv,
891 NULL, 0) == FAIL)
892 return FAIL;
Bram Moolenaar4f0884d2021-08-11 21:49:23 +0200893 clear_tv(&dest_li->li_tv);
894 copy_tv(&src_li->li_tv, &dest_li->li_tv);
895 }
896 src_li = src_li->li_next;
897 if (src_li == NULL || (!empty_idx2 && idx2 == idx))
898 break;
899 if (dest_li->li_next == NULL)
900 {
901 // Need to add an empty item.
902 if (list_append_number(dest, 0) == FAIL)
903 {
904 src_li = NULL;
905 break;
906 }
907 }
908 dest_li = dest_li->li_next;
909 ++idx;
910 }
911 if (src_li != NULL)
912 {
913 emsg(_(e_list_value_has_more_items_than_targets));
914 return FAIL;
915 }
916 if (empty_idx2
917 ? (dest_li != NULL && dest_li->li_next != NULL)
918 : idx != idx2)
919 {
920 emsg(_(e_list_value_does_not_have_enough_items));
921 return FAIL;
922 }
923 return OK;
924}
925
926/*
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000927 * Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth".
928 * When "first" is NULL use the first item.
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200929 * It does nothing if "maxdepth" is 0.
930 * Returns FAIL when out of memory.
931 */
Bram Moolenaar3b690062021-02-01 20:14:51 +0100932 static void
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000933list_flatten(list_T *list, listitem_T *first, long maxitems, long maxdepth)
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200934{
935 listitem_T *item;
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000936 int done = 0;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200937
938 if (maxdepth == 0)
Bram Moolenaar3b690062021-02-01 20:14:51 +0100939 return;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200940 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000941 if (first == NULL)
942 item = list->lv_first;
943 else
944 item = first;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200945
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000946 while (item != NULL && done < maxitems)
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200947 {
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000948 listitem_T *next = item->li_next;
949
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200950 fast_breakcheck();
951 if (got_int)
Bram Moolenaar3b690062021-02-01 20:14:51 +0100952 return;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200953
954 if (item->li_tv.v_type == VAR_LIST)
955 {
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000956 list_T *itemlist = item->li_tv.vval.v_list;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200957
958 vimlist_remove(list, item, item);
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000959 if (list_extend(list, itemlist, next) == FAIL)
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +0200960 {
961 list_free_item(list, item);
Bram Moolenaar3b690062021-02-01 20:14:51 +0100962 return;
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +0200963 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200964
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000965 if (maxdepth > 0)
966 list_flatten(list, item->li_prev == NULL
967 ? list->lv_first : item->li_prev->li_next,
968 itemlist->lv_len, maxdepth - 1);
Bram Moolenaarf3980dc2022-03-26 16:42:23 +0000969 clear_tv(&item->li_tv);
Bram Moolenaarc6c1ec42022-03-26 10:50:11 +0000970 list_free_item(list, item);
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000971 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200972
Bram Moolenaaracf7d732022-03-25 19:50:57 +0000973 ++done;
974 item = next;
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200975 }
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200976}
977
978/*
Bram Moolenaar3b690062021-02-01 20:14:51 +0100979 * "flatten()" and "flattennew()" functions
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200980 */
Bram Moolenaar3b690062021-02-01 20:14:51 +0100981 static void
982flatten_common(typval_T *argvars, typval_T *rettv, int make_copy)
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200983{
984 list_T *l;
985 long maxdepth;
986 int error = FALSE;
987
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200988 if (in_vim9script()
989 && (check_for_list_arg(argvars, 0) == FAIL
990 || check_for_opt_number_arg(argvars, 1) == FAIL))
991 return;
992
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200993 if (argvars[0].v_type != VAR_LIST)
994 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +0000995 semsg(_(e_argument_of_str_must_be_list), "flatten()");
Bram Moolenaar077a1e62020-06-08 20:50:43 +0200996 return;
997 }
998
999 if (argvars[1].v_type == VAR_UNKNOWN)
1000 maxdepth = 999999;
1001 else
1002 {
1003 maxdepth = (long)tv_get_number_chk(&argvars[1], &error);
1004 if (error)
1005 return;
1006 if (maxdepth < 0)
1007 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001008 emsg(_(e_maxdepth_must_be_non_negative_number));
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001009 return;
1010 }
1011 }
1012
1013 l = argvars[0].vval.v_list;
Bram Moolenaar3b690062021-02-01 20:14:51 +01001014 rettv->v_type = VAR_LIST;
1015 rettv->vval.v_list = l;
1016 if (l == NULL)
1017 return;
1018
1019 if (make_copy)
1020 {
Bram Moolenaarc6c1ec42022-03-26 10:50:11 +00001021 l = list_copy(l, FALSE, TRUE, get_copyID());
Bram Moolenaar3b690062021-02-01 20:14:51 +01001022 rettv->vval.v_list = l;
1023 if (l == NULL)
1024 return;
Bram Moolenaarb3bf33a2021-09-11 20:20:38 +02001025 // The type will change.
1026 free_type(l->lv_type);
1027 l->lv_type = NULL;
Bram Moolenaar3b690062021-02-01 20:14:51 +01001028 }
1029 else
1030 {
1031 if (value_check_lock(l->lv_lock,
1032 (char_u *)N_("flatten() argument"), TRUE))
1033 return;
1034 ++l->lv_refcount;
1035 }
1036
Bram Moolenaaracf7d732022-03-25 19:50:57 +00001037 list_flatten(l, NULL, l->lv_len, maxdepth);
Bram Moolenaar3b690062021-02-01 20:14:51 +01001038}
1039
1040/*
1041 * "flatten(list[, {maxdepth}])" function
1042 */
1043 void
1044f_flatten(typval_T *argvars, typval_T *rettv)
1045{
1046 if (in_vim9script())
1047 emsg(_(e_cannot_use_flatten_in_vim9_script));
1048 else
1049 flatten_common(argvars, rettv, FALSE);
1050}
1051
1052/*
1053 * "flattennew(list[, {maxdepth}])" function
1054 */
1055 void
1056f_flattennew(typval_T *argvars, typval_T *rettv)
1057{
1058 flatten_common(argvars, rettv, TRUE);
Bram Moolenaar077a1e62020-06-08 20:50:43 +02001059}
1060
1061/*
Bram Moolenaar976f8592022-08-30 14:34:52 +01001062 * "items(list)" function
1063 * Caller must have already checked that argvars[0] is a List.
1064 */
1065 void
1066list2items(typval_T *argvars, typval_T *rettv)
1067{
1068 list_T *l = argvars[0].vval.v_list;
1069 listitem_T *li;
1070 varnumber_T idx;
1071
1072 if (rettv_list_alloc(rettv) == FAIL)
1073 return;
Bram Moolenaar976f8592022-08-30 14:34:52 +01001074 if (l == NULL)
Bram Moolenaar3e518a82022-08-30 17:45:33 +01001075 return; // null list behaves like an empty list
Bram Moolenaar976f8592022-08-30 14:34:52 +01001076
1077 // TODO: would be more efficient to not materialize the argument
1078 CHECK_LIST_MATERIALIZE(l);
1079 for (idx = 0, li = l->lv_first; li != NULL; li = li->li_next, ++idx)
1080 {
1081 list_T *l2 = list_alloc();
1082
1083 if (l2 == NULL)
1084 break;
Bram Moolenaar9ba61942022-08-31 11:25:06 +01001085 if (list_append_list(rettv->vval.v_list, l2) == FAIL)
1086 {
1087 vim_free(l2);
1088 break;
1089 }
1090 if (list_append_number(l2, idx) == FAIL
Bram Moolenaar976f8592022-08-30 14:34:52 +01001091 || list_append_tv(l2, &li->li_tv) == FAIL)
1092 break;
1093 }
1094}
1095
1096/*
Bram Moolenaar3e518a82022-08-30 17:45:33 +01001097 * "items(string)" function
1098 * Caller must have already checked that argvars[0] is a String.
1099 */
1100 void
1101string2items(typval_T *argvars, typval_T *rettv)
1102{
1103 char_u *p = argvars[0].vval.v_string;
1104 varnumber_T idx;
1105
1106 if (rettv_list_alloc(rettv) == FAIL)
1107 return;
1108 if (p == NULL)
1109 return; // null string behaves like an empty string
1110
1111 for (idx = 0; *p != NUL; ++idx)
1112 {
1113 int len = mb_ptr2len(p);
1114 list_T *l2;
1115
1116 if (len == 0)
1117 break;
1118 l2 = list_alloc();
1119 if (l2 == NULL)
1120 break;
Bram Moolenaar9ba61942022-08-31 11:25:06 +01001121 if (list_append_list(rettv->vval.v_list, l2) == FAIL)
1122 {
1123 vim_free(l2);
1124 break;
1125 }
1126 if (list_append_number(l2, idx) == FAIL
Bram Moolenaar3e518a82022-08-30 17:45:33 +01001127 || list_append_string(l2, p, len) == FAIL)
1128 break;
1129 p += len;
1130 }
1131}
1132
1133/*
Bram Moolenaar1a739232020-10-10 15:37:58 +02001134 * Extend "l1" with "l2". "l1" must not be NULL.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001135 * If "bef" is NULL append at the end, otherwise insert before this item.
1136 * Returns FAIL when out of memory.
1137 */
1138 int
1139list_extend(list_T *l1, list_T *l2, listitem_T *bef)
1140{
1141 listitem_T *item;
Bram Moolenaar1a739232020-10-10 15:37:58 +02001142 int todo;
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001143 listitem_T *bef_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001144
Bram Moolenaar1a739232020-10-10 15:37:58 +02001145 // NULL list is equivalent to an empty list: nothing to do.
1146 if (l2 == NULL || l2->lv_len == 0)
1147 return OK;
1148
1149 todo = l2->lv_len;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001150 CHECK_LIST_MATERIALIZE(l1);
1151 CHECK_LIST_MATERIALIZE(l2);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001152
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001153 // When exending a list with itself, at some point we run into the item
1154 // that was before "bef" and need to skip over the already inserted items
1155 // to "bef".
1156 bef_prev = bef == NULL ? NULL : bef->li_prev;
1157
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001158 // We also quit the loop when we have inserted the original item count of
1159 // the list, avoid a hang when we extend a list with itself.
Bram Moolenaardcae51f2021-04-08 20:10:10 +02001160 for (item = l2->lv_first; item != NULL && --todo >= 0;
1161 item = item == bef_prev ? bef : item->li_next)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001162 if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
1163 return FAIL;
1164 return OK;
1165}
1166
1167/*
1168 * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
1169 * Return FAIL when out of memory.
1170 */
1171 int
1172list_concat(list_T *l1, list_T *l2, typval_T *tv)
1173{
1174 list_T *l;
1175
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001176 // make a copy of the first list.
Bram Moolenaar1a739232020-10-10 15:37:58 +02001177 if (l1 == NULL)
1178 l = list_alloc();
1179 else
Bram Moolenaar381692b2022-02-02 20:01:27 +00001180 l = list_copy(l1, FALSE, TRUE, 0);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001181 if (l == NULL)
1182 return FAIL;
1183 tv->v_type = VAR_LIST;
Bram Moolenaar9681f712020-11-21 13:58:50 +01001184 tv->v_lock = 0;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001185 tv->vval.v_list = l;
Bram Moolenaar1a739232020-10-10 15:37:58 +02001186 if (l1 == NULL)
1187 ++l->lv_refcount;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001188
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001189 // append all items from the second list
Bram Moolenaarda861d62016-07-17 15:46:27 +02001190 return list_extend(l, l2, NULL);
1191}
1192
Bram Moolenaar9af78762020-06-16 11:34:42 +02001193 list_T *
1194list_slice(list_T *ol, long n1, long n2)
1195{
1196 listitem_T *item;
1197 list_T *l = list_alloc();
1198
1199 if (l == NULL)
1200 return NULL;
1201 for (item = list_find(ol, n1); n1 <= n2; ++n1)
1202 {
1203 if (list_append_tv(l, &item->li_tv) == FAIL)
1204 {
1205 list_free(l);
1206 return NULL;
1207 }
1208 item = item->li_next;
1209 }
1210 return l;
1211}
1212
Bram Moolenaared591872020-08-15 22:14:53 +02001213 int
1214list_slice_or_index(
1215 list_T *list,
1216 int range,
Bram Moolenaar6601b622021-01-13 21:47:15 +01001217 varnumber_T n1_arg,
1218 varnumber_T n2_arg,
1219 int exclusive,
Bram Moolenaared591872020-08-15 22:14:53 +02001220 typval_T *rettv,
1221 int verbose)
1222{
1223 long len = list_len(list);
Bram Moolenaar6601b622021-01-13 21:47:15 +01001224 varnumber_T n1 = n1_arg;
1225 varnumber_T n2 = n2_arg;
Bram Moolenaared591872020-08-15 22:14:53 +02001226 typval_T var1;
1227
1228 if (n1 < 0)
1229 n1 = len + n1;
1230 if (n1 < 0 || n1 >= len)
1231 {
Bram Moolenaar92f05f22021-08-12 21:12:56 +02001232 // For a range we allow invalid values and for legacy script return an
1233 // empty list, for Vim9 script start at the first item.
1234 // A list index out of range is an error.
Bram Moolenaared591872020-08-15 22:14:53 +02001235 if (!range)
1236 {
1237 if (verbose)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001238 semsg(_(e_list_index_out_of_range_nr), (long)n1_arg);
Bram Moolenaared591872020-08-15 22:14:53 +02001239 return FAIL;
1240 }
Bram Moolenaar92f05f22021-08-12 21:12:56 +02001241 if (in_vim9script())
1242 n1 = n1 < 0 ? 0 : len;
1243 else
1244 n1 = len;
Bram Moolenaared591872020-08-15 22:14:53 +02001245 }
1246 if (range)
1247 {
1248 list_T *l;
1249
1250 if (n2 < 0)
1251 n2 = len + n2;
1252 else if (n2 >= len)
Bram Moolenaar6601b622021-01-13 21:47:15 +01001253 n2 = len - (exclusive ? 0 : 1);
1254 if (exclusive)
1255 --n2;
Bram Moolenaared591872020-08-15 22:14:53 +02001256 if (n2 < 0 || n2 + 1 < n1)
1257 n2 = -1;
1258 l = list_slice(list, n1, n2);
1259 if (l == NULL)
1260 return FAIL;
1261 clear_tv(rettv);
1262 rettv_list_set(rettv, l);
1263 }
1264 else
1265 {
1266 // copy the item to "var1" to avoid that freeing the list makes it
1267 // invalid.
1268 copy_tv(&list_find(list, n1)->li_tv, &var1);
1269 clear_tv(rettv);
1270 *rettv = var1;
1271 }
1272 return OK;
1273}
1274
Bram Moolenaarda861d62016-07-17 15:46:27 +02001275/*
1276 * Make a copy of list "orig". Shallow if "deep" is FALSE.
1277 * The refcount of the new list is set to 1.
Bram Moolenaar381692b2022-02-02 20:01:27 +00001278 * See item_copy() for "top" and "copyID".
Bram Moolenaarda861d62016-07-17 15:46:27 +02001279 * Returns NULL when out of memory.
1280 */
1281 list_T *
Bram Moolenaar381692b2022-02-02 20:01:27 +00001282list_copy(list_T *orig, int deep, int top, int copyID)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001283{
1284 list_T *copy;
1285 listitem_T *item;
1286 listitem_T *ni;
1287
1288 if (orig == NULL)
1289 return NULL;
1290
1291 copy = list_alloc();
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001292 if (copy == NULL)
1293 return NULL;
1294
1295 if (orig->lv_type == NULL || top || deep)
1296 copy->lv_type = NULL;
1297 else
1298 copy->lv_type = alloc_type(orig->lv_type);
1299 if (copyID != 0)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001300 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001301 // Do this before adding the items, because one of the items may
1302 // refer back to this list.
1303 orig->lv_copyID = copyID;
1304 orig->lv_copylist = copy;
1305 }
1306 CHECK_LIST_MATERIALIZE(orig);
1307 for (item = orig->lv_first; item != NULL && !got_int;
1308 item = item->li_next)
1309 {
1310 ni = listitem_alloc();
1311 if (ni == NULL)
1312 break;
1313 if (deep)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001314 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001315 if (item_copy(&item->li_tv, &ni->li_tv,
1316 deep, FALSE, copyID) == FAIL)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001317 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001318 vim_free(ni);
1319 break;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001320 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001321 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001322 else
1323 copy_tv(&item->li_tv, &ni->li_tv);
1324 list_append(copy, ni);
1325 }
1326 ++copy->lv_refcount;
1327 if (item != NULL)
1328 {
1329 list_unref(copy);
1330 copy = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001331 }
1332
1333 return copy;
1334}
1335
1336/*
1337 * Remove items "item" to "item2" from list "l".
1338 * Does not free the listitem or the value!
1339 * This used to be called list_remove, but that conflicts with a Sun header
1340 * file.
1341 */
1342 void
1343vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
1344{
1345 listitem_T *ip;
1346
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001347 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001348
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001349 // notify watchers
Bram Moolenaarda861d62016-07-17 15:46:27 +02001350 for (ip = item; ip != NULL; ip = ip->li_next)
1351 {
1352 --l->lv_len;
1353 list_fix_watch(l, ip);
1354 if (ip == item2)
1355 break;
1356 }
1357
1358 if (item2->li_next == NULL)
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001359 l->lv_u.mat.lv_last = item->li_prev;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001360 else
1361 item2->li_next->li_prev = item->li_prev;
1362 if (item->li_prev == NULL)
1363 l->lv_first = item2->li_next;
1364 else
1365 item->li_prev->li_next = item2->li_next;
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001366 l->lv_u.mat.lv_idx_item = NULL;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001367}
1368
1369/*
1370 * Return an allocated string with the string representation of a list.
1371 * May return NULL.
1372 */
1373 char_u *
1374list2string(typval_T *tv, int copyID, int restore_copyID)
1375{
1376 garray_T ga;
1377
1378 if (tv->vval.v_list == NULL)
1379 return NULL;
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001380 ga_init2(&ga, sizeof(char), 80);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001381 ga_append(&ga, '[');
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001382 CHECK_LIST_MATERIALIZE(tv->vval.v_list);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001383 if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
1384 FALSE, restore_copyID, copyID) == FAIL)
1385 {
1386 vim_free(ga.ga_data);
1387 return NULL;
1388 }
1389 ga_append(&ga, ']');
1390 ga_append(&ga, NUL);
1391 return (char_u *)ga.ga_data;
1392}
1393
1394typedef struct join_S {
1395 char_u *s;
1396 char_u *tofree;
1397} join_T;
1398
1399 static int
1400list_join_inner(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001401 garray_T *gap, // to store the result in
Bram Moolenaarda861d62016-07-17 15:46:27 +02001402 list_T *l,
1403 char_u *sep,
1404 int echo_style,
1405 int restore_copyID,
1406 int copyID,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001407 garray_T *join_gap) // to keep each list item string
Bram Moolenaarda861d62016-07-17 15:46:27 +02001408{
1409 int i;
1410 join_T *p;
1411 int len;
1412 int sumlen = 0;
1413 int first = TRUE;
1414 char_u *tofree;
1415 char_u numbuf[NUMBUFLEN];
1416 listitem_T *item;
1417 char_u *s;
1418
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001419 // Stringify each item in the list.
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001420 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001421 for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
1422 {
1423 s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
Bram Moolenaar35422f42017-08-05 16:33:56 +02001424 echo_style, restore_copyID, !echo_style);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001425 if (s == NULL)
1426 return FAIL;
1427
1428 len = (int)STRLEN(s);
1429 sumlen += len;
1430
1431 (void)ga_grow(join_gap, 1);
1432 p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
1433 if (tofree != NULL || s != numbuf)
1434 {
1435 p->s = s;
1436 p->tofree = tofree;
1437 }
1438 else
1439 {
1440 p->s = vim_strnsave(s, len);
1441 p->tofree = p->s;
1442 }
1443
1444 line_breakcheck();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001445 if (did_echo_string_emsg) // recursion error, bail out
Bram Moolenaarda861d62016-07-17 15:46:27 +02001446 break;
1447 }
1448
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001449 // Allocate result buffer with its total size, avoid re-allocation and
1450 // multiple copy operations. Add 2 for a tailing ']' and NUL.
Bram Moolenaarda861d62016-07-17 15:46:27 +02001451 if (join_gap->ga_len >= 2)
1452 sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
1453 if (ga_grow(gap, sumlen + 2) == FAIL)
1454 return FAIL;
1455
1456 for (i = 0; i < join_gap->ga_len && !got_int; ++i)
1457 {
1458 if (first)
1459 first = FALSE;
1460 else
1461 ga_concat(gap, sep);
1462 p = ((join_T *)join_gap->ga_data) + i;
1463
1464 if (p->s != NULL)
1465 ga_concat(gap, p->s);
1466 line_breakcheck();
1467 }
1468
1469 return OK;
1470}
1471
1472/*
1473 * Join list "l" into a string in "*gap", using separator "sep".
1474 * When "echo_style" is TRUE use String as echoed, otherwise as inside a List.
1475 * Return FAIL or OK.
1476 */
1477 int
1478list_join(
1479 garray_T *gap,
1480 list_T *l,
1481 char_u *sep,
1482 int echo_style,
1483 int restore_copyID,
1484 int copyID)
1485{
1486 garray_T join_ga;
1487 int retval;
1488 join_T *p;
1489 int i;
1490
1491 if (l->lv_len < 1)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001492 return OK; // nothing to do
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001493 ga_init2(&join_ga, sizeof(join_T), l->lv_len);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001494 retval = list_join_inner(gap, l, sep, echo_style, restore_copyID,
1495 copyID, &join_ga);
1496
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001497 if (join_ga.ga_data == NULL)
1498 return retval;
1499
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001500 // Dispose each item in join_ga.
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001501 p = (join_T *)join_ga.ga_data;
1502 for (i = 0; i < join_ga.ga_len; ++i)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001503 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001504 vim_free(p->tofree);
1505 ++p;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001506 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001507 ga_clear(&join_ga);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001508
1509 return retval;
1510}
1511
1512/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001513 * "join()" function
1514 */
1515 void
1516f_join(typval_T *argvars, typval_T *rettv)
1517{
1518 garray_T ga;
1519 char_u *sep;
1520
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01001521 rettv->v_type = VAR_STRING;
1522
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001523 if (in_vim9script()
1524 && (check_for_list_arg(argvars, 0) == FAIL
1525 || check_for_opt_string_arg(argvars, 1) == FAIL))
1526 return;
1527
Bram Moolenaard83392a2022-09-01 12:22:46 +01001528 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001529 return;
Yegappan Lakshmananca195cc2022-06-14 13:42:26 +01001530
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001531 if (argvars[0].vval.v_list == NULL)
1532 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001533
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001534 if (argvars[1].v_type == VAR_UNKNOWN)
1535 sep = (char_u *)" ";
1536 else
1537 sep = tv_get_string_chk(&argvars[1]);
1538
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001539 if (sep != NULL)
1540 {
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001541 ga_init2(&ga, sizeof(char), 80);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001542 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
1543 ga_append(&ga, NUL);
1544 rettv->vval.v_string = (char_u *)ga.ga_data;
1545 }
1546 else
1547 rettv->vval.v_string = NULL;
1548}
1549
1550/*
Bram Moolenaarda861d62016-07-17 15:46:27 +02001551 * Allocate a variable for a List and fill it from "*arg".
Bram Moolenaar71478202020-06-26 22:46:27 +02001552 * "*arg" points to the "[".
Bram Moolenaarda861d62016-07-17 15:46:27 +02001553 * Return OK or FAIL.
1554 */
1555 int
Bram Moolenaar9a78e6d2020-07-01 18:29:55 +02001556eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001557{
Bram Moolenaar71478202020-06-26 22:46:27 +02001558 int evaluate = evalarg == NULL ? FALSE
1559 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001560 list_T *l = NULL;
1561 typval_T tv;
1562 listitem_T *item;
Bram Moolenaareb6880b2020-07-12 17:07:05 +02001563 int vim9script = in_vim9script();
Bram Moolenaar71478202020-06-26 22:46:27 +02001564 int had_comma;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001565
1566 if (evaluate)
1567 {
1568 l = list_alloc();
1569 if (l == NULL)
1570 return FAIL;
1571 }
1572
Bram Moolenaar962d7212020-07-04 14:15:00 +02001573 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001574 while (**arg != ']' && **arg != NUL)
1575 {
Bram Moolenaar71478202020-06-26 22:46:27 +02001576 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarda861d62016-07-17 15:46:27 +02001577 goto failret;
Ernie Raelfa831102023-12-14 20:06:39 +01001578 if (check_typval_is_value(&tv) == FAIL)
1579 {
1580 if (evaluate)
1581 clear_tv(&tv);
1582 goto failret;
1583 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001584 if (evaluate)
1585 {
1586 item = listitem_alloc();
1587 if (item != NULL)
1588 {
1589 item->li_tv = tv;
1590 item->li_tv.v_lock = 0;
1591 list_append(l, item);
1592 }
1593 else
1594 clear_tv(&tv);
1595 }
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001596 // Legacy Vim script allowed a space before the comma.
1597 if (!vim9script)
1598 *arg = skipwhite(*arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001599
Bram Moolenaare6e03172020-06-27 16:36:05 +02001600 // the comma must come after the value
Bram Moolenaar71478202020-06-26 22:46:27 +02001601 had_comma = **arg == ',';
1602 if (had_comma)
Bram Moolenaare6e03172020-06-27 16:36:05 +02001603 {
Christian Brabandt00cb2472023-09-05 20:46:25 +02001604 if (vim9script && !IS_WHITE_NL_OR_NUL((*arg)[1]) && (*arg)[1] != ']')
Bram Moolenaare6e03172020-06-27 16:36:05 +02001605 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001606 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001607 goto failret;
1608 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001609 *arg = skipwhite(*arg + 1);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001610 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001611
Bram Moolenaare6b53242020-07-01 17:28:33 +02001612 // The "]" can be on the next line. But a double quoted string may
1613 // follow, not a comment.
Bram Moolenaar962d7212020-07-04 14:15:00 +02001614 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001615 if (**arg == ']')
1616 break;
Bram Moolenaar71478202020-06-26 22:46:27 +02001617
1618 if (!had_comma)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001619 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001620 if (do_error)
Bram Moolenaardb199212020-08-12 18:01:53 +02001621 {
1622 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001623 semsg(_(e_no_white_space_allowed_before_str_str),
1624 ",", *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001625 else
Bram Moolenaara6f79292022-01-04 21:30:47 +00001626 semsg(_(e_missing_comma_in_list_str), *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001627 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001628 goto failret;
1629 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001630 }
1631
1632 if (**arg != ']')
1633 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001634 if (do_error)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001635 semsg(_(e_missing_end_of_list_rsb_str), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001636failret:
1637 if (evaluate)
1638 list_free(l);
1639 return FAIL;
1640 }
1641
Bram Moolenaar9d489562020-07-30 20:08:50 +02001642 *arg += 1;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001643 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001644 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001645
1646 return OK;
1647}
1648
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001649/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +01001650 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001651 */
1652 int
1653write_list(FILE *fd, list_T *list, int binary)
1654{
1655 listitem_T *li;
1656 int c;
1657 int ret = OK;
1658 char_u *s;
1659
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001660 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001661 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001662 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001663 for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001664 {
1665 if (*s == '\n')
1666 c = putc(NUL, fd);
1667 else
1668 c = putc(*s, fd);
1669 if (c == EOF)
1670 {
1671 ret = FAIL;
1672 break;
1673 }
1674 }
1675 if (!binary || li->li_next != NULL)
1676 if (putc('\n', fd) == EOF)
1677 {
1678 ret = FAIL;
1679 break;
1680 }
1681 if (ret == FAIL)
1682 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00001683 emsg(_(e_error_while_writing));
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001684 break;
1685 }
1686 }
1687 return ret;
1688}
1689
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001690/*
1691 * Initialize a static list with 10 items.
1692 */
1693 void
1694init_static_list(staticList10_T *sl)
1695{
1696 list_T *l = &sl->sl_list;
1697 int i;
1698
Yegappan Lakshmanan960dcbd2023-03-07 17:45:11 +00001699 CLEAR_POINTER(sl);
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001700 l->lv_first = &sl->sl_items[0];
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001701 l->lv_u.mat.lv_last = &sl->sl_items[9];
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001702 l->lv_refcount = DO_NOT_FREE_CNT;
1703 l->lv_lock = VAR_FIXED;
1704 sl->sl_list.lv_len = 10;
1705
1706 for (i = 0; i < 10; ++i)
1707 {
1708 listitem_T *li = &sl->sl_items[i];
1709
1710 if (i == 0)
1711 li->li_prev = NULL;
1712 else
1713 li->li_prev = li - 1;
1714 if (i == 9)
1715 li->li_next = NULL;
1716 else
1717 li->li_next = li + 1;
1718 }
1719}
1720
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001721/*
1722 * "list2str()" function
1723 */
1724 void
1725f_list2str(typval_T *argvars, typval_T *rettv)
1726{
1727 list_T *l;
1728 listitem_T *li;
1729 garray_T ga;
1730 int utf8 = FALSE;
1731
1732 rettv->v_type = VAR_STRING;
1733 rettv->vval.v_string = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001734
1735 if (in_vim9script()
1736 && (check_for_list_arg(argvars, 0) == FAIL
1737 || check_for_opt_bool_arg(argvars, 1) == FAIL))
1738 return;
1739
Bram Moolenaard83392a2022-09-01 12:22:46 +01001740 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001741 return;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001742
1743 l = argvars[0].vval.v_list;
1744 if (l == NULL)
1745 return; // empty list results in empty string
1746
1747 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaara48f7862020-09-05 20:16:57 +02001748 utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001749
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001750 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001751 ga_init2(&ga, 1, 80);
1752 if (has_mbyte || utf8)
1753 {
1754 char_u buf[MB_MAXBYTES + 1];
1755 int (*char2bytes)(int, char_u *);
1756
1757 if (utf8 || enc_utf8)
1758 char2bytes = utf_char2bytes;
1759 else
1760 char2bytes = mb_char2bytes;
1761
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001762 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001763 {
1764 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
1765 ga_concat(&ga, buf);
1766 }
1767 ga_append(&ga, NUL);
1768 }
1769 else if (ga_grow(&ga, list_len(l) + 1) == OK)
1770 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001771 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001772 ga_append(&ga, tv_get_number(&li->li_tv));
1773 ga_append(&ga, NUL);
1774 }
1775
1776 rettv->v_type = VAR_STRING;
1777 rettv->vval.v_string = ga.ga_data;
1778}
1779
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001780/*
1781 * Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then
1782 * remove the range of items from argvars[1] to argvars[2] (inclusive).
1783 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +02001784 static void
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001785list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1786{
1787 list_T *l;
1788 listitem_T *item, *item2;
1789 listitem_T *li;
1790 int error = FALSE;
Bram Moolenaar239f8d92021-01-17 13:21:20 +01001791 long idx;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001792 long end;
1793 int cnt = 0;
1794 list_T *rl;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001795
1796 if ((l = argvars[0].vval.v_list) == NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001797 || value_check_lock(l->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001798 return;
1799
1800 idx = (long)tv_get_number_chk(&argvars[1], &error);
1801 if (error)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001802 return; // type error: do nothing, errmsg already given
1803
1804 if ((item = list_find(l, idx)) == NULL)
1805 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001806 semsg(_(e_list_index_out_of_range_nr), idx);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001807 return;
1808 }
1809
1810 if (argvars[2].v_type == VAR_UNKNOWN)
1811 {
1812 // Remove one item, return its value.
1813 vimlist_remove(l, item, item);
1814 *rettv = item->li_tv;
1815 list_free_item(l, item);
1816 return;
1817 }
1818
1819 // Remove range of items, return list with values.
1820 end = (long)tv_get_number_chk(&argvars[2], &error);
1821 if (error)
1822 return; // type error: do nothing
1823
1824 if ((item2 = list_find(l, end)) == NULL)
1825 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001826 semsg(_(e_list_index_out_of_range_nr), end);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001827 return;
1828 }
1829
1830 for (li = item; li != NULL; li = li->li_next)
1831 {
1832 ++cnt;
1833 if (li == item2)
1834 break;
1835 }
1836 if (li == NULL) // didn't find "item2" after "item"
1837 {
1838 emsg(_(e_invalid_range));
1839 return;
1840 }
1841
1842 vimlist_remove(l, item, item2);
Bram Moolenaar93a10962022-06-16 11:42:09 +01001843 if (rettv_list_alloc(rettv) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001844 return;
1845
1846 rl = rettv->vval.v_list;
1847
1848 if (l->lv_with_items > 0)
1849 {
1850 // need to copy the list items and move the value
1851 while (item != NULL)
1852 {
1853 li = listitem_alloc();
1854 if (li == NULL)
1855 return;
1856 li->li_tv = item->li_tv;
1857 init_tv(&item->li_tv);
1858 list_append(rl, li);
1859 if (item == item2)
1860 break;
1861 item = item->li_next;
1862 }
1863 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001864 else
1865 {
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001866 rl->lv_first = item;
1867 rl->lv_u.mat.lv_last = item2;
1868 item->li_prev = NULL;
1869 item2->li_next = NULL;
1870 rl->lv_len = cnt;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001871 }
1872}
1873
1874static int item_compare(const void *s1, const void *s2);
1875static int item_compare2(const void *s1, const void *s2);
1876
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001877// struct used in the array that's given to qsort()
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001878typedef struct
1879{
1880 listitem_T *item;
1881 int idx;
1882} sortItem_T;
1883
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001884// struct storing information about current sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001885typedef struct
1886{
1887 int item_compare_ic;
Bram Moolenaar55e29612020-11-01 13:57:44 +01001888 int item_compare_lc;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001889 int item_compare_numeric;
1890 int item_compare_numbers;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001891 int item_compare_float;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001892 char_u *item_compare_func;
1893 partial_T *item_compare_partial;
1894 dict_T *item_compare_selfdict;
1895 int item_compare_func_err;
1896 int item_compare_keep_zero;
1897} sortinfo_T;
1898static sortinfo_T *sortinfo = NULL;
1899#define ITEM_COMPARE_FAIL 999
1900
1901/*
1902 * Compare functions for f_sort() and f_uniq() below.
1903 */
1904 static int
1905item_compare(const void *s1, const void *s2)
1906{
1907 sortItem_T *si1, *si2;
1908 typval_T *tv1, *tv2;
1909 char_u *p1, *p2;
1910 char_u *tofree1 = NULL, *tofree2 = NULL;
1911 int res;
1912 char_u numbuf1[NUMBUFLEN];
1913 char_u numbuf2[NUMBUFLEN];
1914
1915 si1 = (sortItem_T *)s1;
1916 si2 = (sortItem_T *)s2;
1917 tv1 = &si1->item->li_tv;
1918 tv2 = &si2->item->li_tv;
1919
1920 if (sortinfo->item_compare_numbers)
1921 {
Bram Moolenaarbe19d782023-03-09 22:06:49 +00001922 varnumber_T v1 = tv_to_number(tv1);
1923 varnumber_T v2 = tv_to_number(tv2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001924
1925 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1926 }
1927
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001928 if (sortinfo->item_compare_float)
1929 {
1930 float_T v1 = tv_get_float(tv1);
1931 float_T v2 = tv_get_float(tv2);
1932
1933 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1934 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001935
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001936 // tv2string() puts quotes around a string and allocates memory. Don't do
1937 // that for string variables. Use a single quote when comparing with a
1938 // non-string to do what the docs promise.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001939 if (tv1->v_type == VAR_STRING)
1940 {
1941 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1942 p1 = (char_u *)"'";
1943 else
1944 p1 = tv1->vval.v_string;
1945 }
1946 else
1947 p1 = tv2string(tv1, &tofree1, numbuf1, 0);
1948 if (tv2->v_type == VAR_STRING)
1949 {
1950 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
1951 p2 = (char_u *)"'";
1952 else
1953 p2 = tv2->vval.v_string;
1954 }
1955 else
1956 p2 = tv2string(tv2, &tofree2, numbuf2, 0);
1957 if (p1 == NULL)
1958 p1 = (char_u *)"";
1959 if (p2 == NULL)
1960 p2 = (char_u *)"";
1961 if (!sortinfo->item_compare_numeric)
1962 {
Bram Moolenaar55e29612020-11-01 13:57:44 +01001963 if (sortinfo->item_compare_lc)
1964 res = strcoll((char *)p1, (char *)p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001965 else
Bram Moolenaar55e29612020-11-01 13:57:44 +01001966 res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001967 }
1968 else
1969 {
1970 double n1, n2;
1971 n1 = strtod((char *)p1, (char **)&p1);
1972 n2 = strtod((char *)p2, (char **)&p2);
1973 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
1974 }
1975
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001976 // When the result would be zero, compare the item indexes. Makes the
1977 // sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001978 if (res == 0 && !sortinfo->item_compare_keep_zero)
1979 res = si1->idx > si2->idx ? 1 : -1;
1980
1981 vim_free(tofree1);
1982 vim_free(tofree2);
1983 return res;
1984}
1985
1986 static int
1987item_compare2(const void *s1, const void *s2)
1988{
1989 sortItem_T *si1, *si2;
1990 int res;
1991 typval_T rettv;
1992 typval_T argv[3];
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001993 char_u *func_name;
1994 partial_T *partial = sortinfo->item_compare_partial;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001995 funcexe_T funcexe;
Bram Moolenaar23018f22021-12-27 11:54:37 +00001996 int did_emsg_before = did_emsg;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001997
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001998 // shortcut after failure in previous call; compare all items equal
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001999 if (sortinfo->item_compare_func_err)
2000 return 0;
2001
2002 si1 = (sortItem_T *)s1;
2003 si2 = (sortItem_T *)s2;
2004
2005 if (partial == NULL)
2006 func_name = sortinfo->item_compare_func;
2007 else
2008 func_name = partial_name(partial);
2009
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002010 // Copy the values. This is needed to be able to set v_lock to VAR_FIXED
2011 // in the copy without changing the original list items.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002012 copy_tv(&si1->item->li_tv, &argv[0]);
2013 copy_tv(&si2->item->li_tv, &argv[1]);
2014
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002015 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
Bram Moolenaara80faa82020-04-12 19:37:17 +02002016 CLEAR_FIELD(funcexe);
Bram Moolenaar851f86b2021-12-13 14:26:44 +00002017 funcexe.fe_evaluate = TRUE;
2018 funcexe.fe_partial = partial;
2019 funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02002020 res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002021 clear_tv(&argv[0]);
2022 clear_tv(&argv[1]);
2023
Bram Moolenaar23018f22021-12-27 11:54:37 +00002024 if (res == FAIL || did_emsg > did_emsg_before)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002025 res = ITEM_COMPARE_FAIL;
2026 else
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02002027 {
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002028 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02002029 if (res > 0)
2030 res = 1;
2031 else if (res < 0)
2032 res = -1;
2033 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002034 if (sortinfo->item_compare_func_err)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002035 res = ITEM_COMPARE_FAIL; // return value has wrong type
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002036 clear_tv(&rettv);
2037
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002038 // When the result would be zero, compare the pointers themselves. Makes
2039 // the sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002040 if (res == 0 && !sortinfo->item_compare_keep_zero)
2041 res = si1->idx > si2->idx ? 1 : -1;
2042
2043 return res;
2044}
2045
2046/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002047 * sort() List "l"
2048 */
2049 static void
2050do_sort(list_T *l, sortinfo_T *info)
2051{
2052 long len;
2053 sortItem_T *ptrs;
2054 long i = 0;
2055 listitem_T *li;
2056
2057 len = list_len(l);
2058
2059 // Make an array with each entry pointing to an item in the List.
2060 ptrs = ALLOC_MULT(sortItem_T, len);
2061 if (ptrs == NULL)
2062 return;
2063
2064 // sort(): ptrs will be the list to sort
2065 FOR_ALL_LIST_ITEMS(l, li)
2066 {
2067 ptrs[i].item = li;
2068 ptrs[i].idx = i;
2069 ++i;
2070 }
2071
2072 info->item_compare_func_err = FALSE;
2073 info->item_compare_keep_zero = FALSE;
2074 // test the compare function
2075 if ((info->item_compare_func != NULL
2076 || info->item_compare_partial != NULL)
2077 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
2078 == ITEM_COMPARE_FAIL)
Bram Moolenaara6f79292022-01-04 21:30:47 +00002079 emsg(_(e_sort_compare_function_failed));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002080 else
2081 {
2082 // Sort the array with item pointers.
2083 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
2084 info->item_compare_func == NULL
2085 && info->item_compare_partial == NULL
2086 ? item_compare : item_compare2);
2087
2088 if (!info->item_compare_func_err)
2089 {
2090 // Clear the List and append the items in sorted order.
2091 l->lv_first = l->lv_u.mat.lv_last
2092 = l->lv_u.mat.lv_idx_item = NULL;
2093 l->lv_len = 0;
2094 for (i = 0; i < len; ++i)
2095 list_append(l, ptrs[i].item);
2096 }
2097 }
2098
2099 vim_free(ptrs);
2100}
2101
2102/*
2103 * uniq() List "l"
2104 */
2105 static void
2106do_uniq(list_T *l, sortinfo_T *info)
2107{
2108 long len;
2109 sortItem_T *ptrs;
2110 long i = 0;
2111 listitem_T *li;
2112 int (*item_compare_func_ptr)(const void *, const void *);
2113
2114 len = list_len(l);
2115
2116 // Make an array with each entry pointing to an item in the List.
2117 ptrs = ALLOC_MULT(sortItem_T, len);
2118 if (ptrs == NULL)
2119 return;
2120
2121 // f_uniq(): ptrs will be a stack of items to remove
2122 info->item_compare_func_err = FALSE;
2123 info->item_compare_keep_zero = TRUE;
2124 item_compare_func_ptr = info->item_compare_func != NULL
2125 || info->item_compare_partial != NULL
2126 ? item_compare2 : item_compare;
2127
2128 for (li = l->lv_first; li != NULL && li->li_next != NULL;
2129 li = li->li_next)
2130 {
2131 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
2132 == 0)
2133 ptrs[i++].item = li;
2134 if (info->item_compare_func_err)
2135 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00002136 emsg(_(e_uniq_compare_function_failed));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002137 break;
2138 }
2139 }
2140
2141 if (!info->item_compare_func_err)
2142 {
2143 while (--i >= 0)
2144 {
2145 li = ptrs[i].item->li_next;
2146 ptrs[i].item->li_next = li->li_next;
2147 if (li->li_next != NULL)
2148 li->li_next->li_prev = ptrs[i].item;
2149 else
2150 l->lv_u.mat.lv_last = ptrs[i].item;
2151 list_fix_watch(l, li);
2152 listitem_free(l, li);
2153 l->lv_len--;
2154 }
2155 }
2156
2157 vim_free(ptrs);
2158}
2159
2160/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002161 * Parse the optional arguments supplied to the sort() or uniq() function and
2162 * return the values in "info".
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002163 */
2164 static int
2165parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
2166{
2167 info->item_compare_ic = FALSE;
2168 info->item_compare_lc = FALSE;
2169 info->item_compare_numeric = FALSE;
2170 info->item_compare_numbers = FALSE;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002171 info->item_compare_float = FALSE;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002172 info->item_compare_func = NULL;
2173 info->item_compare_partial = NULL;
2174 info->item_compare_selfdict = NULL;
2175
2176 if (argvars[1].v_type == VAR_UNKNOWN)
2177 return OK;
2178
2179 // optional second argument: {func}
2180 if (argvars[1].v_type == VAR_FUNC)
2181 info->item_compare_func = argvars[1].vval.v_string;
2182 else if (argvars[1].v_type == VAR_PARTIAL)
2183 info->item_compare_partial = argvars[1].vval.v_partial;
2184 else
2185 {
2186 int error = FALSE;
2187 int nr = 0;
2188
2189 if (argvars[1].v_type == VAR_NUMBER)
2190 {
2191 nr = tv_get_number_chk(&argvars[1], &error);
2192 if (error)
2193 return FAIL;
2194 if (nr == 1)
2195 info->item_compare_ic = TRUE;
2196 }
2197 if (nr != 1)
2198 {
2199 if (argvars[1].v_type != VAR_NUMBER)
2200 info->item_compare_func = tv_get_string(&argvars[1]);
2201 else if (nr != 0)
2202 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002203 emsg(_(e_invalid_argument));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002204 return FAIL;
2205 }
2206 }
2207 if (info->item_compare_func != NULL)
2208 {
2209 if (*info->item_compare_func == NUL)
2210 {
2211 // empty string means default sort
2212 info->item_compare_func = NULL;
2213 }
2214 else if (STRCMP(info->item_compare_func, "n") == 0)
2215 {
2216 info->item_compare_func = NULL;
2217 info->item_compare_numeric = TRUE;
2218 }
2219 else if (STRCMP(info->item_compare_func, "N") == 0)
2220 {
2221 info->item_compare_func = NULL;
2222 info->item_compare_numbers = TRUE;
2223 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002224 else if (STRCMP(info->item_compare_func, "f") == 0)
2225 {
2226 info->item_compare_func = NULL;
2227 info->item_compare_float = TRUE;
2228 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002229 else if (STRCMP(info->item_compare_func, "i") == 0)
2230 {
2231 info->item_compare_func = NULL;
2232 info->item_compare_ic = TRUE;
2233 }
2234 else if (STRCMP(info->item_compare_func, "l") == 0)
2235 {
2236 info->item_compare_func = NULL;
2237 info->item_compare_lc = TRUE;
2238 }
2239 }
2240 }
2241
2242 if (argvars[2].v_type != VAR_UNKNOWN)
2243 {
2244 // optional third argument: {dict}
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002245 if (check_for_dict_arg(argvars, 2) == FAIL)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002246 return FAIL;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002247 info->item_compare_selfdict = argvars[2].vval.v_dict;
2248 }
2249
2250 return OK;
2251}
2252
2253/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002254 * "sort()" or "uniq()" function
2255 */
2256 static void
2257do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
2258{
2259 list_T *l;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002260 sortinfo_T *old_sortinfo;
2261 sortinfo_T info;
2262 long len;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002263
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002264 if (in_vim9script()
2265 && (check_for_list_arg(argvars, 0) == FAIL
2266 || (argvars[1].v_type != VAR_UNKNOWN
Bram Moolenaar2007dd42022-02-23 13:17:47 +00002267 && (check_for_string_or_func_arg(argvars, 1) == FAIL
2268 || check_for_opt_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002269 return;
2270
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002271 if (argvars[0].v_type != VAR_LIST)
2272 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00002273 semsg(_(e_argument_of_str_must_be_list), sort ? "sort()" : "uniq()");
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002274 return;
2275 }
2276
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002277 // Pointer to current info struct used in compare function. Save and
2278 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002279 old_sortinfo = sortinfo;
2280 sortinfo = &info;
2281
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002282 l = argvars[0].vval.v_list;
2283 if (l != NULL && value_check_lock(l->lv_lock,
2284 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
2285 TRUE))
2286 goto theend;
2287 rettv_list_set(rettv, l);
2288 if (l == NULL)
2289 goto theend;
2290 CHECK_LIST_MATERIALIZE(l);
2291
2292 len = list_len(l);
2293 if (len <= 1)
2294 goto theend; // short list sorts pretty quickly
2295
2296 if (parse_sort_uniq_args(argvars, &info) == FAIL)
2297 goto theend;
2298
2299 if (sort)
2300 do_sort(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002301 else
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002302 do_uniq(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002303
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002304theend:
2305 sortinfo = old_sortinfo;
2306}
2307
2308/*
2309 * "sort({list})" function
2310 */
2311 void
2312f_sort(typval_T *argvars, typval_T *rettv)
2313{
2314 do_sort_uniq(argvars, rettv, TRUE);
2315}
2316
2317/*
2318 * "uniq({list})" function
2319 */
2320 void
2321f_uniq(typval_T *argvars, typval_T *rettv)
2322{
2323 do_sort_uniq(argvars, rettv, FALSE);
2324}
2325
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002326/*
Ernie Raele79e2072024-01-13 11:47:33 +01002327 * Handle one item for map(), filter(), foreach().
Bram Moolenaarea696852020-11-09 18:31:39 +01002328 * Sets v:val to "tv". Caller must set v:key.
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002329 */
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002330 int
Bram Moolenaarea696852020-11-09 18:31:39 +01002331filter_map_one(
2332 typval_T *tv, // original value
2333 typval_T *expr, // callback
2334 filtermap_T filtermap,
Bram Moolenaar82418262022-09-28 16:16:15 +01002335 funccall_T *fc, // from eval_expr_get_funccal()
Bram Moolenaarea696852020-11-09 18:31:39 +01002336 typval_T *newtv, // for map() and mapnew(): new value
2337 int *remp) // for filter(): remove flag
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002338{
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002339 typval_T argv[3];
2340 int retval = FAIL;
2341
2342 copy_tv(tv, get_vim_var_tv(VV_VAL));
Ernie Raele79e2072024-01-13 11:47:33 +01002343
2344 newtv->v_type = VAR_UNKNOWN;
2345 if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING)
2346 {
2347 // foreach() is not limited to an expression
2348 do_cmdline_cmd(expr->vval.v_string);
2349 if (!did_emsg)
2350 retval = OK;
2351 goto theend;
2352 }
2353
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002354 argv[0] = *get_vim_var_tv(VV_KEY);
2355 argv[1] = *get_vim_var_tv(VV_VAL);
zeertzjqad0c4422023-08-17 22:15:47 +02002356 if (eval_expr_typval(expr, FALSE, argv, 2, fc, newtv) == FAIL)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002357 goto theend;
Bram Moolenaarea696852020-11-09 18:31:39 +01002358 if (filtermap == FILTERMAP_FILTER)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002359 {
2360 int error = FALSE;
2361
2362 // filter(): when expr is zero remove the item
Bram Moolenaar56acb092020-08-16 14:48:19 +02002363 if (in_vim9script())
Bram Moolenaar18024052021-12-25 21:43:28 +00002364 *remp = !tv_get_bool_chk(newtv, &error);
Bram Moolenaar56acb092020-08-16 14:48:19 +02002365 else
Bram Moolenaarea696852020-11-09 18:31:39 +01002366 *remp = (tv_get_number_chk(newtv, &error) == 0);
2367 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002368 // On type error, nothing has been removed; return FAIL to stop the
2369 // loop. The error message was given by tv_get_number_chk().
2370 if (error)
2371 goto theend;
2372 }
Ernie Raele79e2072024-01-13 11:47:33 +01002373 else if (filtermap == FILTERMAP_FOREACH)
2374 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002375 retval = OK;
2376theend:
2377 clear_tv(get_vim_var_tv(VV_VAL));
2378 return retval;
2379}
2380
2381/*
Ernie Raele79e2072024-01-13 11:47:33 +01002382 * Implementation of map(), filter(), foreach() for a List. Apply "expr" to
2383 * every item in List "l" and return the result in "rettv".
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002384 */
2385 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002386list_filter_map(
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002387 list_T *l,
2388 filtermap_T filtermap,
2389 type_T *argtype,
2390 char *func_name,
2391 char_u *arg_errmsg,
2392 typval_T *expr,
2393 typval_T *rettv)
2394{
2395 int prev_lock;
2396 list_T *l_ret = NULL;
2397 int idx = 0;
2398 int rem;
2399 listitem_T *li, *nli;
Bram Moolenaar82418262022-09-28 16:16:15 +01002400 typval_T newtv;
2401 funccall_T *fc;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002402
2403 if (filtermap == FILTERMAP_MAPNEW)
2404 {
2405 rettv->v_type = VAR_LIST;
2406 rettv->vval.v_list = NULL;
2407 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01002408 if (l == NULL || (filtermap == FILTERMAP_FILTER
2409 && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002410 return;
2411
2412 prev_lock = l->lv_lock;
2413
2414 if (filtermap == FILTERMAP_MAPNEW)
2415 {
2416 if (rettv_list_alloc(rettv) == FAIL)
2417 return;
2418 l_ret = rettv->vval.v_list;
2419 }
2420 // set_vim_var_nr() doesn't set the type
2421 set_vim_var_type(VV_KEY, VAR_NUMBER);
2422
Ernie Raele6d40dc2023-03-19 21:23:38 +00002423 if (l->lv_lock == 0)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002424 l->lv_lock = VAR_LOCKED;
2425
zeertzjqe7d49462023-04-16 20:53:55 +01002426 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +01002427 fc = eval_expr_get_funccal(expr, &newtv);
2428
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002429 if (l->lv_first == &range_list_item)
2430 {
2431 varnumber_T val = l->lv_u.nonmat.lv_start;
2432 int len = l->lv_len;
2433 int stride = l->lv_u.nonmat.lv_stride;
2434
2435 // List from range(): loop over the numbers
Ernie Raele79e2072024-01-13 11:47:33 +01002436 // NOTE: foreach() returns the range_list_item
2437 if (filtermap != FILTERMAP_MAPNEW && filtermap != FILTERMAP_FOREACH)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002438 {
2439 l->lv_first = NULL;
2440 l->lv_u.mat.lv_last = NULL;
2441 l->lv_len = 0;
2442 l->lv_u.mat.lv_idx_item = NULL;
2443 }
2444
2445 for (idx = 0; idx < len; ++idx)
2446 {
2447 typval_T tv;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002448
2449 tv.v_type = VAR_NUMBER;
2450 tv.v_lock = 0;
2451 tv.vval.v_number = val;
2452 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +01002453 if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002454 break;
2455 if (did_emsg)
2456 {
2457 clear_tv(&newtv);
2458 break;
2459 }
Ernie Raele79e2072024-01-13 11:47:33 +01002460 if (filtermap != FILTERMAP_FOREACH)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002461 {
Ernie Raele79e2072024-01-13 11:47:33 +01002462 if (filtermap != FILTERMAP_FILTER)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002463 {
Ernie Raele79e2072024-01-13 11:47:33 +01002464 if (filtermap == FILTERMAP_MAP && argtype != NULL
2465 && check_typval_arg_type(
2466 argtype->tt_member, &newtv,
2467 func_name, 0) == FAIL)
2468 {
2469 clear_tv(&newtv);
2470 break;
2471 }
2472 // map(), mapnew(): always append the new value to the
2473 // list
2474 if (list_append_tv_move(filtermap == FILTERMAP_MAP
2475 ? l : l_ret, &newtv) == FAIL)
2476 break;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002477 }
Ernie Raele79e2072024-01-13 11:47:33 +01002478 else if (!rem)
2479 {
2480 // filter(): append the list item value when not rem
2481 if (list_append_tv_move(l, &tv) == FAIL)
2482 break;
2483 }
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002484 }
2485
2486 val += stride;
2487 }
2488 }
2489 else
2490 {
2491 // Materialized list: loop over the items
2492 for (li = l->lv_first; li != NULL; li = nli)
2493 {
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002494 if (filtermap == FILTERMAP_MAP && value_check_lock(
2495 li->li_tv.v_lock, arg_errmsg, TRUE))
2496 break;
2497 nli = li->li_next;
2498 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +01002499 if (filter_map_one(&li->li_tv, expr, filtermap, fc,
2500 &newtv, &rem) == FAIL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002501 break;
2502 if (did_emsg)
2503 {
2504 clear_tv(&newtv);
2505 break;
2506 }
2507 if (filtermap == FILTERMAP_MAP)
2508 {
2509 if (argtype != NULL && check_typval_arg_type(
2510 argtype->tt_member, &newtv, func_name, 0) == FAIL)
2511 {
2512 clear_tv(&newtv);
2513 break;
2514 }
2515 // map(): replace the list item value
2516 clear_tv(&li->li_tv);
2517 newtv.v_lock = 0;
2518 li->li_tv = newtv;
2519 }
2520 else if (filtermap == FILTERMAP_MAPNEW)
2521 {
2522 // mapnew(): append the list item value
2523 if (list_append_tv_move(l_ret, &newtv) == FAIL)
2524 break;
2525 }
2526 else if (filtermap == FILTERMAP_FILTER && rem)
Ernie Raele79e2072024-01-13 11:47:33 +01002527 listitem_remove(l, li);
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002528 ++idx;
2529 }
2530 }
2531
2532 l->lv_lock = prev_lock;
Bram Moolenaar82418262022-09-28 16:16:15 +01002533 if (fc != NULL)
2534 remove_funccal();
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002535}
2536
2537/*
Ernie Raele79e2072024-01-13 11:47:33 +01002538 * Implementation of map(), filter() and foreach().
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002539 */
2540 static void
Bram Moolenaarea696852020-11-09 18:31:39 +01002541filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002542{
2543 typval_T *expr;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002544 char *func_name = filtermap == FILTERMAP_MAP ? "map()"
Bram Moolenaarea696852020-11-09 18:31:39 +01002545 : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
Ernie Raele79e2072024-01-13 11:47:33 +01002546 : filtermap == FILTERMAP_FILTER ? "filter()"
2547 : "foreach()";
Bram Moolenaarea696852020-11-09 18:31:39 +01002548 char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
2549 ? N_("map() argument")
2550 : filtermap == FILTERMAP_MAPNEW
2551 ? N_("mapnew() argument")
Ernie Raele79e2072024-01-13 11:47:33 +01002552 : filtermap == FILTERMAP_FILTER
2553 ? N_("filter() argument")
2554 : N_("foreach() argument"));
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002555 int save_did_emsg;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002556 type_T *type = NULL;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002557
Ernie Raele79e2072024-01-13 11:47:33 +01002558 // map(), filter(), foreach() return the first argument, also on failure.
Bram Moolenaar2d877592021-12-16 08:21:09 +00002559 if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
Bram Moolenaarea696852020-11-09 18:31:39 +01002560 copy_tv(&argvars[0], rettv);
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002561
2562 if (in_vim9script()
Bram Moolenaar2d877592021-12-16 08:21:09 +00002563 && (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
2564 == FAIL))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002565 return;
2566
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002567 if (filtermap == FILTERMAP_MAP && in_vim9script())
2568 {
Bram Moolenaar35c807d2022-01-27 16:36:29 +00002569 // Check that map() does not change the declared type of the list or
2570 // dict.
2571 if (argvars[0].v_type == VAR_DICT && argvars[0].vval.v_dict != NULL)
2572 type = argvars[0].vval.v_dict->dv_type;
2573 else if (argvars[0].v_type == VAR_LIST
2574 && argvars[0].vval.v_list != NULL)
2575 type = argvars[0].vval.v_list->lv_type;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002576 }
Bram Moolenaarffdf8ad2020-10-15 22:29:17 +02002577
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002578 if (argvars[0].v_type != VAR_BLOB
2579 && argvars[0].v_type != VAR_LIST
2580 && argvars[0].v_type != VAR_DICT
2581 && argvars[0].v_type != VAR_STRING)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002582 {
rbtnnc479ce02021-12-15 19:14:54 +00002583 semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob),
2584 func_name);
Bram Moolenaar98cd3032022-01-27 17:37:41 +00002585 return;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002586 }
2587
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002588 // On type errors, the preceding call has already displayed an error
2589 // message. Avoid a misleading error message for an empty string that
2590 // was not passed as argument.
Bram Moolenaar35c807d2022-01-27 16:36:29 +00002591 expr = &argvars[1];
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002592 if (expr->v_type == VAR_UNKNOWN)
2593 return;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002594
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002595 typval_T save_val;
2596 typval_T save_key;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002597
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002598 prepare_vimvar(VV_VAL, &save_val);
2599 prepare_vimvar(VV_KEY, &save_key);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002600
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002601 // We reset "did_emsg" to be able to detect whether an error
2602 // occurred during evaluation of the expression.
2603 save_did_emsg = did_emsg;
2604 did_emsg = FALSE;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002605
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002606 if (argvars[0].v_type == VAR_DICT)
2607 dict_filter_map(argvars[0].vval.v_dict, filtermap, type, func_name,
Ernie Raele6d40dc2023-03-19 21:23:38 +00002608 arg_errmsg, expr, rettv);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002609 else if (argvars[0].v_type == VAR_BLOB)
Ernie Raele6d40dc2023-03-19 21:23:38 +00002610 blob_filter_map(argvars[0].vval.v_blob, filtermap, expr,
2611 arg_errmsg, rettv);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002612 else if (argvars[0].v_type == VAR_STRING)
Ernie Raele6d40dc2023-03-19 21:23:38 +00002613 string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, rettv);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002614 else // argvars[0].v_type == VAR_LIST
2615 list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
Ernie Raele6d40dc2023-03-19 21:23:38 +00002616 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002617
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002618 restore_vimvar(VV_KEY, &save_key);
2619 restore_vimvar(VV_VAL, &save_val);
2620
2621 did_emsg |= save_did_emsg;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002622}
2623
2624/*
2625 * "filter()" function
2626 */
2627 void
2628f_filter(typval_T *argvars, typval_T *rettv)
2629{
Bram Moolenaarea696852020-11-09 18:31:39 +01002630 filter_map(argvars, rettv, FILTERMAP_FILTER);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002631}
2632
2633/*
2634 * "map()" function
2635 */
2636 void
2637f_map(typval_T *argvars, typval_T *rettv)
2638{
Bram Moolenaarea696852020-11-09 18:31:39 +01002639 filter_map(argvars, rettv, FILTERMAP_MAP);
2640}
2641
2642/*
2643 * "mapnew()" function
2644 */
2645 void
2646f_mapnew(typval_T *argvars, typval_T *rettv)
2647{
2648 filter_map(argvars, rettv, FILTERMAP_MAPNEW);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002649}
2650
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002651/*
Ernie Raele79e2072024-01-13 11:47:33 +01002652 * "foreach()" function
2653 */
2654 void
2655f_foreach(typval_T *argvars, typval_T *rettv)
2656{
2657 filter_map(argvars, rettv, FILTERMAP_FOREACH);
2658}
2659
2660/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002661 * "add(list, item)" function
2662 */
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002663 static void
2664list_add(typval_T *argvars, typval_T *rettv)
2665{
2666 list_T *l = argvars[0].vval.v_list;
2667
2668 if (l == NULL)
2669 {
2670 if (in_vim9script())
2671 emsg(_(e_cannot_add_to_null_list));
2672 }
2673 else if (!value_check_lock(l->lv_lock,
2674 (char_u *)N_("add() argument"), TRUE)
2675 && list_append_tv(l, &argvars[1]) == OK)
2676 {
2677 copy_tv(&argvars[0], rettv);
2678 }
2679}
2680
2681/*
2682 * "add(object, item)" function
2683 */
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002684 void
2685f_add(typval_T *argvars, typval_T *rettv)
2686{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002687 rettv->vval.v_number = 1; // Default: Failed
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002688
2689 if (in_vim9script()
2690 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2691 || (argvars[0].v_type == VAR_BLOB
2692 && check_for_number_arg(argvars, 1) == FAIL)))
2693 return;
2694
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002695 if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002696 list_add(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002697 else if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002698 blob_add(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002699 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002700 emsg(_(e_list_or_blob_required));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002701}
2702
2703/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002704 * Count the number of times item "needle" occurs in List "l" starting at index
2705 * "idx". Case is ignored if "ic" is TRUE.
2706 */
2707 static long
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002708list_count(list_T *l, typval_T *needle, long idx, int ic)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002709{
2710 long n = 0;
2711 listitem_T *li;
2712
2713 if (l == NULL)
2714 return 0;
2715
2716 CHECK_LIST_MATERIALIZE(l);
2717
2718 if (list_len(l) == 0)
2719 return 0;
2720
2721 li = list_find(l, idx);
2722 if (li == NULL)
2723 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002724 semsg(_(e_list_index_out_of_range_nr), idx);
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002725 return 0;
2726 }
2727
2728 for ( ; li != NULL; li = li->li_next)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02002729 if (tv_equal(&li->li_tv, needle, ic))
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002730 ++n;
2731
2732 return n;
2733}
2734
2735/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002736 * "count()" function
2737 */
2738 void
2739f_count(typval_T *argvars, typval_T *rettv)
2740{
2741 long n = 0;
2742 int ic = FALSE;
2743 int error = FALSE;
2744
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002745 if (in_vim9script()
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002746 && (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002747 || check_for_opt_bool_arg(argvars, 2) == FAIL
2748 || (argvars[2].v_type != VAR_UNKNOWN
2749 && check_for_opt_number_arg(argvars, 3) == FAIL)))
2750 return;
2751
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002752 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar119f5572020-09-02 21:31:22 +02002753 ic = (int)tv_get_bool_chk(&argvars[2], &error);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002754
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002755 if (!error && argvars[0].v_type == VAR_STRING)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002756 n = string_count(argvars[0].vval.v_string,
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002757 tv_get_string_chk(&argvars[1]), ic);
2758 else if (!error && argvars[0].v_type == VAR_LIST)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002759 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002760 long idx = 0;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002761
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002762 if (argvars[2].v_type != VAR_UNKNOWN
2763 && argvars[3].v_type != VAR_UNKNOWN)
2764 idx = (long)tv_get_number_chk(&argvars[3], &error);
2765 if (!error)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002766 n = list_count(argvars[0].vval.v_list, &argvars[1], idx, ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002767 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002768 else if (!error && argvars[0].v_type == VAR_DICT)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002769 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002770 if (argvars[2].v_type != VAR_UNKNOWN
2771 && argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002772 emsg(_(e_invalid_argument));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002773 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002774 n = dict_count(argvars[0].vval.v_dict, &argvars[1], ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002775 }
zeertzjq4f389e72023-08-17 22:10:40 +02002776 else if (!error)
2777 semsg(_(e_argument_of_str_must_be_list_string_or_dictionary),
2778 "count()");
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002779 rettv->vval.v_number = n;
2780}
2781
2782/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002783 * extend() a List. Append List argvars[1] to List argvars[0] before index
2784 * argvars[3] and return the resulting list in "rettv". "is_new" is TRUE for
2785 * extendnew().
2786 */
2787 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002788list_extend_func(
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002789 typval_T *argvars,
2790 type_T *type,
2791 char *func_name,
2792 char_u *arg_errmsg,
2793 int is_new,
2794 typval_T *rettv)
2795{
2796 list_T *l1, *l2;
2797 listitem_T *item;
2798 long before;
2799 int error = FALSE;
2800
2801 l1 = argvars[0].vval.v_list;
2802 if (l1 == NULL)
2803 {
2804 emsg(_(e_cannot_extend_null_list));
2805 return;
2806 }
2807 l2 = argvars[1].vval.v_list;
2808 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar381692b2022-02-02 20:01:27 +00002809 && l2 != NULL)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002810 {
2811 if (is_new)
2812 {
Bram Moolenaar381692b2022-02-02 20:01:27 +00002813 l1 = list_copy(l1, FALSE, TRUE, get_copyID());
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002814 if (l1 == NULL)
2815 return;
2816 }
2817
2818 if (argvars[2].v_type != VAR_UNKNOWN)
2819 {
2820 before = (long)tv_get_number_chk(&argvars[2], &error);
2821 if (error)
2822 return; // type error; errmsg already given
2823
2824 if (before == l1->lv_len)
2825 item = NULL;
2826 else
2827 {
2828 item = list_find(l1, before);
2829 if (item == NULL)
2830 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002831 semsg(_(e_list_index_out_of_range_nr), before);
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002832 return;
2833 }
2834 }
2835 }
2836 else
2837 item = NULL;
2838 if (type != NULL && check_typval_arg_type(
2839 type, &argvars[1], func_name, 2) == FAIL)
2840 return;
2841 list_extend(l1, l2, item);
2842
2843 if (is_new)
2844 {
2845 rettv->v_type = VAR_LIST;
2846 rettv->vval.v_list = l1;
2847 rettv->v_lock = FALSE;
2848 }
2849 else
2850 copy_tv(&argvars[0], rettv);
2851 }
2852}
2853
2854/*
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002855 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002856 */
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002857 static void
2858extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002859{
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002860 type_T *type = NULL;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002861 char *func_name = is_new ? "extendnew()" : "extend()";
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002862
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002863 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002864 {
2865 // Check that extend() does not change the type of the list if it was
2866 // declared.
2867 if (!is_new && in_vim9script() && argvars[0].vval.v_list != NULL)
2868 type = argvars[0].vval.v_list->lv_type;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002869 list_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002870 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002871 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002872 {
Bram Moolenaarbe19d782023-03-09 22:06:49 +00002873 // Check that extend() does not change the type of the dict if it was
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002874 // declared.
2875 if (!is_new && in_vim9script() && argvars[0].vval.v_dict != NULL)
2876 type = argvars[0].vval.v_dict->dv_type;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002877 dict_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002878 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002879 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002880 semsg(_(e_argument_of_str_must_be_list_or_dictionary), func_name);
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002881}
2882
2883/*
2884 * "extend(list, list [, idx])" function
2885 * "extend(dict, dict [, action])" function
2886 */
2887 void
2888f_extend(typval_T *argvars, typval_T *rettv)
2889{
2890 char_u *errmsg = (char_u *)N_("extend() argument");
2891
2892 extend(argvars, rettv, errmsg, FALSE);
2893}
2894
2895/*
2896 * "extendnew(list, list [, idx])" function
2897 * "extendnew(dict, dict [, action])" function
2898 */
2899 void
2900f_extendnew(typval_T *argvars, typval_T *rettv)
2901{
2902 char_u *errmsg = (char_u *)N_("extendnew() argument");
2903
2904 extend(argvars, rettv, errmsg, TRUE);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002905}
2906
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002907 static void
2908list_insert_func(typval_T *argvars, typval_T *rettv)
2909{
2910 list_T *l = argvars[0].vval.v_list;
2911 long before = 0;
2912 listitem_T *item;
2913 int error = FALSE;
2914
2915 if (l == NULL)
2916 {
2917 if (in_vim9script())
2918 emsg(_(e_cannot_add_to_null_list));
2919 return;
2920 }
2921
2922 if (value_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE))
2923 return;
2924
2925 if (argvars[2].v_type != VAR_UNKNOWN)
2926 before = (long)tv_get_number_chk(&argvars[2], &error);
2927 if (error)
2928 return; // type error; errmsg already given
2929
2930 if (before == l->lv_len)
2931 item = NULL;
2932 else
2933 {
2934 item = list_find(l, before);
2935 if (item == NULL)
2936 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002937 semsg(_(e_list_index_out_of_range_nr), before);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002938 l = NULL;
2939 }
2940 }
2941 if (l != NULL)
2942 {
2943 (void)list_insert_tv(l, &argvars[1], item);
2944 copy_tv(&argvars[0], rettv);
2945 }
2946}
2947
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002948/*
2949 * "insert()" function
2950 */
2951 void
2952f_insert(typval_T *argvars, typval_T *rettv)
2953{
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002954 if (in_vim9script()
2955 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2956 || (argvars[0].v_type == VAR_BLOB
2957 && check_for_number_arg(argvars, 1) == FAIL)
2958 || check_for_opt_number_arg(argvars, 2) == FAIL))
2959 return;
2960
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002961 if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002962 blob_insert_func(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002963 else if (argvars[0].v_type != VAR_LIST)
Bram Moolenaard82a47d2022-01-05 20:24:39 +00002964 semsg(_(e_argument_of_str_must_be_list_or_blob), "insert()");
Bram Moolenaar39211cb2021-04-18 15:48:04 +02002965 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002966 list_insert_func(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002967}
2968
2969/*
2970 * "remove()" function
2971 */
2972 void
2973f_remove(typval_T *argvars, typval_T *rettv)
2974{
2975 char_u *arg_errmsg = (char_u *)N_("remove() argument");
2976
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002977 if (in_vim9script()
2978 && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL
2979 || ((argvars[0].v_type == VAR_LIST
2980 || argvars[0].v_type == VAR_BLOB)
2981 && (check_for_number_arg(argvars, 1) == FAIL
2982 || check_for_opt_number_arg(argvars, 2) == FAIL))
2983 || (argvars[0].v_type == VAR_DICT
2984 && check_for_string_or_number_arg(argvars, 1) == FAIL)))
2985 return;
2986
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002987 if (argvars[0].v_type == VAR_DICT)
2988 dict_remove(argvars, rettv, arg_errmsg);
2989 else if (argvars[0].v_type == VAR_BLOB)
Sean Dewar80d73952021-08-04 19:25:54 +02002990 blob_remove(argvars, rettv, arg_errmsg);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002991 else if (argvars[0].v_type == VAR_LIST)
2992 list_remove(argvars, rettv, arg_errmsg);
2993 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002994 semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "remove()");
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002995}
2996
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002997 static void
2998list_reverse(list_T *l, typval_T *rettv)
2999{
3000 listitem_T *li, *ni;
3001
3002 rettv_list_set(rettv, l);
3003 if (l != NULL
3004 && !value_check_lock(l->lv_lock,
3005 (char_u *)N_("reverse() argument"), TRUE))
3006 {
3007 if (l->lv_first == &range_list_item)
3008 {
3009 varnumber_T new_start = l->lv_u.nonmat.lv_start
=?UTF-8?q?Dundar=20G=C3=B6c?=d5cec1f2022-01-29 15:19:23 +00003010 + ((varnumber_T)l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003011 l->lv_u.nonmat.lv_end = new_start
3012 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
3013 l->lv_u.nonmat.lv_start = new_start;
3014 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
3015 return;
3016 }
3017 li = l->lv_u.mat.lv_last;
3018 l->lv_first = l->lv_u.mat.lv_last = NULL;
3019 l->lv_len = 0;
3020 while (li != NULL)
3021 {
3022 ni = li->li_prev;
3023 list_append(l, li);
3024 li = ni;
3025 }
3026 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
3027 }
3028}
3029
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003030/*
3031 * "reverse({list})" function
3032 */
3033 void
3034f_reverse(typval_T *argvars, typval_T *rettv)
3035{
Yegappan Lakshmananf9dc2782023-05-11 15:02:56 +01003036 if (check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003037 return;
3038
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003039 if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003040 blob_reverse(argvars[0].vval.v_blob, rettv);
Yegappan Lakshmanan03ff1c22023-05-06 14:08:21 +01003041 else if (argvars[0].v_type == VAR_STRING)
zeertzjq4dd266c2023-08-19 11:35:03 +02003042 {
3043 rettv->v_type = VAR_STRING;
3044 if (argvars[0].vval.v_string != NULL)
3045 rettv->vval.v_string = reverse_text(argvars[0].vval.v_string);
3046 else
3047 rettv->vval.v_string = NULL;
3048 }
Yegappan Lakshmananf9dc2782023-05-11 15:02:56 +01003049 else if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003050 list_reverse(argvars[0].vval.v_list, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003051}
3052
Bram Moolenaar85629982020-06-01 18:39:20 +02003053/*
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003054 * Implementation of reduce() for list "argvars[0]", using the function "expr"
3055 * starting with the optional initial value argvars[2] and return the result in
3056 * "rettv".
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003057 */
3058 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003059list_reduce(
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003060 typval_T *argvars,
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003061 typval_T *expr,
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003062 typval_T *rettv)
3063{
3064 list_T *l = argvars[0].vval.v_list;
3065 listitem_T *li = NULL;
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003066 int range_list;
3067 int range_idx = 0;
3068 varnumber_T range_val = 0;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003069 typval_T initial;
3070 typval_T argv[3];
3071 int r;
3072 int called_emsg_start = called_emsg;
3073 int prev_locked;
Bram Moolenaar82418262022-09-28 16:16:15 +01003074 funccall_T *fc;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003075
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003076 // Using reduce on a range() uses "range_idx" and "range_val".
3077 range_list = l != NULL && l->lv_first == &range_list_item;
3078 if (range_list)
3079 range_val = l->lv_u.nonmat.lv_start;
3080
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003081 if (argvars[2].v_type == VAR_UNKNOWN)
3082 {
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003083 if (l == NULL || l->lv_len == 0)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003084 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00003085 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List");
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003086 return;
3087 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003088 if (range_list)
3089 {
3090 initial.v_type = VAR_NUMBER;
3091 initial.vval.v_number = range_val;
3092 range_val += l->lv_u.nonmat.lv_stride;
3093 range_idx = 1;
3094 }
3095 else
3096 {
3097 initial = l->lv_first->li_tv;
3098 li = l->lv_first->li_next;
3099 }
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003100 }
3101 else
3102 {
3103 initial = argvars[2];
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003104 if (l != NULL && !range_list)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003105 li = l->lv_first;
3106 }
3107 copy_tv(&initial, rettv);
3108
3109 if (l == NULL)
3110 return;
3111
zeertzjqe7d49462023-04-16 20:53:55 +01003112 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +01003113 fc = eval_expr_get_funccal(expr, rettv);
3114
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003115 prev_locked = l->lv_lock;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003116 l->lv_lock = VAR_FIXED; // disallow the list changing here
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003117
3118 while (range_list ? range_idx < l->lv_len : li != NULL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003119 {
3120 argv[0] = *rettv;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003121 rettv->v_type = VAR_UNKNOWN;
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003122
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003123 if (range_list)
3124 {
3125 argv[1].v_type = VAR_NUMBER;
3126 argv[1].vval.v_number = range_val;
3127 }
3128 else
3129 argv[1] = li->li_tv;
3130
zeertzjqad0c4422023-08-17 22:15:47 +02003131 r = eval_expr_typval(expr, TRUE, argv, 2, fc, rettv);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003132
Bram Moolenaar1936c762022-09-28 15:19:10 +01003133 if (argv[0].v_type != VAR_NUMBER && argv[0].v_type != VAR_UNKNOWN)
3134 clear_tv(&argv[0]);
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003135 if (r == FAIL || called_emsg != called_emsg_start)
3136 break;
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003137
Bram Moolenaar1936c762022-09-28 15:19:10 +01003138 // advance to the next item
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003139 if (range_list)
3140 {
3141 range_val += l->lv_u.nonmat.lv_stride;
3142 ++range_idx;
3143 }
3144 else
3145 li = li->li_next;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003146 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003147
Bram Moolenaar82418262022-09-28 16:16:15 +01003148 if (fc != NULL)
3149 remove_funccal();
3150
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003151 l->lv_lock = prev_locked;
3152}
3153
3154/*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01003155 * "reduce(list, { accumulator, element -> value } [, initial])" function
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003156 * "reduce(blob, { accumulator, element -> value } [, initial])"
3157 * "reduce(string, { accumulator, element -> value } [, initial])"
Bram Moolenaar85629982020-06-01 18:39:20 +02003158 */
3159 void
3160f_reduce(typval_T *argvars, typval_T *rettv)
3161{
Bram Moolenaar85629982020-06-01 18:39:20 +02003162 char_u *func_name;
Bram Moolenaar85629982020-06-01 18:39:20 +02003163
rbtnn0ccb5842021-12-18 18:33:46 +00003164 if (in_vim9script()
3165 && check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
Bram Moolenaar85629982020-06-01 18:39:20 +02003166 return;
rbtnn0ccb5842021-12-18 18:33:46 +00003167
3168 if (argvars[0].v_type != VAR_STRING
3169 && argvars[0].v_type != VAR_LIST
3170 && argvars[0].v_type != VAR_BLOB)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003171 {
zeertzjqad0c4422023-08-17 22:15:47 +02003172 emsg(_(e_string_list_or_blob_required));
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003173 return;
3174 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003175
3176 if (argvars[1].v_type == VAR_FUNC)
3177 func_name = argvars[1].vval.v_string;
3178 else if (argvars[1].v_type == VAR_PARTIAL)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003179 func_name = partial_name(argvars[1].vval.v_partial);
Bram Moolenaar85629982020-06-01 18:39:20 +02003180 else
3181 func_name = tv_get_string(&argvars[1]);
Bram Moolenaar0d90e722020-11-03 18:20:19 +01003182 if (func_name == NULL || *func_name == NUL)
3183 {
3184 emsg(_(e_missing_function_argument));
3185 return;
3186 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003187
Bram Moolenaar85629982020-06-01 18:39:20 +02003188 if (argvars[0].v_type == VAR_LIST)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003189 list_reduce(argvars, &argvars[1], rettv);
rbtnn0ccb5842021-12-18 18:33:46 +00003190 else if (argvars[0].v_type == VAR_STRING)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003191 string_reduce(argvars, &argvars[1], rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003192 else
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003193 blob_reduce(argvars, &argvars[1], rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003194}
3195
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +02003196/*
3197 * slice() function
3198 */
3199 void
3200f_slice(typval_T *argvars, typval_T *rettv)
3201{
3202 if (in_vim9script()
3203 && ((argvars[0].v_type != VAR_STRING
3204 && argvars[0].v_type != VAR_LIST
3205 && argvars[0].v_type != VAR_BLOB
3206 && check_for_list_arg(argvars, 0) == FAIL)
3207 || check_for_number_arg(argvars, 1) == FAIL
3208 || check_for_opt_number_arg(argvars, 2) == FAIL))
3209 return;
3210
3211 if (check_can_index(&argvars[0], TRUE, FALSE) != OK)
3212 return;
3213
3214 copy_tv(argvars, rettv);
3215 eval_index_inner(rettv, TRUE, argvars + 1,
3216 argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
3217 TRUE, NULL, 0, FALSE);
3218}
3219
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02003220#endif // defined(FEAT_EVAL)