blob: c48c75152ba56554258aa7d830c58df3b0fe5e65 [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 Lakshmanan9cb865e2025-03-23 16:42:16 +01001523 if (check_for_list_or_tuple_arg(argvars, 0) == FAIL
1524 || check_for_opt_string_arg(argvars, 1) == FAIL)
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001525 return;
1526
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01001527 if ((argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list == NULL)
1528 || (argvars[0].v_type == VAR_TUPLE
1529 && argvars[0].vval.v_tuple == NULL))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001530 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001531
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001532 if (argvars[1].v_type == VAR_UNKNOWN)
1533 sep = (char_u *)" ";
1534 else
1535 sep = tv_get_string_chk(&argvars[1]);
1536
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001537 if (sep != NULL)
1538 {
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001539 ga_init2(&ga, sizeof(char), 80);
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01001540 if (argvars[0].v_type == VAR_LIST)
1541 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
1542 else
1543 tuple_join(&ga, argvars[0].vval.v_tuple, sep, TRUE, FALSE, 0);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001544 ga_append(&ga, NUL);
1545 rettv->vval.v_string = (char_u *)ga.ga_data;
1546 }
1547 else
1548 rettv->vval.v_string = NULL;
1549}
1550
1551/*
Bram Moolenaarda861d62016-07-17 15:46:27 +02001552 * Allocate a variable for a List and fill it from "*arg".
Bram Moolenaar71478202020-06-26 22:46:27 +02001553 * "*arg" points to the "[".
Bram Moolenaarda861d62016-07-17 15:46:27 +02001554 * Return OK or FAIL.
1555 */
1556 int
Bram Moolenaar9a78e6d2020-07-01 18:29:55 +02001557eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001558{
Bram Moolenaar71478202020-06-26 22:46:27 +02001559 int evaluate = evalarg == NULL ? FALSE
1560 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001561 list_T *l = NULL;
1562 typval_T tv;
1563 listitem_T *item;
Bram Moolenaareb6880b2020-07-12 17:07:05 +02001564 int vim9script = in_vim9script();
Bram Moolenaar71478202020-06-26 22:46:27 +02001565 int had_comma;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001566
1567 if (evaluate)
1568 {
1569 l = list_alloc();
1570 if (l == NULL)
1571 return FAIL;
1572 }
1573
Bram Moolenaar962d7212020-07-04 14:15:00 +02001574 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001575 while (**arg != ']' && **arg != NUL)
1576 {
Bram Moolenaar71478202020-06-26 22:46:27 +02001577 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarda861d62016-07-17 15:46:27 +02001578 goto failret;
Ernie Raelfa831102023-12-14 20:06:39 +01001579 if (check_typval_is_value(&tv) == FAIL)
1580 {
1581 if (evaluate)
1582 clear_tv(&tv);
1583 goto failret;
1584 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001585 if (evaluate)
1586 {
1587 item = listitem_alloc();
1588 if (item != NULL)
1589 {
1590 item->li_tv = tv;
1591 item->li_tv.v_lock = 0;
1592 list_append(l, item);
1593 }
1594 else
1595 clear_tv(&tv);
1596 }
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001597 // Legacy Vim script allowed a space before the comma.
1598 if (!vim9script)
1599 *arg = skipwhite(*arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001600
Bram Moolenaare6e03172020-06-27 16:36:05 +02001601 // the comma must come after the value
Bram Moolenaar71478202020-06-26 22:46:27 +02001602 had_comma = **arg == ',';
1603 if (had_comma)
Bram Moolenaare6e03172020-06-27 16:36:05 +02001604 {
Christian Brabandt00cb2472023-09-05 20:46:25 +02001605 if (vim9script && !IS_WHITE_NL_OR_NUL((*arg)[1]) && (*arg)[1] != ']')
Bram Moolenaare6e03172020-06-27 16:36:05 +02001606 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001607 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001608 goto failret;
1609 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001610 *arg = skipwhite(*arg + 1);
Bram Moolenaare6e03172020-06-27 16:36:05 +02001611 }
Bram Moolenaar71478202020-06-26 22:46:27 +02001612
Bram Moolenaare6b53242020-07-01 17:28:33 +02001613 // The "]" can be on the next line. But a double quoted string may
1614 // follow, not a comment.
Bram Moolenaar962d7212020-07-04 14:15:00 +02001615 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001616 if (**arg == ']')
1617 break;
Bram Moolenaar71478202020-06-26 22:46:27 +02001618
1619 if (!had_comma)
Bram Moolenaarda861d62016-07-17 15:46:27 +02001620 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001621 if (do_error)
Bram Moolenaardb199212020-08-12 18:01:53 +02001622 {
1623 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001624 semsg(_(e_no_white_space_allowed_before_str_str),
1625 ",", *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001626 else
Bram Moolenaara6f79292022-01-04 21:30:47 +00001627 semsg(_(e_missing_comma_in_list_str), *arg);
Bram Moolenaardb199212020-08-12 18:01:53 +02001628 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001629 goto failret;
1630 }
Bram Moolenaarda861d62016-07-17 15:46:27 +02001631 }
1632
1633 if (**arg != ']')
1634 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001635 if (do_error)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001636 semsg(_(e_missing_end_of_list_rsb_str), *arg);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001637failret:
1638 if (evaluate)
1639 list_free(l);
1640 return FAIL;
1641 }
1642
Bram Moolenaar9d489562020-07-30 20:08:50 +02001643 *arg += 1;
Bram Moolenaarda861d62016-07-17 15:46:27 +02001644 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001645 rettv_list_set(rettv, l);
Bram Moolenaarda861d62016-07-17 15:46:27 +02001646
1647 return OK;
1648}
1649
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001650/*
Bram Moolenaarcaa55b62017-01-10 13:51:09 +01001651 * Write "list" of strings to file "fd".
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001652 */
1653 int
1654write_list(FILE *fd, list_T *list, int binary)
1655{
1656 listitem_T *li;
1657 int c;
1658 int ret = OK;
1659 char_u *s;
1660
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001661 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001662 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001663 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001664 for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001665 {
1666 if (*s == '\n')
1667 c = putc(NUL, fd);
1668 else
1669 c = putc(*s, fd);
1670 if (c == EOF)
1671 {
1672 ret = FAIL;
1673 break;
1674 }
1675 }
1676 if (!binary || li->li_next != NULL)
1677 if (putc('\n', fd) == EOF)
1678 {
1679 ret = FAIL;
1680 break;
1681 }
1682 if (ret == FAIL)
1683 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +00001684 emsg(_(e_error_while_writing));
Bram Moolenaar73dad1e2016-07-17 22:13:49 +02001685 break;
1686 }
1687 }
1688 return ret;
1689}
1690
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001691/*
1692 * Initialize a static list with 10 items.
1693 */
1694 void
1695init_static_list(staticList10_T *sl)
1696{
1697 list_T *l = &sl->sl_list;
1698 int i;
1699
Yegappan Lakshmanan960dcbd2023-03-07 17:45:11 +00001700 CLEAR_POINTER(sl);
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001701 l->lv_first = &sl->sl_items[0];
Bram Moolenaar0ff6aad2020-01-29 21:27:21 +01001702 l->lv_u.mat.lv_last = &sl->sl_items[9];
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001703 l->lv_refcount = DO_NOT_FREE_CNT;
1704 l->lv_lock = VAR_FIXED;
1705 sl->sl_list.lv_len = 10;
1706
1707 for (i = 0; i < 10; ++i)
1708 {
1709 listitem_T *li = &sl->sl_items[i];
1710
1711 if (i == 0)
1712 li->li_prev = NULL;
1713 else
1714 li->li_prev = li - 1;
1715 if (i == 9)
1716 li->li_next = NULL;
1717 else
1718 li->li_next = li + 1;
1719 }
1720}
1721
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001722/*
1723 * "list2str()" function
1724 */
1725 void
1726f_list2str(typval_T *argvars, typval_T *rettv)
1727{
1728 list_T *l;
1729 listitem_T *li;
1730 garray_T ga;
1731 int utf8 = FALSE;
1732
1733 rettv->v_type = VAR_STRING;
1734 rettv->vval.v_string = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001735
1736 if (in_vim9script()
1737 && (check_for_list_arg(argvars, 0) == FAIL
1738 || check_for_opt_bool_arg(argvars, 1) == FAIL))
1739 return;
1740
Bram Moolenaard83392a2022-09-01 12:22:46 +01001741 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001742 return;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001743
1744 l = argvars[0].vval.v_list;
1745 if (l == NULL)
1746 return; // empty list results in empty string
1747
1748 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaara48f7862020-09-05 20:16:57 +02001749 utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001750
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001751 CHECK_LIST_MATERIALIZE(l);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001752 ga_init2(&ga, 1, 80);
1753 if (has_mbyte || utf8)
1754 {
1755 char_u buf[MB_MAXBYTES + 1];
1756 int (*char2bytes)(int, char_u *);
1757
1758 if (utf8 || enc_utf8)
1759 char2bytes = utf_char2bytes;
1760 else
1761 char2bytes = mb_char2bytes;
1762
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001763 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001764 {
1765 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
1766 ga_concat(&ga, buf);
1767 }
1768 ga_append(&ga, NUL);
1769 }
1770 else if (ga_grow(&ga, list_len(l) + 1) == OK)
1771 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001772 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001773 ga_append(&ga, tv_get_number(&li->li_tv));
1774 ga_append(&ga, NUL);
1775 }
1776
1777 rettv->v_type = VAR_STRING;
1778 rettv->vval.v_string = ga.ga_data;
1779}
1780
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001781/*
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01001782 * "list2tuple()" function
1783 */
1784 void
1785f_list2tuple(typval_T *argvars, typval_T *rettv)
1786{
1787 list_T *l;
1788 listitem_T *li;
1789 tuple_T *tuple;
1790
1791 rettv->v_type = VAR_TUPLE;
1792 rettv->vval.v_tuple = NULL;
1793
1794 if (check_for_list_arg(argvars, 0) == FAIL)
1795 return;
1796
1797 l = argvars[0].vval.v_list;
1798 if (l == NULL)
1799 return; // empty list results in empty tuple
1800
1801 CHECK_LIST_MATERIALIZE(l);
1802
1803 if (rettv_tuple_set_with_items(rettv, list_len(l)) == FAIL)
1804 return;
1805
1806 tuple = rettv->vval.v_tuple;
1807 FOR_ALL_LIST_ITEMS(l, li)
1808 {
1809 copy_tv(&li->li_tv, TUPLE_ITEM(tuple, TUPLE_LEN(tuple)));
1810 tuple->tv_items.ga_len++;
1811 }
1812}
1813
1814/*
1815 * "tuple2list()" function
1816 */
1817 void
1818f_tuple2list(typval_T *argvars, typval_T *rettv)
1819{
1820 list_T *l;
1821 tuple_T *tuple;
1822
1823 if (rettv_list_alloc(rettv) == FAIL)
1824 return;
1825
1826 if (check_for_tuple_arg(argvars, 0) == FAIL)
1827 return;
1828
1829 tuple = argvars[0].vval.v_tuple;
1830 if (tuple == NULL)
1831 return; // empty tuple results in empty list
1832
1833 l = rettv->vval.v_list;
1834 for (int i = 0; i < tuple_len(tuple); i++)
1835 list_append_tv(l, TUPLE_ITEM(tuple, i));
1836}
1837
1838/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001839 * Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then
1840 * remove the range of items from argvars[1] to argvars[2] (inclusive).
1841 */
Bram Moolenaarbdff0122020-04-05 18:56:05 +02001842 static void
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001843list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1844{
1845 list_T *l;
1846 listitem_T *item, *item2;
1847 listitem_T *li;
1848 int error = FALSE;
Bram Moolenaar239f8d92021-01-17 13:21:20 +01001849 long idx;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001850 long end;
1851 int cnt = 0;
1852 list_T *rl;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001853
1854 if ((l = argvars[0].vval.v_list) == NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001855 || value_check_lock(l->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001856 return;
1857
1858 idx = (long)tv_get_number_chk(&argvars[1], &error);
1859 if (error)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001860 return; // type error: do nothing, errmsg already given
1861
1862 if ((item = list_find(l, idx)) == NULL)
1863 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001864 semsg(_(e_list_index_out_of_range_nr), idx);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001865 return;
1866 }
1867
1868 if (argvars[2].v_type == VAR_UNKNOWN)
1869 {
1870 // Remove one item, return its value.
1871 vimlist_remove(l, item, item);
1872 *rettv = item->li_tv;
1873 list_free_item(l, item);
1874 return;
1875 }
1876
1877 // Remove range of items, return list with values.
1878 end = (long)tv_get_number_chk(&argvars[2], &error);
1879 if (error)
1880 return; // type error: do nothing
1881
1882 if ((item2 = list_find(l, end)) == NULL)
1883 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001884 semsg(_(e_list_index_out_of_range_nr), end);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001885 return;
1886 }
1887
1888 for (li = item; li != NULL; li = li->li_next)
1889 {
1890 ++cnt;
1891 if (li == item2)
1892 break;
1893 }
1894 if (li == NULL) // didn't find "item2" after "item"
1895 {
1896 emsg(_(e_invalid_range));
1897 return;
1898 }
1899
1900 vimlist_remove(l, item, item2);
Bram Moolenaar93a10962022-06-16 11:42:09 +01001901 if (rettv_list_alloc(rettv) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001902 return;
1903
1904 rl = rettv->vval.v_list;
1905
1906 if (l->lv_with_items > 0)
1907 {
1908 // need to copy the list items and move the value
1909 while (item != NULL)
1910 {
1911 li = listitem_alloc();
1912 if (li == NULL)
1913 return;
1914 li->li_tv = item->li_tv;
1915 init_tv(&item->li_tv);
1916 list_append(rl, li);
1917 if (item == item2)
1918 break;
1919 item = item->li_next;
1920 }
1921 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001922 else
1923 {
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001924 rl->lv_first = item;
1925 rl->lv_u.mat.lv_last = item2;
1926 item->li_prev = NULL;
1927 item2->li_next = NULL;
1928 rl->lv_len = cnt;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001929 }
1930}
1931
1932static int item_compare(const void *s1, const void *s2);
1933static int item_compare2(const void *s1, const void *s2);
1934
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001935// struct used in the array that's given to qsort()
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001936typedef struct
1937{
1938 listitem_T *item;
1939 int idx;
1940} sortItem_T;
1941
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001942// struct storing information about current sort
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001943typedef struct
1944{
1945 int item_compare_ic;
Bram Moolenaar55e29612020-11-01 13:57:44 +01001946 int item_compare_lc;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001947 int item_compare_numeric;
1948 int item_compare_numbers;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001949 int item_compare_float;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001950 char_u *item_compare_func;
1951 partial_T *item_compare_partial;
1952 dict_T *item_compare_selfdict;
1953 int item_compare_func_err;
1954 int item_compare_keep_zero;
1955} sortinfo_T;
1956static sortinfo_T *sortinfo = NULL;
1957#define ITEM_COMPARE_FAIL 999
1958
1959/*
1960 * Compare functions for f_sort() and f_uniq() below.
1961 */
1962 static int
1963item_compare(const void *s1, const void *s2)
1964{
1965 sortItem_T *si1, *si2;
1966 typval_T *tv1, *tv2;
1967 char_u *p1, *p2;
1968 char_u *tofree1 = NULL, *tofree2 = NULL;
1969 int res;
1970 char_u numbuf1[NUMBUFLEN];
1971 char_u numbuf2[NUMBUFLEN];
1972
1973 si1 = (sortItem_T *)s1;
1974 si2 = (sortItem_T *)s2;
1975 tv1 = &si1->item->li_tv;
1976 tv2 = &si2->item->li_tv;
1977
1978 if (sortinfo->item_compare_numbers)
1979 {
Bram Moolenaarbe19d782023-03-09 22:06:49 +00001980 varnumber_T v1 = tv_to_number(tv1);
1981 varnumber_T v2 = tv_to_number(tv2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001982
1983 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1984 }
1985
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001986 if (sortinfo->item_compare_float)
1987 {
1988 float_T v1 = tv_get_float(tv1);
1989 float_T v2 = tv_get_float(tv2);
1990
1991 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
1992 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001993
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001994 // tv2string() puts quotes around a string and allocates memory. Don't do
1995 // that for string variables. Use a single quote when comparing with a
1996 // non-string to do what the docs promise.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001997 if (tv1->v_type == VAR_STRING)
1998 {
1999 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
2000 p1 = (char_u *)"'";
2001 else
2002 p1 = tv1->vval.v_string;
2003 }
2004 else
2005 p1 = tv2string(tv1, &tofree1, numbuf1, 0);
2006 if (tv2->v_type == VAR_STRING)
2007 {
2008 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
2009 p2 = (char_u *)"'";
2010 else
2011 p2 = tv2->vval.v_string;
2012 }
2013 else
2014 p2 = tv2string(tv2, &tofree2, numbuf2, 0);
2015 if (p1 == NULL)
2016 p1 = (char_u *)"";
2017 if (p2 == NULL)
2018 p2 = (char_u *)"";
2019 if (!sortinfo->item_compare_numeric)
2020 {
Bram Moolenaar55e29612020-11-01 13:57:44 +01002021 if (sortinfo->item_compare_lc)
2022 res = strcoll((char *)p1, (char *)p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002023 else
Bram Moolenaar55e29612020-11-01 13:57:44 +01002024 res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002025 }
2026 else
2027 {
2028 double n1, n2;
2029 n1 = strtod((char *)p1, (char **)&p1);
2030 n2 = strtod((char *)p2, (char **)&p2);
2031 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
2032 }
2033
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002034 // When the result would be zero, compare the item indexes. Makes the
2035 // sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002036 if (res == 0 && !sortinfo->item_compare_keep_zero)
2037 res = si1->idx > si2->idx ? 1 : -1;
2038
2039 vim_free(tofree1);
2040 vim_free(tofree2);
2041 return res;
2042}
2043
2044 static int
2045item_compare2(const void *s1, const void *s2)
2046{
2047 sortItem_T *si1, *si2;
2048 int res;
2049 typval_T rettv;
2050 typval_T argv[3];
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002051 char_u *func_name;
2052 partial_T *partial = sortinfo->item_compare_partial;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02002053 funcexe_T funcexe;
Bram Moolenaar23018f22021-12-27 11:54:37 +00002054 int did_emsg_before = did_emsg;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002055
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002056 // shortcut after failure in previous call; compare all items equal
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002057 if (sortinfo->item_compare_func_err)
2058 return 0;
2059
2060 si1 = (sortItem_T *)s1;
2061 si2 = (sortItem_T *)s2;
2062
2063 if (partial == NULL)
2064 func_name = sortinfo->item_compare_func;
2065 else
2066 func_name = partial_name(partial);
2067
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002068 // Copy the values. This is needed to be able to set v_lock to VAR_FIXED
2069 // in the copy without changing the original list items.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002070 copy_tv(&si1->item->li_tv, &argv[0]);
2071 copy_tv(&si2->item->li_tv, &argv[1]);
2072
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002073 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
Bram Moolenaara80faa82020-04-12 19:37:17 +02002074 CLEAR_FIELD(funcexe);
Bram Moolenaar851f86b2021-12-13 14:26:44 +00002075 funcexe.fe_evaluate = TRUE;
2076 funcexe.fe_partial = partial;
2077 funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02002078 res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002079 clear_tv(&argv[0]);
2080 clear_tv(&argv[1]);
2081
Bram Moolenaar23018f22021-12-27 11:54:37 +00002082 if (res == FAIL || did_emsg > did_emsg_before)
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002083 res = ITEM_COMPARE_FAIL;
2084 else
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02002085 {
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002086 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
Yasuhiro Matsumotoc04f6232021-09-19 17:01:39 +02002087 if (res > 0)
2088 res = 1;
2089 else if (res < 0)
2090 res = -1;
2091 }
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002092 if (sortinfo->item_compare_func_err)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002093 res = ITEM_COMPARE_FAIL; // return value has wrong type
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002094 clear_tv(&rettv);
2095
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002096 // When the result would be zero, compare the pointers themselves. Makes
2097 // the sort stable.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002098 if (res == 0 && !sortinfo->item_compare_keep_zero)
2099 res = si1->idx > si2->idx ? 1 : -1;
2100
2101 return res;
2102}
2103
2104/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002105 * sort() List "l"
2106 */
2107 static void
2108do_sort(list_T *l, sortinfo_T *info)
2109{
2110 long len;
2111 sortItem_T *ptrs;
2112 long i = 0;
2113 listitem_T *li;
2114
2115 len = list_len(l);
2116
2117 // Make an array with each entry pointing to an item in the List.
2118 ptrs = ALLOC_MULT(sortItem_T, len);
2119 if (ptrs == NULL)
2120 return;
2121
2122 // sort(): ptrs will be the list to sort
2123 FOR_ALL_LIST_ITEMS(l, li)
2124 {
2125 ptrs[i].item = li;
2126 ptrs[i].idx = i;
2127 ++i;
2128 }
2129
2130 info->item_compare_func_err = FALSE;
2131 info->item_compare_keep_zero = FALSE;
2132 // test the compare function
2133 if ((info->item_compare_func != NULL
2134 || info->item_compare_partial != NULL)
2135 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
2136 == ITEM_COMPARE_FAIL)
Bram Moolenaara6f79292022-01-04 21:30:47 +00002137 emsg(_(e_sort_compare_function_failed));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002138 else
2139 {
2140 // Sort the array with item pointers.
2141 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
2142 info->item_compare_func == NULL
2143 && info->item_compare_partial == NULL
2144 ? item_compare : item_compare2);
2145
2146 if (!info->item_compare_func_err)
2147 {
2148 // Clear the List and append the items in sorted order.
2149 l->lv_first = l->lv_u.mat.lv_last
2150 = l->lv_u.mat.lv_idx_item = NULL;
2151 l->lv_len = 0;
2152 for (i = 0; i < len; ++i)
2153 list_append(l, ptrs[i].item);
2154 }
2155 }
2156
2157 vim_free(ptrs);
2158}
2159
2160/*
2161 * uniq() List "l"
2162 */
2163 static void
2164do_uniq(list_T *l, sortinfo_T *info)
2165{
2166 long len;
2167 sortItem_T *ptrs;
2168 long i = 0;
2169 listitem_T *li;
2170 int (*item_compare_func_ptr)(const void *, const void *);
2171
2172 len = list_len(l);
2173
2174 // Make an array with each entry pointing to an item in the List.
2175 ptrs = ALLOC_MULT(sortItem_T, len);
2176 if (ptrs == NULL)
2177 return;
2178
2179 // f_uniq(): ptrs will be a stack of items to remove
2180 info->item_compare_func_err = FALSE;
2181 info->item_compare_keep_zero = TRUE;
2182 item_compare_func_ptr = info->item_compare_func != NULL
2183 || info->item_compare_partial != NULL
2184 ? item_compare2 : item_compare;
2185
2186 for (li = l->lv_first; li != NULL && li->li_next != NULL;
2187 li = li->li_next)
2188 {
2189 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
2190 == 0)
2191 ptrs[i++].item = li;
2192 if (info->item_compare_func_err)
2193 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00002194 emsg(_(e_uniq_compare_function_failed));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002195 break;
2196 }
2197 }
2198
2199 if (!info->item_compare_func_err)
2200 {
2201 while (--i >= 0)
2202 {
2203 li = ptrs[i].item->li_next;
2204 ptrs[i].item->li_next = li->li_next;
2205 if (li->li_next != NULL)
2206 li->li_next->li_prev = ptrs[i].item;
2207 else
2208 l->lv_u.mat.lv_last = ptrs[i].item;
2209 list_fix_watch(l, li);
2210 listitem_free(l, li);
2211 l->lv_len--;
2212 }
2213 }
2214
2215 vim_free(ptrs);
2216}
2217
2218/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002219 * Parse the optional arguments supplied to the sort() or uniq() function and
2220 * return the values in "info".
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002221 */
2222 static int
2223parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
2224{
2225 info->item_compare_ic = FALSE;
2226 info->item_compare_lc = FALSE;
2227 info->item_compare_numeric = FALSE;
2228 info->item_compare_numbers = FALSE;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002229 info->item_compare_float = FALSE;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002230 info->item_compare_func = NULL;
2231 info->item_compare_partial = NULL;
2232 info->item_compare_selfdict = NULL;
2233
2234 if (argvars[1].v_type == VAR_UNKNOWN)
2235 return OK;
2236
2237 // optional second argument: {func}
2238 if (argvars[1].v_type == VAR_FUNC)
2239 info->item_compare_func = argvars[1].vval.v_string;
2240 else if (argvars[1].v_type == VAR_PARTIAL)
2241 info->item_compare_partial = argvars[1].vval.v_partial;
2242 else
2243 {
2244 int error = FALSE;
2245 int nr = 0;
2246
2247 if (argvars[1].v_type == VAR_NUMBER)
2248 {
2249 nr = tv_get_number_chk(&argvars[1], &error);
2250 if (error)
2251 return FAIL;
2252 if (nr == 1)
2253 info->item_compare_ic = TRUE;
2254 }
2255 if (nr != 1)
2256 {
2257 if (argvars[1].v_type != VAR_NUMBER)
2258 info->item_compare_func = tv_get_string(&argvars[1]);
2259 else if (nr != 0)
2260 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002261 emsg(_(e_invalid_argument));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002262 return FAIL;
2263 }
2264 }
2265 if (info->item_compare_func != NULL)
2266 {
2267 if (*info->item_compare_func == NUL)
2268 {
2269 // empty string means default sort
2270 info->item_compare_func = NULL;
2271 }
2272 else if (STRCMP(info->item_compare_func, "n") == 0)
2273 {
2274 info->item_compare_func = NULL;
2275 info->item_compare_numeric = TRUE;
2276 }
2277 else if (STRCMP(info->item_compare_func, "N") == 0)
2278 {
2279 info->item_compare_func = NULL;
2280 info->item_compare_numbers = TRUE;
2281 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002282 else if (STRCMP(info->item_compare_func, "f") == 0)
2283 {
2284 info->item_compare_func = NULL;
2285 info->item_compare_float = TRUE;
2286 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002287 else if (STRCMP(info->item_compare_func, "i") == 0)
2288 {
2289 info->item_compare_func = NULL;
2290 info->item_compare_ic = TRUE;
2291 }
2292 else if (STRCMP(info->item_compare_func, "l") == 0)
2293 {
2294 info->item_compare_func = NULL;
2295 info->item_compare_lc = TRUE;
2296 }
2297 }
2298 }
2299
2300 if (argvars[2].v_type != VAR_UNKNOWN)
2301 {
2302 // optional third argument: {dict}
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002303 if (check_for_dict_arg(argvars, 2) == FAIL)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002304 return FAIL;
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002305 info->item_compare_selfdict = argvars[2].vval.v_dict;
2306 }
2307
2308 return OK;
2309}
2310
2311/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002312 * "sort()" or "uniq()" function
2313 */
2314 static void
2315do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
2316{
2317 list_T *l;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002318 sortinfo_T *old_sortinfo;
2319 sortinfo_T info;
2320 long len;
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002321
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002322 if (in_vim9script()
2323 && (check_for_list_arg(argvars, 0) == FAIL
2324 || (argvars[1].v_type != VAR_UNKNOWN
Bram Moolenaar2007dd42022-02-23 13:17:47 +00002325 && (check_for_string_or_func_arg(argvars, 1) == FAIL
2326 || check_for_opt_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002327 return;
2328
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002329 if (argvars[0].v_type != VAR_LIST)
2330 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00002331 semsg(_(e_argument_of_str_must_be_list), sort ? "sort()" : "uniq()");
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002332 return;
2333 }
2334
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002335 // Pointer to current info struct used in compare function. Save and
2336 // restore the current one for nested calls.
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002337 old_sortinfo = sortinfo;
2338 sortinfo = &info;
2339
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002340 l = argvars[0].vval.v_list;
2341 if (l != NULL && value_check_lock(l->lv_lock,
2342 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
2343 TRUE))
2344 goto theend;
2345 rettv_list_set(rettv, l);
2346 if (l == NULL)
2347 goto theend;
2348 CHECK_LIST_MATERIALIZE(l);
2349
2350 len = list_len(l);
2351 if (len <= 1)
2352 goto theend; // short list sorts pretty quickly
2353
2354 if (parse_sort_uniq_args(argvars, &info) == FAIL)
2355 goto theend;
2356
2357 if (sort)
2358 do_sort(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002359 else
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002360 do_uniq(l, &info);
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002361
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02002362theend:
2363 sortinfo = old_sortinfo;
2364}
2365
2366/*
2367 * "sort({list})" function
2368 */
2369 void
2370f_sort(typval_T *argvars, typval_T *rettv)
2371{
2372 do_sort_uniq(argvars, rettv, TRUE);
2373}
2374
2375/*
2376 * "uniq({list})" function
2377 */
2378 void
2379f_uniq(typval_T *argvars, typval_T *rettv)
2380{
2381 do_sort_uniq(argvars, rettv, FALSE);
2382}
2383
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002384/*
Ernie Raele79e2072024-01-13 11:47:33 +01002385 * Handle one item for map(), filter(), foreach().
Bram Moolenaarea696852020-11-09 18:31:39 +01002386 * Sets v:val to "tv". Caller must set v:key.
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002387 */
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002388 int
Bram Moolenaarea696852020-11-09 18:31:39 +01002389filter_map_one(
2390 typval_T *tv, // original value
2391 typval_T *expr, // callback
2392 filtermap_T filtermap,
Bram Moolenaar82418262022-09-28 16:16:15 +01002393 funccall_T *fc, // from eval_expr_get_funccal()
Bram Moolenaarea696852020-11-09 18:31:39 +01002394 typval_T *newtv, // for map() and mapnew(): new value
2395 int *remp) // for filter(): remove flag
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002396{
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002397 typval_T argv[3];
2398 int retval = FAIL;
2399
2400 copy_tv(tv, get_vim_var_tv(VV_VAL));
Ernie Raele79e2072024-01-13 11:47:33 +01002401
2402 newtv->v_type = VAR_UNKNOWN;
2403 if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING)
2404 {
2405 // foreach() is not limited to an expression
2406 do_cmdline_cmd(expr->vval.v_string);
2407 if (!did_emsg)
2408 retval = OK;
2409 goto theend;
2410 }
2411
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002412 argv[0] = *get_vim_var_tv(VV_KEY);
2413 argv[1] = *get_vim_var_tv(VV_VAL);
zeertzjqad0c4422023-08-17 22:15:47 +02002414 if (eval_expr_typval(expr, FALSE, argv, 2, fc, newtv) == FAIL)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002415 goto theend;
Bram Moolenaarea696852020-11-09 18:31:39 +01002416 if (filtermap == FILTERMAP_FILTER)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002417 {
2418 int error = FALSE;
2419
2420 // filter(): when expr is zero remove the item
Bram Moolenaar56acb092020-08-16 14:48:19 +02002421 if (in_vim9script())
Bram Moolenaar18024052021-12-25 21:43:28 +00002422 *remp = !tv_get_bool_chk(newtv, &error);
Bram Moolenaar56acb092020-08-16 14:48:19 +02002423 else
Bram Moolenaarea696852020-11-09 18:31:39 +01002424 *remp = (tv_get_number_chk(newtv, &error) == 0);
2425 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002426 // On type error, nothing has been removed; return FAIL to stop the
2427 // loop. The error message was given by tv_get_number_chk().
2428 if (error)
2429 goto theend;
2430 }
Ernie Raele79e2072024-01-13 11:47:33 +01002431 else if (filtermap == FILTERMAP_FOREACH)
2432 clear_tv(newtv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002433 retval = OK;
2434theend:
2435 clear_tv(get_vim_var_tv(VV_VAL));
2436 return retval;
2437}
2438
2439/*
Ernie Raele79e2072024-01-13 11:47:33 +01002440 * Implementation of map(), filter(), foreach() for a List. Apply "expr" to
2441 * every item in List "l" and return the result in "rettv".
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002442 */
2443 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002444list_filter_map(
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002445 list_T *l,
2446 filtermap_T filtermap,
2447 type_T *argtype,
2448 char *func_name,
2449 char_u *arg_errmsg,
2450 typval_T *expr,
2451 typval_T *rettv)
2452{
2453 int prev_lock;
2454 list_T *l_ret = NULL;
2455 int idx = 0;
2456 int rem;
2457 listitem_T *li, *nli;
Bram Moolenaar82418262022-09-28 16:16:15 +01002458 typval_T newtv;
2459 funccall_T *fc;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002460
2461 if (filtermap == FILTERMAP_MAPNEW)
2462 {
2463 rettv->v_type = VAR_LIST;
2464 rettv->vval.v_list = NULL;
2465 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01002466 if (l == NULL || (filtermap == FILTERMAP_FILTER
2467 && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002468 return;
2469
2470 prev_lock = l->lv_lock;
2471
2472 if (filtermap == FILTERMAP_MAPNEW)
2473 {
2474 if (rettv_list_alloc(rettv) == FAIL)
2475 return;
2476 l_ret = rettv->vval.v_list;
2477 }
2478 // set_vim_var_nr() doesn't set the type
2479 set_vim_var_type(VV_KEY, VAR_NUMBER);
2480
Ernie Raele6d40dc2023-03-19 21:23:38 +00002481 if (l->lv_lock == 0)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002482 l->lv_lock = VAR_LOCKED;
2483
zeertzjqe7d49462023-04-16 20:53:55 +01002484 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +01002485 fc = eval_expr_get_funccal(expr, &newtv);
2486
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002487 if (l->lv_first == &range_list_item)
2488 {
2489 varnumber_T val = l->lv_u.nonmat.lv_start;
2490 int len = l->lv_len;
2491 int stride = l->lv_u.nonmat.lv_stride;
2492
2493 // List from range(): loop over the numbers
Ernie Raele79e2072024-01-13 11:47:33 +01002494 // NOTE: foreach() returns the range_list_item
2495 if (filtermap != FILTERMAP_MAPNEW && filtermap != FILTERMAP_FOREACH)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002496 {
2497 l->lv_first = NULL;
2498 l->lv_u.mat.lv_last = NULL;
2499 l->lv_len = 0;
2500 l->lv_u.mat.lv_idx_item = NULL;
2501 }
2502
2503 for (idx = 0; idx < len; ++idx)
2504 {
2505 typval_T tv;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002506
2507 tv.v_type = VAR_NUMBER;
2508 tv.v_lock = 0;
2509 tv.vval.v_number = val;
2510 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +01002511 if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002512 break;
2513 if (did_emsg)
2514 {
2515 clear_tv(&newtv);
2516 break;
2517 }
Ernie Raele79e2072024-01-13 11:47:33 +01002518 if (filtermap != FILTERMAP_FOREACH)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002519 {
Ernie Raele79e2072024-01-13 11:47:33 +01002520 if (filtermap != FILTERMAP_FILTER)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002521 {
Ernie Raele79e2072024-01-13 11:47:33 +01002522 if (filtermap == FILTERMAP_MAP && argtype != NULL
2523 && check_typval_arg_type(
2524 argtype->tt_member, &newtv,
2525 func_name, 0) == FAIL)
2526 {
2527 clear_tv(&newtv);
2528 break;
2529 }
2530 // map(), mapnew(): always append the new value to the
2531 // list
2532 if (list_append_tv_move(filtermap == FILTERMAP_MAP
2533 ? l : l_ret, &newtv) == FAIL)
2534 break;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002535 }
Ernie Raele79e2072024-01-13 11:47:33 +01002536 else if (!rem)
2537 {
2538 // filter(): append the list item value when not rem
2539 if (list_append_tv_move(l, &tv) == FAIL)
2540 break;
2541 }
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002542 }
2543
2544 val += stride;
2545 }
2546 }
2547 else
2548 {
2549 // Materialized list: loop over the items
2550 for (li = l->lv_first; li != NULL; li = nli)
2551 {
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002552 if (filtermap == FILTERMAP_MAP && value_check_lock(
2553 li->li_tv.v_lock, arg_errmsg, TRUE))
2554 break;
2555 nli = li->li_next;
2556 set_vim_var_nr(VV_KEY, idx);
Bram Moolenaar82418262022-09-28 16:16:15 +01002557 if (filter_map_one(&li->li_tv, expr, filtermap, fc,
2558 &newtv, &rem) == FAIL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002559 break;
2560 if (did_emsg)
2561 {
2562 clear_tv(&newtv);
2563 break;
2564 }
2565 if (filtermap == FILTERMAP_MAP)
2566 {
2567 if (argtype != NULL && check_typval_arg_type(
2568 argtype->tt_member, &newtv, func_name, 0) == FAIL)
2569 {
2570 clear_tv(&newtv);
2571 break;
2572 }
2573 // map(): replace the list item value
2574 clear_tv(&li->li_tv);
2575 newtv.v_lock = 0;
2576 li->li_tv = newtv;
2577 }
2578 else if (filtermap == FILTERMAP_MAPNEW)
2579 {
2580 // mapnew(): append the list item value
2581 if (list_append_tv_move(l_ret, &newtv) == FAIL)
2582 break;
2583 }
2584 else if (filtermap == FILTERMAP_FILTER && rem)
Ernie Raele79e2072024-01-13 11:47:33 +01002585 listitem_remove(l, li);
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002586 ++idx;
2587 }
2588 }
2589
2590 l->lv_lock = prev_lock;
Bram Moolenaar82418262022-09-28 16:16:15 +01002591 if (fc != NULL)
2592 remove_funccal();
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002593}
2594
2595/*
Ernie Raele79e2072024-01-13 11:47:33 +01002596 * Implementation of map(), filter() and foreach().
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002597 */
2598 static void
Bram Moolenaarea696852020-11-09 18:31:39 +01002599filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002600{
2601 typval_T *expr;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002602 char *func_name = filtermap == FILTERMAP_MAP ? "map()"
Bram Moolenaarea696852020-11-09 18:31:39 +01002603 : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
Ernie Raele79e2072024-01-13 11:47:33 +01002604 : filtermap == FILTERMAP_FILTER ? "filter()"
2605 : "foreach()";
Bram Moolenaarea696852020-11-09 18:31:39 +01002606 char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
2607 ? N_("map() argument")
2608 : filtermap == FILTERMAP_MAPNEW
2609 ? N_("mapnew() argument")
Ernie Raele79e2072024-01-13 11:47:33 +01002610 : filtermap == FILTERMAP_FILTER
2611 ? N_("filter() argument")
2612 : N_("foreach() argument"));
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002613 int save_did_emsg;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002614 type_T *type = NULL;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002615
Ernie Raele79e2072024-01-13 11:47:33 +01002616 // map(), filter(), foreach() return the first argument, also on failure.
Bram Moolenaar2d877592021-12-16 08:21:09 +00002617 if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
Bram Moolenaarea696852020-11-09 18:31:39 +01002618 copy_tv(&argvars[0], rettv);
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002619
2620 if (in_vim9script()
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002621 && (check_for_list_tuple_dict_blob_or_string_arg(argvars, 0)
Bram Moolenaar2d877592021-12-16 08:21:09 +00002622 == FAIL))
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002623 return;
2624
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002625 if (argvars[0].v_type == VAR_TUPLE && filtermap != FILTERMAP_FOREACH)
2626 {
2627 semsg(_(e_cannot_use_tuple_with_function_str), func_name);
2628 return;
2629 }
2630
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002631 if (filtermap == FILTERMAP_MAP && in_vim9script())
2632 {
Bram Moolenaar35c807d2022-01-27 16:36:29 +00002633 // Check that map() does not change the declared type of the list or
2634 // dict.
2635 if (argvars[0].v_type == VAR_DICT && argvars[0].vval.v_dict != NULL)
2636 type = argvars[0].vval.v_dict->dv_type;
2637 else if (argvars[0].v_type == VAR_LIST
2638 && argvars[0].vval.v_list != NULL)
2639 type = argvars[0].vval.v_list->lv_type;
Bram Moolenaar70250fb2021-01-16 19:01:53 +01002640 }
Bram Moolenaarffdf8ad2020-10-15 22:29:17 +02002641
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002642 if (argvars[0].v_type != VAR_BLOB
2643 && argvars[0].v_type != VAR_LIST
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002644 && argvars[0].v_type != VAR_TUPLE
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00002645 && argvars[0].v_type != VAR_DICT
2646 && argvars[0].v_type != VAR_STRING)
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002647 {
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002648 char *msg;
2649
2650 if (filtermap == FILTERMAP_FOREACH)
2651 msg = e_argument_of_str_must_be_list_tuple_string_dictionary_or_blob;
2652 else
2653 msg = e_argument_of_str_must_be_list_string_dictionary_or_blob;
2654 semsg(_(msg), func_name);
Bram Moolenaar98cd3032022-01-27 17:37:41 +00002655 return;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002656 }
2657
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002658 // On type errors, the preceding call has already displayed an error
2659 // message. Avoid a misleading error message for an empty string that
2660 // was not passed as argument.
Bram Moolenaar35c807d2022-01-27 16:36:29 +00002661 expr = &argvars[1];
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002662 if (expr->v_type == VAR_UNKNOWN)
2663 return;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002664
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002665 typval_T save_val;
2666 typval_T save_key;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002667
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002668 prepare_vimvar(VV_VAL, &save_val);
2669 prepare_vimvar(VV_KEY, &save_key);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002670
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002671 // We reset "did_emsg" to be able to detect whether an error
2672 // occurred during evaluation of the expression.
2673 save_did_emsg = did_emsg;
2674 did_emsg = FALSE;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002675
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002676 if (argvars[0].v_type == VAR_DICT)
2677 dict_filter_map(argvars[0].vval.v_dict, filtermap, type, func_name,
Ernie Raele6d40dc2023-03-19 21:23:38 +00002678 arg_errmsg, expr, rettv);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002679 else if (argvars[0].v_type == VAR_BLOB)
Ernie Raele6d40dc2023-03-19 21:23:38 +00002680 blob_filter_map(argvars[0].vval.v_blob, filtermap, expr,
2681 arg_errmsg, rettv);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002682 else if (argvars[0].v_type == VAR_STRING)
Ernie Raele6d40dc2023-03-19 21:23:38 +00002683 string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, rettv);
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002684 else if (argvars[0].v_type == VAR_TUPLE)
2685 tuple_foreach(argvars[0].vval.v_tuple, filtermap, expr);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002686 else // argvars[0].v_type == VAR_LIST
2687 list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
Ernie Raele6d40dc2023-03-19 21:23:38 +00002688 arg_errmsg, expr, rettv);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002689
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002690 restore_vimvar(VV_KEY, &save_key);
2691 restore_vimvar(VV_VAL, &save_val);
2692
2693 did_emsg |= save_did_emsg;
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002694}
2695
2696/*
2697 * "filter()" function
2698 */
2699 void
2700f_filter(typval_T *argvars, typval_T *rettv)
2701{
Bram Moolenaarea696852020-11-09 18:31:39 +01002702 filter_map(argvars, rettv, FILTERMAP_FILTER);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002703}
2704
2705/*
2706 * "map()" function
2707 */
2708 void
2709f_map(typval_T *argvars, typval_T *rettv)
2710{
Bram Moolenaarea696852020-11-09 18:31:39 +01002711 filter_map(argvars, rettv, FILTERMAP_MAP);
2712}
2713
2714/*
2715 * "mapnew()" function
2716 */
2717 void
2718f_mapnew(typval_T *argvars, typval_T *rettv)
2719{
2720 filter_map(argvars, rettv, FILTERMAP_MAPNEW);
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02002721}
2722
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002723/*
Ernie Raele79e2072024-01-13 11:47:33 +01002724 * "foreach()" function
2725 */
2726 void
2727f_foreach(typval_T *argvars, typval_T *rettv)
2728{
2729 filter_map(argvars, rettv, FILTERMAP_FOREACH);
2730}
2731
2732/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002733 * "add(list, item)" function
2734 */
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002735 static void
2736list_add(typval_T *argvars, typval_T *rettv)
2737{
2738 list_T *l = argvars[0].vval.v_list;
2739
2740 if (l == NULL)
2741 {
2742 if (in_vim9script())
2743 emsg(_(e_cannot_add_to_null_list));
2744 }
2745 else if (!value_check_lock(l->lv_lock,
2746 (char_u *)N_("add() argument"), TRUE)
2747 && list_append_tv(l, &argvars[1]) == OK)
2748 {
2749 copy_tv(&argvars[0], rettv);
2750 }
2751}
2752
2753/*
2754 * "add(object, item)" function
2755 */
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002756 void
2757f_add(typval_T *argvars, typval_T *rettv)
2758{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002759 rettv->vval.v_number = 1; // Default: Failed
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002760
2761 if (in_vim9script()
2762 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
2763 || (argvars[0].v_type == VAR_BLOB
2764 && check_for_number_arg(argvars, 1) == FAIL)))
2765 return;
2766
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002767 if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002768 list_add(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002769 else if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002770 blob_add(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002771 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002772 emsg(_(e_list_or_blob_required));
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002773}
2774
2775/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002776 * Count the number of times item "needle" occurs in List "l" starting at index
2777 * "idx". Case is ignored if "ic" is TRUE.
2778 */
2779 static long
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002780list_count(list_T *l, typval_T *needle, long idx, int ic)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002781{
2782 long n = 0;
2783 listitem_T *li;
2784
2785 if (l == NULL)
2786 return 0;
2787
2788 CHECK_LIST_MATERIALIZE(l);
2789
2790 if (list_len(l) == 0)
2791 return 0;
2792
2793 li = list_find(l, idx);
2794 if (li == NULL)
2795 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002796 semsg(_(e_list_index_out_of_range_nr), idx);
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002797 return 0;
2798 }
2799
2800 for ( ; li != NULL; li = li->li_next)
Yinzuo Jiang7ccd1a22024-07-04 17:20:53 +02002801 if (tv_equal(&li->li_tv, needle, ic))
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002802 ++n;
2803
2804 return n;
2805}
2806
2807/*
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002808 * "count()" function
2809 */
2810 void
2811f_count(typval_T *argvars, typval_T *rettv)
2812{
2813 long n = 0;
2814 int ic = FALSE;
2815 int error = FALSE;
2816
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002817 if (in_vim9script()
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002818 && (check_for_string_list_tuple_or_dict_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002819 || check_for_opt_bool_arg(argvars, 2) == FAIL
2820 || (argvars[2].v_type != VAR_UNKNOWN
2821 && check_for_opt_number_arg(argvars, 3) == FAIL)))
2822 return;
2823
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002824 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaar119f5572020-09-02 21:31:22 +02002825 ic = (int)tv_get_bool_chk(&argvars[2], &error);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002826
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002827 if (!error && argvars[0].v_type == VAR_STRING)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002828 n = string_count(argvars[0].vval.v_string,
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002829 tv_get_string_chk(&argvars[1]), ic);
2830 else if (!error && argvars[0].v_type == VAR_LIST)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002831 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002832 long idx = 0;
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002833
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002834 if (argvars[2].v_type != VAR_UNKNOWN
2835 && argvars[3].v_type != VAR_UNKNOWN)
2836 idx = (long)tv_get_number_chk(&argvars[3], &error);
2837 if (!error)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002838 n = list_count(argvars[0].vval.v_list, &argvars[1], idx, ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002839 }
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01002840 else if (!error && argvars[0].v_type == VAR_TUPLE)
2841 {
2842 long idx = 0;
2843
2844 if (argvars[2].v_type != VAR_UNKNOWN
2845 && argvars[3].v_type != VAR_UNKNOWN)
2846 idx = (long)tv_get_number_chk(&argvars[3], &error);
2847 if (!error)
2848 n = tuple_count(argvars[0].vval.v_tuple, &argvars[1], idx, ic);
2849 }
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002850 else if (!error && argvars[0].v_type == VAR_DICT)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002851 {
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002852 if (argvars[2].v_type != VAR_UNKNOWN
2853 && argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002854 emsg(_(e_invalid_argument));
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002855 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002856 n = dict_count(argvars[0].vval.v_dict, &argvars[1], ic);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002857 }
zeertzjq4f389e72023-08-17 22:10:40 +02002858 else if (!error)
2859 semsg(_(e_argument_of_str_must_be_list_string_or_dictionary),
2860 "count()");
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002861 rettv->vval.v_number = n;
2862}
2863
2864/*
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002865 * extend() a List. Append List argvars[1] to List argvars[0] before index
2866 * argvars[3] and return the resulting list in "rettv". "is_new" is TRUE for
2867 * extendnew().
2868 */
2869 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002870list_extend_func(
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002871 typval_T *argvars,
2872 type_T *type,
2873 char *func_name,
2874 char_u *arg_errmsg,
2875 int is_new,
2876 typval_T *rettv)
2877{
2878 list_T *l1, *l2;
2879 listitem_T *item;
2880 long before;
2881 int error = FALSE;
2882
2883 l1 = argvars[0].vval.v_list;
2884 if (l1 == NULL)
2885 {
2886 emsg(_(e_cannot_extend_null_list));
2887 return;
2888 }
2889 l2 = argvars[1].vval.v_list;
2890 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
Bram Moolenaar381692b2022-02-02 20:01:27 +00002891 && l2 != NULL)
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002892 {
2893 if (is_new)
2894 {
Bram Moolenaar381692b2022-02-02 20:01:27 +00002895 l1 = list_copy(l1, FALSE, TRUE, get_copyID());
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002896 if (l1 == NULL)
2897 return;
2898 }
2899
2900 if (argvars[2].v_type != VAR_UNKNOWN)
2901 {
2902 before = (long)tv_get_number_chk(&argvars[2], &error);
2903 if (error)
2904 return; // type error; errmsg already given
2905
2906 if (before == l1->lv_len)
2907 item = NULL;
2908 else
2909 {
2910 item = list_find(l1, before);
2911 if (item == NULL)
2912 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002913 semsg(_(e_list_index_out_of_range_nr), before);
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00002914 return;
2915 }
2916 }
2917 }
2918 else
2919 item = NULL;
2920 if (type != NULL && check_typval_arg_type(
2921 type, &argvars[1], func_name, 2) == FAIL)
2922 return;
2923 list_extend(l1, l2, item);
2924
2925 if (is_new)
2926 {
2927 rettv->v_type = VAR_LIST;
2928 rettv->vval.v_list = l1;
2929 rettv->v_lock = FALSE;
2930 }
2931 else
2932 copy_tv(&argvars[0], rettv);
2933 }
2934}
2935
2936/*
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002937 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002938 */
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002939 static void
2940extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002941{
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002942 type_T *type = NULL;
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02002943 char *func_name = is_new ? "extendnew()" : "extend()";
Bram Moolenaarc03f5c62021-01-31 17:48:30 +01002944
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002945 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002946 {
2947 // Check that extend() does not change the type of the list if it was
2948 // declared.
2949 if (!is_new && in_vim9script() && argvars[0].vval.v_list != NULL)
2950 type = argvars[0].vval.v_list->lv_type;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002951 list_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002952 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002953 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002954 {
Bram Moolenaarbe19d782023-03-09 22:06:49 +00002955 // Check that extend() does not change the type of the dict if it was
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002956 // declared.
2957 if (!is_new && in_vim9script() && argvars[0].vval.v_dict != NULL)
2958 type = argvars[0].vval.v_dict->dv_type;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002959 dict_extend_func(argvars, type, func_name, arg_errmsg, is_new, rettv);
Bram Moolenaarad8f2482022-01-03 16:52:28 +00002960 }
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002961 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002962 semsg(_(e_argument_of_str_must_be_list_or_dictionary), func_name);
Bram Moolenaarb0e6b512021-01-12 20:23:40 +01002963}
2964
2965/*
2966 * "extend(list, list [, idx])" function
2967 * "extend(dict, dict [, action])" function
2968 */
2969 void
2970f_extend(typval_T *argvars, typval_T *rettv)
2971{
2972 char_u *errmsg = (char_u *)N_("extend() argument");
2973
2974 extend(argvars, rettv, errmsg, FALSE);
2975}
2976
2977/*
2978 * "extendnew(list, list [, idx])" function
2979 * "extendnew(dict, dict [, action])" function
2980 */
2981 void
2982f_extendnew(typval_T *argvars, typval_T *rettv)
2983{
2984 char_u *errmsg = (char_u *)N_("extendnew() argument");
2985
2986 extend(argvars, rettv, errmsg, TRUE);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02002987}
2988
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00002989 static void
2990list_insert_func(typval_T *argvars, typval_T *rettv)
2991{
2992 list_T *l = argvars[0].vval.v_list;
2993 long before = 0;
2994 listitem_T *item;
2995 int error = FALSE;
2996
2997 if (l == NULL)
2998 {
2999 if (in_vim9script())
3000 emsg(_(e_cannot_add_to_null_list));
3001 return;
3002 }
3003
3004 if (value_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE))
3005 return;
3006
3007 if (argvars[2].v_type != VAR_UNKNOWN)
3008 before = (long)tv_get_number_chk(&argvars[2], &error);
3009 if (error)
3010 return; // type error; errmsg already given
3011
3012 if (before == l->lv_len)
3013 item = NULL;
3014 else
3015 {
3016 item = list_find(l, before);
3017 if (item == NULL)
3018 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00003019 semsg(_(e_list_index_out_of_range_nr), before);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003020 l = NULL;
3021 }
3022 }
3023 if (l != NULL)
3024 {
3025 (void)list_insert_tv(l, &argvars[1], item);
3026 copy_tv(&argvars[0], rettv);
3027 }
3028}
3029
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003030/*
3031 * "insert()" function
3032 */
3033 void
3034f_insert(typval_T *argvars, typval_T *rettv)
3035{
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003036 if (in_vim9script()
3037 && (check_for_list_or_blob_arg(argvars, 0) == FAIL
3038 || (argvars[0].v_type == VAR_BLOB
3039 && check_for_number_arg(argvars, 1) == FAIL)
3040 || check_for_opt_number_arg(argvars, 2) == FAIL))
3041 return;
3042
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003043 if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003044 blob_insert_func(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003045 else if (argvars[0].v_type != VAR_LIST)
Bram Moolenaard82a47d2022-01-05 20:24:39 +00003046 semsg(_(e_argument_of_str_must_be_list_or_blob), "insert()");
Bram Moolenaar39211cb2021-04-18 15:48:04 +02003047 else
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003048 list_insert_func(argvars, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003049}
3050
3051/*
3052 * "remove()" function
3053 */
3054 void
3055f_remove(typval_T *argvars, typval_T *rettv)
3056{
3057 char_u *arg_errmsg = (char_u *)N_("remove() argument");
3058
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003059 if (in_vim9script()
3060 && (check_for_list_or_dict_or_blob_arg(argvars, 0) == FAIL
3061 || ((argvars[0].v_type == VAR_LIST
3062 || argvars[0].v_type == VAR_BLOB)
3063 && (check_for_number_arg(argvars, 1) == FAIL
3064 || check_for_opt_number_arg(argvars, 2) == FAIL))
3065 || (argvars[0].v_type == VAR_DICT
3066 && check_for_string_or_number_arg(argvars, 1) == FAIL)))
3067 return;
3068
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003069 if (argvars[0].v_type == VAR_DICT)
3070 dict_remove(argvars, rettv, arg_errmsg);
3071 else if (argvars[0].v_type == VAR_BLOB)
Sean Dewar80d73952021-08-04 19:25:54 +02003072 blob_remove(argvars, rettv, arg_errmsg);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003073 else if (argvars[0].v_type == VAR_LIST)
3074 list_remove(argvars, rettv, arg_errmsg);
3075 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00003076 semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "remove()");
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003077}
3078
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003079 static void
3080list_reverse(list_T *l, typval_T *rettv)
3081{
3082 listitem_T *li, *ni;
3083
3084 rettv_list_set(rettv, l);
3085 if (l != NULL
3086 && !value_check_lock(l->lv_lock,
3087 (char_u *)N_("reverse() argument"), TRUE))
3088 {
3089 if (l->lv_first == &range_list_item)
3090 {
3091 varnumber_T new_start = l->lv_u.nonmat.lv_start
=?UTF-8?q?Dundar=20G=C3=B6c?=d5cec1f2022-01-29 15:19:23 +00003092 + ((varnumber_T)l->lv_len - 1) * l->lv_u.nonmat.lv_stride;
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003093 l->lv_u.nonmat.lv_end = new_start
3094 - (l->lv_u.nonmat.lv_end - l->lv_u.nonmat.lv_start);
3095 l->lv_u.nonmat.lv_start = new_start;
3096 l->lv_u.nonmat.lv_stride = -l->lv_u.nonmat.lv_stride;
3097 return;
3098 }
3099 li = l->lv_u.mat.lv_last;
3100 l->lv_first = l->lv_u.mat.lv_last = NULL;
3101 l->lv_len = 0;
3102 while (li != NULL)
3103 {
3104 ni = li->li_prev;
3105 list_append(l, li);
3106 li = ni;
3107 }
3108 l->lv_u.mat.lv_idx = l->lv_len - l->lv_u.mat.lv_idx - 1;
3109 }
3110}
3111
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003112/*
3113 * "reverse({list})" function
3114 */
3115 void
3116f_reverse(typval_T *argvars, typval_T *rettv)
3117{
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003118 if (check_for_string_or_list_or_tuple_or_blob_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02003119 return;
3120
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003121 if (argvars[0].v_type == VAR_BLOB)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003122 blob_reverse(argvars[0].vval.v_blob, rettv);
Yegappan Lakshmanan03ff1c22023-05-06 14:08:21 +01003123 else if (argvars[0].v_type == VAR_STRING)
zeertzjq4dd266c2023-08-19 11:35:03 +02003124 {
3125 rettv->v_type = VAR_STRING;
3126 if (argvars[0].vval.v_string != NULL)
3127 rettv->vval.v_string = reverse_text(argvars[0].vval.v_string);
3128 else
3129 rettv->vval.v_string = NULL;
3130 }
Yegappan Lakshmananf9dc2782023-05-11 15:02:56 +01003131 else if (argvars[0].v_type == VAR_LIST)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003132 list_reverse(argvars[0].vval.v_list, rettv);
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003133 else if (argvars[0].v_type == VAR_TUPLE)
3134 tuple_reverse(argvars[0].vval.v_tuple, rettv);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003135}
3136
Bram Moolenaar85629982020-06-01 18:39:20 +02003137/*
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003138 * Implementation of reduce() for list "argvars[0]", using the function "expr"
3139 * starting with the optional initial value argvars[2] and return the result in
3140 * "rettv".
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003141 */
3142 static void
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00003143list_reduce(
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003144 typval_T *argvars,
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003145 typval_T *expr,
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003146 typval_T *rettv)
3147{
3148 list_T *l = argvars[0].vval.v_list;
3149 listitem_T *li = NULL;
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003150 int range_list;
3151 int range_idx = 0;
3152 varnumber_T range_val = 0;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003153 typval_T initial;
3154 typval_T argv[3];
3155 int r;
3156 int called_emsg_start = called_emsg;
3157 int prev_locked;
Bram Moolenaar82418262022-09-28 16:16:15 +01003158 funccall_T *fc;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003159
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003160 // Using reduce on a range() uses "range_idx" and "range_val".
3161 range_list = l != NULL && l->lv_first == &range_list_item;
3162 if (range_list)
3163 range_val = l->lv_u.nonmat.lv_start;
3164
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003165 if (argvars[2].v_type == VAR_UNKNOWN)
3166 {
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003167 if (l == NULL || l->lv_len == 0)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003168 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00003169 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List");
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003170 return;
3171 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003172 if (range_list)
3173 {
3174 initial.v_type = VAR_NUMBER;
3175 initial.vval.v_number = range_val;
3176 range_val += l->lv_u.nonmat.lv_stride;
3177 range_idx = 1;
3178 }
3179 else
3180 {
3181 initial = l->lv_first->li_tv;
3182 li = l->lv_first->li_next;
3183 }
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003184 }
3185 else
3186 {
3187 initial = argvars[2];
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003188 if (l != NULL && !range_list)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003189 li = l->lv_first;
3190 }
3191 copy_tv(&initial, rettv);
3192
3193 if (l == NULL)
3194 return;
3195
zeertzjqe7d49462023-04-16 20:53:55 +01003196 // Create one funccall_T for all eval_expr_typval() calls.
Bram Moolenaar82418262022-09-28 16:16:15 +01003197 fc = eval_expr_get_funccal(expr, rettv);
3198
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003199 prev_locked = l->lv_lock;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003200 l->lv_lock = VAR_FIXED; // disallow the list changing here
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003201
3202 while (range_list ? range_idx < l->lv_len : li != NULL)
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003203 {
3204 argv[0] = *rettv;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003205 rettv->v_type = VAR_UNKNOWN;
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003206
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003207 if (range_list)
3208 {
3209 argv[1].v_type = VAR_NUMBER;
3210 argv[1].vval.v_number = range_val;
3211 }
3212 else
3213 argv[1] = li->li_tv;
3214
zeertzjqad0c4422023-08-17 22:15:47 +02003215 r = eval_expr_typval(expr, TRUE, argv, 2, fc, rettv);
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003216
Bram Moolenaar1936c762022-09-28 15:19:10 +01003217 if (argv[0].v_type != VAR_NUMBER && argv[0].v_type != VAR_UNKNOWN)
3218 clear_tv(&argv[0]);
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003219 if (r == FAIL || called_emsg != called_emsg_start)
3220 break;
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003221
Bram Moolenaar1936c762022-09-28 15:19:10 +01003222 // advance to the next item
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003223 if (range_list)
3224 {
3225 range_val += l->lv_u.nonmat.lv_stride;
3226 ++range_idx;
3227 }
3228 else
3229 li = li->li_next;
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003230 }
Bram Moolenaar52df40e2022-09-28 13:22:59 +01003231
Bram Moolenaar82418262022-09-28 16:16:15 +01003232 if (fc != NULL)
3233 remove_funccal();
3234
Yegappan Lakshmanan389b7212021-12-19 10:35:15 +00003235 l->lv_lock = prev_locked;
3236}
3237
3238/*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01003239 * "reduce(list, { accumulator, element -> value } [, initial])" function
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003240 * "reduce(tuple, { accumulator, element -> value } [, initial])" function
Yegappan Lakshmanand92813a2021-12-21 13:19:42 +00003241 * "reduce(blob, { accumulator, element -> value } [, initial])"
3242 * "reduce(string, { accumulator, element -> value } [, initial])"
Bram Moolenaar85629982020-06-01 18:39:20 +02003243 */
3244 void
3245f_reduce(typval_T *argvars, typval_T *rettv)
3246{
Bram Moolenaar85629982020-06-01 18:39:20 +02003247 char_u *func_name;
Bram Moolenaar85629982020-06-01 18:39:20 +02003248
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003249 if (check_for_string_or_list_or_tuple_or_blob_arg(argvars, 0) == FAIL)
Bram Moolenaar85629982020-06-01 18:39:20 +02003250 return;
rbtnn0ccb5842021-12-18 18:33:46 +00003251
Bram Moolenaar85629982020-06-01 18:39:20 +02003252 if (argvars[1].v_type == VAR_FUNC)
3253 func_name = argvars[1].vval.v_string;
3254 else if (argvars[1].v_type == VAR_PARTIAL)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003255 func_name = partial_name(argvars[1].vval.v_partial);
Bram Moolenaar85629982020-06-01 18:39:20 +02003256 else
3257 func_name = tv_get_string(&argvars[1]);
Bram Moolenaar0d90e722020-11-03 18:20:19 +01003258 if (func_name == NULL || *func_name == NUL)
3259 {
3260 emsg(_(e_missing_function_argument));
3261 return;
3262 }
Bram Moolenaar85629982020-06-01 18:39:20 +02003263
Bram Moolenaar85629982020-06-01 18:39:20 +02003264 if (argvars[0].v_type == VAR_LIST)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003265 list_reduce(argvars, &argvars[1], rettv);
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003266 else if (argvars[0].v_type == VAR_TUPLE)
3267 tuple_reduce(argvars, &argvars[1], rettv);
rbtnn0ccb5842021-12-18 18:33:46 +00003268 else if (argvars[0].v_type == VAR_STRING)
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003269 string_reduce(argvars, &argvars[1], rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003270 else
Bram Moolenaarf1c60d42022-09-22 17:07:00 +01003271 blob_reduce(argvars, &argvars[1], rettv);
Bram Moolenaar85629982020-06-01 18:39:20 +02003272}
3273
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +02003274/*
3275 * slice() function
3276 */
3277 void
3278f_slice(typval_T *argvars, typval_T *rettv)
3279{
3280 if (in_vim9script()
3281 && ((argvars[0].v_type != VAR_STRING
3282 && argvars[0].v_type != VAR_LIST
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +01003283 && argvars[0].v_type != VAR_TUPLE
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +02003284 && argvars[0].v_type != VAR_BLOB
3285 && check_for_list_arg(argvars, 0) == FAIL)
3286 || check_for_number_arg(argvars, 1) == FAIL
3287 || check_for_opt_number_arg(argvars, 2) == FAIL))
3288 return;
3289
3290 if (check_can_index(&argvars[0], TRUE, FALSE) != OK)
3291 return;
3292
3293 copy_tv(argvars, rettv);
3294 eval_index_inner(rettv, TRUE, argvars + 1,
3295 argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
3296 TRUE, NULL, 0, FALSE);
3297}
3298
Bram Moolenaar1e1d3002019-09-04 14:41:14 +02003299#endif // defined(FEAT_EVAL)