-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
/
Copy pathsyntax.c
10340 lines (9546 loc) · 267 KB
/
syntax.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* syntax.c: code for syntax highlighting
*/
#include "vim.h"
/*
* Structure that stores information about a highlight group.
* The ID of a highlight group is also called group ID. It is the index in
* the highlight_ga array PLUS ONE.
*/
struct hl_group
{
char_u *sg_name; /* highlight group name */
char_u *sg_name_u; /* uppercase of sg_name */
int sg_cleared; /* "hi clear" was used */
/* for normal terminals */
int sg_term; /* "term=" highlighting attributes */
char_u *sg_start; /* terminal string for start highl */
char_u *sg_stop; /* terminal string for stop highl */
int sg_term_attr; /* Screen attr for term mode */
/* for color terminals */
int sg_cterm; /* "cterm=" highlighting attr */
int sg_cterm_bold; /* bold attr was set for light color */
int sg_cterm_fg; /* terminal fg color number + 1 */
int sg_cterm_bg; /* terminal bg color number + 1 */
int sg_cterm_attr; /* Screen attr for color term mode */
/* for when using the GUI */
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
guicolor_T sg_gui_fg; /* GUI foreground color handle */
guicolor_T sg_gui_bg; /* GUI background color handle */
#endif
#ifdef FEAT_GUI
guicolor_T sg_gui_sp; /* GUI special color handle */
GuiFont sg_font; /* GUI font handle */
#ifdef FEAT_XFONTSET
GuiFontset sg_fontset; /* GUI fontset handle */
#endif
char_u *sg_font_name; /* GUI font or fontset name */
int sg_gui_attr; /* Screen attr for GUI mode */
#endif
#if defined(FEAT_GUI) || defined(FEAT_EVAL)
/* Store the sp color name for the GUI or synIDattr() */
int sg_gui; /* "gui=" highlighting attributes */
char_u *sg_gui_fg_name;/* GUI foreground color name */
char_u *sg_gui_bg_name;/* GUI background color name */
char_u *sg_gui_sp_name;/* GUI special color name */
#endif
int sg_link; /* link to this highlight group ID */
int sg_set; /* combination of SG_* flags */
#ifdef FEAT_EVAL
sctx_T sg_script_ctx; /* script in which the group was last set */
#endif
};
#define SG_TERM 1 /* term has been set */
#define SG_CTERM 2 /* cterm has been set */
#define SG_GUI 4 /* gui has been set */
#define SG_LINK 8 /* link has been set */
static garray_T highlight_ga; /* highlight groups for 'highlight' option */
#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
#ifdef FEAT_CMDL_COMPL
/* Flags to indicate an additional string for highlight name completion. */
static int include_none = 0; /* when 1 include "None" */
static int include_default = 0; /* when 1 include "default" */
static int include_link = 0; /* when 2 include "link" and "clear" */
#endif
/*
* The "term", "cterm" and "gui" arguments can be any combination of the
* following names, separated by commas (but no spaces!).
*/
static char *(hl_name_table[]) =
{"bold", "standout", "underline", "undercurl",
"italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
static int hl_attr_table[] =
{HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
static void syn_unadd_group(void);
static void set_hl_attr(int idx);
static void highlight_list_one(int id);
static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
static int syn_add_group(char_u *name);
static int syn_list_header(int did_header, int outlen, int id);
static int hl_has_settings(int idx, int check_link);
static void highlight_clear(int idx);
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
#endif
#ifdef FEAT_GUI
static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
#endif
/*
* An attribute number is the index in attr_table plus ATTR_OFF.
*/
#define ATTR_OFF (HL_ALL + 1)
#if defined(FEAT_SYN_HL) || defined(PROTO)
#define SYN_NAMELEN 50 /* maximum length of a syntax name */
/* different types of offsets that are possible */
#define SPO_MS_OFF 0 /* match start offset */
#define SPO_ME_OFF 1 /* match end offset */
#define SPO_HS_OFF 2 /* highl. start offset */
#define SPO_HE_OFF 3 /* highl. end offset */
#define SPO_RS_OFF 4 /* region start offset */
#define SPO_RE_OFF 5 /* region end offset */
#define SPO_LC_OFF 6 /* leading context offset */
#define SPO_COUNT 7
static char *(spo_name_tab[SPO_COUNT]) =
{"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
/*
* The patterns that are being searched for are stored in a syn_pattern.
* A match item consists of one pattern.
* A start/end item consists of n start patterns and m end patterns.
* A start/skip/end item consists of n start patterns, one skip pattern and m
* end patterns.
* For the latter two, the patterns are always consecutive: start-skip-end.
*
* A character offset can be given for the matched text (_m_start and _m_end)
* and for the actually highlighted text (_h_start and _h_end).
*
* Note that ordering of members is optimized to reduce padding.
*/
typedef struct syn_pattern
{
char sp_type; /* see SPTYPE_ defines below */
char sp_syncing; /* this item used for syncing */
short sp_syn_match_id; /* highlight group ID of pattern */
short sp_off_flags; /* see below */
int sp_offsets[SPO_COUNT]; /* offsets */
int sp_flags; /* see HL_ defines below */
#ifdef FEAT_CONCEAL
int sp_cchar; /* conceal substitute character */
#endif
int sp_ic; /* ignore-case flag for sp_prog */
int sp_sync_idx; /* sync item index (syncing only) */
int sp_line_id; /* ID of last line where tried */
int sp_startcol; /* next match in sp_line_id line */
short *sp_cont_list; /* cont. group IDs, if non-zero */
short *sp_next_list; /* next group IDs, if non-zero */
struct sp_syn sp_syn; /* struct passed to in_id_list() */
char_u *sp_pattern; /* regexp to match, pattern */
regprog_T *sp_prog; /* regexp to match, program */
#ifdef FEAT_PROFILE
syn_time_T sp_time;
#endif
} synpat_T;
/* The sp_off_flags are computed like this:
* offset from the start of the matched text: (1 << SPO_XX_OFF)
* offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
* When both are present, only one is used.
*/
#define SPTYPE_MATCH 1 /* match keyword with this group ID */
#define SPTYPE_START 2 /* match a regexp, start of item */
#define SPTYPE_END 3 /* match a regexp, end of item */
#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
/*
* Flags for b_syn_sync_flags:
*/
#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
#define SF_MATCH 0x02 /* sync by matching a pattern */
#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
#define MAXKEYWLEN 80 /* maximum length of a keyword */
/*
* The attributes of the syntax item that has been recognized.
*/
static int current_attr = 0; /* attr of current syntax word */
#ifdef FEAT_EVAL
static int current_id = 0; /* ID of current char for syn_get_id() */
static int current_trans_id = 0; /* idem, transparency removed */
#endif
#ifdef FEAT_CONCEAL
static int current_flags = 0;
static int current_seqnr = 0;
static int current_sub_char = 0;
#endif
typedef struct syn_cluster_S
{
char_u *scl_name; /* syntax cluster name */
char_u *scl_name_u; /* uppercase of scl_name */
short *scl_list; /* IDs in this syntax cluster */
} syn_cluster_T;
/*
* Methods of combining two clusters
*/
#define CLUSTER_REPLACE 1 /* replace first list with second */
#define CLUSTER_ADD 2 /* add second list to first */
#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
/*
* Syntax group IDs have different types:
* 0 - 19999 normal syntax groups
* 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
* 21000 - 21999 TOP indicator (current_syn_inc_tag added)
* 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
* 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
*/
#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
/*
* Annoying Hack(TM): ":syn include" needs this pointer to pass to
* expand_filename(). Most of the other syntax commands don't need it, so
* instead of passing it to them, we stow it here.
*/
static char_u **syn_cmdlinep;
/*
* Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
* files from leaking into ALLBUT lists, we assign a unique ID to the
* rules in each ":syn include"'d file.
*/
static int current_syn_inc_tag = 0;
static int running_syn_inc_tag = 0;
/*
* In a hashtable item "hi_key" points to "keyword" in a keyentry.
* This avoids adding a pointer to the hashtable item.
* KE2HIKEY() converts a var pointer to a hashitem key pointer.
* HIKEY2KE() converts a hashitem key pointer to a var pointer.
* HI2KE() converts a hashitem pointer to a var pointer.
*/
static keyentry_T dumkey;
#define KE2HIKEY(kp) ((kp)->keyword)
#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
/*
* To reduce the time spent in keepend(), remember at which level in the state
* stack the first item with "keepend" is present. When "-1", there is no
* "keepend" on the stack.
*/
static int keepend_level = -1;
static char msg_no_items[] = N_("No Syntax items defined for this buffer");
/*
* For the current state we need to remember more than just the idx.
* When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
* (The end positions have the column number of the next char)
*/
typedef struct state_item
{
int si_idx; /* index of syntax pattern or
KEYWORD_IDX */
int si_id; /* highlight group ID for keywords */
int si_trans_id; /* idem, transparency removed */
int si_m_lnum; /* lnum of the match */
int si_m_startcol; /* starting column of the match */
lpos_T si_m_endpos; /* just after end posn of the match */
lpos_T si_h_startpos; /* start position of the highlighting */
lpos_T si_h_endpos; /* end position of the highlighting */
lpos_T si_eoe_pos; /* end position of end pattern */
int si_end_idx; /* group ID for end pattern or zero */
int si_ends; /* if match ends before si_m_endpos */
int si_attr; /* attributes in this state */
long si_flags; /* HL_HAS_EOL flag in this state, and
* HL_SKIP* for si_next_list */
#ifdef FEAT_CONCEAL
int si_seqnr; /* sequence number */
int si_cchar; /* substitution character for conceal */
#endif
short *si_cont_list; /* list of contained groups */
short *si_next_list; /* nextgroup IDs after this item ends */
reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
* pattern */
} stateitem_T;
#define KEYWORD_IDX -1 /* value of si_idx for keywords */
#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
but contained groups */
#ifdef FEAT_CONCEAL
static int next_seqnr = 1; /* value to use for si_seqnr */
#endif
/*
* Struct to reduce the number of arguments to get_syn_options(), it's used
* very often.
*/
typedef struct
{
int flags; /* flags for contained and transparent */
int keyword; /* TRUE for ":syn keyword" */
int *sync_idx; /* syntax item for "grouphere" argument, NULL
if not allowed */
char has_cont_list; /* TRUE if "cont_list" can be used */
short *cont_list; /* group IDs for "contains" argument */
short *cont_in_list; /* group IDs for "containedin" argument */
short *next_list; /* group IDs for "nextgroup" argument */
} syn_opt_arg_T;
/*
* The next possible match in the current line for any pattern is remembered,
* to avoid having to try for a match in each column.
* If next_match_idx == -1, not tried (in this line) yet.
* If next_match_col == MAXCOL, no match found in this line.
* (All end positions have the column of the char after the end)
*/
static int next_match_col; /* column for start of next match */
static lpos_T next_match_m_endpos; /* position for end of next match */
static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
static int next_match_idx; /* index of matched item */
static long next_match_flags; /* flags for next match */
static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
static int next_match_end_idx; /* ID of group for end pattn or zero */
static reg_extmatch_T *next_match_extmatch = NULL;
/*
* A state stack is an array of integers or stateitem_T, stored in a
* garray_T. A state stack is invalid if its itemsize entry is zero.
*/
#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
/*
* The current state (within the line) of the recognition engine.
* When current_state.ga_itemsize is 0 the current state is invalid.
*/
static win_T *syn_win; /* current window for highlighting */
static buf_T *syn_buf; /* current buffer for highlighting */
static synblock_T *syn_block; /* current buffer for highlighting */
#ifdef FEAT_RELTIME
static proftime_T *syn_tm; /* timeout limit */
#endif
static linenr_T current_lnum = 0; /* lnum of current state */
static colnr_T current_col = 0; /* column of current state */
static int current_state_stored = 0; /* TRUE if stored current state
* after setting current_finished */
static int current_finished = 0; /* current line has been finished */
static garray_T current_state /* current stack of state_items */
= {0, 0, 0, 0, NULL};
static short *current_next_list = NULL; /* when non-zero, nextgroup list */
static int current_next_flags = 0; /* flags for current_next_list */
static int current_line_id = 0; /* unique number for current line */
#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
static int syn_match_linecont(linenr_T lnum);
static void syn_start_line(void);
static void syn_update_ends(int startofline);
static void syn_stack_alloc(void);
static int syn_stack_cleanup(void);
static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
static synstate_T *syn_stack_find_entry(linenr_T lnum);
static synstate_T *store_current_state(void);
static void load_current_state(synstate_T *from);
static void invalidate_current_state(void);
static int syn_stack_equal(synstate_T *sp);
static void validate_current_state(void);
static int syn_finish_line(int syncing);
static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
static int did_match_already(int idx, garray_T *gap);
static stateitem_T *push_next_match(stateitem_T *cur_si);
static void check_state_ends(void);
static void update_si_attr(int idx);
static void check_keepend(void);
static void update_si_end(stateitem_T *sip, int startcol, int force);
static short *copy_id_list(short *list);
static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
static int push_current_state(int idx);
static void pop_current_state(void);
#ifdef FEAT_PROFILE
static void syn_clear_time(syn_time_T *tt);
static void syntime_clear(void);
static void syntime_report(void);
static int syn_time_on = FALSE;
# define IF_SYN_TIME(p) (p)
#else
# define IF_SYN_TIME(p) NULL
typedef int syn_time_T;
#endif
static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
static void limit_pos(lpos_T *pos, lpos_T *limit);
static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
static char_u *syn_getcurline(void);
static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
static void syn_remove_pattern(synblock_T *block, int idx);
static void syn_clear_pattern(synblock_T *block, int i);
static void syn_clear_cluster(synblock_T *block, int i);
static void syn_clear_one(int id, int syncing);
static void syn_cmd_onoff(exarg_T *eap, char *name);
static void syn_lines_msg(void);
static void syn_match_msg(void);
static void syn_list_one(int id, int syncing, int link_only);
static void syn_list_cluster(int id);
static void put_id_list(char_u *name, short *list, int attr);
static void put_pattern(char *s, int c, synpat_T *spp, int attr);
static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
static void syn_clear_keyword(int id, hashtab_T *ht);
static void clear_keywtab(hashtab_T *ht);
static int syn_scl_namen2id(char_u *linep, int len);
static int syn_check_cluster(char_u *pp, int len);
static int syn_add_cluster(char_u *name);
static void init_syn_patterns(void);
static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
static int get_id_list(char_u **arg, int keylen, short **list, int skip);
static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
#if defined(FEAT_RELTIME) || defined(PROTO)
/*
* Set the timeout used for syntax highlighting.
* Use NULL to reset, no timeout.
*/
void
syn_set_timeout(proftime_T *tm)
{
syn_tm = tm;
}
#endif
/*
* Start the syntax recognition for a line. This function is normally called
* from the screen updating, once for each displayed line.
* The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
* it. Careful: curbuf and curwin are likely to point to another buffer and
* window.
*/
void
syntax_start(win_T *wp, linenr_T lnum)
{
synstate_T *p;
synstate_T *last_valid = NULL;
synstate_T *last_min_valid = NULL;
synstate_T *sp, *prev = NULL;
linenr_T parsed_lnum;
linenr_T first_stored;
int dist;
static varnumber_T changedtick = 0; /* remember the last change ID */
#ifdef FEAT_CONCEAL
current_sub_char = NUL;
#endif
/*
* After switching buffers, invalidate current_state.
* Also do this when a change was made, the current state may be invalid
* then.
*/
if (syn_block != wp->w_s
|| syn_buf != wp->w_buffer
|| changedtick != CHANGEDTICK(syn_buf))
{
invalidate_current_state();
syn_buf = wp->w_buffer;
syn_block = wp->w_s;
}
changedtick = CHANGEDTICK(syn_buf);
syn_win = wp;
/*
* Allocate syntax stack when needed.
*/
syn_stack_alloc();
if (syn_block->b_sst_array == NULL)
return; /* out of memory */
syn_block->b_sst_lasttick = display_tick;
/*
* If the state of the end of the previous line is useful, store it.
*/
if (VALID_STATE(¤t_state)
&& current_lnum < lnum
&& current_lnum < syn_buf->b_ml.ml_line_count)
{
(void)syn_finish_line(FALSE);
if (!current_state_stored)
{
++current_lnum;
(void)store_current_state();
}
/*
* If the current_lnum is now the same as "lnum", keep the current
* state (this happens very often!). Otherwise invalidate
* current_state and figure it out below.
*/
if (current_lnum != lnum)
invalidate_current_state();
}
else
invalidate_current_state();
/*
* Try to synchronize from a saved state in b_sst_array[].
* Only do this if lnum is not before and not to far beyond a saved state.
*/
if (INVALID_STATE(¤t_state) && syn_block->b_sst_array != NULL)
{
/* Find last valid saved state before start_lnum. */
for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
{
if (p->sst_lnum > lnum)
break;
if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
{
last_valid = p;
if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
last_min_valid = p;
}
}
if (last_min_valid != NULL)
load_current_state(last_min_valid);
}
/*
* If "lnum" is before or far beyond a line with a saved state, need to
* re-synchronize.
*/
if (INVALID_STATE(¤t_state))
{
syn_sync(wp, lnum, last_valid);
if (current_lnum == 1)
/* First line is always valid, no matter "minlines". */
first_stored = 1;
else
/* Need to parse "minlines" lines before state can be considered
* valid to store. */
first_stored = current_lnum + syn_block->b_syn_sync_minlines;
}
else
first_stored = current_lnum;
/*
* Advance from the sync point or saved state until the current line.
* Save some entries for syncing with later on.
*/
if (syn_block->b_sst_len <= Rows)
dist = 999999;
else
dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
while (current_lnum < lnum)
{
syn_start_line();
(void)syn_finish_line(FALSE);
++current_lnum;
/* If we parsed at least "minlines" lines or started at a valid
* state, the current state is considered valid. */
if (current_lnum >= first_stored)
{
/* Check if the saved state entry is for the current line and is
* equal to the current state. If so, then validate all saved
* states that depended on a change before the parsed line. */
if (prev == NULL)
prev = syn_stack_find_entry(current_lnum - 1);
if (prev == NULL)
sp = syn_block->b_sst_first;
else
sp = prev;
while (sp != NULL && sp->sst_lnum < current_lnum)
sp = sp->sst_next;
if (sp != NULL
&& sp->sst_lnum == current_lnum
&& syn_stack_equal(sp))
{
parsed_lnum = current_lnum;
prev = sp;
while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
{
if (sp->sst_lnum <= lnum)
/* valid state before desired line, use this one */
prev = sp;
else if (sp->sst_change_lnum == 0)
/* past saved states depending on change, break here. */
break;
sp->sst_change_lnum = 0;
sp = sp->sst_next;
}
load_current_state(prev);
}
/* Store the state at this line when it's the first one, the line
* where we start parsing, or some distance from the previously
* saved state. But only when parsed at least 'minlines'. */
else if (prev == NULL
|| current_lnum == lnum
|| current_lnum >= prev->sst_lnum + dist)
prev = store_current_state();
}
/* This can take a long time: break when CTRL-C pressed. The current
* state will be wrong then. */
line_breakcheck();
if (got_int)
{
current_lnum = lnum;
break;
}
}
syn_start_line();
}
/*
* We cannot simply discard growarrays full of state_items or buf_states; we
* have to manually release their extmatch pointers first.
*/
static void
clear_syn_state(synstate_T *p)
{
int i;
garray_T *gap;
if (p->sst_stacksize > SST_FIX_STATES)
{
gap = &(p->sst_union.sst_ga);
for (i = 0; i < gap->ga_len; i++)
unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
ga_clear(gap);
}
else
{
for (i = 0; i < p->sst_stacksize; i++)
unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
}
}
/*
* Cleanup the current_state stack.
*/
static void
clear_current_state(void)
{
int i;
stateitem_T *sip;
sip = (stateitem_T *)(current_state.ga_data);
for (i = 0; i < current_state.ga_len; i++)
unref_extmatch(sip[i].si_extmatch);
ga_clear(¤t_state);
}
/*
* Try to find a synchronisation point for line "lnum".
*
* This sets current_lnum and the current state. One of three methods is
* used:
* 1. Search backwards for the end of a C-comment.
* 2. Search backwards for given sync patterns.
* 3. Simply start on a given number of lines above "lnum".
*/
static void
syn_sync(
win_T *wp,
linenr_T start_lnum,
synstate_T *last_valid)
{
buf_T *curbuf_save;
win_T *curwin_save;
pos_T cursor_save;
int idx;
linenr_T lnum;
linenr_T end_lnum;
linenr_T break_lnum;
int had_sync_point;
stateitem_T *cur_si;
synpat_T *spp;
char_u *line;
int found_flags = 0;
int found_match_idx = 0;
linenr_T found_current_lnum = 0;
int found_current_col= 0;
lpos_T found_m_endpos;
colnr_T prev_current_col;
/*
* Clear any current state that might be hanging around.
*/
invalidate_current_state();
/*
* Start at least "minlines" back. Default starting point for parsing is
* there.
* Start further back, to avoid that scrolling backwards will result in
* resyncing for every line. Now it resyncs only one out of N lines,
* where N is minlines * 1.5, or minlines * 2 if minlines is small.
* Watch out for overflow when minlines is MAXLNUM.
*/
if (syn_block->b_syn_sync_minlines > start_lnum)
start_lnum = 1;
else
{
if (syn_block->b_syn_sync_minlines == 1)
lnum = 1;
else if (syn_block->b_syn_sync_minlines < 10)
lnum = syn_block->b_syn_sync_minlines * 2;
else
lnum = syn_block->b_syn_sync_minlines * 3 / 2;
if (syn_block->b_syn_sync_maxlines != 0
&& lnum > syn_block->b_syn_sync_maxlines)
lnum = syn_block->b_syn_sync_maxlines;
if (lnum >= start_lnum)
start_lnum = 1;
else
start_lnum -= lnum;
}
current_lnum = start_lnum;
/*
* 1. Search backwards for the end of a C-style comment.
*/
if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
{
/* Need to make syn_buf the current buffer for a moment, to be able to
* use find_start_comment(). */
curwin_save = curwin;
curwin = wp;
curbuf_save = curbuf;
curbuf = syn_buf;
/*
* Skip lines that end in a backslash.
*/
for ( ; start_lnum > 1; --start_lnum)
{
line = ml_get(start_lnum - 1);
if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
break;
}
current_lnum = start_lnum;
/* set cursor to start of search */
cursor_save = wp->w_cursor;
wp->w_cursor.lnum = start_lnum;
wp->w_cursor.col = 0;
/*
* If the line is inside a comment, need to find the syntax item that
* defines the comment.
* Restrict the search for the end of a comment to b_syn_sync_maxlines.
*/
if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
{
for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
if (SYN_ITEMS(syn_block)[idx].sp_syn.id
== syn_block->b_syn_sync_id
&& SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
{
validate_current_state();
if (push_current_state(idx) == OK)
update_si_attr(current_state.ga_len - 1);
break;
}
}
/* restore cursor and buffer */
wp->w_cursor = cursor_save;
curwin = curwin_save;
curbuf = curbuf_save;
}
/*
* 2. Search backwards for given sync patterns.
*/
else if (syn_block->b_syn_sync_flags & SF_MATCH)
{
if (syn_block->b_syn_sync_maxlines != 0
&& start_lnum > syn_block->b_syn_sync_maxlines)
break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
else
break_lnum = 0;
found_m_endpos.lnum = 0;
found_m_endpos.col = 0;
end_lnum = start_lnum;
lnum = start_lnum;
while (--lnum > break_lnum)
{
/* This can take a long time: break when CTRL-C pressed. */
line_breakcheck();
if (got_int)
{
invalidate_current_state();
current_lnum = start_lnum;
break;
}
/* Check if we have run into a valid saved state stack now. */
if (last_valid != NULL && lnum == last_valid->sst_lnum)
{
load_current_state(last_valid);
break;
}
/*
* Check if the previous line has the line-continuation pattern.
*/
if (lnum > 1 && syn_match_linecont(lnum - 1))
continue;
/*
* Start with nothing on the state stack
*/
validate_current_state();
for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
{
syn_start_line();
for (;;)
{
had_sync_point = syn_finish_line(TRUE);
/*
* When a sync point has been found, remember where, and
* continue to look for another one, further on in the line.
*/
if (had_sync_point && current_state.ga_len)
{
cur_si = &CUR_STATE(current_state.ga_len - 1);
if (cur_si->si_m_endpos.lnum > start_lnum)
{
/* ignore match that goes to after where started */
current_lnum = end_lnum;
break;
}
if (cur_si->si_idx < 0)
{
/* Cannot happen? */
found_flags = 0;
found_match_idx = KEYWORD_IDX;
}
else
{
spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
found_flags = spp->sp_flags;
found_match_idx = spp->sp_sync_idx;
}
found_current_lnum = current_lnum;
found_current_col = current_col;
found_m_endpos = cur_si->si_m_endpos;
/*
* Continue after the match (be aware of a zero-length
* match).
*/
if (found_m_endpos.lnum > current_lnum)
{
current_lnum = found_m_endpos.lnum;
current_col = found_m_endpos.col;
if (current_lnum >= end_lnum)
break;
}
else if (found_m_endpos.col > current_col)
current_col = found_m_endpos.col;
else
++current_col;
/* syn_current_attr() will have skipped the check for
* an item that ends here, need to do that now. Be
* careful not to go past the NUL. */
prev_current_col = current_col;
if (syn_getcurline()[current_col] != NUL)
++current_col;
check_state_ends();
current_col = prev_current_col;
}
else
break;
}
}
/*
* If a sync point was encountered, break here.
*/
if (found_flags)
{
/*
* Put the item that was specified by the sync point on the
* state stack. If there was no item specified, make the
* state stack empty.
*/
clear_current_state();
if (found_match_idx >= 0
&& push_current_state(found_match_idx) == OK)
update_si_attr(current_state.ga_len - 1);
/*
* When using "grouphere", continue from the sync point
* match, until the end of the line. Parsing starts at
* the next line.
* For "groupthere" the parsing starts at start_lnum.
*/
if (found_flags & HL_SYNC_HERE)
{
if (current_state.ga_len)
{
cur_si = &CUR_STATE(current_state.ga_len - 1);
cur_si->si_h_startpos.lnum = found_current_lnum;
cur_si->si_h_startpos.col = found_current_col;
update_si_end(cur_si, (int)current_col, TRUE);
check_keepend();
}
current_col = found_m_endpos.col;
current_lnum = found_m_endpos.lnum;
(void)syn_finish_line(FALSE);
++current_lnum;
}
else
current_lnum = start_lnum;
break;
}
end_lnum = lnum;
invalidate_current_state();
}
/* Ran into start of the file or exceeded maximum number of lines */
if (lnum <= break_lnum)
{
invalidate_current_state();
current_lnum = break_lnum + 1;
}
}
validate_current_state();
}
static void
save_chartab(char_u *chartab)
{
if (syn_block->b_syn_isk != empty_option)
{
mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
(size_t)32);
}
}
static void
restore_chartab(char_u *chartab)
{
if (syn_win->w_s->b_syn_isk != empty_option)
mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
}
/*
* Return TRUE if the line-continuation pattern matches in line "lnum".
*/
static int
syn_match_linecont(linenr_T lnum)
{
regmmatch_T regmatch;
int r;
char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
if (syn_block->b_syn_linecont_prog != NULL)
{
/* use syntax iskeyword option */
save_chartab(buf_chartab);
regmatch.rmm_ic = syn_block->b_syn_linecont_ic;