Mercurial > vim
comparison src/change.c @ 16666:978bcd70883d v8.1.1335
patch 8.1.1335: listener callback is called after inserting text
commit https://github.com/vim/vim/commit/dda4144d39a9d685b8dda830978e7410bd372c40
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu May 16 22:11:47 2019 +0200
patch 8.1.1335: listener callback is called after inserting text
Problem: Listener callback is called after inserting text.
Solution: Flush the changes before inserting or deleting a line. Store
changes per buffer.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 16 May 2019 22:15:06 +0200 |
parents | 1fc9cd08cf3c |
children | 1c2d9f67d98f |
comparison
equal
deleted
inserted
replaced
16665:64f31042d128 | 16666:978bcd70883d |
---|---|
150 need_maketitle = TRUE; // set window title later | 150 need_maketitle = TRUE; // set window title later |
151 #endif | 151 #endif |
152 } | 152 } |
153 | 153 |
154 #ifdef FEAT_EVAL | 154 #ifdef FEAT_EVAL |
155 static list_T *recorded_changes = NULL; | |
156 static long next_listener_id = 0; | 155 static long next_listener_id = 0; |
157 | 156 |
158 /* | 157 /* |
158 * Check if the change at "lnum" / "col" is above or overlaps with an existing | |
159 * changed. If above then flush changes and invoke listeners. | |
160 * If "merge" is TRUE do the merge. | |
161 * Returns TRUE if the change was merged. | |
162 */ | |
163 static int | |
164 check_recorded_changes( | |
165 buf_T *buf, | |
166 linenr_T lnum, | |
167 colnr_T col, | |
168 linenr_T lnume, | |
169 long xtra, | |
170 int merge) | |
171 { | |
172 if (buf->b_recorded_changes != NULL && xtra != 0) | |
173 { | |
174 listitem_T *li; | |
175 linenr_T nr; | |
176 | |
177 for (li = buf->b_recorded_changes->lv_first; li != NULL; | |
178 li = li->li_next) | |
179 { | |
180 nr = (linenr_T)dict_get_number( | |
181 li->li_tv.vval.v_dict, (char_u *)"lnum"); | |
182 if (nr >= lnum || nr > lnume) | |
183 { | |
184 if (li->li_next == NULL && lnum == nr | |
185 && col + 1 == (colnr_T)dict_get_number( | |
186 li->li_tv.vval.v_dict, (char_u *)"col")) | |
187 { | |
188 if (merge) | |
189 { | |
190 dictitem_T *di; | |
191 | |
192 // Same start point and nothing is following, entries | |
193 // can be merged. | |
194 di = dict_find(li->li_tv.vval.v_dict, | |
195 (char_u *)"end", -1); | |
196 nr = tv_get_number(&di->di_tv); | |
197 if (lnume > nr) | |
198 di->di_tv.vval.v_number = lnume; | |
199 di = dict_find(li->li_tv.vval.v_dict, | |
200 (char_u *)"added", -1); | |
201 di->di_tv.vval.v_number += xtra; | |
202 return TRUE; | |
203 } | |
204 } | |
205 else | |
206 { | |
207 // the current change is going to make the line number in | |
208 // the older change invalid, flush now | |
209 invoke_listeners(curbuf); | |
210 break; | |
211 } | |
212 } | |
213 } | |
214 } | |
215 return FALSE; | |
216 } | |
217 | |
218 /* | |
159 * Record a change for listeners added with listener_add(). | 219 * Record a change for listeners added with listener_add(). |
220 * Always for the current buffer. | |
160 */ | 221 */ |
161 static void | 222 static void |
162 may_record_change( | 223 may_record_change( |
163 linenr_T lnum, | 224 linenr_T lnum, |
164 colnr_T col, | 225 colnr_T col, |
170 if (curbuf->b_listener == NULL) | 231 if (curbuf->b_listener == NULL) |
171 return; | 232 return; |
172 | 233 |
173 // If the new change is going to change the line numbers in already listed | 234 // If the new change is going to change the line numbers in already listed |
174 // changes, then flush. | 235 // changes, then flush. |
175 if (recorded_changes != NULL && xtra != 0) | 236 if (check_recorded_changes(curbuf, lnum, col, lnume, xtra, TRUE)) |
176 { | 237 return; |
177 listitem_T *li; | 238 |
178 linenr_T nr; | 239 if (curbuf->b_recorded_changes == NULL) |
179 | 240 { |
180 for (li = recorded_changes->lv_first; li != NULL; li = li->li_next) | 241 curbuf->b_recorded_changes = list_alloc(); |
181 { | 242 if (curbuf->b_recorded_changes == NULL) // out of memory |
182 nr = (linenr_T)dict_get_number( | |
183 li->li_tv.vval.v_dict, (char_u *)"lnum"); | |
184 if (nr >= lnum || nr > lnume) | |
185 { | |
186 if (li->li_next == NULL && lnum == nr | |
187 && col + 1 == (colnr_T)dict_get_number( | |
188 li->li_tv.vval.v_dict, (char_u *)"col")) | |
189 { | |
190 dictitem_T *di; | |
191 | |
192 // Same start point and nothing is following, entries can | |
193 // be merged. | |
194 di = dict_find(li->li_tv.vval.v_dict, (char_u *)"end", -1); | |
195 nr = tv_get_number(&di->di_tv); | |
196 if (lnume > nr) | |
197 di->di_tv.vval.v_number = lnume; | |
198 di = dict_find(li->li_tv.vval.v_dict, | |
199 (char_u *)"added", -1); | |
200 di->di_tv.vval.v_number += xtra; | |
201 return; | |
202 } | |
203 | |
204 // the current change is going to make the line number in the | |
205 // older change invalid, flush now | |
206 invoke_listeners(curbuf); | |
207 break; | |
208 } | |
209 } | |
210 } | |
211 | |
212 if (recorded_changes == NULL) | |
213 { | |
214 recorded_changes = list_alloc(); | |
215 if (recorded_changes == NULL) // out of memory | |
216 return; | 243 return; |
217 ++recorded_changes->lv_refcount; | 244 ++curbuf->b_recorded_changes->lv_refcount; |
218 recorded_changes->lv_lock = VAR_FIXED; | 245 curbuf->b_recorded_changes->lv_lock = VAR_FIXED; |
219 } | 246 } |
220 | 247 |
221 dict = dict_alloc(); | 248 dict = dict_alloc(); |
222 if (dict == NULL) | 249 if (dict == NULL) |
223 return; | 250 return; |
224 dict_add_number(dict, "lnum", (varnumber_T)lnum); | 251 dict_add_number(dict, "lnum", (varnumber_T)lnum); |
225 dict_add_number(dict, "end", (varnumber_T)lnume); | 252 dict_add_number(dict, "end", (varnumber_T)lnume); |
226 dict_add_number(dict, "added", (varnumber_T)xtra); | 253 dict_add_number(dict, "added", (varnumber_T)xtra); |
227 dict_add_number(dict, "col", (varnumber_T)col + 1); | 254 dict_add_number(dict, "col", (varnumber_T)col + 1); |
228 | 255 |
229 list_append_dict(recorded_changes, dict); | 256 list_append_dict(curbuf->b_recorded_changes, dict); |
230 } | 257 } |
231 | 258 |
232 /* | 259 /* |
233 * listener_add() function | 260 * listener_add() function |
234 */ | 261 */ |
315 prev = lnr; | 342 prev = lnr; |
316 } | 343 } |
317 } | 344 } |
318 | 345 |
319 /* | 346 /* |
347 * Called before inserting a line above "lnum"/"lnum3" or deleting line "lnum" | |
348 * to "lnume". | |
349 */ | |
350 void | |
351 may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added) | |
352 { | |
353 check_recorded_changes(buf, lnum, 0, lnume, added, FALSE); | |
354 } | |
355 | |
356 /* | |
320 * Called when a sequence of changes is done: invoke listeners added with | 357 * Called when a sequence of changes is done: invoke listeners added with |
321 * listener_add(). | 358 * listener_add(). |
322 */ | 359 */ |
323 void | 360 void |
324 invoke_listeners(buf_T *buf) | 361 invoke_listeners(buf_T *buf) |
330 listitem_T *li; | 367 listitem_T *li; |
331 linenr_T start = MAXLNUM; | 368 linenr_T start = MAXLNUM; |
332 linenr_T end = 0; | 369 linenr_T end = 0; |
333 linenr_T added = 0; | 370 linenr_T added = 0; |
334 | 371 |
335 if (recorded_changes == NULL // nothing changed | 372 if (buf->b_recorded_changes == NULL // nothing changed |
336 || buf->b_listener == NULL) // no listeners | 373 || buf->b_listener == NULL) // no listeners |
337 return; | 374 return; |
338 | 375 |
339 argv[0].v_type = VAR_NUMBER; | 376 argv[0].v_type = VAR_NUMBER; |
340 argv[0].vval.v_number = buf->b_fnum; // a:bufnr | 377 argv[0].vval.v_number = buf->b_fnum; // a:bufnr |
341 | 378 |
342 | 379 |
343 for (li = recorded_changes->lv_first; li != NULL; li = li->li_next) | 380 for (li = buf->b_recorded_changes->lv_first; li != NULL; li = li->li_next) |
344 { | 381 { |
345 varnumber_T lnum; | 382 varnumber_T lnum; |
346 | 383 |
347 lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"lnum"); | 384 lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"lnum"); |
348 if (start > lnum) | 385 if (start > lnum) |
358 argv[2].vval.v_number = end; | 395 argv[2].vval.v_number = end; |
359 argv[3].v_type = VAR_NUMBER; | 396 argv[3].v_type = VAR_NUMBER; |
360 argv[3].vval.v_number = added; | 397 argv[3].vval.v_number = added; |
361 | 398 |
362 argv[4].v_type = VAR_LIST; | 399 argv[4].v_type = VAR_LIST; |
363 argv[4].vval.v_list = recorded_changes; | 400 argv[4].vval.v_list = buf->b_recorded_changes; |
364 ++textlock; | 401 ++textlock; |
365 | 402 |
366 for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next) | 403 for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next) |
367 { | 404 { |
368 call_func(lnr->lr_callback, -1, &rettv, | 405 call_func(lnr->lr_callback, -1, &rettv, |
369 5, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL); | 406 5, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL); |
370 clear_tv(&rettv); | 407 clear_tv(&rettv); |
371 } | 408 } |
372 | 409 |
373 --textlock; | 410 --textlock; |
374 list_unref(recorded_changes); | 411 list_unref(buf->b_recorded_changes); |
375 recorded_changes = NULL; | 412 buf->b_recorded_changes = NULL; |
376 } | 413 } |
377 #endif | 414 #endif |
378 | 415 |
379 /* | 416 /* |
380 * Common code for when a change was made. | 417 * Common code for when a change was made. |