From 7528d1f6b5422750eb778dfb550cfd0b0e540964 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 19 Sep 2019 23:06:20 +0200 Subject: patch 8.1.2057: the screen.c file is much too big Problem: The screen.c file is much too big. Solution: Split it in three parts. (Yegappan Lakshmanan, closes #4943) --- src/Make_cyg_ming.mak | 2 + src/Make_morph.mak | 2 + src/Make_mvc.mak | 8 + src/Make_vms.mms | 72 +- src/Makefile | 20 + src/README.md | 2 + src/drawline.c | 3131 +++++++++++++++++++++ src/drawscreen.c | 3112 +++++++++++++++++++++ src/globals.h | 25 + src/proto.h | 2 + src/proto/drawline.pro | 3 + src/proto/drawscreen.pro | 25 + src/proto/screen.pro | 33 +- src/screen.c | 6919 ++-------------------------------------------- src/version.c | 2 + src/vim.h | 7 + 16 files changed, 6646 insertions(+), 6719 deletions(-) create mode 100644 src/drawline.c create mode 100644 src/drawscreen.c create mode 100644 src/proto/drawline.pro create mode 100644 src/proto/drawscreen.pro (limited to 'src') diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 9d8abf8be3..281d147cac 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -719,6 +719,8 @@ OBJ = \ $(OUTDIR)/dict.o \ $(OUTDIR)/diff.o \ $(OUTDIR)/digraph.o \ + $(OUTDIR)/drawline.o \ + $(OUTDIR)/drawscreen.o \ $(OUTDIR)/edit.o \ $(OUTDIR)/eval.o \ $(OUTDIR)/evalbuffer.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak index 87b7e45cc5..a69eda0edd 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -39,6 +39,8 @@ SRC = arabic.c \ dict.c \ diff.c \ digraph.c \ + drawline.c \ + drawscreen.c \ edit.c \ eval.c \ evalbuffer.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 1205a6e230..8861994adb 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -726,6 +726,8 @@ OBJ = \ $(OUTDIR)\dict.obj \ $(OUTDIR)\diff.obj \ $(OUTDIR)\digraph.obj \ + $(OUTDIR)\drawline.obj \ + $(OUTDIR)\drawscreen.obj \ $(OUTDIR)\edit.obj \ $(OUTDIR)\eval.obj \ $(OUTDIR)\evalbuffer.obj \ @@ -1484,6 +1486,10 @@ $(OUTDIR)/xpatience.obj: $(OUTDIR) xdiff/xpatience.c $(XDIFF_DEPS) $(OUTDIR)/digraph.obj: $(OUTDIR) digraph.c $(INCL) +$(OUTDIR)/drawline.obj: $(OUTDIR) drawline.c $(INCL) + +$(OUTDIR)/drawscreen.obj: $(OUTDIR) drawscreen.c $(INCL) + $(OUTDIR)/edit.obj: $(OUTDIR) edit.c $(INCL) $(OUTDIR)/eval.obj: $(OUTDIR) eval.c $(INCL) @@ -1783,6 +1789,8 @@ proto.h: \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ + proto/drawline.pro \ + proto/drawscreen.pro \ proto/edit.pro \ proto/eval.pro \ proto/evalbuffer.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 42adcb8d19..c86ec35e87 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -309,8 +309,8 @@ ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \ SRC = arabic.c arglist.c autocmd.c beval.c blob.c blowfish.c buffer.c \ change.c charset.c cmdexpand.c cmdhist.c crypt.c crypt_zip.c \ - debugger.c dict.c diff.c digraph.c edit.c eval.c evalbuffer.c \ - evalfunc.c \ + debugger.c dict.c diff.c digraph.c drawline.c drawscreen.c edit.c \ + eval.c evalbuffer.c evalfunc.c \ evalvars.c evalwindow.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c \ ex_getln.c \ if_cscope.c if_xcmdsrv.c fileio.c filepath.c, findfile.c fold.c \ @@ -329,8 +329,8 @@ SRC = arabic.c arglist.c autocmd.c beval.c blob.c blowfish.c buffer.c \ OBJ = arabic.obj arglist.obj autocmd.obj beval.obj blob.obj blowfish.obj \ buffer.obj change.obj charset.obj cmdexpand.obj cmdhist.obj \ crypt.obj crypt_zip.obj debugger.obj dict.obj diff.obj digraph.obj \ - edit.obj eval.obj evalbuffer.obj evalfunc.obj evalvars.obj \ - evalwindow.obj ex_cmds.obj ex_cmds2.obj \ + drawline.obj drawscreen.obj edit.obj eval.obj evalbuffer.obj \ + evalfunc.obj evalvars.obj evalwindow.obj ex_cmds.obj ex_cmds2.obj \ ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ fileio.obj filepath.obj \ findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ @@ -556,16 +556,22 @@ dict.obj : dict.c vim.h [.auto]config.h feature.h os_unix.h \ globals.h diff.obj : diff.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h digraph.obj : digraph.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h +drawline.obj : drawline.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h +drawscreen.obj : drawscreen.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h edit.obj : edit.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h eval.obj : eval.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ @@ -620,8 +626,7 @@ findfile.obj : findfile.c vim.h [.auto]config.h feature.h os_unix.h \ globals.h fold.obj : fold.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h getchar.obj : getchar.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ @@ -669,8 +674,7 @@ map.obj : map.c vim.h [.auto]config.h feature.h os_unix.h \ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ mark.obj : mark.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h memfile.obj : memfile.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ @@ -681,8 +685,7 @@ memline.obj : memline.c vim.h [.auto]config.h feature.h os_unix.h \ globals.h menu.obj : menu.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h message.obj : message.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ @@ -693,24 +696,20 @@ misc1.obj : misc1.c vim.h [.auto]config.h feature.h os_unix.h \ version.h misc2.obj : misc2.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h move.obj : move.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h mbyte.obj : mbyte.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h normal.obj : normal.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h ops.obj : ops.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h option.obj : option.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ @@ -789,42 +788,33 @@ syntax.obj : syntax.c vim.h [.auto]config.h feature.h os_unix.h \ globals.h tag.obj : tag.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h term.obj : term.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h termlib.obj : termlib.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h testing.obj : testing.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h textprop.obj : textprop.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h ui.obj : ui.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h undo.obj : undo.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h usercmd.obj : usercmd.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ proto.h globals.h - userfunc.obj : userfunc.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ proto.h globals.h - version.obj : version.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ @@ -839,8 +829,7 @@ window.obj : window.c vim.h [.auto]config.h feature.h os_unix.h \ globals.h gui.obj : gui.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h gui_gtk.obj : gui_gtk.c gui_gtk_f.h vim.h [.auto]config.h feature.h \ os_unix.h ascii.h keymap.h term.h macros.h structs.h \ regexp.h gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h \ @@ -893,8 +882,7 @@ gui_at_fs.obj : gui_at_fs.c vim.h [.auto]config.h feature.h os_unix.h \ globals.h gui_at_sb.h pty.obj : pty.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ - [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ - + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h hangulin.obj : hangulin.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ diff --git a/src/Makefile b/src/Makefile index c89fdb1351..0bfc92d940 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1593,6 +1593,8 @@ BASIC_SRC = \ dict.c \ diff.c \ digraph.c \ + drawline.c \ + drawscreen.c \ edit.c \ eval.c \ evalbuffer.c \ @@ -1727,6 +1729,8 @@ OBJ_COMMON = \ objects/dict.o \ objects/diff.o \ objects/digraph.o \ + objects/drawline.o \ + objects/drawscreen.o \ objects/edit.o \ objects/eval.o \ objects/evalbuffer.o \ @@ -1874,6 +1878,8 @@ PRO_AUTO = \ dict.pro \ diff.pro \ digraph.pro \ + drawline.pro \ + drawscreen.pro \ edit.pro \ eval.pro \ evalbuffer.pro \ @@ -3083,6 +3089,12 @@ objects/diff.o: diff.c $(XDIFF_INCL) objects/digraph.o: digraph.c $(CCC) -o $@ digraph.c +objects/drawline.o: drawline.c + $(CCC) -o $@ drawline.c + +objects/drawscreen.o: drawscreen.c + $(CCC) -o $@ drawscreen.c + objects/edit.o: edit.c $(CCC) -o $@ edit.c @@ -3613,6 +3625,14 @@ objects/digraph.o: digraph.c vim.h protodef.h auto/config.h feature.h os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/drawline.o: drawline.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h +objects/drawscreen.o: drawscreen.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/edit.o: edit.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index f4f9df1f4e..d1a27d5e41 100644 --- a/src/README.md +++ b/src/README.md @@ -32,6 +32,8 @@ cmdexpand.c | command-line completion cmdhist.c | command-line history debugger.c | vim script debugger diff.c | diff mode (vimdiff) +drawline.c | drawing a window line +drawscreen.c | drawing the windows eval.c | expression evaluation evalbuffer.c | buffer related built-in functions evalfunc.c | built-in functions diff --git a/src/drawline.c b/src/drawline.c new file mode 100644 index 0000000000..39ec5ed406 --- /dev/null +++ b/src/drawline.c @@ -0,0 +1,3131 @@ +/* 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. + */ + +/* + * drawline.c: Functions for drawing window lines on the screen. + * This is the middle level, drawscreen. is the higher level and screen.c the + * lower level. + */ + +#include "vim.h" + +#ifdef FEAT_SYN_HL +/* + * Advance **color_cols and return TRUE when there are columns to draw. + */ + static int +advance_color_col(int vcol, int **color_cols) +{ + while (**color_cols >= 0 && vcol > **color_cols) + ++*color_cols; + return (**color_cols >= 0); +} +#endif + +#ifdef FEAT_SYN_HL +/* + * Used when 'cursorlineopt' contains "screenline": compute the margins between + * which the highlighting is used. + */ + static void +margin_columns_win(win_T *wp, int *left_col, int *right_col) +{ + // cache previous calculations depending on w_virtcol + static int saved_w_virtcol; + static win_T *prev_wp; + static int prev_left_col; + static int prev_right_col; + static int prev_col_off; + + int cur_col_off = win_col_off(wp); + int width1; + int width2; + + if (saved_w_virtcol == wp->w_virtcol + && prev_wp == wp && prev_col_off == cur_col_off) + { + *right_col = prev_right_col; + *left_col = prev_left_col; + return; + } + + width1 = wp->w_width - cur_col_off; + width2 = width1 + win_col_off2(wp); + + *left_col = 0; + *right_col = width1; + + if (wp->w_virtcol >= (colnr_T)width1) + *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2; + if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) + *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1; + + // cache values + prev_left_col = *left_col; + prev_right_col = *right_col; + prev_wp = wp; + saved_w_virtcol = wp->w_virtcol; + prev_col_off = cur_col_off; +} +#endif + +#ifdef FEAT_SIGNS +/* + * Get information needed to display the sign in line 'lnum' in window 'wp'. + * If 'nrcol' is TRUE, the sign is going to be displayed in the number column. + * Otherwise the sign is going to be displayed in the sign column. + */ + static void +get_sign_display_info( + int nrcol, + win_T *wp, + linenr_T lnum UNUSED, + sign_attrs_T *sattr, + int wcr_attr, + int row, + int startrow, + int filler_lines UNUSED, + int filler_todo UNUSED, + int *c_extrap, + int *c_finalp, + char_u *extra, + char_u **pp_extra, + int *n_extrap, + int *char_attrp) +{ + int text_sign; +# ifdef FEAT_SIGN_ICONS + int icon_sign; +# endif + + // Draw two cells with the sign value or blank. + *c_extrap = ' '; + *c_finalp = NUL; + if (nrcol) + *n_extrap = number_width(wp) + 1; + else + { + *char_attrp = hl_combine_attr(wcr_attr, HL_ATTR(HLF_SC)); + *n_extrap = 2; + } + + if (row == startrow +#ifdef FEAT_DIFF + + filler_lines && filler_todo <= 0 +#endif + ) + { + text_sign = (sattr->text != NULL) ? sattr->typenr : 0; +# ifdef FEAT_SIGN_ICONS + icon_sign = (sattr->icon != NULL) ? sattr->typenr : 0; + if (gui.in_use && icon_sign != 0) + { + // Use the image in this position. + if (nrcol) + { + *c_extrap = NUL; + sprintf((char *)extra, "%-*c ", number_width(wp), SIGN_BYTE); + *pp_extra = extra; + *n_extrap = (int)STRLEN(*pp_extra); + } + else + *c_extrap = SIGN_BYTE; +# ifdef FEAT_NETBEANS_INTG + if (netbeans_active() && (buf_signcount(wp->w_buffer, lnum) > 1)) + { + if (nrcol) + { + *c_extrap = NUL; + sprintf((char *)extra, "%-*c ", number_width(wp), + MULTISIGN_BYTE); + *pp_extra = extra; + *n_extrap = (int)STRLEN(*pp_extra); + } + else + *c_extrap = MULTISIGN_BYTE; + } +# endif + *c_finalp = NUL; + *char_attrp = icon_sign; + } + else +# endif + if (text_sign != 0) + { + *pp_extra = sattr->text; + if (*pp_extra != NULL) + { + if (nrcol) + { + int n, width = number_width(wp) - 2; + + for (n = 0; n < width; n++) + extra[n] = ' '; + extra[n] = 0; + STRCAT(extra, *pp_extra); + STRCAT(extra, " "); + *pp_extra = extra; + } + *c_extrap = NUL; + *c_finalp = NUL; + *n_extrap = (int)STRLEN(*pp_extra); + } + *char_attrp = sattr->texthl; + } + } +} +#endif + +#ifdef FEAT_TEXT_PROP +static textprop_T *current_text_props = NULL; +static buf_T *current_buf = NULL; + + static int +text_prop_compare(const void *s1, const void *s2) +{ + int idx1, idx2; + proptype_T *pt1, *pt2; + colnr_T col1, col2; + + idx1 = *(int *)s1; + idx2 = *(int *)s2; + pt1 = text_prop_type_by_id(current_buf, current_text_props[idx1].tp_type); + pt2 = text_prop_type_by_id(current_buf, current_text_props[idx2].tp_type); + if (pt1 == pt2) + return 0; + if (pt1 == NULL) + return -1; + if (pt2 == NULL) + return 1; + if (pt1->pt_priority != pt2->pt_priority) + return pt1->pt_priority > pt2->pt_priority ? 1 : -1; + col1 = current_text_props[idx1].tp_col; + col2 = current_text_props[idx2].tp_col; + return col1 == col2 ? 0 : col1 > col2 ? 1 : -1; +} +#endif + +/* + * Display line "lnum" of window 'wp' on the screen. + * Start at row "startrow", stop when "endrow" is reached. + * wp->w_virtcol needs to be valid. + * + * Return the number of last row the line occupies. + */ + int +win_line( + win_T *wp, + linenr_T lnum, + int startrow, + int endrow, + int nochange UNUSED, // not updating for changed text + int number_only) // only update the number column +{ + int col = 0; // visual column on screen + unsigned off; // offset in ScreenLines/ScreenAttrs + int c = 0; // init for GCC + long vcol = 0; // virtual column (for tabs) +#ifdef FEAT_LINEBREAK + long vcol_sbr = -1; // virtual column after showbreak +#endif + long vcol_prev = -1; // "vcol" of previous character + char_u *line; // current line + char_u *ptr; // current position in "line" + int row; // row in the window, excl w_winrow + int screen_row; // row on the screen, incl w_winrow + + char_u extra[21]; // "%ld " and 'fdc' must fit in here + int n_extra = 0; // number of extra chars + char_u *p_extra = NULL; // string of extra chars, plus NUL + char_u *p_extra_free = NULL; // p_extra needs to be freed + int c_extra = NUL; // extra chars, all the same + int c_final = NUL; // final char, mandatory if set + int extra_attr = 0; // attributes when n_extra != 0 + static char_u *at_end_str = (char_u *)""; // used for p_extra when + // displaying lcs_eol at end-of-line + int lcs_eol_one = lcs_eol; // lcs_eol until it's been used + int lcs_prec_todo = lcs_prec; // lcs_prec until it's been used + + // saved "extra" items for when draw_state becomes WL_LINE (again) + int saved_n_extra = 0; + char_u *saved_p_extra = NULL; + int saved_c_extra = 0; + int saved_c_final = 0; + int saved_char_attr = 0; + + int n_attr = 0; // chars with special attr + int saved_attr2 = 0; // char_attr saved for n_attr + int n_attr3 = 0; // chars with overruling special attr + int saved_attr3 = 0; // char_attr saved for n_attr3 + + int n_skip = 0; // nr of chars to skip for 'nowrap' + + int fromcol = -10; // start of inverting + int tocol = MAXCOL; // end of inverting + int fromcol_prev = -2; // start of inverting after cursor + int noinvcur = FALSE; // don't invert the cursor + pos_T *top, *bot; + int lnum_in_visual_area = FALSE; + pos_T pos; + long v; + + int char_attr = 0; // attributes for next character + int attr_pri = FALSE; // char_attr has priority + int area_highlighting = FALSE; // Visual or incsearch highlighting + // in this line + int vi_attr = 0; // attributes for Visual and incsearch + // highlighting + int wcr_attr = 0; // attributes from 'wincolor' + int win_attr = 0; // background for whole window, except + // margins and "~" lines. + int area_attr = 0; // attributes desired by highlighting + int search_attr = 0; // attributes desired by 'hlsearch' +#ifdef FEAT_SYN_HL + int vcol_save_attr = 0; // saved attr for 'cursorcolumn' + int syntax_attr = 0; // attributes desired by syntax + int has_syntax = FALSE; // this buffer has syntax highl. + int save_did_emsg; + int draw_color_col = FALSE; // highlight colorcolumn + int *color_cols = NULL; // pointer to according columns array +#endif + int eol_hl_off = 0; // 1 if highlighted char after EOL +#ifdef FEAT_TEXT_PROP + int text_prop_count; + int text_prop_next = 0; // next text property to use + textprop_T *text_props = NULL; + int *text_prop_idxs = NULL; + int text_props_active = 0; + proptype_T *text_prop_type = NULL; + int text_prop_attr = 0; + int text_prop_combine = FALSE; +#endif +#ifdef FEAT_SPELL + int has_spell = FALSE; // this buffer has spell checking +# define SPWORDLEN 150 + char_u nextline[SPWORDLEN * 2];// text with start of the next line + int nextlinecol = 0; // column where nextline[] starts + int nextline_idx = 0; // index in nextline[] where next line + // starts + int spell_attr = 0; // attributes desired by spelling + int word_end = 0; // last byte with same spell_attr + static linenr_T checked_lnum = 0; // line number for "checked_col" + static int checked_col = 0; // column in "checked_lnum" up to which + // there are no spell errors + static int cap_col = -1; // column to check for Cap word + static linenr_T capcol_lnum = 0; // line number where "cap_col" used + int cur_checked_col = 0; // checked column for current line +#endif + int extra_check = 0; // has extra highlighting + int multi_attr = 0; // attributes desired by multibyte + int mb_l = 1; // multi-byte byte length + int mb_c = 0; // decoded multi-byte character + int mb_utf8 = FALSE; // screen char is UTF-8 char + int u8cc[MAX_MCO]; // composing UTF-8 chars +#if defined(FEAT_DIFF) || defined(FEAT_SIGNS) + int filler_lines = 0; // nr of filler lines to be drawn + int filler_todo = 0; // nr of filler lines still to do + 1 +#endif +#ifdef FEAT_DIFF + hlf_T diff_hlf = (hlf_T)0; // type of diff highlighting + int change_start = MAXCOL; // first col of changed area + int change_end = -1; // last col of changed area +#endif + colnr_T trailcol = MAXCOL; // start of trailing spaces +#ifdef FEAT_LINEBREAK + int need_showbreak = FALSE; // overlong line, skipping first x + // chars +#endif +#if defined(FEAT_SIGNS) || defined(FEAT_QUICKFIX) \ + || defined(FEAT_SYN_HL) || defined(FEAT_DIFF) +# define LINE_ATTR + int line_attr = 0; // attribute for the whole line + int line_attr_save; +#endif +#ifdef FEAT_SIGNS + int sign_present = FALSE; + sign_attrs_T sattr; +#endif +#ifdef FEAT_ARABIC + int prev_c = 0; // previous Arabic character + int prev_c1 = 0; // first composing char for prev_c +#endif +#if defined(LINE_ATTR) + int did_line_attr = 0; +#endif +#ifdef FEAT_TERMINAL + int get_term_attr = FALSE; +#endif +#ifdef FEAT_SYN_HL + int cul_attr = 0; // set when 'cursorline' active + + // 'cursorlineopt' has "screenline" and cursor is in this line + int cul_screenline = FALSE; + + // margin columns for the screen line, needed for when 'cursorlineopt' + // contains "screenline" + int left_curline_col = 0; + int right_curline_col = 0; +#endif + + // draw_state: items that are drawn in sequence: +#define WL_START 0 // nothing done yet +#ifdef FEAT_CMDWIN +# define WL_CMDLINE WL_START + 1 // cmdline window column +#else +# define WL_CMDLINE WL_START +#endif +#ifdef FEAT_FOLDING +# define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn' +#else +# define WL_FOLD WL_CMDLINE +#endif +#ifdef FEAT_SIGNS +# define WL_SIGN WL_FOLD + 1 // column for signs +#else +# define WL_SIGN WL_FOLD // column for signs +#endif +#define WL_NR WL_SIGN + 1 // line number +#ifdef FEAT_LINEBREAK +# define WL_BRI WL_NR + 1 // 'breakindent' +#else +# define WL_BRI WL_NR +#endif +#if defined(FEAT_LINEBREAK) || defined(FEAT_DIFF) +# define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff' +#else +# define WL_SBR WL_BRI +#endif +#define WL_LINE WL_SBR + 1 // text in the line + int draw_state = WL_START; // what to draw next +#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + int feedback_col = 0; + int feedback_old_attr = -1; +#endif + int screen_line_flags = 0; + +#if defined(FEAT_CONCEAL) || defined(FEAT_SEARCH_EXTRA) + int match_conc = 0; // cchar for match functions +#endif +#ifdef FEAT_CONCEAL + int syntax_flags = 0; + int syntax_seqnr = 0; + int prev_syntax_id = 0; + int conceal_attr = HL_ATTR(HLF_CONCEAL); + int is_concealing = FALSE; + int boguscols = 0; // nonexistent columns added to force + // wrapping + int vcol_off = 0; // offset for concealed characters + int did_wcol = FALSE; + int old_boguscols = 0; +# define VCOL_HLC (vcol - vcol_off) +# define FIX_FOR_BOGUSCOLS \ + { \ + n_extra += vcol_off; \ + vcol -= vcol_off; \ + vcol_off = 0; \ + col -= boguscols; \ + old_boguscols = boguscols; \ + boguscols = 0; \ + } +#else +# define VCOL_HLC (vcol) +#endif + + if (startrow > endrow) // past the end already! + return startrow; + + row = startrow; + screen_row = row + W_WINROW(wp); + + if (!number_only) + { + // To speed up the loop below, set extra_check when there is linebreak, + // trailing white space and/or syntax processing to be done. +#ifdef FEAT_LINEBREAK + extra_check = wp->w_p_lbr; +#endif +#ifdef FEAT_SYN_HL + if (syntax_present(wp) && !wp->w_s->b_syn_error +# ifdef SYN_TIME_LIMIT + && !wp->w_s->b_syn_slow +# endif + ) + { + // Prepare for syntax highlighting in this line. When there is an + // error, stop syntax highlighting. + save_did_emsg = did_emsg; + did_emsg = FALSE; + syntax_start(wp, lnum); + if (did_emsg) + wp->w_s->b_syn_error = TRUE; + else + { + did_emsg = save_did_emsg; +#ifdef SYN_TIME_LIMIT + if (!wp->w_s->b_syn_slow) +#endif + { + has_syntax = TRUE; + extra_check = TRUE; + } + } + } + + // Check for columns to display for 'colorcolumn'. + color_cols = wp->w_p_cc_cols; + if (color_cols != NULL) + draw_color_col = advance_color_col(VCOL_HLC, &color_cols); +#endif + +#ifdef FEAT_TERMINAL + if (term_show_buffer(wp->w_buffer)) + { + extra_check = TRUE; + get_term_attr = TRUE; + win_attr = term_get_attr(wp->w_buffer, lnum, -1); + } +#endif + +#ifdef FEAT_SPELL + if (wp->w_p_spell + && *wp->w_s->b_p_spl != NUL + && wp->w_s->b_langp.ga_len > 0 + && *(char **)(wp->w_s->b_langp.ga_data) != NULL) + { + // Prepare for spell checking. + has_spell = TRUE; + extra_check = TRUE; + + // Get the start of the next line, so that words that wrap to the + // next line are found too: "etal.". + // Trick: skip a few chars for C/shell/Vim comments + nextline[SPWORDLEN] = NUL; + if (lnum < wp->w_buffer->b_ml.ml_line_count) + { + line = ml_get_buf(wp->w_buffer, lnum + 1, FALSE); + spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); + } + + // When a word wrapped from the previous line the start of the + // current line is valid. + if (lnum == checked_lnum) + cur_checked_col = checked_col; + checked_lnum = 0; + + // When there was a sentence end in the previous line may require a + // word starting with capital in this line. In line 1 always check + // the first word. + if (lnum != capcol_lnum) + cap_col = -1; + if (lnum == 1) + cap_col = 0; + capcol_lnum = 0; + } +#endif + + // handle Visual active in this window + if (VIsual_active && wp->w_buffer == curwin->w_buffer) + { + if (LTOREQ_POS(curwin->w_cursor, VIsual)) + { + // Visual is after curwin->w_cursor + top = &curwin->w_cursor; + bot = &VIsual; + } + else + { + // Visual is before curwin->w_cursor + top = &VIsual; + bot = &curwin->w_cursor; + } + lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum); + if (VIsual_mode == Ctrl_V) + { + // block mode + if (lnum_in_visual_area) + { + fromcol = wp->w_old_cursor_fcol; + tocol = wp->w_old_cursor_lcol; + } + } + else + { + // non-block mode + if (lnum > top->lnum && lnum <= bot->lnum) + fromcol = 0; + else if (lnum == top->lnum) + { + if (VIsual_mode == 'V') // linewise + fromcol = 0; + else + { + getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL); + if (gchar_pos(top) == NUL) + tocol = fromcol + 1; + } + } + if (VIsual_mode != 'V' && lnum == bot->lnum) + { + if (*p_sel == 'e' && bot->col == 0 && bot->coladd == 0) + { + fromcol = -10; + tocol = MAXCOL; + } + else if (bot->col == MAXCOL) + tocol = MAXCOL; + else + { + pos = *bot; + if (*p_sel == 'e') + getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL); + else + { + getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol); + ++tocol; + } + } + } + } + + // Check if the character under the cursor should not be inverted + if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin +#ifdef FEAT_GUI + && !gui.in_use +#endif + ) + noinvcur = TRUE; + + // if inverting in this line set area_highlighting + if (fromcol >= 0) + { + area_highlighting = TRUE; + vi_attr = HL_ATTR(HLF_V); +#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) + if ((clip_star.available && !clip_star.owned + && clip_isautosel_star()) + || (clip_plus.available && !clip_plus.owned + && clip_isautosel_plus())) + vi_attr = HL_ATTR(HLF_VNC); +#endif + } + } + + // handle 'incsearch' and ":s///c" highlighting + else if (highlight_match + && wp == curwin + && lnum >= curwin->w_cursor.lnum + && lnum <= curwin->w_cursor.lnum + search_match_lines) + { + if (lnum == curwin->w_cursor.lnum) + getvcol(curwin, &(curwin->w_cursor), + (colnr_T *)&fromcol, NULL, NULL); + else + fromcol = 0; + if (lnum == curwin->w_cursor.lnum + search_match_lines) + { + pos.lnum = lnum; + pos.col = search_match_endcol; + getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL); + } + else + tocol = MAXCOL; + // do at least one character; happens when past end of line + if (fromcol == tocol) + tocol = fromcol + 1; + area_highlighting = TRUE; + vi_attr = HL_ATTR(HLF_I); + } + } + +#ifdef FEAT_DIFF + filler_lines = diff_check(wp, lnum); + if (filler_lines < 0) + { + if (filler_lines == -1) + { + if (diff_find_change(wp, lnum, &change_start, &change_end)) + diff_hlf = HLF_ADD; // added line + else if (change_start == 0) + diff_hlf = HLF_TXD; // changed text + else + diff_hlf = HLF_CHD; // changed line + } + else + diff_hlf = HLF_ADD; // added line + filler_lines = 0; + area_highlighting = TRUE; + } + if (lnum == wp->w_topline) + filler_lines = wp->w_topfill; + filler_todo = filler_lines; +#endif + +#ifdef FEAT_SIGNS + sign_present = buf_get_signattrs(wp->w_buffer, lnum, &sattr); +#endif + +#ifdef LINE_ATTR +# ifdef FEAT_SIGNS + // If this line has a sign with line highlighting set line_attr. + if (sign_present) + line_attr = sattr.linehl; +# endif +# if defined(FEAT_QUICKFIX) + // Highlight the current line in the quickfix window. + if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) + line_attr = HL_ATTR(HLF_QFL); +# endif + if (line_attr != 0) + area_highlighting = TRUE; +#endif + + line = ml_get_buf(wp->w_buffer, lnum, FALSE); + ptr = line; + +#ifdef FEAT_SPELL + if (has_spell && !number_only) + { + // For checking first word with a capital skip white space. + if (cap_col == 0) + cap_col = getwhitecols(line); + + // To be able to spell-check over line boundaries copy the end of the + // current line into nextline[]. Above the start of the next line was + // copied to nextline[SPWORDLEN]. + if (nextline[SPWORDLEN] == NUL) + { + // No next line or it is empty. + nextlinecol = MAXCOL; + nextline_idx = 0; + } + else + { + v = (long)STRLEN(line); + if (v < SPWORDLEN) + { + // Short line, use it completely and append the start of the + // next line. + nextlinecol = 0; + mch_memmove(nextline, line, (size_t)v); + STRMOVE(nextline + v, nextline + SPWORDLEN); + nextline_idx = v + 1; + } + else + { + // Long line, use only the last SPWORDLEN bytes. + nextlinecol = v - SPWORDLEN; + mch_memmove(nextline, line + nextlinecol, SPWORDLEN); + nextline_idx = SPWORDLEN + 1; + } + } + } +#endif + + if (wp->w_p_list) + { + if (lcs_space || lcs_trail || lcs_nbsp) + extra_check = TRUE; + // find start of trailing whitespace + if (lcs_trail) + { + trailcol = (colnr_T)STRLEN(ptr); + while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1])) + --trailcol; + trailcol += (colnr_T) (ptr - line); + } + } + + wcr_attr = get_wcr_attr(wp); + if (wcr_attr != 0) + { + win_attr = wcr_attr; + area_highlighting = TRUE; + } +#ifdef FEAT_TEXT_PROP + if (WIN_IS_POPUP(wp)) + screen_line_flags |= SLF_POPUP; +#endif + + // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the + // first character to be displayed. + if (wp->w_p_wrap) + v = wp->w_skipcol; + else + v = wp->w_leftcol; + if (v > 0 && !number_only) + { + char_u *prev_ptr = ptr; + + while (vcol < v && *ptr != NUL) + { + c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); + vcol += c; + prev_ptr = ptr; + MB_PTR_ADV(ptr); + } + + // When: + // - 'cuc' is set, or + // - 'colorcolumn' is set, or + // - 'virtualedit' is set, or + // - the visual mode is active, + // the end of the line may be before the start of the displayed part. + if (vcol < v && ( +#ifdef FEAT_SYN_HL + wp->w_p_cuc || draw_color_col || +#endif + virtual_active() || + (VIsual_active && wp->w_buffer == curwin->w_buffer))) + vcol = v; + + // Handle a character that's not completely on the screen: Put ptr at + // that character but skip the first few screen characters. + if (vcol > v) + { + vcol -= c; + ptr = prev_ptr; + // If the character fits on the screen, don't need to skip it. + // Except for a TAB. + if (( (*mb_ptr2cells)(ptr) >= c || *ptr == TAB) && col == 0) + n_skip = v - vcol; + } + + // Adjust for when the inverted text is before the screen, + // and when the start of the inverted text is before the screen. + if (tocol <= vcol) + fromcol = 0; + else if (fromcol >= 0 && fromcol < vcol) + fromcol = vcol; + +#ifdef FEAT_LINEBREAK + // When w_skipcol is non-zero, first line needs 'showbreak' + if (wp->w_p_wrap) + need_showbreak = TRUE; +#endif +#ifdef FEAT_SPELL + // When spell checking a word we need to figure out the start of the + // word and if it's badly spelled or not. + if (has_spell) + { + int len; + colnr_T linecol = (colnr_T)(ptr - line); + hlf_T spell_hlf = HLF_COUNT; + + pos = wp->w_cursor; + wp->w_cursor.lnum = lnum; + wp->w_cursor.col = linecol; + len = spell_move_to(wp, FORWARD, TRUE, TRUE, &spell_hlf); + + // spell_move_to() may call ml_get() and make "line" invalid + line = ml_get_buf(wp->w_buffer, lnum, FALSE); + ptr = line + linecol; + + if (len == 0 || (int)wp->w_cursor.col > ptr - line) + { + // no bad word found at line start, don't check until end of a + // word + spell_hlf = HLF_COUNT; + word_end = (int)(spell_to_word_end(ptr, wp) - line + 1); + } + else + { + // bad word found, use attributes until end of word + word_end = wp->w_cursor.col + len + 1; + + // Turn index into actual attributes. + if (spell_hlf != HLF_COUNT) + spell_attr = highlight_attr[spell_hlf]; + } + wp->w_cursor = pos; + +# ifdef FEAT_SYN_HL + // Need to restart syntax highlighting for this line. + if (has_syntax) + syntax_start(wp, lnum); +# endif + } +#endif + } + + // Correct highlighting for cursor that can't be disabled. + // Avoids having to check this for each character. + if (fromcol >= 0) + { + if (noinvcur) + { + if ((colnr_T)fromcol == wp->w_virtcol) + { + // highlighting starts at cursor, let it start just after the + // cursor + fromcol_prev = fromcol; + fromcol = -1; + } + else if ((colnr_T)fromcol < wp->w_virtcol) + // restart highlighting after the cursor + fromcol_prev = wp->w_virtcol; + } + if (fromcol >= tocol) + fromcol = -1; + } + +#ifdef FEAT_SEARCH_EXTRA + if (!number_only) + { + v = (long)(ptr - line); + area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v, + &line, &screen_search_hl, + &search_attr); + ptr = line + v; // "line" may have been updated + } +#endif + +#ifdef FEAT_SYN_HL + // Cursor line highlighting for 'cursorline' in the current window. + if (wp->w_p_cul && lnum == wp->w_cursor.lnum) + { + // Do not show the cursor line in the text when Visual mode is active, + // because it's not clear what is selected then. Do update + // w_last_cursorline. + if (!(wp == curwin && VIsual_active) + && wp->w_p_culopt_flags != CULOPT_NBR) + { + cul_screenline = (wp->w_p_wrap + && (wp->w_p_culopt_flags & CULOPT_SCRLINE)); + + // Only set line_attr here when "screenline" is not present in + // 'cursorlineopt'. Otherwise it's done later. + if (!cul_screenline) + { + cul_attr = HL_ATTR(HLF_CUL); + line_attr = cul_attr; + wp->w_last_cursorline = wp->w_cursor.lnum; + } + else + { + line_attr_save = line_attr; + wp->w_last_cursorline = 0; + margin_columns_win(wp, &left_curline_col, &right_curline_col); + } + area_highlighting = TRUE; + } + else + wp->w_last_cursorline = wp->w_cursor.lnum; + } +#endif + +#ifdef FEAT_TEXT_PROP + { + char_u *prop_start; + + text_prop_count = get_text_props(wp->w_buffer, lnum, + &prop_start, FALSE); + if (text_prop_count > 0) + { + // Make a copy of the properties, so that they are properly + // aligned. + text_props = ALLOC_MULT(textprop_T, text_prop_count); + if (text_props != NULL) + mch_memmove(text_props, prop_start, + text_prop_count * sizeof(textprop_T)); + + // Allocate an array for the indexes. + text_prop_idxs = ALLOC_MULT(int, text_prop_count); + area_highlighting = TRUE; + extra_check = TRUE; + } + } +#endif + + off = (unsigned)(current_ScreenLine - ScreenLines); + col = 0; + +#ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) + { + // Rightleft window: process the text in the normal direction, but put + // it in current_ScreenLine[] from right to left. Start at the + // rightmost column of the window. + col = wp->w_width - 1; + off += col; + screen_line_flags |= SLF_RIGHTLEFT; + } +#endif + + // Repeat for the whole displayed line. + for (;;) + { +#if defined(FEAT_CONCEAL) || defined(FEAT_SEARCH_EXTRA) + int has_match_conc = 0; // match wants to conceal +#endif +#ifdef FEAT_CONCEAL + int did_decrement_ptr = FALSE; +#endif + // Skip this quickly when working on the text. + if (draw_state != WL_LINE) + { +#ifdef FEAT_CMDWIN + if (draw_state == WL_CMDLINE - 1 && n_extra == 0) + { + draw_state = WL_CMDLINE; + if (cmdwin_type != 0 && wp == curwin) + { + // Draw the cmdline character. + n_extra = 1; + c_extra = cmdwin_type; + c_final = NUL; + char_attr = hl_combine_attr(wcr_attr, HL_ATTR(HLF_AT)); + } + } +#endif + +#ifdef FEAT_FOLDING + if (draw_state == WL_FOLD - 1 && n_extra == 0) + { + int fdc = compute_foldcolumn(wp, 0); + + draw_state = WL_FOLD; + if (fdc > 0) + { + // Draw the 'foldcolumn'. Allocate a buffer, "extra" may + // already be in use. + vim_free(p_extra_free); + p_extra_free = alloc(12 + 1); + + if (p_extra_free != NULL) + { + fill_foldcolumn(p_extra_free, wp, FALSE, lnum); + n_extra = fdc; + p_extra_free[n_extra] = NUL; + p_extra = p_extra_free; + c_extra = NUL; + c_final = NUL; + char_attr = hl_combine_attr(wcr_attr, HL_ATTR(HLF_FC)); + } + } + } +#endif + +#ifdef FEAT_SIGNS + if (draw_state == WL_SIGN - 1 && n_extra == 0) + { + draw_state = WL_SIGN; + // Show the sign column when there are any signs in this + // buffer or when using Netbeans. + if (signcolumn_on(wp)) + get_sign_display_info(FALSE, wp, lnum, &sattr, wcr_attr, + row, startrow, filler_lines, filler_todo, &c_extra, + &c_final, extra, &p_extra, &n_extra, &char_attr); + } +#endif + + if (draw_state == WL_NR - 1 && n_extra == 0) + { + draw_state = WL_NR; + // Display the absolute or relative line number. After the + // first fill with blanks when the 'n' flag isn't in 'cpo' + if ((wp->w_p_nu || wp->w_p_rnu) + && (row == startrow +#ifdef FEAT_DIFF + + filler_lines +#endif + || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) + { +#ifdef FEAT_SIGNS + // If 'signcolumn' is set to 'number' and a sign is present + // in 'lnum', then display the sign instead of the line + // number. + if ((*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u') + && sign_present) + get_sign_display_info(TRUE, wp, lnum, &sattr, wcr_attr, + row, startrow, filler_lines, filler_todo, + &c_extra, &c_final, extra, &p_extra, &n_extra, + &char_attr); + else +#endif + { + // Draw the line number (empty space after wrapping). + if (row == startrow +#ifdef FEAT_DIFF + + filler_lines +#endif + ) + { + long num; + char *fmt = "%*ld "; + + if (wp->w_p_nu && !wp->w_p_rnu) + // 'number' + 'norelativenumber' + num = (long)lnum; + else + { + // 'relativenumber', don't use negative numbers + num = labs((long)get_cursor_rel_lnum(wp, lnum)); + if (num == 0 && wp->w_p_nu && wp->w_p_rnu) + { + // 'number' + 'relativenumber' + num = lnum; + fmt = "%-*ld "; + } + } + + sprintf((char *)extra, fmt, + number_width(wp), num); + if (wp->w_skipcol > 0) + for (p_extra = extra; *p_extra == ' '; ++p_extra) + *p_extra = '-'; +#ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) // reverse line numbers + { + char_u *p1, *p2; + int t; + + // like rl_mirror(), but keep the space at the end + p2 = skiptowhite(extra) - 1; + for (p1 = extra; p1 < p2; ++p1, --p2) + { + t = *p1; + *p1 = *p2; + *p2 = t; + } + } +#endif + p_extra = extra; + c_extra = NUL; + c_final = NUL; + } + else + { + c_extra = ' '; + c_final = NUL; + } + n_extra = number_width(wp) + 1; + char_attr = hl_combine_attr(wcr_attr, HL_ATTR(HLF_N)); +#ifdef FEAT_SYN_HL + // When 'cursorline' is set highlight the line number of + // the current line differently. + // When 'cursorlineopt' has "screenline" only highlight + // the line number itself. + // TODO: Can we use CursorLine instead of CursorLineNr + // when CursorLineNr isn't set? + if ((wp->w_p_cul || wp->w_p_rnu) + && (wp->w_p_culopt_flags & CULOPT_NBR) + && (row == startrow + || wp->w_p_culopt_flags & CULOPT_LINE) + && lnum == wp->w_cursor.lnum) + char_attr = hl_combine_attr(wcr_attr, HL_ATTR(HLF_CLN)); +#endif + } + } + } + +#ifdef FEAT_LINEBREAK + if (wp->w_p_brisbr && draw_state == WL_BRI - 1 + && n_extra == 0 && *p_sbr != NUL) + // draw indent after showbreak value + draw_state = WL_BRI; + else if (wp->w_p_brisbr && draw_state == WL_SBR && n_extra == 0) + // After the showbreak, draw the breakindent + draw_state = WL_BRI - 1; + + // draw 'breakindent': indent wrapped text accordingly + if (draw_state == WL_BRI - 1 && n_extra == 0) + { + draw_state = WL_BRI; + // if need_showbreak is set, breakindent also applies + if (wp->w_p_bri && n_extra == 0 + && (row != startrow || need_showbreak) +# ifdef FEAT_DIFF + && filler_lines == 0 +# endif + ) + { + char_attr = 0; +# ifdef FEAT_DIFF + if (diff_hlf != (hlf_T)0) + { + char_attr = HL_ATTR(diff_hlf); +# ifdef FEAT_SYN_HL + if (cul_attr != 0) + char_attr = hl_combine_attr(char_attr, cul_attr); +# endif + } +# endif + p_extra = NULL; + c_extra = ' '; + n_extra = get_breakindent_win(wp, + ml_get_buf(wp->w_buffer, lnum, FALSE)); + // Correct end of highlighted area for 'breakindent', + // required when 'linebreak' is also set. + if (tocol == vcol) + tocol += n_extra; + } + } +#endif + +#if defined(FEAT_LINEBREAK) || defined(FEAT_DIFF) + if (draw_state == WL_SBR - 1 && n_extra == 0) + { + draw_state = WL_SBR; +# ifdef FEAT_DIFF + if (filler_todo > 0) + { + // Draw "deleted" diff line(s). + if (char2cells(fill_diff) > 1) + { + c_extra = '-'; + c_final = NUL; + } + else + { + c_extra = fill_diff; + c_final = NUL; + } +# ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) + n_extra = col + 1; + else +# endif + n_extra = wp->w_width - col; + char_attr = HL_ATTR(HLF_DED); + } +# endif +# ifdef FEAT_LINEBREAK + if (*p_sbr != NUL && need_showbreak) + { + // Draw 'showbreak' at the start of each broken line. + p_extra = p_sbr; + c_extra = NUL; + c_final = NUL; + n_extra = (int)STRLEN(p_sbr); + char_attr = HL_ATTR(HLF_AT); + need_showbreak = FALSE; + vcol_sbr = vcol + MB_CHARLEN(p_sbr); + // Correct end of highlighted area for 'showbreak', + // required when 'linebreak' is also set. + if (tocol == vcol) + tocol += n_extra; + // combine 'showbreak' with 'wincolor' + if (win_attr != 0) + char_attr = hl_combine_attr(win_attr, char_attr); +# ifdef FEAT_SYN_HL + // combine 'showbreak' with 'cursorline' + if (cul_attr != 0) + char_attr = hl_combine_attr(char_attr, cul_attr); +# endif + } +# endif + } +#endif + + if (draw_state == WL_LINE - 1 && n_extra == 0) + { + draw_state = WL_LINE; + if (saved_n_extra) + { + // Continue item from end of wrapped line. + n_extra = saved_n_extra; + c_extra = saved_c_extra; + c_final = saved_c_final; + p_extra = saved_p_extra; + char_attr = saved_char_attr; + } + else + char_attr = win_attr; + } + } +#ifdef FEAT_SYN_HL + if (cul_screenline) + { + if (draw_state == WL_LINE + && vcol >= left_curline_col + && vcol < right_curline_col) + { + cul_attr = HL_ATTR(HLF_CUL); + line_attr = cul_attr; + } + else + { + cul_attr = 0; + line_attr = line_attr_save; + } + } +#endif + + // When still displaying '$' of change command, stop at cursor. + // When only displaying the (relative) line number and that's done, + // stop here. + if ((dollar_vcol >= 0 && wp == curwin + && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol +#ifdef FEAT_DIFF + && filler_todo <= 0 +#endif + ) + || (number_only && draw_state > WL_NR)) + { + screen_line(screen_row, wp->w_wincol, col, -(int)wp->w_width, + screen_line_flags); + // Pretend we have finished updating the window. Except when + // 'cursorcolumn' is set. +#ifdef FEAT_SYN_HL + if (wp->w_p_cuc) + row = wp->w_cline_row + wp->w_cline_height; + else +#endif + row = wp->w_height; + break; + } + + if (draw_state == WL_LINE && (area_highlighting +#ifdef FEAT_SPELL + || has_spell +#endif + )) + { + // handle Visual or match highlighting in this line + if (vcol == fromcol + || (has_mbyte && vcol + 1 == fromcol && n_extra == 0 + && (*mb_ptr2cells)(ptr) > 1) + || ((int)vcol_prev == fromcol_prev + && vcol_prev < vcol // not at margin + && vcol < tocol)) + area_attr = vi_attr; // start highlighting + else if (area_attr != 0 + && (vcol == tocol + || (noinvcur && (colnr_T)vcol == wp->w_virtcol))) + area_attr = 0; // stop highlighting + +#ifdef FEAT_SEARCH_EXTRA + if (!n_extra) + { + // Check for start/end of 'hlsearch' and other matches. + // After end, check for start/end of next match. + // When another match, have to check for start again. + v = (long)(ptr - line); + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, + &screen_search_hl, &has_match_conc, + &match_conc, did_line_attr, lcs_eol_one); + ptr = line + v; // "line" may have been changed + } +#endif + +#ifdef FEAT_DIFF + if (diff_hlf != (hlf_T)0) + { + if (diff_hlf == HLF_CHD && ptr - line >= change_start + && n_extra == 0) + diff_hlf = HLF_TXD; // changed text + if (diff_hlf == HLF_TXD && ptr - line > change_end + && n_extra == 0) + diff_hlf = HLF_CHD; // changed line + line_attr = HL_ATTR(diff_hlf); + if (wp->w_p_cul && lnum == wp->w_cursor.lnum + && wp->w_p_culopt_flags != CULOPT_NBR + && (!cul_screenline || (vcol >= left_curline_col + && vcol <= right_curline_col))) + line_attr = hl_combine_attr( + line_attr, HL_ATTR(HLF_CUL)); + } +#endif + +#ifdef FEAT_TEXT_PROP + if (text_props != NULL) + { + int pi; + int bcol = (int)(ptr - line); + + if (n_extra > 0) + --bcol; // still working on the previous char, e.g. Tab + + // Check if any active property ends. + for (pi = 0; pi < text_props_active; ++pi) + { + int tpi = text_prop_idxs[pi]; + + if (bcol >= text_props[tpi].tp_col - 1 + + text_props[tpi].tp_len) + { + if (pi + 1 < text_props_active) + mch_memmove(text_prop_idxs + pi, + text_prop_idxs + pi + 1, + sizeof(int) + * (text_props_active - (pi + 1))); + --text_props_active; + --pi; + } + } + + // Add any text property that starts in this column. + while (text_prop_next < text_prop_count + && bcol >= text_props[text_prop_next].tp_col - 1) + text_prop_idxs[text_props_active++] = text_prop_next++; + + text_prop_attr = 0; + text_prop_combine = FALSE; + if (text_props_active > 0) + { + // Sort the properties on priority and/or starting last. + // Then combine the attributes, highest priority last. + current_text_props = text_props; + current_buf = wp->w_buffer; + qsort((void *)text_prop_idxs, (size_t)text_props_active, + sizeof(int), text_prop_compare); + + for (pi = 0; pi < text_props_active; ++pi) + { + int tpi = text_prop_idxs[pi]; + proptype_T *pt = text_prop_type_by_id( + wp->w_buffer, text_props[tpi].tp_type); + + if (pt != NULL && pt->pt_hl_id > 0) + { + int pt_attr = syn_id2attr(pt->pt_hl_id); + + text_prop_type = pt; + text_prop_attr = + hl_combine_attr(text_prop_attr, pt_attr); + text_prop_combine = pt->pt_flags & PT_FLAG_COMBINE; + } + } + } + } +#endif + + // Decide which of the highlight attributes to use. + attr_pri = TRUE; +#ifdef LINE_ATTR + if (area_attr != 0) + char_attr = hl_combine_attr(line_attr, area_attr); + else if (search_attr != 0) + char_attr = hl_combine_attr(line_attr, search_attr); +# ifdef FEAT_TEXT_PROP + else if (text_prop_type != NULL) + { + char_attr = hl_combine_attr( + line_attr != 0 ? line_attr : win_attr, text_prop_attr); + } +# endif + else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL) + || vcol < fromcol || vcol_prev < fromcol_prev + || vcol >= tocol)) + { + // Use line_attr when not in the Visual or 'incsearch' area + // (area_attr may be 0 when "noinvcur" is set). + char_attr = line_attr; + attr_pri = FALSE; + } +#else + if (area_attr != 0) + char_attr = area_attr; + else if (search_attr != 0) + char_attr = search_attr; +#endif + else + { + attr_pri = FALSE; +#ifdef FEAT_TEXT_PROP + if (text_prop_type != NULL) + { + if (text_prop_combine) + char_attr = hl_combine_attr( + syntax_attr, text_prop_attr); + else + char_attr = hl_combine_attr( + win_attr, text_prop_attr); + } + else +#endif +#ifdef FEAT_SYN_HL + if (has_syntax) + char_attr = syntax_attr; + else +#endif + char_attr = 0; + } + } + if (char_attr == 0) + char_attr = win_attr; + + // Get the next character to put on the screen. + + // The "p_extra" points to the extra stuff that is inserted to + // represent special characters (non-printable stuff) and other + // things. When all characters are the same, c_extra is used. + // If c_final is set, it will compulsorily be used at the end. + // "p_extra" must end in a NUL to avoid mb_ptr2len() reads past + // "p_extra[n_extra]". + // For the '$' of the 'list' option, n_extra == 1, p_extra == "". + if (n_extra > 0) + { + if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) + { + c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra; + mb_c = c; // doesn't handle non-utf-8 multi-byte! + if (enc_utf8 && utf_char2len(c) > 1) + { + mb_utf8 = TRUE; + u8cc[0] = 0; + c = 0xc0; + } + else + mb_utf8 = FALSE; + } + else + { + c = *p_extra; + if (has_mbyte) + { + mb_c = c; + if (enc_utf8) + { + // If the UTF-8 character is more than one byte: + // Decode it into "mb_c". + mb_l = utfc_ptr2len(p_extra); + mb_utf8 = FALSE; + if (mb_l > n_extra) + mb_l = 1; + else if (mb_l > 1) + { + mb_c = utfc_ptr2char(p_extra, u8cc); + mb_utf8 = TRUE; + c = 0xc0; + } + } + else + { + // if this is a DBCS character, put it in "mb_c" + mb_l = MB_BYTE2LEN(c); + if (mb_l >= n_extra) + mb_l = 1; + else if (mb_l > 1) + mb_c = (c << 8) + p_extra[1]; + } + if (mb_l == 0) // at the NUL at end-of-line + mb_l = 1; + + // If a double-width char doesn't fit display a '>' in the + // last column. + if (( +# ifdef FEAT_RIGHTLEFT + wp->w_p_rl ? (col <= 0) : +# endif + (col >= wp->w_width - 1)) + && (*mb_char2cells)(mb_c) == 2) + { + c = '>'; + mb_c = c; + mb_l = 1; + mb_utf8 = FALSE; + multi_attr = HL_ATTR(HLF_AT); +#ifdef FEAT_SYN_HL + if (cul_attr) + multi_attr = hl_combine_attr(multi_attr, cul_attr); +#endif + // put the pointer back to output the double-width + // character at the start of the next line. + ++n_extra; + --p_extra; + } + else + { + n_extra -= mb_l - 1; + p_extra += mb_l - 1; + } + } + ++p_extra; + } + --n_extra; + } + else + { +#ifdef FEAT_LINEBREAK + int c0; +#endif + + if (p_extra_free != NULL) + VIM_CLEAR(p_extra_free); + // Get a character from the line itself. + c = *ptr; +#ifdef FEAT_LINEBREAK + c0 = *ptr; +#endif + if (has_mbyte) + { + mb_c = c; + if (enc_utf8) + { + // If the UTF-8 character is more than one byte: Decode it + // into "mb_c". + mb_l = utfc_ptr2len(ptr); + mb_utf8 = FALSE; + if (mb_l > 1) + { + mb_c = utfc_ptr2char(ptr, u8cc); + // Overlong encoded ASCII or ASCII with composing char + // is displayed normally, except a NUL. + if (mb_c < 0x80) + { + c = mb_c; +#ifdef FEAT_LINEBREAK + c0 = mb_c; +#endif + } + mb_utf8 = TRUE; + + // At start of the line we can have a composing char. + // Draw it as a space with a composing char. + if (utf_iscomposing(mb_c)) + { + int i; + + for (i = Screen_mco - 1; i > 0; --i) + u8cc[i] = u8cc[i - 1]; + u8cc[0] = mb_c; + mb_c = ' '; + } + } + + if ((mb_l == 1 && c >= 0x80) + || (mb_l >= 1 && mb_c == 0) + || (mb_l > 1 && (!vim_isprintc(mb_c)))) + { + // Illegal UTF-8 byte: display as . + // Non-BMP character : display as ? or fullwidth ?. + transchar_hex(extra, mb_c); +# ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) // reverse + rl_mirror(extra); +# endif + p_extra = extra; + c = *p_extra; + mb_c = mb_ptr2char_adv(&p_extra); + mb_utf8 = (c >= 0x80); + n_extra = (int)STRLEN(p_extra); + c_extra = NUL; + c_final = NUL; + if (area_attr == 0 && search_attr == 0) + { + n_attr = n_extra + 1; + extra_attr = HL_ATTR(HLF_8); + saved_attr2 = char_attr; // save current attr + } + } + else if (mb_l == 0) // at the NUL at end-of-line + mb_l = 1; +#ifdef FEAT_ARABIC + else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) + { + // Do Arabic shaping. + int pc, pc1, nc; + int pcc[MAX_MCO]; + + // The idea of what is the previous and next + // character depends on 'rightleft'. + if (wp->w_p_rl) + { + pc = prev_c; + pc1 = prev_c1; + nc = utf_ptr2char(ptr + mb_l); + prev_c1 = u8cc[0]; + } + else + { + pc = utfc_ptr2char(ptr + mb_l, pcc); + nc = prev_c; + pc1 = pcc[0]; + } + prev_c = mb_c; + + mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc); + } + else + prev_c = mb_c; +#endif + } + else // enc_dbcs + { + mb_l = MB_BYTE2LEN(c); + if (mb_l == 0) // at the NUL at end-of-line + mb_l = 1; + else if (mb_l > 1) + { + // We assume a second byte below 32 is illegal. + // Hopefully this is OK for all double-byte encodings! + if (ptr[1] >= 32) + mb_c = (c << 8) + ptr[1]; + else + { + if (ptr[1] == NUL) + { + // head byte at end of line + mb_l = 1; + transchar_nonprint(extra, c); + } + else + { + // illegal tail byte + mb_l = 2; + STRCPY(extra, "XX"); + } + p_extra = extra; + n_extra = (int)STRLEN(extra) - 1; + c_extra = NUL; + c_final = NUL; + c = *p_extra++; + if (area_attr == 0 && search_attr == 0) + { + n_attr = n_extra + 1; + extra_attr = HL_ATTR(HLF_8); + saved_attr2 = char_attr; // save current attr + } + mb_c = c; + } + } + } + // If a double-width char doesn't fit display a '>' in the + // last column; the character is displayed at the start of the + // next line. + if (( +# ifdef FEAT_RIGHTLEFT + wp->w_p_rl ? (col <= 0) : +# endif + (col >= wp->w_width - 1)) + && (*mb_char2cells)(mb_c) == 2) + { + c = '>'; + mb_c = c; + mb_utf8 = FALSE; + mb_l = 1; + multi_attr = HL_ATTR(HLF_AT); + // Put pointer back so that the character will be + // displayed at the start of the next line. + --ptr; +#ifdef FEAT_CONCEAL + did_decrement_ptr = TRUE; +#endif + } + else if (*ptr != NUL) + ptr += mb_l - 1; + + // If a double-width char doesn't fit at the left side display + // a '<' in the first column. Don't do this for unprintable + // characters. + if (n_skip > 0 && mb_l > 1 && n_extra == 0) + { + n_extra = 1; + c_extra = MB_FILLER_CHAR; + c_final = NUL; + c = ' '; + if (area_attr == 0 && search_attr == 0) + { + n_attr = n_extra + 1; + extra_attr = HL_ATTR(HLF_AT); + saved_attr2 = char_attr; // save current attr + } + mb_c = c; + mb_utf8 = FALSE; + mb_l = 1; + } + + } + ++ptr; + + if (extra_check) + { +#ifdef FEAT_SPELL + int can_spell = TRUE; +#endif + +#ifdef FEAT_TERMINAL + if (get_term_attr) + { + syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol); + + if (!attr_pri) + char_attr = syntax_attr; + else + char_attr = hl_combine_attr(syntax_attr, char_attr); + } +#endif + +#ifdef FEAT_SYN_HL + // Get syntax attribute, unless still at the start of the line + // (double-wide char that doesn't fit). + v = (long)(ptr - line); + if (has_syntax && v > 0) + { + // Get the syntax attribute for the character. If there + // is an error, disable syntax highlighting. + save_did_emsg = did_emsg; + did_emsg = FALSE; + + syntax_attr = get_syntax_attr((colnr_T)v - 1, +# ifdef FEAT_SPELL + has_spell ? &can_spell : +# endif + NULL, FALSE); + + if (did_emsg) + { + wp->w_s->b_syn_error = TRUE; + has_syntax = FALSE; + syntax_attr = 0; + } + else + did_emsg = save_did_emsg; + + // combine syntax attribute with 'wincolor' + if (win_attr != 0) + syntax_attr = hl_combine_attr(win_attr, syntax_attr); + +# ifdef SYN_TIME_LIMIT + if (wp->w_s->b_syn_slow) + has_syntax = FALSE; +# endif + + // Need to get the line again, a multi-line regexp may + // have made it invalid. + line = ml_get_buf(wp->w_buffer, lnum, FALSE); + ptr = line + v; + +# ifdef FEAT_TEXT_PROP + // Text properties overrule syntax highlighting or combine. + if (text_prop_attr == 0 || text_prop_combine) +# endif + { + int comb_attr = syntax_attr; +# ifdef FEAT_TEXT_PROP + comb_attr = hl_combine_attr(text_prop_attr, comb_attr); +# endif + if (!attr_pri) + { +#ifdef FEAT_SYN_HL + if (cul_attr) + char_attr = hl_combine_attr( + comb_attr, cul_attr); + else +#endif + if (line_attr) + char_attr = hl_combine_attr( + comb_attr, line_attr); + else + char_attr = comb_attr; + } + else + char_attr = hl_combine_attr(comb_attr, char_attr); + } +# ifdef FEAT_CONCEAL + // no concealing past the end of the line, it interferes + // with line highlighting + if (c == NUL) + syntax_flags = 0; + else + syntax_flags = get_syntax_info(&syntax_seqnr); +# endif + } +#endif + +#ifdef FEAT_SPELL + // Check spelling (unless at the end of the line). + // Only do this when there is no syntax highlighting, the + // @Spell cluster is not used or the current syntax item + // contains the @Spell cluster. + if (has_spell && v >= word_end && v > cur_checked_col) + { + spell_attr = 0; + if (c != 0 && ( +# ifdef FEAT_SYN_HL + !has_syntax || +# endif + can_spell)) +