changeset 2250:1bac28a53fae vim73

Add the conceal patch from Vince Negri.
author Bram Moolenaar <bram@vim.org>
date Sat, 05 Jun 2010 23:22:07 +0200
parents 6d3d35ff2c2b
children 646d34788036
files runtime/doc/eval.txt runtime/doc/options.txt runtime/doc/syntax.txt runtime/doc/tags runtime/doc/todo.txt runtime/doc/various.txt runtime/optwin.vim src/buffer.c src/diff.c src/edit.c src/eval.c src/ex_cmds.c src/ex_cmds.h src/ex_cmds2.c src/ex_docmd.c src/feature.h src/globals.h src/hardcopy.c src/if_python.c src/integration.c src/mbyte.c src/move.c src/normal.c src/option.c src/option.h src/proto/diff.pro src/proto/move.pro src/proto/screen.pro src/proto/spell.pro src/proto/syntax.pro src/screen.c src/search.c src/spell.c src/structs.h src/syntax.c src/ui.c src/version.c src/vim.h src/window.c src/workshop.c
diffstat 40 files changed, 1780 insertions(+), 741 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1163,6 +1163,13 @@ b:changedtick	The total number of change
 A variable name that is preceded with "w:" is local to the current window.  It
 is deleted when the window is closed.
 
+One local window variable is predefined:
+					*w:ownsyntax-variable* *ownsyntax*
+w:ownsyntax	Set to 1 if the window has an independent syntax installed
+		via the |:ownsyntax| command. The default for a window is
+		0. Syntax scripts can use this to determine whether they
+		should set b:current_syntax or w:current_syntax.
+
 						*tabpage-variable* *t:var*
 A variable name that is preceded with "t:" is local to the current tab page,
 It is deleted when the tab page is closed. {not available when compiled
@@ -5772,7 +5779,7 @@ undofile({name})					*undofile()*
 		Return the name of the undo file that would be used for a file
 		with name {name} when writing.  This uses the 'undodir'
 		option, finding directories that exist.  It does not check if
-		the undo file exist.
+		the undo file exists.
 		{name} is always expanded to the full path, since that is what
 		is used internally.
 		Useful in combination with |:wundo| and |:rundo|.
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1707,6 +1707,25 @@ A jump table for the options with a shor
 		    combination with "menu" or "menuone".
 
 
+'conceallevel' 'conc'		*'conceallevel'* *'conc'*
+			number (default 0)
+			local to window
+			{not in Vi}
+			{not available when compiled without the |+conceal|
+			feature}
+	Determine how text with the "conceal" syntax attribute is shown:
+
+	'conceallevel'	Effect
+	0		Text is shown normally
+	1		Each block of concealed text is replaced with the
+			character defined in 'listchars' (default is a dash)
+			and highlighted with the "Conceal" highlight group.
+	2		Concealed text is completely hidden unless it has a
+			custom replacement character defined (see
+			|:syn-cchar|.
+	3		Concealed text is completely hidden.
+
+
 				*'confirm'* *'cf'* *'noconfirm'* *'nocf'*
 'confirm' 'cf'		boolean (default off)
 			global
@@ -2132,6 +2151,20 @@ A jump table for the options with a shor
 	Give messages when adding a cscope database.  See |cscopeverbose|.
 	NOTE: This option is reset when 'compatible' is set.
 
+			*'cursorbind'* *'crb'* *'nocursorbind'* *'nocrb'*
+'cursorbind' 'crb'	boolean  (default off)
+			local to window
+			{not in Vi}
+			{not available when compiled without the |+cursorbind|
+			feature}
+	When this option is set, as the cursor in the current
+	window moves other cursorbound windows (windows that also have
+	this option set) move their cursors to the corresponding line and
+	column.  This option is useful for viewing the
+	differences between two versions of a file (see 'diff'); in diff mode,
+	inserted and deleted lines (though not characters within a line) are
+	taken into account. 
+
 
 			*'cursorcolumn'* *'cuc'* *'nocursorcolumn'* *'nocuc'*
 'cursorcolumn' 'cuc'	boolean	(default off)
@@ -3615,7 +3648,7 @@ A jump table for the options with a shor
 				     f:Folded,F:FoldColumn,A:DiffAdd,
 				     C:DiffChange,D:DiffDelete,T:DiffText,
 				     >:SignColumn,B:SpellBad,P:SpellCap,
-				     R:SpellRare,L:SpellLocal,
+				     R:SpellRare,L:SpellLocal,-:Conceal,
 				     +:Pmenu,=:PmenuSel,
 				     x:PmenuSbar,X:PmenuThumb")
 			global
@@ -3659,6 +3692,8 @@ A jump table for the options with a shor
 	|hl-SpellCap|	 P  word that should start with capital|spell|
 	|hl-SpellRare|	 R  rare word |spell|
 	|hl-SpellLocal|	 L  word from other region |spell|
+	|hl-Conceal|	 -  the placeholders used for concealed characters
+			    (see 'conceallevel')
 	|hl-Pmenu|       +  popup menu normal line
 	|hl-PmenuSel|    =  popup menu normal line
 	|hl-PmenuSbar|   x  popup menu scrollbar
@@ -4416,6 +4451,8 @@ A jump table for the options with a shor
 	  precedes:c	Character to show in the first column, when 'wrap'
 			is off and there is text preceding the character
 			visible in the first column.
+	  conceal:c	Character to show in place of concealed text, when
+	  		'conceallevel' is set to 1.
 	  nbsp:c	Character to show for a non-breakable space (character
 			0xA0, 160).  Left blank when omitted.
 
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -35,7 +35,8 @@ 12. Highlight command		|:highlight|
 13. Linking groups		|:highlight-link|
 14. Cleaning up			|:syn-clear|
 15. Highlighting tags		|tag-highlight|
-16. Color xterms		|xterm-color|
+16. Window-local syntax		|:ownsyntax|
+17. Color xterms		|xterm-color|
 
 {Vi does not have any of these commands}
 
@@ -3115,12 +3116,14 @@ and may be mixed with patterns.
 Not all commands accept all arguments.	This table shows which arguments
 can not be used for all commands:
 							*E395* *E396*
-		    contains  oneline	fold  display  extend ~
-:syntax keyword		 -	 -	 -	 -	 -
-:syntax match		yes	 -	yes	yes	yes
-:syntax region		yes	yes	yes	yes	yes
+		    contains  oneline	fold  display  extend concealends~
+:syntax keyword		 -	 -	 -	 -	 -      -
+:syntax match		yes	 -	yes	yes	yes     -
+:syntax region		yes	yes	yes	yes	yes    yes
 
 These arguments can be used for all three commands:
+	conceal
+	cchar
 	contained
 	containedin
 	nextgroup
@@ -3129,6 +3132,27 @@ These arguments can be used for all thre
 	skipnl
 	skipempty
 
+conceal						*conceal* *:syn-conceal*
+
+When the "conceal" argument is given, the item is marked as concealable.
+Whether or not it is actually concealed depends on the setting on the
+'conceallevel' option.
+
+concealends						*:syn-concealends*
+
+When the "concealends" argument is given, the start and end matches of
+the region, but not the contents of the region, are marked as concealable.
+Whether or not they are actually concealed depends on the setting on the
+'conceallevel' option. The ends of a region can only be concealed separately
+in this way when they have their own highlighting via "matchgroup"
+
+cchar							*:syn-cchar*
+
+The "cchar" argument defines the character shown in place of the item
+when it is concealed (setting "cchar" only makes sense when the conceal
+argument is given.) If "cchar" is not set then the default conceal
+character defined in the 'listchars' option is used. Example: >
+   :syntax match Entity "&amp;" conceal cchar=&
 
 contained						*:syn-contained*
 
@@ -3385,6 +3409,16 @@ Note that this example doesn't work for 
 "contains" arguments to make that work (omitted for simplicity of the
 example).
 
+IMPLICIT CONCEAL					*:syn-conceal-implicit*
+
+:sy[ntax] conceal [on|off]
+	This defines if the following ":syntax" commands will define keywords,
+	matches or regions with the "conceal" flag set. After ":syn conceal
+	on", all subsequent ":syn keyword", ":syn match" or ":syn region"
+	defined will have the "conceal" flag set implicitly. ":syn conceal
+	off" returns to the normal state where the "conceal" flag must be
+	given explicitly.
+
 ==============================================================================
 7. Syntax patterns				*:syn-pattern* *E401* *E402*
 
@@ -4169,6 +4203,9 @@ These are the default highlighting group
 'highlight' option default.  Note that the highlighting depends on the value
 of 'background'.  You can see the current settings with the ":highlight"
 command.
+							*hl-Conceal*
+Conceal		placeholder characters substituted for concealed
+		text (see 'conceallevel')
 							*hl-Cursor*
 Cursor		the character under the cursor
 							*hl-CursorIM*
@@ -4472,6 +4509,28 @@ And put these lines in your .vimrc: >
    autocmd BufRead,BufNewFile *.[ch] endif
 
 ==============================================================================
+16. Window-local syntax				*:ownsyntax*
+
+Normally all windows on a buffer share the same syntax settings. It is
+possible, however, to set a particular window on a file to have its own
+private syntax setting. A possible example would be to edit LaTeX source
+with conventional highlighting in one window, while seeing the same source
+highlighted differently (so as to hide control sequences and indicate bold,
+italic etc regions) in another. The 'scrollbind' option is useful here.
+
+To set the current window to have the syntax "foo", separately from all other
+windows on the buffer: >
+   :ownsyntax foo
+
+Once a window has its own syntax, syntax commands executed from other windows
+on the same buffer (including :syntax clear) have no effect. Conversely, 
+syntax commands executed from that window do not effect other windows on the
+same buffer.
+
+A window with its own syntax reverts to normal behaviour when another buffer
+is loaded into that window.
+
+==============================================================================
 16. Color xterms				*xterm-color* *color-xterm*
 
 Most color xterms have only eight colors.  If you don't get colors with the
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -134,6 +134,8 @@
 'complete'	options.txt	/*'complete'*
 'completefunc'	options.txt	/*'completefunc'*
 'completeopt'	options.txt	/*'completeopt'*
+'conc'	options.txt	/*'conc'*
+'conceallevel'	options.txt	/*'conceallevel'*
 'confirm'	options.txt	/*'confirm'*
 'consk'	options.txt	/*'consk'*
 'conskey'	options.txt	/*'conskey'*
@@ -143,6 +145,7 @@
 'cpo'	options.txt	/*'cpo'*
 'cpoptions'	options.txt	/*'cpoptions'*
 'cpt'	options.txt	/*'cpt'*
+'crb'	options.txt	/*'crb'*
 'cryptmethod'	options.txt	/*'cryptmethod'*
 'cscopepathcomp'	options.txt	/*'cscopepathcomp'*
 'cscopeprg'	options.txt	/*'cscopeprg'*
@@ -158,6 +161,7 @@
 'csverb'	options.txt	/*'csverb'*
 'cuc'	options.txt	/*'cuc'*
 'cul'	options.txt	/*'cul'*
+'cursorbind'	options.txt	/*'cursorbind'*
 'cursorcolumn'	options.txt	/*'cursorcolumn'*
 'cursorline'	options.txt	/*'cursorline'*
 'cwh'	options.txt	/*'cwh'*
@@ -482,12 +486,14 @@
 'noconskey'	options.txt	/*'noconskey'*
 'nocopyindent'	options.txt	/*'nocopyindent'*
 'nocp'	options.txt	/*'nocp'*
+'nocrb'	options.txt	/*'nocrb'*
 'nocscopetag'	options.txt	/*'nocscopetag'*
 'nocscopeverbose'	options.txt	/*'nocscopeverbose'*
 'nocst'	options.txt	/*'nocst'*
 'nocsverb'	options.txt	/*'nocsverb'*
 'nocuc'	options.txt	/*'nocuc'*
 'nocul'	options.txt	/*'nocul'*
+'nocursorbind'	options.txt	/*'nocursorbind'*
 'nocursorcolumn'	options.txt	/*'nocursorcolumn'*
 'nocursorline'	options.txt	/*'nocursorline'*
 'nodeco'	options.txt	/*'nodeco'*
@@ -1120,8 +1126,10 @@
 +cmdline_hist	various.txt	/*+cmdline_hist*
 +cmdline_info	various.txt	/*+cmdline_info*
 +comments	various.txt	/*+comments*
++conceal	various.txt	/*+conceal*
 +cryptv	various.txt	/*+cryptv*
 +cscope	various.txt	/*+cscope*
++cursorbind	various.txt	/*+cursorbind*
 +cursorshape	various.txt	/*+cursorshape*
 +debug	various.txt	/*+debug*
 +dialog_con	various.txt	/*+dialog_con*
@@ -2495,6 +2503,7 @@ 90.5	usr_90.txt	/*90.5*
 :ounmap	map.txt	/*:ounmap*
 :ounme	gui.txt	/*:ounme*
 :ounmenu	gui.txt	/*:ounmenu*
+:ownsyntax	syntax.txt	/*:ownsyntax*
 :p	various.txt	/*:p*
 :pc	windows.txt	/*:pc*
 :pclose	windows.txt	/*:pclose*
@@ -2761,8 +2770,12 @@ 90.5	usr_90.txt	/*90.5*
 :syn	syntax.txt	/*:syn*
 :syn-arguments	syntax.txt	/*:syn-arguments*
 :syn-case	syntax.txt	/*:syn-case*
+:syn-cchar	syntax.txt	/*:syn-cchar*
 :syn-clear	syntax.txt	/*:syn-clear*
 :syn-cluster	syntax.txt	/*:syn-cluster*
+:syn-conceal	syntax.txt	/*:syn-conceal*
+:syn-conceal-implicit	syntax.txt	/*:syn-conceal-implicit*
+:syn-concealends	syntax.txt	/*:syn-concealends*
 :syn-contained	syntax.txt	/*:syn-contained*
 :syn-containedin	syntax.txt	/*:syn-containedin*
 :syn-contains	syntax.txt	/*:syn-contains*
@@ -4956,6 +4969,7 @@ complete_check()	eval.txt	/*complete_che
 complex-change	change.txt	/*complex-change*
 complex-repeat	repeat.txt	/*complex-repeat*
 compress	pi_gzip.txt	/*compress*
+conceal	syntax.txt	/*conceal*
 confirm()	eval.txt	/*confirm()*
 connection-refused	message.txt	/*connection-refused*
 console-menus	gui.txt	/*console-menus*
@@ -6019,6 +6033,7 @@ hit-enter-prompt	message.txt	/*hit-enter
 hit-return	message.txt	/*hit-return*
 hitest.vim	syntax.txt	/*hitest.vim*
 hjkl	usr_02.txt	/*hjkl*
+hl-Conceal	syntax.txt	/*hl-Conceal*
 hl-Cursor	syntax.txt	/*hl-Cursor*
 hl-CursorColumn	syntax.txt	/*hl-CursorColumn*
 hl-CursorIM	syntax.txt	/*hl-CursorIM*
@@ -6895,6 +6910,7 @@ os_unix.txt	os_unix.txt	/*os_unix.txt*
 os_vms.txt	os_vms.txt	/*os_vms.txt*
 os_win32.txt	os_win32.txt	/*os_win32.txt*
 other-features	vi_diff.txt	/*other-features*
+ownsyntax	eval.txt	/*ownsyntax*
 p	change.txt	/*p*
 page-down	intro.txt	/*page-down*
 page-up	intro.txt	/*page-up*
@@ -8200,6 +8216,7 @@ vt100-cursor-keys	term.txt	/*vt100-curso
 vt100-function-keys	term.txt	/*vt100-function-keys*
 w	motion.txt	/*w*
 w32-clientserver	remote.txt	/*w32-clientserver*
+w:ownsyntax-variable	eval.txt	/*w:ownsyntax-variable*
 w:var	eval.txt	/*w:var*
 warningmsg-variable	eval.txt	/*warningmsg-variable*
 white-space	pattern.txt	/*white-space*
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1082,6 +1082,10 @@ restored. (Luc St-Louis)
 
 
 Vim 7.3:
+- Included conceal patch.
+    remove w:ownsyntax, automatically set w:current_syntax to the value of
+    b:current_syntax after loading a syntax file.
+    :ownsyntax only sets w:current_syntax.
 - using NSIS 2.46: install on Windows 7 works, but no "Edit with Vim" menu.
    Use register_shell_extension()? (George Reilly, 2010 May 26)
    Ron's version: http://dev.ronware.org/p/vim/finfo?name=gvim.nsi
@@ -1108,10 +1112,6 @@ Vim 7.3:
 - Create a helphelp.txt file, move instructions there to write help files from
   various.txt and list by Tony.
 Patches to include:
-- Include conceal patch?
-  http://vince.negri.googlepages.com/
-  http://vim.wikia.com/wiki/Patch_to_conceal_parts_of_lines
-  http://sites.google.com/site/vincenegri/conceal-ownsyntax.diff?attredirects=0
 - Patch for Lisp support with ECL (Mikael Jansson, 2008 Oct 25)
 - Minor patches from Dominique Pelle, 2010 May 15
 - Gvimext patch to support wide file names. (Szabolcs Horvat 2008 Sep 10)
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -293,8 +293,10 @@ N  *+cmdline_compl*	command line complet
 N  *+cmdline_hist*	command line history |cmdline-history|
 N  *+cmdline_info*	|'showcmd'| and |'ruler'|
 N  *+comments*		|'comments'| support
+m  *+conceal*		"conceal" support, see ||conceal|| |:syn-conceal| etc.
 N  *+cryptv*		encryption support |encryption|
 B  *+cscope*		|cscope| support
+m  *+cursorbind*	|'cursorbind'| support
 m  *+cursorshape*	|termcap-cursor-shape| support
 m  *+debug*		Compiled for debugging.
 N  *+dialog_gui*	Support for |:confirm| with GUI dialog.
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -373,6 +373,11 @@ if has("linebreak")
   call append("$", "\t(local to window)")
   call <SID>OptionL("nuw")
 endif
+if has("conceal")
+  call append("$", "conceallevel\tcontrols whether concealable elements are hidden")
+  call append("$", "\t(local to window)")
+  call <SID>OptionL("conc")
+endif
 
 
 call <SID>Header("syntax, highlighting and spelling")
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -581,7 +581,7 @@ buf_freeall(buf, del_buf, wipe_buf)
     buf->b_ml.ml_line_count = 0;    /* no lines in buffer */
     u_clearall(buf);		    /* reset all undo information */
 #ifdef FEAT_SYN_HL
-    syntax_clear(buf);		    /* reset syntax info */
+    syntax_clear(&buf->b_s);	    /* reset syntax info */
 #endif
     buf->b_flags &= ~BF_READERR;    /* a read error is no longer relevant */
 }
@@ -648,7 +648,7 @@ free_buffer_stuff(buf, free_options)
     buf->b_start_fenc = NULL;
 #endif
 #ifdef FEAT_SPELL
-    ga_clear(&buf->b_langp);
+    ga_clear(&buf->b_s.b_langp);
 #endif
 }
 
@@ -1378,6 +1378,15 @@ enter_buffer(buf)
     foldUpdateAll(curwin);	/* update folds (later). */
 #endif
 
+#ifdef FEAT_SYN_HL
+    if (curwin->w_s != &curwin->w_buffer->b_s)
+    {
+	/* Get rid of independant syntax */
+	syntax_clear(curwin->w_s);
+	vim_free(curwin->w_s);
+    }
+    curwin->w_s = &(buf->b_s);
+#endif
     /* Get the buffer in the current window. */
     curwin->w_buffer = buf;
     curbuf = buf;
@@ -1460,8 +1469,8 @@ enter_buffer(buf)
 #ifdef FEAT_SPELL
     /* May need to set the spell language.  Can only do this after the buffer
      * has been properly setup. */
-    if (!curbuf->b_help && curwin->w_p_spell && *curbuf->b_p_spl != NUL)
-	(void)did_set_spelllang(curbuf);
+    if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
+	(void)did_set_spelllang(curwin);
 #endif
 
     redraw_later(NOT_VALID);
@@ -1672,8 +1681,8 @@ buflist_new(ffname, sfname, lnum, flags)
     init_var_dict(&buf->b_vars, &buf->b_bufvar);    /* init b: variables */
 #endif
 #ifdef FEAT_SYN_HL
-    hash_init(&buf->b_keywtab);
-    hash_init(&buf->b_keywtab_ic);
+    hash_init(&buf->b_s.b_keywtab);
+    hash_init(&buf->b_s.b_keywtab_ic);
 #endif
 
     buf->b_fname = buf->b_sfname;
@@ -1772,11 +1781,11 @@ free_buf_options(buf, free_p_ff)
     clear_string_option(&buf->b_p_syn);
 #endif
 #ifdef FEAT_SPELL
-    clear_string_option(&buf->b_p_spc);
-    clear_string_option(&buf->b_p_spf);
-    vim_free(buf->b_cap_prog);
-    buf->b_cap_prog = NULL;
-    clear_string_option(&buf->b_p_spl);
+    clear_string_option(&buf->b_s.b_p_spc);
+    clear_string_option(&buf->b_s.b_p_spf);
+    vim_free(buf->b_s.b_cap_prog);
+    buf->b_s.b_cap_prog = NULL;
+    clear_string_option(&buf->b_s.b_p_spl);
 #endif
 #ifdef FEAT_SEARCHPATH
     clear_string_option(&buf->b_p_sua);
--- a/src/diff.c
+++ b/src/diff.c
@@ -1127,6 +1127,10 @@ diff_win_options(wp, addbuf)
 # endif
 
     wp->w_p_diff = TRUE;
+#ifdef FEAT_CURSORBIND
+    /* Use cursorbind if it's available */
+    wp->w_p_crb = TRUE;
+#endif
     wp->w_p_scb = TRUE;
     wp->w_p_wrap = FALSE;
 # ifdef FEAT_FOLDING
@@ -2473,6 +2477,77 @@ diff_move_to(dir, count)
     return OK;
 }
 
+#if defined(FEAT_CURSORBIND) || defined(PROTO)
+    linenr_T
+diff_get_corresponding_line(buf1, lnum1, buf2, lnum3)
+    buf_T	*buf1;
+    linenr_T	lnum1;
+    buf_T	*buf2;
+    linenr_T	lnum3;
+{
+    int		idx1;
+    int		idx2;
+    diff_T	*dp;
+    int		baseline = 0;
+    linenr_T	lnum2;
+
+    idx1 = diff_buf_idx(buf1);
+    idx2 = diff_buf_idx(buf2);
+    if (idx1 == DB_COUNT || idx2 == DB_COUNT || curtab->tp_first_diff == NULL)
+	return lnum1;
+
+    if (curtab->tp_diff_invalid)
+	ex_diffupdate(NULL);		/* update after a big change */
+
+    if (curtab->tp_first_diff == NULL)		/* no diffs today */
+	return lnum1;
+
+    for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
+    {
+	if (dp->df_lnum[idx1] > lnum1)
+	{
+	    lnum2 = lnum1 - baseline;
+	    /* don't end up past the end of the file */
+	    if (lnum2 > buf2->b_ml.ml_line_count)
+		lnum2 = buf2->b_ml.ml_line_count;
+
+	    return lnum2;
+	}
+	else if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1)
+	{
+	    /* Inside the diffblock */
+	    baseline = lnum1 - dp->df_lnum[idx1];
+	    if (baseline > dp->df_count[idx2])
+		baseline = dp->df_count[idx2];
+
+	    return dp->df_lnum[idx2] + baseline;
+	}
+	else if (   (dp->df_lnum[idx1] == lnum1)
+		 && (dp->df_count[idx1] == 0)
+		 && (dp->df_lnum[idx2] <= lnum3)
+		 && ((dp->df_lnum[idx2] + dp->df_count[idx2]) > lnum3))
+	    /*
+	     * Special case: if the cursor is just after a zero-count
+	     * block (i.e. all filler) and the target cursor is already
+	     * inside the corresponding block, leave the target cursor
+	     * unmoved. This makes repeated CTRL-W W operations work
+	     * as expected.
+	     */
+	    return lnum3;
+	baseline = (dp->df_lnum[idx1] + dp->df_count[idx1])
+				   - (dp->df_lnum[idx2] + dp->df_count[idx2]);
+    }
+
+    /* If we get here then the cursor is after the last diff */
+    lnum2 = lnum1 - baseline;
+    /* don't end up past the end of the file */
+    if (lnum2 > buf2->b_ml.ml_line_count)
+	lnum2 = buf2->b_ml.ml_line_count;
+
+    return lnum2;
+}
+#endif
+
 #if defined(FEAT_FOLDING) || defined(PROTO)
 /*
  * For line "lnum" in the current window find the equivalent lnum in window
--- a/src/edit.c
+++ b/src/edit.c
@@ -698,6 +698,10 @@ edit(cmdchar, startln, count)
 	    do_check_scrollbind(TRUE);
 #endif
 
+#ifdef FEAT_CURSORBIND
+	if (curwin->w_p_crb)
+	    do_check_cursorbind();
+#endif
 	update_curswant();
 	old_topline = curwin->w_topline;
 #ifdef FEAT_DIFF
@@ -1277,7 +1281,7 @@ doESCkey:
 	    inserted_space = FALSE;
 	    break;
 
-#if defined(FEAT_DIGRAPHS) || defined (FEAT_INS_EXPAND)
+#if defined(FEAT_DIGRAPHS) || defined(FEAT_INS_EXPAND)
 	case Ctrl_K:	    /* digraph or keyword completion */
 # ifdef FEAT_INS_EXPAND
 	    if (ctrl_x_mode == CTRL_X_DICTIONARY)
@@ -1470,7 +1474,7 @@ ins_redraw(ready)
 	     * highlighting is correct after making a change (e.g., inserting
 	     * a "(".  The autocommand may also require a redraw, so it's done
 	     * again below, unfortunately. */
-	    if (syntax_present(curbuf) && must_redraw)
+	    if (syntax_present(curwin) && must_redraw)
 		update_screen(0);
 # endif
 	    apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, FALSE, curbuf);
@@ -2960,7 +2964,7 @@ ins_compl_dictionaries(dict_start, pat, 
 		ptr = pat + 2;
 	    else
 		ptr = pat;
-	    spell_dump_compl(curbuf, ptr, regmatch.rm_ic, &dir, 0);
+	    spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0);
 	}
 	else
 # endif
@@ -9119,6 +9123,9 @@ ins_s_right()
 ins_up(startcol)
     int		startcol;	/* when TRUE move to Insstart.col */
 {
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
     pos_T	tpos;
     linenr_T	old_topline = curwin->w_topline;
 #ifdef FEAT_DIFF
@@ -9141,6 +9148,13 @@ ins_up(startcol)
 #ifdef FEAT_CINDENT
 	can_cindent = TRUE;
 #endif
+#ifdef FEAT_CONCEAL
+	if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+	{
+	    update_single_line(curwin, oldline);
+	    update_single_line(curwin, curwin->w_cursor.lnum);
+	}
+#endif
     }
     else
 	vim_beep();
@@ -9182,6 +9196,10 @@ ins_pageup()
 ins_down(startcol)
     int		startcol;	/* when TRUE move to Insstart.col */
 {
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+    linenr_T	oldbotline = curwin->w_botline;
+#endif
     pos_T	tpos;
     linenr_T	old_topline = curwin->w_topline;
 #ifdef FEAT_DIFF
@@ -9204,6 +9222,16 @@ ins_down(startcol)
 #ifdef FEAT_CINDENT
 	can_cindent = TRUE;
 #endif
+#ifdef FEAT_CONCEAL
+	if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+	{
+	    update_single_line(curwin, oldline);
+	    /* Don't do this if we've scrolled, the line is already
+	     * drawn */
+	    if (oldbotline == curwin->w_botline)
+		update_single_line(curwin, curwin->w_cursor.lnum);
+	}
+#endif
     }
     else
 	vim_beep();
--- a/src/eval.c
+++ b/src/eval.c
@@ -3884,6 +3884,11 @@ get_user_var_name(xp, idx)
 	    ++hi;
 	return cat_prefix_varname('w', hi->hi_key);
     }
+    if (wdone == ht->ht_used)
+    {
+	++wdone;
+	return (char_u *)"w:ownsyntax";
+    }
 
 #ifdef FEAT_WINDOWS
     /* t: variables */
@@ -9389,6 +9394,9 @@ f_cursor(argvars, rettv)
     typval_T	*rettv;
 {
     long	line, col;
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 #ifdef FEAT_VIRTUALEDIT
     long	coladd = 0;
 #endif
@@ -9438,6 +9446,13 @@ f_cursor(argvars, rettv)
 #endif
 
     curwin->w_set_curswant = TRUE;
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
     rettv->vval.v_number = 0;
 }
 
@@ -11722,12 +11737,18 @@ f_has(argvars, rettv)
 #ifdef FEAT_COMMENTS
 	"comments",
 #endif
+#ifdef FEAT_CONCEAL
+	"conceal",
+#endif
 #ifdef FEAT_CRYPT
 	"cryptv",
 #endif
 #ifdef FEAT_CSCOPE
 	"cscope",
 #endif
+#ifdef FEAT_CURSORBIND
+	"cursorbind",
+#endif
 #ifdef CURSOR_SHAPE
 	"cursorshape",
 #endif
@@ -12138,7 +12159,7 @@ f_has(argvars, rettv)
 #endif
 #ifdef FEAT_SYN_HL
 	else if (STRICMP(name, "syntax_items") == 0)
-	    n = syntax_present(curbuf);
+	    n = syntax_present(curwin);
 #endif
 #if defined(WIN3264)
 	else if (STRICMP(name, "win95") == 0)
@@ -15103,6 +15124,15 @@ search_cmn(argvars, match_pos, flagsp)
     /* If 'n' flag is used: restore cursor position. */
     if (flags & SP_NOMOVE)
 	curwin->w_cursor = save_cursor;
+#ifdef FEAT_CONCEAL
+	else if (curwin->w_p_conceal
+				 && save_cursor.lnum != curwin->w_cursor.lnum)
+	{
+	    curwin->w_set_curswant = TRUE;
+	    update_single_line(curwin, save_cursor.lnum);
+	    update_single_line(curwin, curwin->w_cursor.lnum);
+	}
+#endif
     else
 	curwin->w_set_curswant = TRUE;
 theend:
@@ -16329,7 +16359,7 @@ f_spellbadword(argvars, rettv)
 	if (len != 0)
 	    word = ml_get_cursor();
     }
-    else if (curwin->w_p_spell && *curbuf->b_p_spl != NUL)
+    else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL)
     {
 	char_u	*str = get_tv_string_chk(&argvars[0]);
 	int	capcol = -1;
@@ -16382,7 +16412,7 @@ f_spellsuggest(argvars, rettv)
 	return;
 
 #ifdef FEAT_SPELL
-    if (curwin->w_p_spell && *curbuf->b_p_spl != NUL)
+    if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
     {
 	str = get_tv_string(&argvars[0]);
 	if (argvars[1].v_type != VAR_UNKNOWN)
@@ -18728,6 +18758,18 @@ get_var_tv(name, len, rettv, verbose)
 	tv = &atv;
     }
 
+    if (STRCMP(name, "w:ownsyntax") == 0)
+    {
+	atv.v_type = VAR_NUMBER;
+#ifdef FEAT_SYN_HL
+	atv.vval.v_number = (curwin->w_s != &curwin->w_buffer->b_s) ? 1 : 0;
+#else
+	atv.vval.v_number = 0;
+#endif
+	tv = &atv;
+    }
+
+
     /*
      * Check for user-defined variables.
      */
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3411,6 +3411,14 @@ do_ecmd(fnum, ffname, sfname, eap, newln
 		else
 #endif
 		{
+#ifdef FEAT_SYN_HL
+		    /*
+		     * <VN> We could instead free the synblock
+		     * and re-attach to buffer, perhaps.
+		     */
+		    if (curwin->w_s == &(curwin->w_buffer->b_s))
+			    curwin->w_s = &(buf->b_s);
+#endif
 		    curwin->w_buffer = buf;
 		    curbuf = buf;
 		    ++curbuf->b_nwindows;
@@ -3717,8 +3725,8 @@ do_ecmd(fnum, ffname, sfname, eap, newln
 #ifdef FEAT_SPELL
     /* If the window options were changed may need to set the spell language.
      * Can only do this after the buffer has been properly setup. */
-    if (did_get_winopts && curwin->w_p_spell && *curbuf->b_p_spl != NUL)
-	(void)did_set_spelllang(curbuf);
+    if (did_get_winopts && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
+	(void)did_set_spelllang(curwin);
 #endif
 
     if (command == NULL)
@@ -5963,7 +5971,7 @@ fix_help_buffer()
     set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL);
 
 #ifdef FEAT_SYN_HL
-    if (!syntax_present(curbuf))
+    if (!syntax_present(curwin))
 #endif
     {
 	for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -677,6 +677,8 @@ EX(CMD_ounmap,		"ounmap",	ex_unmap,
 			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN),
 EX(CMD_ounmenu,		"ounmenu",	ex_menu,
 			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN),
+EX(CMD_ownsyntax,	"ownsyntax",	ex_ownsyntax,
+			EXTRA|NOTRLCOM|SBOXOK|CMDWIN),
 EX(CMD_print,		"print",	ex_print,
 			RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|SBOXOK),
 EX(CMD_pclose,		"pclose",	ex_pclose,
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -2824,7 +2824,7 @@ struct source_cookie
     FILE	*fp;		/* opened file for sourcing */
     char_u      *nextline;      /* if not NULL: line that was read ahead */
     int		finished;	/* ":finish" used */
-#if defined (USE_CRNL) || defined (USE_CR)
+#if defined(USE_CRNL) || defined(USE_CR)
     int		fileformat;	/* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
     int		error;		/* TRUE if LF found after CR-LF */
 #endif
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -235,6 +235,7 @@ static void	ex_popup __ARGS((exarg_T *ea
 #endif
 #ifndef FEAT_SYN_HL
 # define ex_syntax		ex_ni
+# define ex_ownsyntax		ex_ni
 #endif
 #ifndef FEAT_SPELL
 # define ex_spell		ex_ni
--- a/src/feature.h
+++ b/src/feature.h
@@ -535,10 +535,17 @@
 #endif
 
 /*
+ * +conceal		'conceal' option.  Needs syntax highlighting
+ *			as this is how the concealed text is defined.
+ */
+#if defined(FEAT_BIG) && defined(FEAT_SYN_HL)
+# define FEAT_CONCEAL
+#endif
+
+/*
  * +spell		spell checking
  *
- * Disabled for EBCDIC:
- * Doesn't work (SIGSEGV). 
+ * Disabled for EBCDIC: * Doesn't work (SIGSEGV).
  */
 #if (defined(FEAT_NORMAL) || defined(PROTO)) && !defined(EBCDIC)
 # define FEAT_SPELL
@@ -730,6 +737,13 @@
 #endif
 
 /*
+ * +cursorbind		synchronization of split windows
+ */
+#if defined(FEAT_NORMAL) && defined(FEAT_WINDOWS)
+# define FEAT_CURSORBIND
+#endif
+
+/*
  * +menu		":menu" command
  */
 #ifdef FEAT_NORMAL
@@ -770,7 +784,8 @@
     && (defined(FEAT_GUI_GTK) \
 	|| (defined(FEAT_GUI_MOTIF) && defined(HAVE_XM_NOTEBOOK_H)) \
 	|| defined(FEAT_GUI_MAC) \
-	|| (defined(FEAT_GUI_MSWIN) && (!defined(_MSC_VER) || _MSC_VER > 1020)))
+	|| (defined(FEAT_GUI_MSWIN) && !defined(WIN16) \
+	    && (!defined(_MSC_VER) || _MSC_VER > 1020)))
 # define FEAT_GUI_TABLINE
 #endif
 
--- a/src/globals.h
+++ b/src/globals.h
@@ -1151,6 +1151,9 @@ EXTERN int	lcs_nbsp INIT(= NUL);
 EXTERN int	lcs_tab1 INIT(= NUL);
 EXTERN int	lcs_tab2 INIT(= NUL);
 EXTERN int	lcs_trail INIT(= NUL);
+#ifdef FEAT_CONCEAL
+EXTERN int	lcs_conceal INIT(= '-');
+#endif
 
 #if defined(FEAT_WINDOWS) || defined(FEAT_WILDMENU) || defined(FEAT_STL_OPT) \
 	|| defined(FEAT_FOLDING)
@@ -1412,7 +1415,7 @@ EXTERN char_u e_invexpr2[]	INIT(= N_("E1
 #endif
 EXTERN char_u e_invrange[]	INIT(= N_("E16: Invalid range"));
 EXTERN char_u e_invcmd[]	INIT(= N_("E476: Invalid command"));
-#if defined(UNIX) || defined(FEAT_SYN_HL)
+#if defined(UNIX) || defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
 EXTERN char_u e_isadir2[]	INIT(= N_("E17: \"%s\" is a directory"));
 #endif
 #ifdef FEAT_LIBCALL
--- a/src/hardcopy.c
+++ b/src/hardcopy.c
@@ -616,7 +616,7 @@ ex_hardcopy(eap)
 	else
 	    settings.modec = 't';
 
-    if (!syntax_present(curbuf))
+    if (!syntax_present(curwin))
 	settings.do_syntax = FALSE;
     else if (printer_opts[OPT_PRINT_SYNTAX].present
 	    && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
--- a/src/if_python.c
+++ b/src/if_python.c
@@ -26,7 +26,7 @@
 # undef _POSIX_THREADS
 #endif
 
-#if defined(_WIN32) && defined (HAVE_FCNTL_H)
+#if defined(_WIN32) && defined(HAVE_FCNTL_H)
 # undef HAVE_FCNTL_H
 #endif
 
--- a/src/integration.c
+++ b/src/integration.c
@@ -86,6 +86,7 @@ static void process_menuItem(char *);
 static void process_toolbarButton(char *);
 static void workshop_set_option_first(char *name, char *value);
 
+static size_t dummy;  /* to ignore return value of write() */
 
 #define CMDBUFSIZ	2048
 
@@ -183,7 +184,7 @@ messageFromEserve(XtPointer clientData U
 			ackNum = atoi(&cmd[4]);
 			vim_snprintf(buf, sizeof(buf),
 					       NOCATGETS("ack %d\n"), ackNum);
-			(void)write(sd, buf, strlen(buf));
+			dummy = write(sd, buf, strlen(buf));
 		} else if (strncmp(cmd,
 		    NOCATGETS("addMarkType "), 12) == 0) {
 			int idx;
@@ -280,7 +281,7 @@ messageFromEserve(XtPointer clientData U
 			vim_snprintf(buf, sizeof(buf),
 					     NOCATGETS("markLine %s %d %d\n"),
 			    file, markid, line);
-			(void)write(sd, buf, strlen(buf));
+			dummy = write(sd, buf, strlen(buf));
 		} else if (cmd[1] == 'o' && cmd[4] == 'L' &&
 		    strncmp(cmd, NOCATGETS("gotoLine "), 9) == 0) {
 			char *file;
@@ -729,10 +730,10 @@ void	workshop_connect(XtAppContext conte
 		workshop_get_editor_name(),
 		PROTOCOL_VERSION,
 		workshop_get_editor_version());
-	(void)write(sd, buf, strlen(buf));
+	dummy = write(sd, buf, strlen(buf));
 
 	vim_snprintf(buf, sizeof(buf), NOCATGETS("ack 1\n"));
-	(void)write(sd, buf, strlen(buf));
+	dummy = write(sd, buf, strlen(buf));
 }
 
 void	workshop_disconnect()
@@ -1059,7 +1060,7 @@ void workshop_file_closed(char *filename
 	char buffer[2*MAXPATHLEN];
 	vim_snprintf(buffer, sizeof(buffer),
 			NOCATGETS("deletedFile %s\n"), filename);
-	(void)write(sd, buffer, strlen(buffer));
+	dummy = write(sd, buffer, strlen(buffer));
 }
 #endif
 
@@ -1068,7 +1069,7 @@ void workshop_file_closed_lineno(char *f
 	char buffer[2*MAXPATHLEN];
 	vim_snprintf(buffer, sizeof(buffer),
 			NOCATGETS("deletedFile %s %d\n"), filename, lineno);
-	(void)write(sd, buffer, strlen(buffer));
+	dummy = write(sd, buffer, strlen(buffer));
 }
 
 void workshop_file_opened(char *filename, int readOnly)
@@ -1076,7 +1077,7 @@ void workshop_file_opened(char *filename
 	char buffer[2*MAXPATHLEN];
 	vim_snprintf(buffer, sizeof(buffer),
 			NOCATGETS("loadedFile %s %d\n"), filename, readOnly);
-	(void)write(sd, buffer, strlen(buffer));
+	dummy = write(sd, buffer, strlen(buffer));
 }
 
 
@@ -1085,7 +1086,7 @@ void workshop_file_saved(char *filename)
 	char buffer[2*MAXPATHLEN];
 	vim_snprintf(buffer, sizeof(buffer),
 			NOCATGETS("savedFile %s\n"), filename);
-	(void)write(sd, buffer, strlen(buffer));
+	dummy = write(sd, buffer, strlen(buffer));
 
 	/* Let editor report any moved marks that the eserve client
 	 * should deal with (for example, moving location-based breakpoints) */
@@ -1098,7 +1099,7 @@ void workshop_file_modified(char *filena
 	char buffer[2*MAXPATHLEN];
 	vim_snprintf(buffer, sizeof(buffer),
 			NOCATGETS("modifiedFile %s\n"), filename);
-	(void)write(sd, buffer, strlen(buffer));
+	dummy = write(sd, buffer, strlen(buffer));
 }
 
 void workshop_move_mark(char *filename, int markId, int newLineno)
@@ -1106,7 +1107,7 @@ void workshop_move_mark(char *filename, 
 	char buffer[2*MAXPATHLEN];
 	vim_snprintf(buffer, sizeof(buffer),
 	       NOCATGETS("moveMark %s %d %d\n"), filename, markId, newLineno);
-	(void)write(sd, buffer, strlen(buffer));
+	dummy = write(sd, buffer, strlen(buffer));
 }
 #endif
 
@@ -1119,7 +1120,7 @@ void workshop_frame_moved(int new_x, int
 		vim_snprintf(buffer, sizeof(buffer),
 				NOCATGETS("frameAt %d %d %d %d\n"),
 				new_x, new_y, new_w, new_h);
-		(void)write(sd, buffer, strlen(buffer));
+		dummy = write(sd, buffer, strlen(buffer));
 	}
 }
 
@@ -1179,7 +1180,7 @@ void workshop_perform_verb(char *verb, v
 			selEndLine, selEndCol,
 			selLength,
 			selection);
-		(void)write(sd, buf, strlen(buf));
+		dummy = write(sd, buf, strlen(buf));
 		if (*selection) {
 			free(selection);
 		}
@@ -1190,7 +1191,7 @@ void workshop_perform_verb(char *verb, v
 #if defined(NOHANDS_SUPPORT_FUNCTIONS) || defined(FEAT_BEVAL)
 void workshop_send_message(char *buf)
 {
-	(void)write(sd, buf, strlen(buf));
+	dummy = write(sd, buf, strlen(buf));
 }
 #endif
 
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -5222,7 +5222,7 @@ im_set_active(active)
     /* If 'imdisable' is set, XIM is never active. */
     if (p_imdisable)
 	active = FALSE;
-#if !defined (FEAT_GUI_GTK)
+#if !defined(FEAT_GUI_GTK)
     else if (input_style & XIMPreeditPosition)
 	/* There is a problem in switching XIM off when preediting is used,
 	 * and it is not clear how this can be solved.  For now, keep XIM on
--- a/src/move.c
+++ b/src/move.c
@@ -2884,3 +2884,68 @@ halfpage(flag, Prenum)
     beginline(BL_SOL | BL_FIX);
     redraw_later(VALID);
 }
+
+#if defined(FEAT_CURSORBIND) || defined(PROTO)
+    void
+do_check_cursorbind()
+{
+    linenr_T	line = curwin->w_cursor.lnum;
+    colnr_T	col =  curwin->w_cursor.col;
+    win_T	*old_curwin = curwin;
+    buf_T	*old_curbuf = curbuf;
+# ifdef FEAT_VISUAL
+    int		old_VIsual_select = VIsual_select;
+    int		old_VIsual_active = VIsual_active;
+# endif
+
+    /*
+     * loop through the cursorbound windows
+     */
+# ifdef FEAT_VISUAL
+    VIsual_select = VIsual_active = 0;
+# endif
+    for (curwin = firstwin; curwin; curwin = curwin->w_next)
+    {
+	curbuf = curwin->w_buffer;
+	/* skip original window  and windows with 'noscrollbind' */
+	if (curwin != old_curwin && curwin->w_p_crb)
+	{
+# ifdef FEAT_DIFF
+	    if (curwin->w_p_diff)
+		curwin->w_cursor.lnum
+			= diff_get_corresponding_line(old_curbuf,
+						      line,
+						      curbuf,
+						      curwin->w_cursor.lnum);
+	    else
+# endif
+		curwin->w_cursor.lnum = line;
+	    curwin->w_cursor.col = col;
+
+	    /* Make sure the cursor is in a valid position. */
+	    check_cursor();
+# ifdef FEAT_MBYTE
+	    /* Correct cursor for multi-byte character. */
+	    if (has_mbyte)
+		mb_adjust_cursor();
+# endif
+
+	    redraw_later(VALID);
+	    update_topline();
+# ifdef FEAT_WINDOWS
+	    curwin->w_redr_status = TRUE;
+# endif
+	}
+    }
+
+    /*
+     * reset current-window
+     */
+# ifdef FEAT_VISUAL
+    VIsual_select = old_VIsual_select;
+    VIsual_active = old_VIsual_active;
+# endif
+    curwin = old_curwin;
+    curbuf = old_curbuf;
+}
+#endif /* FEAT_CURSORBIND */
--- a/src/normal.c
+++ b/src/normal.c
@@ -1335,6 +1335,14 @@ normal_end:
     }
 #endif
 
+#ifdef FEAT_CURSORBIND
+    if (curwin->w_p_crb && toplevel)
+    {
+	validate_cursor();	/* may need to update w_leftcol */
+	do_check_cursorbind();
+    }
+#endif
+
     /*
      * May restart edit(), if we got here with CTRL-O in Insert mode (but not
      * if still inside a mapping that started in Visual mode).
@@ -2290,6 +2298,9 @@ do_mouse(oap, c, dir, count, fixindent)
     int		old_mode = VIsual_mode;
 #endif
     int		regname;
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 
 #if defined(FEAT_FOLDING)
     save_cursor = curwin->w_cursor;
@@ -2762,6 +2773,14 @@ do_mouse(oap, c, dir, count, fixindent)
 	    curwin->w_cursor = save_cursor;
     }
 #endif
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && moved
+		&& (old_curwin != curwin || oldline != curwin->w_cursor.lnum))
+    {
+	update_single_line(old_curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 
 #if defined(FEAT_CLIPBOARD) && defined(FEAT_CMDWIN)
     if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
@@ -5302,7 +5321,7 @@ nv_clear(cap)
 #endif
 #ifdef FEAT_SYN_HL
 	/* Clear all syntax states to force resyncing. */
-	syn_stack_free_all(curbuf);
+	syn_stack_free_all(curwin->w_s);
 #endif
 	redraw_later(CLEAR);
     }
@@ -5694,6 +5713,9 @@ nv_scroll(cap)
     linenr_T	lnum;
 #endif
     int		half;
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 
     cap->oap->motion_type = MLINE;
     setpcmark();
@@ -5781,6 +5803,13 @@ nv_scroll(cap)
 
     cursor_correct();	/* correct for 'so' */
     beginline(BL_SOL | BL_FIX);
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 }
 
 /*
@@ -5796,6 +5825,9 @@ nv_right(cap)
 #else
 # define PAST_LINE 0
 #endif
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 
     if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
     {
@@ -5900,6 +5932,13 @@ nv_right(cap)
 					       && cap->oap->op_type == OP_NOP)
 	foldOpenCursor();
 #endif
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 }
 
 /*
@@ -5912,6 +5951,9 @@ nv_left(cap)
     cmdarg_T	*cap;
 {
     long	n;
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 
     if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
     {
@@ -5970,6 +6012,13 @@ nv_left(cap)
 					       && cap->oap->op_type == OP_NOP)
 	foldOpenCursor();
 #endif
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 }
 
 /*
@@ -5988,11 +6037,21 @@ nv_up(cap)
     }
     else
     {
+#ifdef FEAT_CONCEAL
+	linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 	cap->oap->motion_type = MLINE;
 	if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
 	    clearopbeep(cap->oap);
 	else if (cap->arg)
 	    beginline(BL_WHITE | BL_FIX);
+#ifdef FEAT_CONCEAL
+	if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+	{
+	    update_single_line(curwin, oldline);
+	    update_single_line(curwin, curwin->w_cursor.lnum);
+	}
+#endif
     }
 }
 
@@ -6021,6 +6080,10 @@ nv_down(cap)
     else
 #endif
     {
+#ifdef FEAT_CONCEAL
+	linenr_T	oldline = curwin->w_cursor.lnum;
+	linenr_T	oldbotline = curwin->w_botline;
+#endif
 #ifdef FEAT_CMDWIN
 	/* In the cmdline window a <CR> executes the command. */
 	if (cmdwin_type != 0 && cap->cmdchar == CAR)
@@ -6033,6 +6096,16 @@ nv_down(cap)
 		clearopbeep(cap->oap);
 	    else if (cap->arg)
 		beginline(BL_WHITE | BL_FIX);
+#ifdef FEAT_CONCEAL
+	    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+	    {
+		update_single_line(curwin, oldline);
+		/* Don't do this if we've scrolled, the line is already
+		 * drawn */
+		if (oldbotline == curwin->w_botline)
+		    update_single_line(curwin, curwin->w_cursor.lnum);
+	    }
+#endif
 	}
     }
 }
@@ -8197,6 +8270,10 @@ nv_g_cmd(cap)
 n_opencmd(cap)
     cmdarg_T	*cap;
 {
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
+
     if (!checkclearopq(cap->oap))
     {
 #ifdef FEAT_FOLDING
@@ -8220,6 +8297,10 @@ n_opencmd(cap)
 #endif
 		    0, 0))
 	{
+#ifdef FEAT_CONCEAL
+	    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+		update_single_line(curwin, oldline);
+#endif
 	    /* When '#' is in 'cpoptions' ignore the count. */
 	    if (vim_strchr(p_cpo, CPO_HASH) != NULL)
 		cap->count1 = 1;
@@ -8424,6 +8505,9 @@ nv_pipe(cap)
 nv_bck_word(cap)
     cmdarg_T	*cap;
 {
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
     cap->oap->motion_type = MCHAR;
     cap->oap->inclusive = FALSE;
     curwin->w_set_curswant = TRUE;
@@ -8433,6 +8517,13 @@ nv_bck_word(cap)
     else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
 	foldOpenCursor();
 #endif
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 }
 
 /*
@@ -8447,6 +8538,9 @@ nv_wordcmd(cap)
     int		word_end;
     int		flag = FALSE;
     pos_T	startpos = curwin->w_cursor;
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = startpos.lnum;
+#endif
 
     /*
      * Set inclusive for the "E" and "e" command.
@@ -8524,6 +8618,13 @@ nv_wordcmd(cap)
 	    foldOpenCursor();
 #endif
     }
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 }
 
 /*
@@ -8686,6 +8787,10 @@ nv_goto(cap)
     if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP)
 	foldOpenCursor();
 #endif
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal)
+	changed_window_setting();
+#endif
 }
 
 /*
@@ -9111,6 +9216,9 @@ nv_put(cap)
     int		empty = FALSE;
     int		was_visual = FALSE;
 #endif
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
     int		dir;
     int		flags = 0;
 
@@ -9230,6 +9338,13 @@ nv_put(cap)
 	}
 #endif
 	auto_format(FALSE, TRUE);
+#ifdef FEAT_CONCEAL
+	if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+	{
+	    update_single_line(curwin, oldline);
+	    update_single_line(curwin, curwin->w_cursor.lnum);
+	}
+#endif
     }
 }
 
--- a/src/option.c
+++ b/src/option.c
@@ -243,7 +243,12 @@
 # define PV_WFW		OPT_WIN(WV_WFW)
 #endif
 #define PV_WRAP		OPT_WIN(WV_WRAP)
-
+#ifdef FEAT_CURSORBIND
+# define PV_CRBIND	OPT_WIN(WV_CRBIND)
+#endif
+#ifdef FEAT_CONCEAL
+# define PV_CONCEAL	OPT_WIN(WV_CONCEAL)
+#endif
 
 /* WV_ and BV_ values get typecasted to this for the "indir" field */
 typedef enum
@@ -460,8 +465,8 @@ struct vimoption
  * possible when compiling with few features. */
 #if defined(FEAT_DIFF) || defined(FEAT_FOLDING) || defined(FEAT_SPELL) \
 	|| defined(FEAT_VERTSPLIT) || defined(FEAT_CLIPBOARD) \
-	|| defined(FEAT_INS_EXPAND) || defined(FEAT_SYN_HL)
-# define HIGHLIGHT_INIT "8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine"
+	|| defined(FEAT_INS_EXPAND) || defined(FEAT_SYN_HL) || defined(FEAT_CONCEAL)
+# define HIGHLIGHT_INIT "8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine"
 #else
 # define HIGHLIGHT_INIT "8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,r:Question,s:StatusLine,S:StatusLineNC,t:Title,v:Visual,w:WarningMsg,W:WildMenu,>:SignColumn,*:TabLine,#:TabLineSel,_:TabLineFill"
 #endif
@@ -805,6 +810,14 @@ static struct vimoption
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCRIPTID_INIT},
+    {"conceallevel","conc", P_NUM|P_RWIN|P_VI_DEF,
+#ifdef FEAT_CONCEAL
+			    (char_u *)VAR_WIN, PV_CONCEAL,
+#else
+			    (char_u *)NULL, PV_NONE,
+#endif
+			    {(char_u *)0L, (char_u *)0L}
+			    SCRIPTID_INIT},
     {"completefunc", "cfu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE,
 #ifdef FEAT_COMPL_FUNC
 			    (char_u *)&p_cfu, PV_CFU,
@@ -897,6 +910,13 @@ static struct vimoption
 			    (char_u *)NULL, PV_NONE,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT},
+    {"cursorbind",  "crb",  P_BOOL|P_VI_DEF,
+#ifdef FEAT_CURSORBIND
+			    (char_u *)VAR_WIN, PV_CRBIND,
+#else
+			    (char_u *)NULL, PV_NONE,
+#endif
+			    {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
     {"cursorcolumn", "cuc", P_BOOL|P_VI_DEF|P_RWIN,
 #ifdef FEAT_SYN_HL
 			    (char_u *)VAR_WIN, PV_CUC,
@@ -2959,7 +2979,7 @@ static char_u *set_chars_option __ARGS((
 static char_u *check_clipboard_option __ARGS((void));
 #endif
 #ifdef FEAT_SPELL
-static char_u *compile_cap_prog __ARGS((buf_T *buf));
+static char_u *compile_cap_prog __ARGS((synblock_T *synblock));
 #endif
 #ifdef FEAT_EVAL
 static void set_option_scriptID_idx __ARGS((int opt_idx, int opt_flags, int id));
@@ -5127,7 +5147,7 @@ didset_options()
 #ifdef FEAT_SPELL
     (void)spell_check_msm();
     (void)spell_check_sps();
-    (void)compile_cap_prog(curbuf);
+    (void)compile_cap_prog(curwin->w_s);
 #endif
 #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32)
     (void)opt_strings_flags(p_toolbar, p_toolbar_values, &toolbar_flags, TRUE);
@@ -5208,9 +5228,9 @@ check_buf_options(buf)
     check_string_option(&buf->b_p_syn);
 #endif
 #ifdef FEAT_SPELL
-    check_string_option(&buf->b_p_spc);
-    check_string_option(&buf->b_p_spf);
-    check_string_option(&buf->b_p_spl);
+    check_string_option(&buf->b_s.b_p_spc);
+    check_string_option(&buf->b_s.b_p_spf);
+    check_string_option(&buf->b_s.b_p_spl);
 #endif
 #ifdef FEAT_SEARCHPATH
     check_string_option(&buf->b_p_sua);
@@ -6407,15 +6427,15 @@ did_set_string_option(opt_idx, varp, new
 #ifdef FEAT_SPELL
     /* When 'spelllang' or 'spellfile' is set and there is a window for this
      * buffer in which 'spell' is set load the wordlists. */
-    else if (varp == &(curbuf->b_p_spl) || varp == &(curbuf->b_p_spf))
+    else if (varp == &(curbuf->b_s.b_p_spl) || varp == &(curbuf->b_s.b_p_spf))
     {
 	win_T	    *wp;
 	int	    l;
 
-	if (varp == &(curbuf->b_p_spf))
-	{
-	    l = (int)STRLEN(curbuf->b_p_spf);
-	    if (l > 0 && (l < 4 || STRCMP(curbuf->b_p_spf + l - 4,
+	if (varp == &(curbuf->b_s.b_p_spf))
+	{
+	    l = (int)STRLEN(curbuf->b_s.b_p_spf);
+	    if (l > 0 && (l < 4 || STRCMP(curbuf->b_s.b_p_spf + l - 4,
 								".add") != 0))
 		errmsg = e_invarg;
 	}
@@ -6425,7 +6445,7 @@ did_set_string_option(opt_idx, varp, new
 	    FOR_ALL_WINDOWS(wp)
 		if (wp->w_buffer == curbuf && wp->w_p_spell)
 		{
-		    errmsg = did_set_spelllang(curbuf);
+		    errmsg = did_set_spelllang(wp);
 # ifdef FEAT_WINDOWS
 		    break;
 # endif
@@ -6433,9 +6453,9 @@ did_set_string_option(opt_idx, varp, new
 	}
     }
     /* When 'spellcapcheck' is set compile the regexp program. */
-    else if (varp == &(curbuf->b_p_spc))
-    {
-	errmsg = compile_cap_prog(curbuf);
+    else if (varp == &(curwin->w_s->b_p_spc))
+    {
+	errmsg = compile_cap_prog(curwin->w_s);
     }
     /* 'spellsuggest' */
     else if (varp == &p_sps)
@@ -6843,7 +6863,7 @@ did_set_string_option(opt_idx, varp, new
 	}
 #endif
 #ifdef FEAT_SPELL
-	if (varp == &(curbuf->b_p_spl))
+	if (varp == &(curwin->w_s->b_p_spl))
 	{
 	    char_u	fname[200];
 
@@ -6853,11 +6873,11 @@ did_set_string_option(opt_idx, varp, new
 	     * Use the first name in 'spelllang' up to '_region' or
 	     * '.encoding'.
 	     */
-	    for (p = curbuf->b_p_spl; *p != NUL; ++p)
+	    for (p = curwin->w_s->b_p_spl; *p != NUL; ++p)
 		if (vim_strchr((char_u *)"_.,", *p) != NULL)
 		    break;
 	    vim_snprintf((char *)fname, 200, "spell/%.*s.vim",
-				 (int)(p - curbuf->b_p_spl), curbuf->b_p_spl);
+				 (int)(p - curwin->w_s->b_p_spl), curwin->w_s->b_p_spl);
 	    source_runtime(fname, TRUE);
 	}
 #endif
@@ -6920,6 +6940,11 @@ set_chars_option(varp)
 	{&lcs_prec,	"precedes"},
 	{&lcs_tab2,	"tab"},
 	{&lcs_trail,	"trail"},
+#ifdef FEAT_CONCEAL
+	{&lcs_conceal,	"conceal"},
+#else
+	{NULL,		"conceal"},
+#endif
     };
     struct charstab *tab;
 
@@ -6941,12 +6966,13 @@ set_chars_option(varp)
     /* first round: check for valid value, second round: assign values */
     for (round = 0; round <= 1; ++round)
     {
-	if (round)
+	if (round > 0)
 	{
 	    /* After checking that the value is valid: set defaults: space for
 	     * 'fillchars', NUL for 'listchars' */
 	    for (i = 0; i < entries; ++i)
-		*(tab[i].cp) = (varp == &p_lcs ? NUL : ' ');
+		if (tab[i].cp != NULL)
+		    *(tab[i].cp) = (varp == &p_lcs ? NUL : ' ');
 	    if (varp == &p_lcs)
 		lcs_tab1 = NUL;
 #if defined(FEAT_WINDOWS) || defined(FEAT_FOLDING)
@@ -6993,7 +7019,7 @@ set_chars_option(varp)
 				lcs_tab1 = c1;
 				lcs_tab2 = c2;
 			    }
-			    else
+			    else if (tab[i].cp != NULL)
 				*(tab[i].cp) = c1;
 
 			}
@@ -7170,24 +7196,24 @@ check_clipboard_option()
  * Return error message when failed, NULL when OK.
  */
     static char_u *
-compile_cap_prog(buf)
-    buf_T	*buf;
-{
-    regprog_T   *rp = buf->b_cap_prog;
+compile_cap_prog(synblock)
+    synblock_T *synblock;
+{
+    regprog_T   *rp = synblock->b_cap_prog;
     char_u	*re;
 
-    if (*buf->b_p_spc == NUL)
-	buf->b_cap_prog = NULL;
+    if (*synblock->b_p_spc == NUL)
+	synblock->b_cap_prog = NULL;
     else
     {
 	/* Prepend a ^ so that we only match at one column */
-	re = concat_str((char_u *)"^", buf->b_p_spc);
+	re = concat_str((char_u *)"^", synblock->b_p_spc);
 	if (re != NULL)
 	{
-	    buf->b_cap_prog = vim_regcomp(re, RE_MAGIC);
-	    if (buf->b_cap_prog == NULL)
-	    {
-		buf->b_cap_prog = rp; /* restore the previous program */
+	    synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
+	    if (synblock->b_cap_prog == NULL)
+	    {
+		synblock->b_cap_prog = rp; /* restore the previous program */
 		return e_invarg;
 	    }
 	    vim_free(re);
@@ -7588,8 +7614,7 @@ set_bool_option(opt_idx, varp, value, op
     {
 	if (curwin->w_p_spell)
 	{
-	    char_u	*errmsg = did_set_spelllang(curbuf);
-
+	    char_u	*errmsg = did_set_spelllang(curwin);
 	    if (errmsg != NULL)
 		EMSG(_(errmsg));
 	}
@@ -8089,6 +8114,21 @@ set_num_option(opt_idx, varp, value, err
 	if (p_uc && !old_value)
 	    ml_open_files();
     }
+#ifdef FEAT_CONCEAL
+    else if (pp == (long *)&curwin->w_p_conceal)
+    {
+	if (curwin->w_p_conceal < 0)
+	{
+	    errmsg = e_positive;
+	    curwin->w_p_conceal = 0;
+	}
+	else if (curwin->w_p_conceal > 3)
+	{
+	    errmsg = e_invarg;
+	    curwin->w_p_conceal = 3;
+	}
+    }
+#endif
 #ifdef MZSCHEME_GUI_THREADS
     else if (pp == &p_mzq)
 	mzvim_reset_timer();
@@ -9327,6 +9367,12 @@ get_varp(p)
 #ifdef FEAT_SCROLLBIND
 	case PV_SCBIND: return (char_u *)&(curwin->w_p_scb);
 #endif
+#ifdef FEAT_CURSORBIND
+	case PV_CRBIND: return (char_u *)&(curwin->w_p_crb);
+#endif
+#ifdef FEAT_CONCEAL
+	case PV_CONCEAL:    return (char_u *)&(curwin->w_p_conceal);
+#endif
 
 	case PV_AI:	return (char_u *)&(curbuf->b_p_ai);
 	case PV_BIN:	return (char_u *)&(curbuf->b_p_bin);
@@ -9425,9 +9471,9 @@ get_varp(p)
 	case PV_SYN:	return (char_u *)&(curbuf->b_p_syn);
 #endif
 #ifdef FEAT_SPELL
-	case PV_SPC:	return (char_u *)&(curbuf->b_p_spc);
-	case PV_SPF:	return (char_u *)&(curbuf->b_p_spf);
-	case PV_SPL:	return (char_u *)&(curbuf->b_p_spl);
+	case PV_SPC:	return (char_u *)&(curwin->w_s->b_p_spc);
+	case PV_SPF:	return (char_u *)&(curwin->w_s->b_p_spf);
+	case PV_SPL:	return (char_u *)&(curwin->w_s->b_p_spl);
 #endif
 	case PV_SW:	return (char_u *)&(curbuf->b_p_sw);
 	case PV_TS:	return (char_u *)&(curbuf->b_p_ts);
@@ -9750,10 +9796,10 @@ buf_copy_options(buf, flags)
 	    buf->b_p_smc = p_smc;
 #endif
 #ifdef FEAT_SPELL
-	    buf->b_p_spc = vim_strsave(p_spc);
-	    (void)compile_cap_prog(buf);
-	    buf->b_p_spf = vim_strsave(p_spf);
-	    buf->b_p_spl = vim_strsave(p_spl);
+	    buf->b_s.b_p_spc = vim_strsave(p_spf);
+	    (void)compile_cap_prog(&buf->b_s);
+	    buf->b_s.b_p_spf = vim_strsave(p_spf);
+	    buf->b_s.b_p_spl = vim_strsave(p_spl);
 #endif
 #if defined(FEAT_CINDENT) && defined(FEAT_EVAL)
 	    buf->b_p_inde = vim_strsave(p_inde);
--- a/src/option.h
+++ b/src/option.h
@@ -1021,6 +1021,12 @@ enum
 #ifdef FEAT_ARABIC
     , WV_ARAB
 #endif
+#ifdef FEAT_CONCEAL
+    , WV_CONCEAL
+#endif
+#ifdef FEAT_CURSORBIND
+    , WV_CRBIND
+#endif
 #ifdef FEAT_DIFF
     , WV_DIFF
 #endif
--- a/src/proto/diff.pro
+++ b/src/proto/diff.pro
@@ -22,5 +22,6 @@ void nv_diffgetput __ARGS((int put));
 void ex_diffgetput __ARGS((exarg_T *eap));
 int diff_mode_buf __ARGS((buf_T *buf));
 int diff_move_to __ARGS((int dir, long count));
+linenr_T diff_get_corresponding_line __ARGS((buf_T *buf1, linenr_T lnum1, buf_T *buf2, linenr_T lnum2));
 linenr_T diff_lnum_win __ARGS((linenr_T lnum, win_T *wp));
 /* vim: set ft=c : */
--- a/src/proto/move.pro
+++ b/src/proto/move.pro
@@ -1,4 +1,5 @@
 /* move.c */
+void do_check_cursorbind __ARGS((void));
 void update_topline_redraw __ARGS((void));
 void update_topline __ARGS((void));
 void update_curswant __ARGS((void));
--- a/src/proto/screen.pro
+++ b/src/proto/screen.pro
@@ -46,5 +46,6 @@ void get_trans_bufname __ARGS((buf_T *bu
 int redrawing __ARGS((void));
 int messaging __ARGS((void));
 void showruler __ARGS((int always));
+void update_single_line __ARGS((win_T *buf, linenr_T lnum));
 int number_width __ARGS((win_T *wp));
 /* vim: set ft=c : */
--- a/src/proto/spell.pro
+++ b/src/proto/spell.pro
@@ -2,14 +2,10 @@
 int spell_check __ARGS((win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, int docount));
 int spell_move_to __ARGS((win_T *wp, int dir, int allwords, int curline, hlf_T *attrp));
 void spell_cat_line __ARGS((char_u *buf, char_u *line, int maxlen));
-int get2c __ARGS((FILE *fd));
-int get3c __ARGS((FILE *fd));
-int get4c __ARGS((FILE *fd));
-char_u *did_set_spelllang __ARGS((buf_T *buf));
+char_u *did_set_spelllang __ARGS((win_T *wp));
 void spell_free_all __ARGS((void));
 void spell_reload __ARGS((void));
 int spell_check_msm __ARGS((void));
-int put_bytes __ARGS((FILE *fd, long_u nr, int len));
 void ex_mkspell __ARGS((exarg_T *eap));
 void ex_spell __ARGS((exarg_T *eap));
 void spell_add_word __ARGS((char_u *word, int len, int bad, int idx, int undo));
@@ -21,8 +17,8 @@ void spell_suggest_list __ARGS((garray_T
 char_u *eval_soundfold __ARGS((char_u *word));
 void ex_spellinfo __ARGS((exarg_T *eap));
 void ex_spelldump __ARGS((exarg_T *eap));
-void spell_dump_compl __ARGS((buf_T *buf, char_u *pat, int ic, int *dir, int dumpflags_arg));
-char_u *spell_to_word_end __ARGS((char_u *start, buf_T *buf));
+void spell_dump_compl __ARGS((char_u *pat, int ic, int *dir, int dumpflags_arg));
+char_u *spell_to_word_end __ARGS((char_u *start, win_T *win));
 int spell_word_start __ARGS((int startcol));
 void spell_expand_check_cap __ARGS((colnr_T col));
 int expand_spelling __ARGS((linenr_T lnum, char_u *pat, char_u ***matchp));
--- a/src/proto/syntax.pro
+++ b/src/proto/syntax.pro
@@ -1,18 +1,20 @@
 /* syntax.c */
 void syntax_start __ARGS((win_T *wp, linenr_T lnum));
-void syn_stack_free_all __ARGS((buf_T *buf));
+void syn_stack_free_all __ARGS((synblock_T *block));
 void syn_stack_apply_changes __ARGS((buf_T *buf));
 void syntax_end_parsing __ARGS((linenr_T lnum));
 int syntax_check_changed __ARGS((linenr_T lnum));
-int get_syntax_attr __ARGS((colnr_T col, int *can_spell, int keep_state));
-void syntax_clear __ARGS((buf_T *buf));
+int get_syntax_attr __ARGS((colnr_T col, int *p_flags, int *can_spell, int keep_state));
+void syntax_clear __ARGS((synblock_T *block));
 void ex_syntax __ARGS((exarg_T *eap));
-int syntax_present __ARGS((buf_T *buf));
+void ex_ownsyntax __ARGS((exarg_T *eap));
+int syntax_present __ARGS((win_T *win));
 void reset_expand_highlight __ARGS((void));
 void set_context_in_echohl_cmd __ARGS((expand_T *xp, char_u *arg));
 void set_context_in_syntax_cmd __ARGS((expand_T *xp, char_u *arg));
 char_u *get_syntax_name __ARGS((expand_T *xp, int idx));
 int syn_get_id __ARGS((win_T *wp, long lnum, colnr_T col, int trans, int *spellp, int keep_state));
+int syn_get_sub_char __ARGS((void));
 int syn_get_stack_item __ARGS((int i));
 int syn_get_foldlevel __ARGS((win_T *wp, long lnum));
 void init_highlight __ARGS((int both, int reset));
--- a/src/screen.c
+++ b/src/screen.c
@@ -485,7 +485,7 @@ update_screen(type)
 # ifdef FEAT_WINDOWS
 		    wwp == wp &&
 # endif
-		    syntax_present(wp->w_buffer))
+		    syntax_present(wp))
 		syn_stack_apply_changes(wp->w_buffer);
 	}
     }
@@ -585,6 +585,54 @@ update_screen(type)
 #endif
 }
 
+#if defined(FEAT_CONCEAL) || defined(PROTO)
+    void
+update_single_line(wp, lnum)
+    win_T	*wp;
+    linenr_T	lnum;
+{
+    int		row;
+    int		j;
+
+    if (lnum >= wp->w_topline && lnum < wp->w_botline
+					  && foldedCount(wp, lnum, NULL) == 0)
+    {
+# ifdef FEAT_GUI
+	/* Remove the cursor before starting to do anything, because scrolling
+	 * may make it difficult to redraw the text under it. */
+	if (gui.in_use)
+	    gui_undraw_cursor();
+# endif
+	row = 0;
+	for (j = 0; j < wp->w_lines_valid; ++j)
+	{
+	    if (lnum == wp->w_lines[j].wl_lnum)
+	    {
+		screen_start();	/* not sure of screen cursor */
+# if defined(FEAT_SEARCH_EXTRA)
+		start_search_hl();
+		prepare_search_hl(wp, lnum);
+# endif
+		win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE);
+# if defined(FEAT_SEARCH_EXTRA)
+		end_search_hl();
+# endif
+		break;
+	    }
+	    row += wp->w_lines[j].wl_size;
+	}
+# ifdef FEAT_GUI
+	/* Redraw the cursor */
+	if (gui.in_use)
+	{
+	    out_flush();	/* required before updating the cursor */
+	    gui_update_cursor(FALSE, FALSE);
+	}
+# endif
+    }
+}
+#endif
+
 #if defined(FEAT_SIGNS) || defined(FEAT_GUI)
 static void update_prepare __ARGS((void));
 static void update_finish __ARGS((void));
@@ -917,9 +965,9 @@ win_update(wp)
 #ifdef FEAT_SYN_HL
 		/* Need to redraw lines above the change that may be included
 		 * in a pattern match. */
-		if (syntax_present(buf))
-		{
-		    mod_top -= buf->b_syn_sync_linebreaks;
+		if (syntax_present(wp))
+		{
+		    mod_top -= buf->b_s.b_syn_sync_linebreaks;
 		    if (mod_top < 1)
 			mod_top = 1;
 		}
@@ -1010,7 +1058,7 @@ win_update(wp)
 	    if (mod_bot > wp->w_topline)
 		mod_top = wp->w_topline;
 #ifdef FEAT_SYN_HL
-	    else if (syntax_present(buf))
+	    else if (syntax_present(wp))
 		top_end = 1;
 #endif
 	}
@@ -1545,7 +1593,7 @@ win_update(wp)
 #ifdef FEAT_SYN_HL
 				|| did_update == DID_FOLD
 				|| (did_update == DID_LINE
-				    && syntax_present(buf)
+				    && syntax_present(wp)
 				    && (
 # ifdef FEAT_FOLDING
 					(foldmethodIsSyntax(wp)
@@ -1771,7 +1819,7 @@ win_update(wp)
 #ifdef FEAT_SYN_HL
 		/* Let the syntax stuff know we skipped a few lines. */
 		if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
-						       && syntax_present(buf))
+						       && syntax_present(wp))
 		    syntax_end_parsing(syntax_last_parsed + 1);
 #endif
 
@@ -1843,7 +1891,7 @@ win_update(wp)
     /*
      * Let the syntax stuff know we stop parsing here.
      */
-    if (syntax_last_parsed != 0 && syntax_present(buf))
+    if (syntax_last_parsed != 0 && syntax_present(wp))
 	syntax_end_parsing(syntax_last_parsed + 1);
 #endif
 
@@ -2726,6 +2774,14 @@ win_line(wp, lnum, startrow, endrow, noc
     int		feedback_old_attr = -1;
 #endif
 
+#ifdef FEAT_CONCEAL
+    int		syntax_flags	= 0;
+    int		conceal_attr	= hl_attr(HLF_CONCEAL);
+    int		first_conceal	= (wp->w_p_conceal != 3);
+    int		is_concealing	= FALSE;
+    int		boguscols	= 0;	/* nonexistent columns added to force
+					   wrapping */
+#endif
 
     if (startrow > endrow)		/* past the end already! */
 	return startrow;
@@ -2743,7 +2799,7 @@ win_line(wp, lnum, startrow, endrow, noc
     extra_check = 0;
 #endif
 #ifdef FEAT_SYN_HL
-    if (syntax_present(wp->w_buffer) && !wp->w_buffer->b_syn_error)
+    if (syntax_present(wp) && !wp->w_s->b_syn_error)
     {
 	/* Prepare for syntax highlighting in this line.  When there is an
 	 * error, stop syntax highlighting. */
@@ -2751,7 +2807,7 @@ win_line(wp, lnum, startrow, endrow, noc
 	did_emsg = FALSE;
 	syntax_start(wp, lnum);
 	if (did_emsg)
-	    wp->w_buffer->b_syn_error = TRUE;
+	    wp->w_s->b_syn_error = TRUE;
 	else
 	{
 	    did_emsg = save_did_emsg;
@@ -2763,9 +2819,9 @@ win_line(wp, lnum, startrow, endrow, noc
 
 #ifdef FEAT_SPELL
     if (wp->w_p_spell
-	    && *wp->w_buffer->b_p_spl != NUL
-	    && wp->w_buffer->b_langp.ga_len > 0
-	    && *(char **)(wp->w_buffer->b_langp.ga_data) != NULL)
+	    && *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;
@@ -3113,7 +3169,7 @@ win_line(wp, lnum, startrow, endrow, noc
 		/* 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->w_buffer)
+		word_end = (int)(spell_to_word_end(ptr, wp)
 								  - line + 1);
 	    }
 	    else
@@ -3962,14 +4018,19 @@ win_line(wp, lnum, startrow, endrow, noc
 		    did_emsg = FALSE;
 
 		    syntax_attr = get_syntax_attr((colnr_T)v - 1,
+# ifdef FEAT_CONCEAL
+						&syntax_flags,
+# else
+						NULL,
+# endif
 # ifdef FEAT_SPELL
-					       has_spell ? &can_spell :
-# endif
-					       NULL, FALSE);
+						has_spell ? &can_spell :
+# endif
+						NULL, FALSE);
 
 		    if (did_emsg)
 		    {
-			wp->w_buffer->b_syn_error = TRUE;
+			wp->w_s->b_syn_error = TRUE;
 			has_syntax = FALSE;
 		    }
 		    else
@@ -4304,6 +4365,74 @@ win_line(wp, lnum, startrow, endrow, noc
 		}
 #endif
 	    }
+
+#ifdef FEAT_CONCEAL
+	    if (    wp->w_p_conceal
+		    && (!area_highlighting)
+		    && ((lnum != wp->w_cursor.lnum)
+			|| (curwin != wp) || (wp->w_buffer->b_p_ma == FALSE))
+		    && ((syntax_flags & HL_CONCEAL) != 0))
+
+	    {
+		char_attr = conceal_attr;
+		if (first_conceal
+			&& (syn_get_sub_char() != NUL || wp->w_p_conceal == 1))
+		{
+		    if (syn_get_sub_char() != NUL)
+			c = syn_get_sub_char();
+		    else if (lcs_conceal != NUL)
+			c = lcs_conceal;
+		    else
+			c = ' ';
+
+		    first_conceal = FALSE;
+
+# ifdef FEAT_HLCOLUMN
+		    if (hlc > 0 && n_extra > 0)
+			hlc += n_extra;
+# endif
+		    vcol += n_extra;
+		    if (wp->w_p_wrap && n_extra > 0)
+		    {
+# ifdef FEAT_RIGHTLEFT
+			if (wp->w_p_rl)
+			{
+			    col -= n_extra;
+			    boguscols -= n_extra;
+			}
+			else
+# endif
+			{
+			    boguscols += n_extra;
+			    col += n_extra;
+			}
+		    }
+		    n_extra = 0;
+		    n_attr = 0;
+		}
+		else if (n_skip == 0)
+		{
+		    is_concealing = TRUE;
+		    n_skip = 1;
+		}
+# ifdef FEAT_MBYTE
+		mb_c = c;
+		if (enc_utf8 && (*mb_char2len)(c) > 1)
+		{
+		    mb_utf8 = TRUE;
+		    u8cc[0] = 0;
+		    c = 0xc0;
+		}
+		else
+		    mb_utf8 = FALSE;	/* don't draw as UTF-8 */
+# endif
+	    }
+	    else
+	    {
+		first_conceal	= (wp->w_p_conceal != 3);
+		is_concealing	= FALSE;
+	    }
+#endif /* FEAT_CONCEAL */
 	}
 
 	/* Don't override visual selection highlighting. */
@@ -4570,8 +4699,14 @@ win_line(wp, lnum, startrow, endrow, noc
 	    }
 #endif
 
-	    SCREEN_LINE(screen_row, W_WINCOL(wp), col, (int)W_WIDTH(wp),
-								  wp->w_p_rl);
+#ifdef FEAT_CONCEAL
+	    SCREEN_LINE(screen_row, W_WINCOL(wp), col - boguscols,
+						(int)W_WIDTH(wp), wp->w_p_rl);
+	    boguscols = 0;
+#else
+	    SCREEN_LINE(screen_row, W_WINCOL(wp), col,
+						(int)W_WIDTH(wp), wp->w_p_rl);
+#endif
 	    row++;
 
 	    /*
@@ -4730,6 +4865,97 @@ win_line(wp, lnum, startrow, endrow, noc
 		++col;
 	    }
 	}
+#ifdef FEAT_CONCEAL
+	else if (wp->w_p_conceal && is_concealing)
+	{
+	    --n_skip;
+# ifdef FEAT_HLCOLUMN
+	    if (hlc)
+	    {
+		++hlc;
+		if (n_extra > 0)
+		    hlc += n_extra;
+	    }
+# endif
+	    if (wp->w_p_wrap)
+	    {
+		/*
+		 * Special voodoo required if 'wrap' is on.
+		 *
+		 * Advance the column indicator to force the line
+		 * drawing to wrap early. This will make the line
+		 * take up the same screen space when parts are concealed,
+		 * so that cursor line computations aren't messed up.
+		 *
+		 * To avoid the fictitious advance of 'col' causing
+		 * trailing junk to be written out of the screen line
+		 * we are building, 'boguscols' keeps track of the number
+		 * of bad columns we have advanced.
+		 */
+		if (n_extra > 0)
+		{
+		    vcol += n_extra;
+# ifdef FEAT_RIGHTLEFT
+		    if (wp->w_p_rl)
+		    {
+			col -= n_extra;
+			boguscols -= n_extra;
+		    }
+		    else
+# endif
+		    {
+			col += n_extra;
+			boguscols += n_extra;
+		    }
+		    n_extra = 0;
+		    n_attr = 0;
+		}
+
+
+# ifdef FEAT_MBYTE
+		if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
+		{
+		    /* Need to fill two screen columns. */
+#  ifdef FEAT_RIGHTLEFT
+		    if (wp->w_p_rl)
+		    {
+			--boguscols;
+			--col;
+		    }
+		    else
+#  endif
+		    {
+			++boguscols;
+			++col;
+		    }
+		}
+# endif
+
+# ifdef FEAT_RIGHTLEFT
+		if (wp->w_p_rl)
+		{
+		    --boguscols;
+		    --col;
+		}
+		else
+# endif
+		{
+		    ++boguscols;
+		    ++col;
+		}
+	    }
+	    else
+	    {
+		if (n_extra > 0)
+		{
+		    vcol += n_extra;
+		    n_extra = 0;
+		    n_attr = 0;
+		}
+	    }
+
+	}
+#endif /* FEAT_CONCEAL */
 	else
 	    --n_skip;
 
@@ -4772,8 +4998,14 @@ win_line(wp, lnum, startrow, endrow, noc
 		    || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
 		)
 	{
-	    SCREEN_LINE(screen_row, W_WINCOL(wp), col, (int)W_WIDTH(wp),
-								  wp->w_p_rl);
+#ifdef FEAT_CONCEAL
+	    SCREEN_LINE(screen_row, W_WINCOL(wp), col - boguscols,
+						(int)W_WIDTH(wp), wp->w_p_rl);
+	    boguscols = 0;
+#else
+	    SCREEN_LINE(screen_row, W_WINCOL(wp), col,
+						(int)W_WIDTH(wp), wp->w_p_rl);
+#endif
 	    ++row;
 	    ++screen_row;
 
--- a/src/search.c
+++ b/src/search.c
@@ -1077,6 +1077,9 @@ do_search(oap, dirc, pat, count, options
     char_u	    *dircp;
     char_u	    *strcopy = NULL;
     char_u	    *ps;
+#ifdef FEAT_CONCEAL
+    linenr_T	oldline = curwin->w_cursor.lnum;
+#endif
 
     /*
      * A line offset is not remembered, this is vi compatible.
@@ -1422,6 +1425,13 @@ do_search(oap, dirc, pat, count, options
 	setpcmark();
     curwin->w_cursor = pos;
     curwin->w_set_curswant = TRUE;
+#ifdef FEAT_CONCEAL
+    if (curwin->w_p_conceal && oldline != curwin->w_cursor.lnum)
+    {
+	update_single_line(curwin, oldline);
+	update_single_line(curwin, curwin->w_cursor.lnum);
+    }
+#endif
 
 end_do_search:
     if (options & SEARCH_KEEP)
--- a/src/spell.c
+++ b/src/spell.c
@@ -720,7 +720,7 @@ typedef struct matchinf_S
     /* others */
     int		mi_result;		/* result so far: SP_BAD, SP_OK, etc. */
     int		mi_capflags;		/* WF_ONECAP WF_ALLCAP WF_KEEPCAP */
-    buf_T	*mi_buf;		/* buffer being checked */
+    win_T	*mi_win;		/* buffer being checked */
 
     /* for NOBREAK */
     int		mi_result2;		/* "mi_resul" without following word */
@@ -747,11 +747,11 @@ static int	    did_set_spelltab;
 
 static void clear_spell_chartab __ARGS((spelltab_T *sp));
 static int set_spell_finish __ARGS((spelltab_T	*new_st));
-static int spell_iswordp __ARGS((char_u *p, buf_T *buf));
+static int spell_iswordp __ARGS((char_u *p, win_T *wp));
 static int spell_iswordp_nmw __ARGS((char_u *p));
 #ifdef FEAT_MBYTE
 static int spell_mb_isword_class __ARGS((int cl));
-static int spell_iswordp_w __ARGS((int *p, buf_T *buf));
+static int spell_iswordp_w __ARGS((int *p, win_T *wp));
 #endif
 static int write_spell_prefcond __ARGS((FILE *fd, garray_T *gap));
 
@@ -874,9 +874,9 @@ static void set_sal_first __ARGS((slang_
 static int *mb_str2wide __ARGS((char_u *s));
 #endif
 static int spell_read_tree __ARGS((FILE *fd, char_u **bytsp, idx_T **idxsp, int prefixtree, int prefixcnt));
-static idx_T read_tree_node __ARGS((FILE *fd, char_u *byts, idx_T *idxs, int maxidx, int startidx, int prefixtree, int maxprefcondnr));
-static void clear_midword __ARGS((buf_T *buf));
-static void use_midword __ARGS((slang_T *lp, buf_T *buf));
+static idx_T read_tree_node __ARGS((FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx_T startidx, int prefixtree, int maxprefcondnr));
+static void clear_midword __ARGS((win_T *buf));
+static void use_midword __ARGS((slang_T *lp, win_T *buf));
 static int find_region __ARGS((char_u *rp, char_u *region));
 static int captype __ARGS((char_u *word, char_u *end));
 static int badword_captype __ARGS((char_u *word, char_u *end));
@@ -1030,7 +1030,7 @@ spell_check(wp, ptr, attrp, capcol, doco
 	return 1;
 
     /* Return here when loading language files failed. */
-    if (wp->w_buffer->b_langp.ga_len == 0)
+    if (wp->w_s->b_langp.ga_len == 0)
 	return 1;
 
     vim_memset(&mi, 0, sizeof(matchinf_T));
@@ -1050,14 +1050,14 @@ spell_check(wp, ptr, attrp, capcol, doco
     /* Find the normal end of the word (until the next non-word character). */
     mi.mi_word = ptr;
     mi.mi_fend = ptr;
-    if (spell_iswordp(mi.mi_fend, wp->w_buffer))
+    if (spell_iswordp(mi.mi_fend, wp))
     {
 	do
 	{
 	    mb_ptr_adv(mi.mi_fend);
-	} while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp->w_buffer));
-
-	if (capcol != NULL && *capcol == 0 && wp->w_buffer->b_cap_prog != NULL)
+	} while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp));
+
+	if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL)
 	{
 	    /* Check word starting with capital letter. */
 	    c = PTR2CHAR(ptr);
@@ -1073,7 +1073,9 @@ spell_check(wp, ptr, attrp, capcol, doco
     mi.mi_end = mi.mi_fend;
 
     /* Check caps type later. */
-    mi.mi_buf = wp->w_buffer;
+    mi.mi_capflags = 0;
+    mi.mi_cend = NULL;
+    mi.mi_win = wp;
 
     /* case-fold the word with one non-word character, so that we can check
      * for the word end. */
@@ -1093,9 +1095,9 @@ spell_check(wp, ptr, attrp, capcol, doco
      * We check them all, because a word may be matched longer in another
      * language.
      */
-    for (lpi = 0; lpi < wp->w_buffer->b_langp.ga_len; ++lpi)
-    {
-	mi.mi_lp = LANGP_ENTRY(wp->w_buffer->b_langp, lpi);
+    for (lpi = 0; lpi < wp->w_s->b_langp.ga_len; ++lpi)
+    {
+	mi.mi_lp = LANGP_ENTRY(wp->w_s->b_langp, lpi);
 
 	/* If reloading fails the language is still in the list but everything
 	 * has been cleared. */
@@ -1143,12 +1145,12 @@ spell_check(wp, ptr, attrp, capcol, doco
 	 * skip over the character (try looking for a word after it). */
 	else if (!spell_iswordp_nmw(ptr))
 	{
-	    if (capcol != NULL && wp->w_buffer->b_cap_prog != NULL)
+	    if (capcol != NULL && wp->w_s->b_cap_prog != NULL)
 	    {
 		regmatch_T	regmatch;
 
 		/* Check for end of sentence. */
-		regmatch.regprog = wp->w_buffer->b_cap_prog;
+		regmatch.regprog = wp->w_s->b_cap_prog;
 		regmatch.rm_ic = FALSE;
 		if (vim_regexec(&regmatch, ptr, 0))
 		    *capcol = (int)(regmatch.endp[0] - ptr);
@@ -1165,14 +1167,14 @@ spell_check(wp, ptr, attrp, capcol, doco
 	     * is a mixup in "midword". */
 	    mb_ptr_adv(mi.mi_end);
 	else if (mi.mi_result == SP_BAD
-		&& LANGP_ENTRY(wp->w_buffer->b_langp, 0)->lp_slang->sl_nobreak)
+		&& LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak)
 	{
 	    char_u	*p, *fp;
 	    int		save_result = mi.mi_result;
 
 	    /* First language in 'spelllang' is NOBREAK.  Find first position
 	     * at which any word would be valid. */
-	    mi.mi_lp = LANGP_ENTRY(wp->w_buffer->b_langp, 0);
+	    mi.mi_lp = LANGP_ENTRY(wp->w_s->b_langp, 0);
 	    if (mi.mi_lp->lp_slang->sl_fidxs != NULL)
 	    {
 		p = mi.mi_word;
@@ -1389,7 +1391,7 @@ find_word(mip, mode)
 	if ((*mb_head_off)(ptr, ptr + wlen) > 0)
 	    continue;	    /* not at first byte of character */
 #endif
-	if (spell_iswordp(ptr + wlen, mip->mi_buf))
+	if (spell_iswordp(ptr + wlen, mip->mi_win))
 	{
 	    if (slang->sl_compprog == NULL && !slang->sl_nobreak)
 		continue;	    /* next char is a word character */
@@ -1634,11 +1636,11 @@ find_word(mip, mode)
 
 		/* For NOBREAK we need to try all NOBREAK languages, at least
 		 * to find the ".add" file(s). */
-		for (lpi = 0; lpi < mip->mi_buf->b_langp.ga_len; ++lpi)
+		for (lpi = 0; lpi < mip->mi_win->w_s->b_langp.ga_len; ++lpi)
 		{
 		    if (slang->sl_nobreak)
 		    {
-			mip->mi_lp = LANGP_ENTRY(mip->mi_buf->b_langp, lpi);
+			mip->mi_lp = LANGP_ENTRY(mip->mi_win->w_s->b_langp, lpi);
 			if (mip->mi_lp->lp_slang->sl_fidxs == NULL
 					 || !mip->mi_lp->lp_slang->sl_nobreak)
 			    continue;
@@ -2102,7 +2104,7 @@ fold_more(mip)
     do
     {
 	mb_ptr_adv(mip->mi_fend);
-    } while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_buf));
+    } while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_win));
 
     /* Include the non-word character so that we can check for the word end. */
     if (*mip->mi_fend != NUL)
@@ -2138,8 +2140,8 @@ spell_valid_case(wordflags, treeflags)
 no_spell_checking(wp)
     win_T	*wp;
 {
-    if (!wp->w_p_spell || *wp->w_buffer->b_p_spl == NUL
-					 || wp->w_buffer->b_langp.ga_len == 0)
+    if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL
+					 || wp->w_s->b_langp.ga_len == 0)
     {
 	EMSG(_("E756: Spell checking is not enabled"));
 	return TRUE;
@@ -2173,7 +2175,7 @@ spell_move_to(wp, dir, allwords, curline
     hlf_T	attr;
     int		len;
 # ifdef FEAT_SYN_HL
-    int		has_syntax = syntax_present(wp->w_buffer);
+    int		has_syntax = syntax_present(wp);
 # endif
     int		col;
     int		can_spell;
@@ -4165,12 +4167,12 @@ read_tree_node(fd, byts, idxs, maxidx, s
 }
 
 /*
- * Parse 'spelllang' and set buf->b_langp accordingly.
+ * Parse 'spelllang' and set w_s->b_langp accordingly.
  * Returns NULL if it's OK, an error message otherwise.
  */
     char_u *
-did_set_spelllang(buf)
-    buf_T	*buf;
+did_set_spelllang(wp)
+    win_T	*wp;
 {
     garray_T	ga;
     char_u	*splp;
@@ -4203,11 +4205,11 @@ did_set_spelllang(buf)
     recursive = TRUE;
 
     ga_init2(&ga, sizeof(langp_T), 2);
-    clear_midword(buf);
+    clear_midword(wp);
 
     /* Make a copy of 'spellang', the SpellFileMissing autocommands may change
      * it under our fingers. */
-    spl_copy = vim_strsave(buf->b_p_spl);
+    spl_copy = vim_strsave(wp->w_s->b_p_spl);
     if (spl_copy == NULL)
 	goto theend;
 
@@ -4216,7 +4218,6 @@ did_set_spelllang(buf)
     {
 	/* Get one language name. */
 	copy_option_part(&splp, lang, MAXWLEN, ",");
-
 	region = NULL;
 	len = (int)STRLEN(lang);
 
@@ -4283,7 +4284,7 @@ did_set_spelllang(buf)
 #ifdef FEAT_AUTOCMD
 		/* SpellFileMissing autocommands may do anything, including
 		 * destroying the buffer we are using... */
-		if (!buf_valid(buf))
+		if (!buf_valid(wp->w_buffer))
 		{
 		    ret_msg = (char_u *)"E797: SpellFileMissing autocommand deleted buffer";
 		    goto theend;
@@ -4334,7 +4335,7 @@ did_set_spelllang(buf)
 		    LANGP_ENTRY(ga, ga.ga_len)->lp_slang = slang;
 		    LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask;
 		    ++ga.ga_len;
-		    use_midword(slang, buf);
+		    use_midword(slang, wp);
 		    if (slang->sl_nobreak)
 			nobreak = TRUE;
 		}
@@ -4345,7 +4346,7 @@ did_set_spelllang(buf)
      * round 1: load first name in 'spellfile'.
      * round 2: load second name in 'spellfile.
      * etc. */
-    spf = buf->b_p_spf;
+    spf = curwin->w_s->b_p_spf;
     for (round = 0; round == 0 || *spf != NUL; ++round)
     {
 	if (round == 0)
@@ -4418,14 +4419,14 @@ did_set_spelllang(buf)
 		LANGP_ENTRY(ga, ga.ga_len)->lp_replang = NULL;
 		LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask;
 		++ga.ga_len;
-		use_midword(slang, buf);
+		use_midword(slang, wp);
 	    }
 	}
     }
 
     /* Everything is fine, store the new b_langp value. */
-    ga_clear(&buf->b_langp);
-    buf->b_langp = ga;
+    ga_clear(&wp->w_s->b_langp);
+    wp->w_s->b_langp = ga;
 
     /* For each language figure out what language to use for sound folding and
      * REP items.  If the language doesn't support it itself use another one
@@ -4481,13 +4482,13 @@ theend:
  * Clear the midword characters for buffer "buf".
  */
     static void
-clear_midword(buf)
-    buf_T	*buf;
-{
-    vim_memset(buf->b_spell_ismw, 0, 256);
-#ifdef FEAT_MBYTE
-    vim_free(buf->b_spell_ismw_mb);
-    buf->b_spell_ismw_mb = NULL;
+clear_midword(wp)
+    win_T	*wp;
+{
+    vim_memset(wp->w_s->b_spell_ismw, 0, 256);
+#ifdef FEAT_MBYTE
+    vim_free(wp->w_s->b_spell_ismw_mb);
+    wp->w_s->b_spell_ismw_mb = NULL;
 #endif
 }
 
@@ -4496,9 +4497,9 @@ clear_midword(buf)
  * They add up to any currently used midword characters.
  */
     static void
-use_midword(lp, buf)
+use_midword(lp, wp)
     slang_T	*lp;
-    buf_T	*buf;
+    win_T	*wp;
 {
     char_u	*p;
 
@@ -4515,19 +4516,19 @@ use_midword(lp, buf)
 	    c = mb_ptr2char(p);
 	    l = (*mb_ptr2len)(p);
 	    if (c < 256 && l <= 2)
-		buf->b_spell_ismw[c] = TRUE;
-	    else if (buf->b_spell_ismw_mb == NULL)
+		wp->w_s->b_spell_ismw[c] = TRUE;
+	    else if (wp->w_s->b_spell_ismw_mb == NULL)
 		/* First multi-byte char in "b_spell_ismw_mb". */
-		buf->b_spell_ismw_mb = vim_strnsave(p, l);
+		wp->w_s->b_spell_ismw_mb = vim_strnsave(p, l);
 	    else
 	    {
 		/* Append multi-byte chars to "b_spell_ismw_mb". */
-		n = (int)STRLEN(buf->b_spell_ismw_mb);
-		bp = vim_strnsave(buf->b_spell_ismw_mb, n + l);
+		n = (int)STRLEN(wp->w_s->b_spell_ismw_mb);
+		bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, n + l);
 		if (bp != NULL)
 		{
-		    vim_free(buf->b_spell_ismw_mb);
-		    buf->b_spell_ismw_mb = bp;
+		    vim_free(wp->w_s->b_spell_ismw_mb);
+		    wp->w_s->b_spell_ismw_mb = bp;
 		    vim_strncpy(bp + n, p, l);
 		}
 	    }
@@ -4535,7 +4536,7 @@ use_midword(lp, buf)
 	}
 	else
 #endif
-	    buf->b_spell_ismw[*p++] = TRUE;
+	    wp->w_s->b_spell_ismw[*p++] = TRUE;
 }
 
 /*
@@ -4678,9 +4679,9 @@ spell_free_all()
     buf_T	*buf;
     char_u	fname[MAXPATHL];
 
-    /* Go through all buffers and handle 'spelllang'. */
+    /* Go through all buffers and handle 'spelllang'. */ //<VN>
     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
-	ga_clear(&buf->b_langp);
+	ga_clear(&buf->b_s.b_langp);
 
     while (first_lang != NULL)
     {
@@ -4716,7 +4717,6 @@ spell_free_all()
     void
 spell_reload()
 {
-    buf_T	*buf;
     win_T	*wp;
 
     /* Initialize the table for spell_iswordp(). */
@@ -4726,16 +4726,15 @@ spell_reload()
     spell_free_all();
 
     /* Go through all buffers and handle 'spelllang'. */
-    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+    for (wp = firstwin; wp != NULL; wp = wp->w_next)
     {
 	/* Only load the wordlists when 'spelllang' is set and there is a
 	 * window for this buffer in which 'spell' is set. */
-	if (*buf->b_p_spl != NUL)
-	{
-	    FOR_ALL_WINDOWS(wp)
-		if (wp->w_buffer == buf && wp->w_p_spell)
-		{
-		    (void)did_set_spelllang(buf);
+	if (*wp->w_s->b_p_spl != NUL)
+	{
+		if (wp->w_p_spell)
+		{
+		    (void)did_set_spelllang(wp);
 # ifdef FEAT_WINDOWS
 		    break;
 # endif
@@ -4772,7 +4771,7 @@ spell_reload_one(fname, added_word)
     /* When "zg" was used and the file wasn't loaded yet, should redo
      * 'spelllang' to load it now. */
     if (added_word && !didit)
-	did_set_spelllang(curbuf);
+	did_set_spelllang(curwin);
 }
 
 
@@ -9369,19 +9368,19 @@ spell_add_word(word, len, bad, idx, undo
     else
     {
 	/* If 'spellfile' isn't set figure out a good default value. */
-	if (*curbuf->b_p_spf == NUL)
+	if (*curwin->w_s->b_p_spf == NUL)
 	{
 	    init_spellfile();
 	    new_spf = TRUE;
 	}
 
-	if (*curbuf->b_p_spf == NUL)
+	if (*curwin->w_s->b_p_spf == NUL)
 	{
 	    EMSG2(_(e_notset), "spellfile");
 	    return;
 	}
 
-	for (spf = curbuf->b_p_spf, i = 1; *spf != NUL; ++i)
+	for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; ++i)
 	{
 	    copy_option_part(&spf, fnamebuf, MAXPATHL, ",");
 	    if (i == idx)
@@ -9507,13 +9506,13 @@ init_spellfile()
     char_u	*rtp;
     char_u	*lend;
     int		aspath = FALSE;
-    char_u	*lstart = curbuf->b_p_spl;
-
-    if (*curbuf->b_p_spl != NUL && curbuf->b_langp.ga_len > 0)
+    char_u	*lstart = curbuf->b_s.b_p_spl;
+
+    if (*curwin->w_s->b_p_spl != NUL && curwin->w_s->b_langp.ga_len > 0)
     {
 	/* Find the end of the language name.  Exclude the region.  If there
 	 * is a path separator remember the start of the tail. */
-	for (lend = curbuf->b_p_spl; *lend != NUL
+	for (lend = curwin->w_s->b_p_spl; *lend != NUL
 			&& vim_strchr((char_u *)",._", *lend) == NULL; ++lend)
 	    if (vim_ispathsep(*lend))
 	    {
@@ -9529,7 +9528,7 @@ init_spellfile()
 	    if (aspath)
 		/* Use directory of an entry with path, e.g., for
 		 * "/dir/lg.utf-8.spl" use "/dir". */
-		vim_strncpy(buf, curbuf->b_p_spl, lstart - curbuf->b_p_spl - 1);
+		vim_strncpy(buf, curbuf->b_s.b_p_spl, lstart - curbuf->b_s.b_p_spl - 1);
 	    else
 		/* Copy the path from 'runtimepath' to buf[]. */
 		copy_option_part(&rtp, buf, MAXPATHL, ",");
@@ -9538,7 +9537,7 @@ init_spellfile()
 		/* Use the first language name from 'spelllang' and the
 		 * encoding used in the first loaded .spl file. */
 		if (aspath)
-		    vim_strncpy(buf, curbuf->b_p_spl, lend - curbuf->b_p_spl);
+		    vim_strncpy(buf, curbuf->b_s.b_p_spl, lend - curbuf->b_s.b_p_spl);
 		else
 		{
 		    /* Create the "spell" directory if it doesn't exist yet. */
@@ -9552,7 +9551,7 @@ init_spellfile()
 				 "/%.*s", (int)(lend - lstart), lstart);
 		}
 		l = (int)STRLEN(buf);
-		fname = LANGP_ENTRY(curbuf->b_langp, 0)->lp_slang->sl_fname;
+		fname = LANGP_ENTRY(curwin->w_s->b_langp, 0)->lp_slang->sl_fname;
 		vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add",
 			fname != NULL
 			  && strstr((char *)gettail(fname), ".ascii.") != NULL
@@ -9819,9 +9818,9 @@ set_spell_finish(new_st)
  * Thus this only works properly when past the first character of the word.
  */
     static int
-spell_iswordp(p, buf)
+spell_iswordp(p, wp)
     char_u	*p;
-    buf_T	*buf;	    /* buffer used */
+    win_T	*wp;	    /* buffer used */
 {
 #ifdef FEAT_MBYTE
     char_u	*s;
@@ -9835,7 +9834,7 @@ spell_iswordp(p, buf)
 	if (l == 1)
 	{
 	    /* be quick for ASCII */
-	    if (buf->b_spell_ismw[*p])
+	    if (wp->w_s->b_spell_ismw[*p])
 	    {
 		s = p + 1;		/* skip a mid-word character */
 		l = MB_BYTE2LEN(*s);
@@ -9844,9 +9843,9 @@ spell_iswordp(p, buf)
 	else
 	{
 	    c = mb_ptr2char(p);
-	    if (c < 256 ? buf->b_spell_ismw[c]
-		    : (buf->b_spell_ismw_mb != NULL
-			   && vim_strchr(buf->b_spell_ismw_mb, c) != NULL))
+	    if (c < 256 ? wp->w_s->b_spell_ismw[c]
+		    : (wp->w_s->b_spell_ismw_mb != NULL
+			   && vim_strchr(wp->w_s->b_spell_ismw_mb, c) != NULL))
 	    {
 		s = p + l;
 		l = MB_BYTE2LEN(*s);
@@ -9860,7 +9859,7 @@ spell_iswordp(p, buf)
     }
 #endif
 
-    return spelltab.st_isw[buf->b_spell_ismw[*p] ? p[1] : p[0]];
+    return spelltab.st_isw[wp->w_s->b_spell_ismw[*p] ? p[1] : p[0]];
 }
 
 /*
@@ -9903,15 +9902,15 @@ spell_mb_isword_class(cl)
  * Wide version of spell_iswordp().
  */
     static int
-spell_iswordp_w(p, buf)
+spell_iswordp_w(p, wp)
     int		*p;
-    buf_T	*buf;
+    win_T	*wp;
 {
     int		*s;
 
-    if (*p < 256 ? buf->b_spell_ismw[*p]
-		 : (buf->b_spell_ismw_mb != NULL
-			     && vim_strchr(buf->b_spell_ismw_mb, *p) != NULL))
+    if (*p < 256 ? wp->w_s->b_spell_ismw[*p]
+		 : (wp->w_s->b_spell_ismw_mb != NULL
+			     && vim_strchr(wp->w_s->b_spell_ismw_mb, *p) != NULL))
 	s = p + 1;
     else
 	s = p;
@@ -10347,7 +10346,7 @@ check_need_cap(lnum, col)
     colnr_T	endcol;
     regmatch_T	regmatch;
 
-    if (curbuf->b_cap_prog == NULL)
+    if (curwin->w_s->b_cap_prog == NULL)
 	return FALSE;
 
     line = ml_get_curline();
@@ -10378,7 +10377,7 @@ check_need_cap(lnum, col)
     if (endcol > 0)
     {
 	/* Check if sentence ends before the bad word. */
-	regmatch.regprog = curbuf->b_cap_prog;
+	regmatch.regprog = curwin->w_s->b_cap_prog;
 	regmatch.rm_ic = FALSE;
 	p = line + endcol;
 	for (;;)
@@ -10577,9 +10576,9 @@ spell_find_suggest(badptr, badlen, su, m
      * one in 'spelllang' that supports sound folding.  That's good for when
      * using multiple files for one language, it's not that bad when mixing
      * languages (e.g., "pl,en"). */
-    for (i = 0; i < curbuf->b_langp.ga_len; ++i)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, i);
+    for (i = 0; i < curbuf->b_s.b_langp.ga_len; ++i)
+    {
+	lp = LANGP_ENTRY(curbuf->b_s.b_langp, i);
 	if (lp->lp_sallang != NULL)
 	{
 	    su->su_sallang = lp->lp_sallang;
@@ -10862,9 +10861,9 @@ suggest_load_files()
     int		c;
 
     /* Do this for all languages that support sound folding. */
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	slang = lp->lp_slang;
 	if (slang->sl_sugtime != 0 && !slang->sl_sugloaded)
 	{
@@ -11215,9 +11214,9 @@ suggest_try_change(su)
     p = su->su_badptr + su->su_badlen;
     (void)spell_casefold(p, (int)STRLEN(p), fword + n, MAXWLEN - n);
 
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 
 	/* If reloading a spell file fails it's still in the list but
 	 * everything has been cleared. */
@@ -11445,7 +11444,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 	    fword_ends = (fword[sp->ts_fidx] == NUL
 			   || (soundfold
 			       ? vim_iswhite(fword[sp->ts_fidx])
-			       : !spell_iswordp(fword + sp->ts_fidx, curbuf)));
+			       : !spell_iswordp(fword + sp->ts_fidx, curwin)));
 	    tword[sp->ts_twordlen] = NUL;
 
 	    if (sp->ts_prefixdepth <= PFD_NOTSPECIAL
@@ -11663,11 +11662,11 @@ suggest_trie_walk(su, lp, fword, soundfo
 		     * char, e.g., "thes," -> "these". */
 		    p = fword + sp->ts_fidx;
 		    mb_ptr_back(fword, p);
-		    if (!spell_iswordp(p, curbuf))
+		    if (!spell_iswordp(p, curwin))
 		    {
 			p = preword + STRLEN(preword);
 			mb_ptr_back(preword, p);
-			if (spell_iswordp(p, curbuf))
+			if (spell_iswordp(p, curwin))
 			    newscore += SCORE_NONWORD;
 		    }
 
@@ -12270,7 +12269,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 
 	    /* Don't swap if the first character is not a word character.
 	     * SWAP3 etc. also don't make sense then. */
-	    if (!soundfold && !spell_iswordp(p, curbuf))
+	    if (!soundfold && !spell_iswordp(p, curwin))
 	    {
 		sp->ts_state = STATE_REP_INI;
 		break;
@@ -12283,7 +12282,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 		c = mb_ptr2char(p);
 		if (p[n] == NUL)
 		    c2 = NUL;
-		else if (!soundfold && !spell_iswordp(p + n, curbuf))
+		else if (!soundfold && !spell_iswordp(p + n, curwin))
 		    c2 = c; /* don't swap non-word char */
 		else
 		    c2 = mb_ptr2char(p + n);
@@ -12293,7 +12292,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 	    {
 		if (p[1] == NUL)
 		    c2 = NUL;
-		else if (!soundfold && !spell_iswordp(p + 1, curbuf))
+		else if (!soundfold && !spell_iswordp(p + 1, curwin))
 		    c2 = c; /* don't swap non-word char */
 		else
 		    c2 = p[1];
@@ -12375,7 +12374,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 		c = mb_ptr2char(p);
 		fl = mb_cptr2len(p + n);
 		c2 = mb_ptr2char(p + n);
-		if (!soundfold && !spell_iswordp(p + n + fl, curbuf))
+		if (!soundfold && !spell_iswordp(p + n + fl, curwin))
 		    c3 = c;	/* don't swap non-word char */
 		else
 		    c3 = mb_ptr2char(p + n + fl);
@@ -12385,7 +12384,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 	    {
 		c = *p;
 		c2 = p[1];
-		if (!soundfold && !spell_iswordp(p + 2, curbuf))
+		if (!soundfold && !spell_iswordp(p + 2, curwin))
 		    c3 = c;	/* don't swap non-word char */
 		else
 		    c3 = p[2];
@@ -12458,7 +12457,7 @@ suggest_trie_walk(su, lp, fword, soundfo
 		++p;
 	    }
 
-	    if (!soundfold && !spell_iswordp(p, curbuf))
+	    if (!soundfold && !spell_iswordp(p, curwin))
 	    {
 		/* Middle char is not a word char, skip the rotate.  First and
 		 * third char were already checked at swap and swap3. */
@@ -12934,9 +12933,9 @@ score_comp_sal(su)
 	return;
 
     /*	Use the sound-folding of the first language that supports it. */
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	if (lp->lp_slang->sl_sal.ga_len > 0)
 	{
 	    /* soundfold the bad word */
@@ -12990,9 +12989,9 @@ score_combine(su)
     slang_T	*slang = NULL;
 
     /* Add the alternate score to su_ga. */
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	if (lp->lp_slang->sl_sal.ga_len > 0)
 	{
 	    /* soundfold the bad word */
@@ -13165,9 +13164,9 @@ suggest_try_soundalike_prep()
 
     /* Do this for all languages that support sound folding and for which a
      * .sug file has been loaded. */
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	slang = lp->lp_slang;
 	if (slang->sl_sal.ga_len > 0 && slang->sl_sbyts != NULL)
 	    /* prepare the hashtable used by add_sound_suggest() */
@@ -13190,9 +13189,9 @@ suggest_try_soundalike(su)
 
     /* Do this for all languages that support sound folding and for which a
      * .sug file has been loaded. */
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	slang = lp->lp_slang;
 	if (slang->sl_sal.ga_len > 0 && slang->sl_sbyts != NULL)
 	{
@@ -13221,9 +13220,9 @@ suggest_try_soundalike_finish()
 
     /* Do this for all languages that support sound folding and for which a
      * .sug file has been loaded. */
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	slang = lp->lp_slang;
 	if (slang->sl_sal.ga_len > 0 && slang->sl_sbyts != NULL)
 	{
@@ -14000,11 +13999,11 @@ eval_soundfold(word)
     char_u	sound[MAXWLEN];
     int		lpi;
 
-    if (curwin->w_p_spell && *curbuf->b_p_spl != NUL)
+    if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
 	/* Use the sound-folding of the first language that supports it. */
-	for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi)
-	{
-	    lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+	for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+	{
+	    lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	    if (lp->lp_slang->sl_sal.ga_len > 0)
 	    {
 		/* soundfold the word */
@@ -14255,12 +14254,12 @@ spell_soundfold_sal(slang, inword, res)
 		if (*s == NUL
 			|| (*s == '^'
 			    && (i == 0 || !(word[i - 1] == ' '
-				      || spell_iswordp(word + i - 1, curbuf)))
+				      || spell_iswordp(word + i - 1, curwin)))
 			    && (*(s + 1) != '$'
-				|| (!spell_iswordp(word + i + k0, curbuf))))
+				|| (!spell_iswordp(word + i + k0, curwin))))
 			|| (*s == '$' && i > 0
-			    && spell_iswordp(word + i - 1, curbuf)
-			    && (!spell_iswordp(word + i + k0, curbuf))))
+			    && spell_iswordp(word + i - 1, curwin)
+			    && (!spell_iswordp(word + i + k0, curwin))))
 		{
 		    /* search for followup rules, if:    */
 		    /* followup and k > 1  and  NO '-' in searchstring */
@@ -14323,7 +14322,7 @@ spell_soundfold_sal(slang, inword, res)
 				    /* *s == '^' cuts */
 				    || (*s == '$'
 					    && !spell_iswordp(word + i + k0,
-								     curbuf)))
+								     curwin)))
 			    {
 				if (k0 == k)
 				    /* this is just a piece of the string */
@@ -14547,12 +14546,12 @@ spell_soundfold_wsal(slang, inword, res)
 		if (*s == NUL
 			|| (*s == '^'
 			    && (i == 0 || !(word[i - 1] == ' '
-				    || spell_iswordp_w(word + i - 1, curbuf)))
+				    || spell_iswordp_w(word + i - 1, curwin)))
 			    && (*(s + 1) != '$'
-				|| (!spell_iswordp_w(word + i + k0, curbuf))))
+				|| (!spell_iswordp_w(word + i + k0, curwin))))
 			|| (*s == '$' && i > 0
-			    && spell_iswordp_w(word + i - 1, curbuf)
-			    && (!spell_iswordp_w(word + i + k0, curbuf))))
+			    && spell_iswordp_w(word + i - 1, curwin)
+			    && (!spell_iswordp_w(word + i + k0, curwin))))
 		{
 		    /* search for followup rules, if:    */
 		    /* followup and k > 1  and  NO '-' in searchstring */
@@ -14619,7 +14618,7 @@ spell_soundfold_wsal(slang, inword, res)
 				    /* *s == '^' cuts */
 				    || (*s == '$'
 					 && !spell_iswordp_w(word + i + k0,
-								     curbuf)))
+								     curwin)))
 			    {
 				if (k0 == k)
 				    /* this is just a piece of the string */
@@ -15478,9 +15477,9 @@ ex_spellinfo(eap)
 	return;
 
     msg_start();
-    for (lpi = 0; lpi < curbuf->b_langp.ga_len && !got_int; ++lpi)
-    {
-	lp = LANGP_ENTRY(curbuf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len && !got_int; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	msg_puts((char_u *)"file: ");
 	msg_puts(lp->lp_slang->sl_fname);
 	msg_putchar('\n');
@@ -15507,17 +15506,15 @@ ex_spellinfo(eap)
 ex_spelldump(eap)
     exarg_T *eap;
 {
-    buf_T	*buf = curbuf;
-
     if (no_spell_checking(curwin))
 	return;
 
     /* Create a new empty buffer by splitting the window. */
     do_cmdline_cmd((char_u *)"new");
-    if (!bufempty() || !buf_valid(buf))
+    if (!bufempty() || !buf_valid(curbuf))
 	return;
 
-    spell_dump_compl(buf, NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0);
+    spell_dump_compl(NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0);
 
     /* Delete the empty line that we started with. */
     if (curbuf->b_ml.ml_line_count > 1)
@@ -15533,8 +15530,7 @@ ex_spelldump(eap)
  * 2. When "pat" is not NULL: add matching words to insert mode completion.
  */
     void
-spell_dump_compl(buf, pat, ic, dir, dumpflags_arg)
-    buf_T	*buf;	    /* buffer with spell checking */
+spell_dump_compl(pat, ic, dir, dumpflags_arg)
     char_u	*pat;	    /* leading part of the word */
     int		ic;	    /* ignore case */
     int		*dir;	    /* direction for adding matches */
@@ -15584,9 +15580,9 @@ spell_dump_compl(buf, pat, ic, dir, dump
 
     /* Find out if we can support regions: All languages must support the same
      * regions or none at all. */
-    for (lpi = 0; lpi < buf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(buf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	p = lp->lp_slang->sl_regions;
 	if (p[0] != 0)
 	{
@@ -15614,9 +15610,9 @@ spell_dump_compl(buf, pat, ic, dir, dump
     /*
      * Loop over all files loaded for the entries in 'spelllang'.
      */
-    for (lpi = 0; lpi < buf->b_langp.ga_len; ++lpi)
-    {
-	lp = LANGP_ENTRY(buf->b_langp, lpi);
+    for (lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi)
+    {
+	lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
 	slang = lp->lp_slang;
 	if (slang->sl_fbyts == NULL)	    /* reloading failed */
 	    continue;
@@ -15941,13 +15937,13 @@ dump_prefixes(slang, word, pat, dir, dum
  * Uses the spell-checking word characters.
  */
     char_u *
-spell_to_word_end(start, buf)
+spell_to_word_end(start, win)
     char_u  *start;
-    buf_T   *buf;
+    win_T   *win;
 {
     char_u  *p = start;
 
-    while (*p != NUL && spell_iswordp(p, buf))
+    while (*p != NUL && spell_iswordp(p, win))
 	mb_ptr_adv(p);
     return p;
 }
@@ -15985,7 +15981,7 @@ spell_word_start(startcol)
     {
 	col = (int)(p - line);
 	mb_ptr_back(line, p);
-	if (!spell_iswordp(p, curbuf))
+	if (!spell_iswordp(p, curwin))
 	    break;
 	col = 0;
     }
--- a/src/structs.h
+++ b/src/structs.h
@@ -213,6 +213,14 @@ typedef struct
 #endif
     int		wo_wrap;
 #define w_p_wrap w_onebuf_opt.wo_wrap	/* 'wrap' */
+#ifdef FEAT_CONCEAL
+    int		wo_conceal;		/* 'conceal' */
+# define w_p_conceal w_onebuf_opt.wo_conceal
+#endif
+#ifdef FEAT_CURSORBIND
+    int		wo_crb;
+# define w_p_crb w_onebuf_opt.wo_crb	/* 'cursorbind' */
+#endif
 
 #ifdef FEAT_EVAL
     int		wo_scriptID[WV_COUNT];	/* SIDs for window-local options */
@@ -769,7 +777,8 @@ struct keyentry
     keyentry_T	*ke_next;	/* next entry with identical "keyword[]" */
     struct sp_syn k_syn;	/* struct passed to in_id_list() */
     short	*next_list;	/* ID list for next match (if non-zero) */
-    short	flags;		/* see syntax.c */
+    int		flags;
+    int		k_char;		/* conceal substitute character */
     char_u	keyword[1];	/* actually longer */
 };
 
@@ -779,7 +788,7 @@ struct keyentry
 typedef struct buf_state
 {
     int		    bs_idx;	 /* index of pattern */
-    long	    bs_flags;	 /* flags for pattern */
+    int		    bs_flags;	 /* flags for pattern */
     reg_extmatch_T *bs_extmatch; /* external matches from start pattern */
 } bufstate_T;
 
@@ -968,6 +977,11 @@ struct stl_hlrec
     int		userhl;		/* 0: no HL, 1-9: User HL, < 0 for syn ID */
 };
 
+
+/*
+ * Syntax items - usually buffer-specific.
+ */
+
 /* Item for a hashtable.  "hi_key" can be one of three values:
  * NULL:	   Never been used
  * HI_KEY_REMOVED: Entry was removed
@@ -1140,6 +1154,73 @@ struct dictvar_S
 typedef struct qf_info_S qf_info_T;
 #endif
 
+typedef struct {
+#ifdef FEAT_SYN_HL
+    hashtab_T	b_keywtab;		/* syntax keywords hash table */
+    hashtab_T	b_keywtab_ic;		/* idem, ignore case */
+    int		b_syn_error;		/* TRUE when error occured in HL */
+    int		b_syn_ic;		/* ignore case for :syn cmds */
+    int		b_syn_spell;		/* SYNSPL_ values */
+    garray_T	b_syn_patterns;		/* table for syntax patterns */
+    garray_T	b_syn_clusters;		/* table for syntax clusters */
+    int		b_spell_cluster_id;	/* @Spell cluster ID or 0 */
+    int		b_nospell_cluster_id;	/* @NoSpell cluster ID or 0 */
+    int		b_syn_containedin;	/* TRUE when there is an item with a
+					   "containedin" argument */
+    int		b_syn_sync_flags;	/* flags about how to sync */
+    short	b_syn_sync_id;		/* group to sync on */
+    long	b_syn_sync_minlines;	/* minimal sync lines offset */
+    long	b_syn_sync_maxlines;	/* maximal sync lines offset */
+    long	b_syn_sync_linebreaks;	/* offset for multi-line pattern */
+    char_u	*b_syn_linecont_pat;	/* line continuation pattern */
+    regprog_T	*b_syn_linecont_prog;	/* line continuation program */
+    int		b_syn_linecont_ic;	/* ignore-case flag for above */
+    int		b_syn_topgrp;		/* for ":syntax include" */
+# ifdef FEAT_CONCEAL
+    int		b_syn_conceal;		/* auto-conceal for :syn cmds */
+# endif
+# ifdef FEAT_FOLDING
+    int		b_syn_folditems;	/* number of patterns with the HL_FOLD
+					   flag set */
+# endif
+/*
+ * b_sst_array[] contains the state stack for a number of lines, for the start
+ * of that line (col == 0).  This avoids having to recompute the syntax state
+ * too often.
+ * b_sst_array[] is allocated to hold the state for all displayed lines, and
+ * states for 1 out of about 20 other lines.
+ * b_sst_array		pointer to an array of synstate_T
+ * b_sst_len		number of entries in b_sst_array[]
+ * b_sst_first		pointer to first used entry in b_sst_array[] or NULL
+ * b_sst_firstfree	pointer to first free entry in b_sst_array[] or NULL
+ * b_sst_freecount	number of free entries in b_sst_array[]
+ * b_sst_check_lnum	entries after this lnum need to be checked for
+ *			validity (MAXLNUM means no check needed)
+ */
+    synstate_T	*b_sst_array;
+    int		b_sst_len;
+    synstate_T	*b_sst_first;
+    synstate_T	*b_sst_firstfree;
+    int		b_sst_freecount;
+    linenr_T	b_sst_check_lnum;
+    short_u	b_sst_lasttick;	/* last display tick */
+#endif /* FEAT_SYN_HL */
+
+#ifdef FEAT_SPELL
+    /* for spell checking */
+    garray_T	b_langp;	/* list of pointers to slang_T, see spell.c */
+    char_u	b_spell_ismw[256];/* flags: is midword char */
+# ifdef FEAT_MBYTE
+    char_u	*b_spell_ismw_mb; /* multi-byte midword chars */
+# endif
+    char_u	*b_p_spc;	/* 'spellcapcheck' */
+    regprog_T	*b_cap_prog;	/* program for 'spellcapcheck' */
+    char_u	*b_p_spf;	/* 'spellfile' */
+    char_u	*b_p_spl;	/* 'spelllang' */
+#endif
+} synblock_T;
+
+
 /*
  * buffer: structure that holds information about one file
  *
@@ -1427,12 +1508,6 @@ struct file_buffer
     long	b_p_smc;	/* 'synmaxcol' */
     char_u	*b_p_syn;	/* 'syntax' */
 #endif
-#ifdef FEAT_SPELL
-    char_u	*b_p_spc;	/* 'spellcapcheck' */
-    regprog_T	*b_cap_prog;	/* program for 'spellcapcheck' */
-    char_u	*b_p_spf;	/* 'spellfile' */
-    char_u	*b_p_spl;	/* 'spelllang' */
-#endif
     long	b_p_ts;		/* 'tabstop' */
     int		b_p_tx;		/* 'textmode' */
     long	b_p_tw;		/* 'textwidth' */
@@ -1528,61 +1603,10 @@ struct file_buffer
     void	*b_ruby_ref;
 #endif
 
-#ifdef FEAT_SYN_HL
-    hashtab_T	b_keywtab;		/* syntax keywords hash table */
-    hashtab_T	b_keywtab_ic;		/* idem, ignore case */
-    int		b_syn_error;		/* TRUE when error occured in HL */
-    int		b_syn_ic;		/* ignore case for :syn cmds */
-    int		b_syn_spell;		/* SYNSPL_ values */
-    garray_T	b_syn_patterns;		/* table for syntax patterns */
-    garray_T	b_syn_clusters;		/* table for syntax clusters */
-    int		b_spell_cluster_id;	/* @Spell cluster ID or 0 */
-    int		b_nospell_cluster_id;	/* @NoSpell cluster ID or 0 */
-    int		b_syn_containedin;	/* TRUE when there is an item with a
-					   "containedin" argument */
-    int		b_syn_sync_flags;	/* flags about how to sync */
-    short	b_syn_sync_id;		/* group to sync on */
-    long	b_syn_sync_minlines;	/* minimal sync lines offset */
-    long	b_syn_sync_maxlines;	/* maximal sync lines offset */
-    long	b_syn_sync_linebreaks;	/* offset for multi-line pattern */
-    char_u	*b_syn_linecont_pat;	/* line continuation pattern */
-    regprog_T	*b_syn_linecont_prog;	/* line continuation program */
-    int		b_syn_linecont_ic;	/* ignore-case flag for above */
-    int		b_syn_topgrp;		/* for ":syntax include" */
-# ifdef FEAT_FOLDING
-    int		b_syn_folditems;	/* number of patterns with the HL_FOLD
-					   flag set */
-# endif
-/*
- * b_sst_array[] contains the state stack for a number of lines, for the start
- * of that line (col == 0).  This avoids having to recompute the syntax state
- * too often.
- * b_sst_array[] is allocated to hold the state for all displayed lines, and
- * states for 1 out of about 20 other lines.
- * b_sst_array		pointer to an array of synstate_T
- * b_sst_len		number of entries in b_sst_array[]
- * b_sst_first		pointer to first used entry in b_sst_array[] or NULL
- * b_sst_firstfree	pointer to first free entry in b_sst_array[] or NULL
- * b_sst_freecount	number of free entries in b_sst_array[]
- * b_sst_check_lnum	entries after this lnum need to be checked for
- *			validity (MAXLNUM means no check needed)
- */
-    synstate_T	*b_sst_array;
-    int		b_sst_len;
-    synstate_T	*b_sst_first;
-    synstate_T	*b_sst_firstfree;
-    int		b_sst_freecount;
-    linenr_T	b_sst_check_lnum;
-    short_u	b_sst_lasttick;	/* last display tick */
-#endif /* FEAT_SYN_HL */
-
-#ifdef FEAT_SPELL
-    /* for spell checking */
-    garray_T	b_langp;	/* list of pointers to slang_T, see spell.c */
-    char_u	b_spell_ismw[256];/* flags: is midword char */
-# ifdef FEAT_MBYTE
-    char_u	*b_spell_ismw_mb; /* multi-byte midword chars */
-# endif
+#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
+    synblock_T	b_s;		/* Info related to syntax highlighting.  w_s
+				 * normally points to this, but some windows
+				 * may use a different synblock_T. */
 #endif
 
 #ifdef FEAT_SIGNS
@@ -1767,6 +1791,10 @@ struct window_S
     buf_T	*w_buffer;	    /* buffer we are a window into (used
 				       often, keep it the first item!) */
 
+#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
+    synblock_T	*w_s;
+#endif
+
 #ifdef FEAT_WINDOWS
     win_T	*w_prev;	    /* link to previous window */
     win_T	*w_next;	    /* link to next window */
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -140,7 +140,10 @@ typedef struct syn_pattern
 {
     char	 sp_type;		/* see SPTYPE_ defines below */
     char	 sp_syncing;		/* this item used for syncing */
-    short	 sp_flags;		/* see HL_ defines below */
+    int		 sp_flags;		/* see HL_ defines below */
+#ifdef FEAT_CONCEAL
+    int		 sp_char;		/* conceal substitute character */
+#endif
     struct sp_syn sp_syn;		/* struct passed to in_id_list() */
     short	 sp_syn_match_id;	/* highlight group ID of pattern */
     char_u	*sp_pattern;		/* regexp to match, pattern */
@@ -166,25 +169,6 @@ typedef struct syn_pattern
 #define SPTYPE_END	3	/* match a regexp, end of item */
 #define SPTYPE_SKIP	4	/* match a regexp, skip within item */
 
-#define HL_CONTAINED	0x01	/* not used on toplevel */
-#define HL_TRANSP	0x02	/* has no highlighting	*/
-#define HL_ONELINE	0x04	/* match within one line only */
-#define HL_HAS_EOL	0x08	/* end pattern that matches with $ */
-#define HL_SYNC_HERE	0x10	/* sync point after this item (syncing only) */
-#define HL_SYNC_THERE	0x20	/* sync point at current line (syncing only) */
-#define HL_MATCH	0x40	/* use match ID instead of item ID */
-#define HL_SKIPNL	0x80	/* nextgroup can skip newlines */
-#define HL_SKIPWHITE	0x100	/* nextgroup can skip white space */
-#define HL_SKIPEMPTY	0x200	/* nextgroup can skip empty lines */
-#define HL_KEEPEND	0x400	/* end match always kept */
-#define HL_EXCLUDENL	0x800	/* exclude NL from match */
-#define HL_DISPLAY	0x1000	/* only used for displaying, not syncing */
-#define HL_FOLD		0x2000	/* define fold */
-#define HL_EXTEND	0x4000	/* ignore a keepend */
-/* These don't fit in a short, thus can't be used for syntax items, only for
- * si_flags and bs_flags. */
-#define HL_MATCHCONT	0x8000	/* match continued from previous line */
-#define HL_TRANS_CONT	0x10000L /* transparent item without contains arg */
 
 #define SYN_ITEMS(buf)	((synpat_T *)((buf)->b_syn_patterns.ga_data))
 
@@ -208,6 +192,10 @@ static int current_attr = 0;	    /* attr
 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_sub_char = 0;
+#endif
 
 typedef struct syn_cluster_S
 {
@@ -294,6 +282,9 @@ typedef struct state_item
     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_char;		/* 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
@@ -351,6 +342,7 @@ static reg_extmatch_T *next_match_extmat
  */
 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 */
 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
@@ -370,7 +362,7 @@ static void syn_start_line __ARGS((void)
 static void syn_update_ends __ARGS((int startofline));
 static void syn_stack_alloc __ARGS((void));
 static int syn_stack_cleanup __ARGS((void));
-static void syn_stack_free_entry __ARGS((buf_T *buf, synstate_T *p));
+static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
 static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
 static synstate_T *store_current_state __ARGS((void));
 static void load_current_state __ARGS((synstate_T *from));
@@ -390,6 +382,7 @@ static int in_id_list __ARGS((stateitem_
 static int push_current_state __ARGS((int idx));
 static void pop_current_state __ARGS((void));
 
+static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
 static void find_endpos __ARGS((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 clear_syn_state __ARGS((synstate_T *p));
 static void clear_current_state __ARGS((void));
@@ -400,14 +393,15 @@ static void syn_add_end_off __ARGS((lpos
 static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
 static char_u *syn_getcurline __ARGS((void));
 static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
-static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si));
+static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp));
 static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
 static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
 static void syntax_sync_clear __ARGS((void));
-static void syn_remove_pattern __ARGS((buf_T *buf, int idx));
-static void syn_clear_pattern __ARGS((buf_T *buf, int i));
-static void syn_clear_cluster __ARGS((buf_T *buf, int i));
+static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
+static void syn_clear_pattern __ARGS((synblock_T *block, int i));
+static void syn_clear_cluster __ARGS((synblock_T *block, int i));
 static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
+static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
 static void syn_clear_one __ARGS((int id, int syncing));
 static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
 static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
@@ -418,6 +412,7 @@ static void syn_cmd_onoff __ARGS((exarg_
 static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
 static void syn_lines_msg __ARGS((void));
 static void syn_match_msg __ARGS((void));
+static void syn_stack_free_block __ARGS((synblock_T *block));
 static void syn_list_one __ARGS((int id, int syncing, int link_only));
 static void syn_list_cluster __ARGS((int id));
 static void put_id_list __ARGS((char_u *name, short *list, int attr));
@@ -425,9 +420,9 @@ static void put_pattern __ARGS((char *s,
 static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
 static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
 static void clear_keywtab __ARGS((hashtab_T *ht));
-static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list));
+static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char));
 static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
-static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt));
+static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
 static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
 static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
 static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
@@ -475,10 +470,11 @@ syntax_start(wp, lnum)
      * Also do this when a change was made, the current state may be invalid
      * then.
      */
-    if (syn_buf != wp->w_buffer || changedtick != syn_buf->b_changedtick)
+    if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
     {
 	invalidate_current_state();
 	syn_buf = wp->w_buffer;
+	syn_block = wp->w_s;
     }
     changedtick = syn_buf->b_changedtick;
     syn_win = wp;
@@ -487,9 +483,9 @@ syntax_start(wp, lnum)
      * Allocate syntax stack when needed.
      */
     syn_stack_alloc();
-    if (syn_buf->b_sst_array == NULL)
+    if (syn_block->b_sst_array == NULL)
 	return;		/* out of memory */
-    syn_buf->b_sst_lasttick = display_tick;
+    syn_block->b_sst_lasttick = display_tick;
 
     /*
      * If the state of the end of the previous line is useful, store it.
@@ -520,17 +516,17 @@ syntax_start(wp, lnum)
      * 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(&current_state) && syn_buf->b_sst_array != NULL)
+    if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
     {
 	/* Find last valid saved state before start_lnum. */
-	for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
+	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_buf->b_syn_sync_minlines)
+		if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
 		    last_min_valid = p;
 	    }
 	}
@@ -545,7 +541,7 @@ syntax_start(wp, lnum)
     if (INVALID_STATE(&current_state))
     {
 	syn_sync(wp, lnum, last_valid);
-	first_stored = current_lnum + syn_buf->b_syn_sync_minlines;
+	first_stored = current_lnum + syn_block->b_syn_sync_minlines;
     }
     else
 	first_stored = current_lnum;
@@ -554,10 +550,10 @@ syntax_start(wp, lnum)
      * Advance from the sync point or saved state until the current line.
      * Save some entries for syncing with later on.
      */
-    if (syn_buf->b_sst_len <= Rows)
+    if (syn_block->b_sst_len <= Rows)
 	dist = 999999;
     else
-	dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
+	dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
     while (current_lnum < lnum)
     {
 	syn_start_line();
@@ -574,7 +570,7 @@ syntax_start(wp, lnum)
 	    if (prev == NULL)
 		prev = syn_stack_find_entry(current_lnum - 1);
 	    if (prev == NULL)
-		sp = syn_buf->b_sst_first;
+		sp = syn_block->b_sst_first;
 	    else
 		sp = prev;
 	    while (sp != NULL && sp->sst_lnum < current_lnum)
@@ -706,19 +702,19 @@ syn_sync(wp, start_lnum, last_valid)
      * where N is minlines * 1.5, or minlines * 2 if minlines is small.
      * Watch out for overflow when minlines is MAXLNUM.
      */
-    if (syn_buf->b_syn_sync_minlines > start_lnum)
+    if (syn_block->b_syn_sync_minlines > start_lnum)
 	start_lnum = 1;
     else
     {
-	if (syn_buf->b_syn_sync_minlines == 1)
+	if (syn_block->b_syn_sync_minlines == 1)
 	    lnum = 1;
-	else if (syn_buf->b_syn_sync_minlines < 10)
-	    lnum = syn_buf->b_syn_sync_minlines * 2;
+	else if (syn_block->b_syn_sync_minlines < 10)
+	    lnum = syn_block->b_syn_sync_minlines * 2;
 	else
-	    lnum = syn_buf->b_syn_sync_minlines * 3 / 2;
-	if (syn_buf->b_syn_sync_maxlines != 0
-				       && lnum > syn_buf->b_syn_sync_maxlines)
-	    lnum = syn_buf->b_syn_sync_maxlines;
+	    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
@@ -729,7 +725,7 @@ syn_sync(wp, start_lnum, last_valid)
     /*
      * 1. Search backwards for the end of a C-style comment.
      */
-    if (syn_buf->b_syn_sync_flags & SF_CCOMMENT)
+    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(). */
@@ -759,11 +755,12 @@ syn_sync(wp, start_lnum, last_valid)
 	 * defines the comment.
 	 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
 	 */
-	if (find_start_comment((int)syn_buf->b_syn_sync_maxlines) != NULL)
-	{
-	    for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
-		if (SYN_ITEMS(syn_buf)[idx].sp_syn.id == syn_buf->b_syn_sync_id
-			&& SYN_ITEMS(syn_buf)[idx].sp_type == SPTYPE_START)
+	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)
@@ -781,11 +778,11 @@ syn_sync(wp, start_lnum, last_valid)
     /*
      * 2. Search backwards for given sync patterns.
      */
-    else if (syn_buf->b_syn_sync_flags & SF_MATCH)
-    {
-	if (syn_buf->b_syn_sync_maxlines != 0
-				 && start_lnum > syn_buf->b_syn_sync_maxlines)
-	    break_lnum = start_lnum - syn_buf->b_syn_sync_maxlines;
+    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;
 
@@ -849,7 +846,7 @@ syn_sync(wp, start_lnum, last_valid)
 			}
 			else
 			{
-			    spp = &(SYN_ITEMS(syn_buf)[cur_si->si_idx]);
+			    spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
 			    found_flags = spp->sp_flags;
 			    found_match_idx = spp->sp_sync_idx;
 			}
@@ -952,10 +949,10 @@ syn_match_linecont(lnum)
 {
     regmmatch_T regmatch;
 
-    if (syn_buf->b_syn_linecont_prog != NULL)
-    {
-	regmatch.rmm_ic = syn_buf->b_syn_linecont_ic;
-	regmatch.regprog = syn_buf->b_syn_linecont_prog;
+    if (syn_block->b_syn_linecont_prog != NULL)
+    {
+	regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
+	regmatch.regprog = syn_block->b_syn_linecont_prog;
 	return syn_regexec(&regmatch, lnum, (colnr_T)0);
     }
     return FALSE;
@@ -1002,7 +999,7 @@ syn_update_ends(startofline)
 	{
 	    cur_si = &CUR_STATE(i);
 	    if (cur_si->si_idx >= 0
-		    && (SYN_ITEMS(syn_buf)[cur_si->si_idx]).sp_type
+		    && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
 							       == SPTYPE_MATCH
 		    && cur_si->si_m_endpos.lnum < current_lnum)
 	    {
@@ -1088,30 +1085,39 @@ syn_update_ends(startofline)
  * number of entries SST_MAX_ENTRIES, and the distance is computed.
  */
 
+    static void
+syn_stack_free_block(block)
+    synblock_T	*block;
+{
+    synstate_T	*p;
+
+    if (block->b_sst_array != NULL)
+    {
+	for (p = block->b_sst_first; p != NULL; p = p->sst_next)
+	    clear_syn_state(p);
+	vim_free(block->b_sst_array);
+	block->b_sst_array = NULL;
+	block->b_sst_len = 0;
+    }
+}
 /*
  * Free b_sst_array[] for buffer "buf".
  * Used when syntax items changed to force resyncing everywhere.
  */
     void
-syn_stack_free_all(buf)
-    buf_T	*buf;
-{
-    synstate_T	*p;
+syn_stack_free_all(block)
+    synblock_T	*block;
+{
     win_T	*wp;
 
-    if (buf->b_sst_array != NULL)
-    {
-	for (p = buf->b_sst_first; p != NULL; p = p->sst_next)
-	    clear_syn_state(p);
-	vim_free(buf->b_sst_array);
-	buf->b_sst_array = NULL;
-	buf->b_sst_len = 0;
-    }
+    syn_stack_free_block(block);
+
+
 #ifdef FEAT_FOLDING
     /* When using "syntax" fold method, must update all folds. */
     FOR_ALL_WINDOWS(wp)
     {
-	if (wp->w_buffer == buf && foldmethodIsSyntax(wp))
+	if (wp->w_s == block && foldmethodIsSyntax(wp))
 	    foldUpdateAll(wp);
     }
 #endif
@@ -1135,7 +1141,7 @@ syn_stack_alloc()
 	len = SST_MIN_ENTRIES;
     else if (len > SST_MAX_ENTRIES)
 	len = SST_MAX_ENTRIES;
-    if (syn_buf->b_sst_len > len * 2 || syn_buf->b_sst_len < len)
+    if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
     {
 	/* Allocate 50% too much, to avoid reallocating too often. */
 	len = syn_buf->b_ml.ml_line_count;
@@ -1145,15 +1151,15 @@ syn_stack_alloc()
 	else if (len > SST_MAX_ENTRIES)
 	    len = SST_MAX_ENTRIES;
 
-	if (syn_buf->b_sst_array != NULL)
+	if (syn_block->b_sst_array != NULL)
 	{
 	    /* When shrinking the array, cleanup the existing stack.
 	     * Make sure that all valid entries fit in the new array. */
-	    while (syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2 > len
+	    while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
 		    && syn_stack_cleanup())
 		;
-	    if (len < syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2)
-		len = syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2;
+	    if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
+		len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
 	}
 
 	sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
@@ -1161,10 +1167,10 @@ syn_stack_alloc()
 	    return;
 
 	to = sstp - 1;
-	if (syn_buf->b_sst_array != NULL)
+	if (syn_block->b_sst_array != NULL)
 	{
 	    /* Move the states from the old array to the new one. */
-	    for (from = syn_buf->b_sst_first; from != NULL;
+	    for (from = syn_block->b_sst_first; from != NULL;
 							from = from->sst_next)
 	    {
 		++to;
@@ -1175,24 +1181,24 @@ syn_stack_alloc()
 	if (to != sstp - 1)
 	{
 	    to->sst_next = NULL;
-	    syn_buf->b_sst_first = sstp;
-	    syn_buf->b_sst_freecount = len - (int)(to - sstp) - 1;
+	    syn_block->b_sst_first = sstp;
+	    syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
 	}
 	else
 	{
-	    syn_buf->b_sst_first = NULL;
-	    syn_buf->b_sst_freecount = len;
+	    syn_block->b_sst_first = NULL;
+	    syn_block->b_sst_freecount = len;
 	}
 
 	/* Create the list of free entries. */
-	syn_buf->b_sst_firstfree = to + 1;
+	syn_block->b_sst_firstfree = to + 1;
 	while (++to < sstp + len)
 	    to->sst_next = to + 1;
 	(sstp + len - 1)->sst_next = NULL;
 
-	vim_free(syn_buf->b_sst_array);
-	syn_buf->b_sst_array = sstp;
-	syn_buf->b_sst_len = len;
+	vim_free(syn_block->b_sst_array);
+	syn_block->b_sst_array = sstp;
+	syn_block->b_sst_len = len;
     }
 }
 
@@ -1206,16 +1212,32 @@ syn_stack_alloc()
 syn_stack_apply_changes(buf)
     buf_T	*buf;
 {
+    win_T	*wp;
+
+    syn_stack_apply_changes_block(&buf->b_s, buf);
+
+    FOR_ALL_WINDOWS(wp)
+    {
+	if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
+	    syn_stack_apply_changes_block(wp->w_s, buf);
+    }
+}
+
+    static void
+syn_stack_apply_changes_block(block, buf)
+    synblock_T	*block;
+    buf_T	*buf;
+{
     synstate_T	*p, *prev, *np;
     linenr_T	n;
 
-    if (buf->b_sst_array == NULL)	/* nothing to do */
+    if (block->b_sst_array == NULL)	/* nothing to do */
 	return;
 
     prev = NULL;
-    for (p = buf->b_sst_first; p != NULL; )
-    {
-	if (p->sst_lnum + buf->b_syn_sync_linebreaks > buf->b_mod_top)
+    for (p = block->b_sst_first; p != NULL; )
+    {
+	if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
 	{
 	    n = p->sst_lnum + buf->b_mod_xlines;
 	    if (n <= buf->b_mod_bot)
@@ -1223,10 +1245,10 @@ syn_stack_apply_changes(buf)
 		/* this state is inside the changed area, remove it */
 		np = p->sst_next;
 		if (prev == NULL)
-		    buf->b_sst_first = np;
+		    block->b_sst_first = np;
 		else
 		    prev->sst_next = np;
-		syn_stack_free_entry(buf, p);
+		syn_stack_free_entry(block, p);
 		p = np;
 		continue;
 	    }
@@ -1264,28 +1286,28 @@ syn_stack_cleanup()
     int		dist;
     int		retval = FALSE;
 
-    if (syn_buf->b_sst_array == NULL || syn_buf->b_sst_first == NULL)
+    if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
 	return retval;
 
     /* Compute normal distance between non-displayed entries. */
-    if (syn_buf->b_sst_len <= Rows)
+    if (syn_block->b_sst_len <= Rows)
 	dist = 999999;
     else
-	dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
+	dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
 
     /*
      * Go through the list to find the "tick" for the oldest entry that can
      * be removed.  Set "above" when the "tick" for the oldest entry is above
      * "b_sst_lasttick" (the display tick wraps around).
      */
-    tick = syn_buf->b_sst_lasttick;
+    tick = syn_block->b_sst_lasttick;
     above = FALSE;
-    prev = syn_buf->b_sst_first;
+    prev = syn_block->b_sst_first;
     for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
     {
 	if (prev->sst_lnum + dist > p->sst_lnum)
 	{
-	    if (p->sst_tick > syn_buf->b_sst_lasttick)
+	    if (p->sst_tick > syn_block->b_sst_lasttick)
 	    {
 		if (!above || p->sst_tick < tick)
 		    tick = p->sst_tick;
@@ -1300,14 +1322,14 @@ syn_stack_cleanup()
      * Go through the list to make the entries for the oldest tick at an
      * interval of several lines.
      */
-    prev = syn_buf->b_sst_first;
+    prev = syn_block->b_sst_first;
     for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
     {
 	if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
 	{
 	    /* Move this entry from used list to free list */
 	    prev->sst_next = p->sst_next;
-	    syn_stack_free_entry(syn_buf, p);
+	    syn_stack_free_entry(syn_block, p);
 	    p = prev;
 	    retval = TRUE;
 	}
@@ -1320,14 +1342,14 @@ syn_stack_cleanup()
  * Move the entry into the free list.
  */
     static void
-syn_stack_free_entry(buf, p)
-    buf_T	*buf;
+syn_stack_free_entry(block, p)
+    synblock_T	*block;
     synstate_T	*p;
 {
     clear_syn_state(p);
-    p->sst_next = buf->b_sst_firstfree;
-    buf->b_sst_firstfree = p;
-    ++buf->b_sst_freecount;
+    p->sst_next = block->b_sst_firstfree;
+    block->b_sst_firstfree = p;
+    ++block->b_sst_freecount;
 }
 
 /*
@@ -1341,7 +1363,7 @@ syn_stack_find_entry(lnum)
     synstate_T	*p, *prev;
 
     prev = NULL;
-    for (p = syn_buf->b_sst_first; p != NULL; prev = p, p = p->sst_next)
+    for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
     {
 	if (p->sst_lnum == lnum)
 	    return p;
@@ -1383,19 +1405,19 @@ store_current_state()
 	if (sp != NULL)
 	{
 	    /* find "sp" in the list and remove it */
-	    if (syn_buf->b_sst_first == sp)
+	    if (syn_block->b_sst_first == sp)
 		/* it's the first entry */
-		syn_buf->b_sst_first = sp->sst_next;
+		syn_block->b_sst_first = sp->sst_next;
 	    else
 	    {
 		/* find the entry just before this one to adjust sst_next */
-		for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
+		for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
 		    if (p->sst_next == sp)
 			break;
 		if (p != NULL)	/* just in case */
 		    p->sst_next = sp->sst_next;
 	    }
-	    syn_stack_free_entry(syn_buf, sp);
+	    syn_stack_free_entry(syn_block, sp);
 	    sp = NULL;
 	}
     }
@@ -1405,27 +1427,27 @@ store_current_state()
 	 * Add a new entry
 	 */
 	/* If no free items, cleanup the array first. */
-	if (syn_buf->b_sst_freecount == 0)
+	if (syn_block->b_sst_freecount == 0)
 	{
 	    (void)syn_stack_cleanup();
 	    /* "sp" may have been moved to the freelist now */
 	    sp = syn_stack_find_entry(current_lnum);
 	}
 	/* Still no free items?  Must be a strange problem... */
-	if (syn_buf->b_sst_freecount == 0)
+	if (syn_block->b_sst_freecount == 0)
 	    sp = NULL;
 	else
 	{
 	    /* Take the first item from the free list and put it in the used
 	     * list, after *sp */
-	    p = syn_buf->b_sst_firstfree;
-	    syn_buf->b_sst_firstfree = p->sst_next;
-	    --syn_buf->b_sst_freecount;
+	    p = syn_block->b_sst_firstfree;
+	    syn_block->b_sst_firstfree = p->sst_next;
+	    --syn_block->b_sst_freecount;
 	    if (sp == NULL)
 	    {
 		/* Insert in front of the list */
-		p->sst_next = syn_buf->b_sst_first;
-		syn_buf->b_sst_first = p;
+		p->sst_next = syn_block->b_sst_first;
+		syn_block->b_sst_first = p;
 	    }
 	    else
 	    {
@@ -1502,7 +1524,7 @@ load_current_state(from)
 	    CUR_STATE(i).si_m_lnum = 0;
 	    if (CUR_STATE(i).si_idx >= 0)
 		CUR_STATE(i).si_next_list =
-		       (SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_next_list;
+		     (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
 	    else
 		CUR_STATE(i).si_next_list = NULL;
 	    update_si_attr(i);
@@ -1564,7 +1586,7 @@ syn_stack_equal(sp)
 			if (bsx->matches[j] == NULL
 				|| six->matches[j] == NULL)
 			    break;
-			if ((SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_ic
+			if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
 				? MB_STRICMP(bsx->matches[j],
 							 six->matches[j]) != 0
 				: STRCMP(bsx->matches[j], six->matches[j]) != 0)
@@ -1701,7 +1723,7 @@ syn_finish_line(syncing)
 		 */
 		cur_si = &CUR_STATE(current_state.ga_len - 1);
 		if (cur_si->si_idx >= 0
-			&& (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags
+			&& (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
 					      & (HL_SYNC_HERE|HL_SYNC_THERE)))
 		    return TRUE;
 
@@ -1730,8 +1752,9 @@ syn_finish_line(syncing)
  * done.
  */
     int
-get_syntax_attr(col, can_spell, keep_state)
+get_syntax_attr(col, p_flags, can_spell, keep_state)
     colnr_T	col;
+    int		*p_flags UNUSED;
     int		*can_spell;
     int		keep_state;	/* keep state of char at "col" */
 {
@@ -1740,12 +1763,12 @@ get_syntax_attr(col, can_spell, keep_sta
     if (can_spell != NULL)
 	/* Default: Only do spelling when there is no @Spell cluster or when
 	 * ":syn spell toplevel" was used. */
-	*can_spell = syn_buf->b_syn_spell == SYNSPL_DEFAULT
-		    ? (syn_buf->b_spell_cluster_id == 0)
-		    : (syn_buf->b_syn_spell == SYNSPL_TOP);
+	*can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
+		    ? (syn_block->b_spell_cluster_id == 0)
+		    : (syn_block->b_syn_spell == SYNSPL_TOP);
 
     /* check for out of memory situation */
-    if (syn_buf->b_sst_array == NULL)
+    if (syn_block->b_sst_array == NULL)
 	return 0;
 
     /* After 'synmaxcol' the attribute is always zero. */
@@ -1773,6 +1796,10 @@ get_syntax_attr(col, can_spell, keep_sta
 	++current_col;
     }
 
+#ifdef FEAT_CONCEAL
+    if (p_flags != NULL)
+	*p_flags = current_flags;
+#endif
     return attr;
 }
 
@@ -1799,6 +1826,7 @@ syn_current_attr(syncing, displaying, ca
     int		startcol;
     int		endcol;
     long	flags;
+    int		cchar;
     short	*next_list;
     int		found_match;		    /* found usable match */
     static int	try_next_column = FALSE;    /* must try in next col */
@@ -1854,8 +1882,8 @@ syn_current_attr(syncing, displaying, ca
 
     /* Only check for keywords when not syncing and there are some. */
     do_keywords = !syncing
-		    && (syn_buf->b_keywtab.ht_used > 0
-			    || syn_buf->b_keywtab_ic.ht_used > 0);
+		    && (syn_block->b_keywtab.ht_used > 0
+			    || syn_block->b_keywtab_ic.ht_used > 0);
 
     /* Init the list of zero-width matches with a nextlist.  This is used to
      * avoid matching the same item in the same position twice. */
@@ -1884,7 +1912,7 @@ syn_current_attr(syncing, displaying, ca
 	else
 	    cur_si = NULL;
 
-	if (syn_buf->b_syn_containedin || cur_si == NULL
+	if (syn_block->b_syn_containedin || cur_si == NULL
 					      || cur_si->si_cont_list != NULL)
 	{
 	    /*
@@ -1905,7 +1933,8 @@ syn_current_attr(syncing, displaying, ca
 			       , syn_buf)))
 	      {
 		syn_id = check_keyword_id(line, (int)current_col,
-					 &endcol, &flags, &next_list, cur_si);
+					 &endcol, &flags, &next_list, cur_si,
+					 &cchar);
 		if (syn_id != 0)
 		{
 		    if (push_current_state(KEYWORD_IDX) == OK)
@@ -1921,6 +1950,13 @@ syn_current_attr(syncing, displaying, ca
 			cur_si->si_ends = TRUE;
 			cur_si->si_end_idx = 0;
 			cur_si->si_flags = flags;
+#ifdef FEAT_CONCEAL
+			cur_si->si_char = cchar;
+			if (current_state.ga_len > 1)
+			    cur_si->si_flags |=
+				  CUR_STATE(current_state.ga_len - 2).si_flags
+								 & HL_CONCEAL;
+#endif
 			cur_si->si_id = syn_id;
 			cur_si->si_trans_id = syn_id;
 			if (flags & HL_TRANSP)
@@ -1953,7 +1989,7 @@ syn_current_attr(syncing, displaying, ca
 	    /*
 	     * 3. Check for patterns (only if no keyword found).
 	     */
-	    if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
+	    if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
 	    {
 		/*
 		 * If we didn't check for a match yet, or we are past it, check
@@ -1969,9 +2005,9 @@ syn_current_attr(syncing, displaying, ca
 		     */
 		    next_match_idx = 0;		/* no match in this line yet */
 		    next_match_col = MAXCOL;
-		    for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
+		    for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
 		    {
-			spp = &(SYN_ITEMS(syn_buf)[idx]);
+			spp = &(SYN_ITEMS(syn_block)[idx]);
 			if (	   spp->sp_syncing == syncing
 				&& (displaying || !(spp->sp_flags & HL_DISPLAY))
 				&& (spp->sp_type == SPTYPE_MATCH
@@ -2147,7 +2183,7 @@ syn_current_attr(syncing, displaying, ca
 
 		    /* When a zero-width item matched which has a nextgroup,
 		     * don't push the item but set nextgroup. */
-		    lspp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
+		    lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
 		    if (next_match_m_endpos.lnum == current_lnum
 			    && next_match_m_endpos.col == current_col
 			    && lspp->sp_next_list != NULL)
@@ -2219,6 +2255,9 @@ syn_current_attr(syncing, displaying, ca
     current_id = 0;
     current_trans_id = 0;
 #endif
+#ifdef FEAT_CONCEAL
+    current_flags = 0;
+#endif
     if (cur_si != NULL)
     {
 #ifndef FEAT_EVAL
@@ -2240,6 +2279,10 @@ syn_current_attr(syncing, displaying, ca
 		current_id = sip->si_id;
 #endif
 		current_trans_id = sip->si_trans_id;
+#ifdef FEAT_CONCEAL
+		current_flags = sip->si_flags;
+		current_sub_char = sip->si_char;
+#endif
 		break;
 	    }
 	}
@@ -2252,16 +2295,17 @@ syn_current_attr(syncing, displaying, ca
 	     * set "can_spell" to TRUE if spell checking is supposed to be
 	     * done in the current item.
 	     */
-	    if (syn_buf->b_spell_cluster_id == 0)
+	    if (syn_block->b_spell_cluster_id == 0)
 	    {
 		/* There is no @Spell cluster: Do spelling for items without
 		 * @NoSpell cluster. */
-		if (syn_buf->b_nospell_cluster_id == 0 || current_trans_id == 0)
-		    *can_spell = (syn_buf->b_syn_spell != SYNSPL_NOTOP);
+		if (syn_block->b_nospell_cluster_id == 0
+						     || current_trans_id == 0)
+		    *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
 		else
 		{
 		    sps.inc_tag = 0;
-		    sps.id = syn_buf->b_nospell_cluster_id;
+		    sps.id = syn_block->b_nospell_cluster_id;
 		    sps.cont_in_list = NULL;
 		    *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
 		}
@@ -2273,17 +2317,17 @@ syn_current_attr(syncing, displaying, ca
 		 * At the toplevel only spell check when ":syn spell toplevel"
 		 * was used. */
 		if (current_trans_id == 0)
-		    *can_spell = (syn_buf->b_syn_spell == SYNSPL_TOP);
+		    *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
 		else
 		{
 		    sps.inc_tag = 0;
-		    sps.id = syn_buf->b_spell_cluster_id;
+		    sps.id = syn_block->b_spell_cluster_id;
 		    sps.cont_in_list = NULL;
 		    *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
 
-		    if (syn_buf->b_nospell_cluster_id != 0)
+		    if (syn_block->b_nospell_cluster_id != 0)
 		    {
-			sps.id = syn_buf->b_nospell_cluster_id;
+			sps.id = syn_block->b_nospell_cluster_id;
 			if (in_id_list(sip, sip->si_cont_list, &sps, 0))
 			    *can_spell = FALSE;
 		    }
@@ -2315,9 +2359,9 @@ syn_current_attr(syncing, displaying, ca
     else if (can_spell != NULL)
 	/* Default: Only do spelling when there is no @Spell cluster or when
 	 * ":syn spell toplevel" was used. */
-	*can_spell = syn_buf->b_syn_spell == SYNSPL_DEFAULT
-		    ? (syn_buf->b_spell_cluster_id == 0)
-		    : (syn_buf->b_syn_spell == SYNSPL_TOP);
+	*can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
+		    ? (syn_block->b_spell_cluster_id == 0)
+		    : (syn_block->b_syn_spell == SYNSPL_TOP);
 
     /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
     if (current_next_list != NULL
@@ -2370,8 +2414,11 @@ push_next_match(cur_si)
     stateitem_T	*cur_si;
 {
     synpat_T	*spp;
-
-    spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
+#ifdef FEAT_CONCEAL
+    int		 save_flags;
+#endif
+
+    spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
 
     /*
      * Push the item in current_state stack;
@@ -2387,6 +2434,12 @@ push_next_match(cur_si)
 	cur_si->si_m_startcol = current_col;
 	cur_si->si_m_lnum = current_lnum;
 	cur_si->si_flags = spp->sp_flags;
+#ifdef FEAT_CONCEAL
+	cur_si->si_char = spp->sp_char;
+	if (current_state.ga_len > 1)
+	    cur_si->si_flags |=
+		    CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
+#endif
 	cur_si->si_next_list = spp->sp_next_list;
 	cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
 	if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
@@ -2409,6 +2462,9 @@ push_next_match(cur_si)
 	check_keepend();
 	update_si_attr(current_state.ga_len - 1);
 
+#ifdef FEAT_CONCEAL
+	save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
+#endif
 	/*
 	 * If the start pattern has another highlight group, push another item
 	 * on the stack for the start pattern.
@@ -2426,6 +2482,11 @@ push_next_match(cur_si)
 	    cur_si->si_ends = TRUE;
 	    cur_si->si_end_idx = 0;
 	    cur_si->si_flags = HL_MATCH;
+#ifdef FEAT_CONCEAL
+	    cur_si->si_flags |= save_flags;
+	    if (cur_si->si_flags & HL_CONCEALENDS)
+		cur_si->si_flags |= HL_CONCEAL;
+#endif
 	    cur_si->si_next_list = NULL;
 	    check_keepend();
 	    update_si_attr(current_state.ga_len - 1);
@@ -2470,6 +2531,10 @@ check_state_ends()
 		cur_si->si_m_endpos = cur_si->si_eoe_pos;
 		cur_si->si_h_endpos = cur_si->si_eoe_pos;
 		cur_si->si_flags |= HL_MATCH;
+#ifdef FEAT_CONCEAL
+		if (cur_si->si_flags & HL_CONCEALENDS)
+		    cur_si->si_flags |= HL_CONCEAL;
+#endif
 		update_si_attr(current_state.ga_len - 1);
 
 		/* what matches next may be different now, clear it */
@@ -2516,7 +2581,7 @@ check_state_ends()
 		 * - "excludenl" is used (HL_HAS_EOL won't be set)
 		 */
 		if (cur_si->si_idx >= 0
-			&& SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
+			&& SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
 							       == SPTYPE_START
 			&& !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
 		{
@@ -2549,7 +2614,7 @@ update_si_attr(idx)
     if (sip->si_idx < 0)
 	return;
 
-    spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
+    spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
     if (sip->si_flags & HL_MATCH)
 	sip->si_id = spp->sp_syn_match_id;
     else
@@ -2689,7 +2754,7 @@ update_si_end(sip, startcol, force)
     if (endpos.lnum == 0)
     {
 	/* No end pattern matched. */
-	if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
+	if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
 	{
 	    /* a "oneline" never continues in the next line */
 	    sip->si_ends = TRUE;
@@ -2791,7 +2856,7 @@ find_endpos(idx, startpos, m_endpos, hl_
      * Can happen with a match that continues to the next line, because it
      * contained a region.
      */
-    spp = &(SYN_ITEMS(syn_buf)[idx]);
+    spp = &(SYN_ITEMS(syn_block)[idx]);
     if (spp->sp_type != SPTYPE_START)
     {
 	*hl_endpos = *startpos;
@@ -2803,7 +2868,7 @@ find_endpos(idx, startpos, m_endpos, hl_
      */
     for (;;)
     {
-	spp = &(SYN_ITEMS(syn_buf)[idx]);
+	spp = &(SYN_ITEMS(syn_block)[idx]);
 	if (spp->sp_type != SPTYPE_START)
 	    break;
 	++idx;
@@ -2833,11 +2898,11 @@ find_endpos(idx, startpos, m_endpos, hl_
 	 * Find end pattern that matches first after "matchcol".
 	 */
 	best_idx = -1;
-	for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
+	for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
 	{
 	    int lc_col = matchcol;
 
-	    spp = &(SYN_ITEMS(syn_buf)[idx]);
+	    spp = &(SYN_ITEMS(syn_block)[idx]);
 	    if (spp->sp_type != SPTYPE_END)	/* past last END pattern */
 		break;
 	    lc_col -= spp->sp_offsets[SPO_LC_OFF];
@@ -2915,7 +2980,7 @@ find_endpos(idx, startpos, m_endpos, hl_
 	 * Match from start pattern to end pattern.
 	 * Correct for match and highlight offset of end pattern.
 	 */
-	spp = &(SYN_ITEMS(syn_buf)[best_idx]);
+	spp = &(SYN_ITEMS(syn_block)[best_idx]);
 	syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
 	/* can't end before the start */
 	if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
@@ -3146,13 +3211,14 @@ syn_regexec(rmp, lnum, col)
  * Return it's ID if found, 0 otherwise.
  */
     static int
-check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
+check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
     char_u	*line;
     int		startcol;	/* position in line to check for keyword */
     int		*endcolp;	/* return: character after found keyword */
     long	*flagsp;	/* return: flags of matching keyword */
     short	**next_listp;	/* return: next_list of matching keyword */
     stateitem_T	*cur_si;	/* item at the top of the stack */
+    int		*ccharp UNUSED;	/* conceal substitution char */
 {
     keyentry_T	*kp;
     char_u	*kwp;
@@ -3193,7 +3259,7 @@ check_keyword_id(line, startcol, endcolp
      */
     for (round = 1; round <= 2; ++round)
     {
-	ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
+	ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
 	if (ht->ht_used == 0)
 	    continue;
 	if (round == 2)	/* ignore case */
@@ -3220,6 +3286,9 @@ check_keyword_id(line, startcol, endcolp
 		    *endcolp = startcol + kwlen;
 		    *flagsp = kp->flags;
 		    *next_listp = kp->next_list;
+#ifdef FEAT_CONCEAL
+		    *ccharp = kp->k_char;
+#endif
 		    return kp->k_syn.id;
 		}
 	    }
@@ -3228,6 +3297,32 @@ check_keyword_id(line, startcol, endcolp
 }
 
 /*
+ * Handle ":syntax conceal" command.
+ */
+    static void
+syn_cmd_conceal(eap, syncing)
+    exarg_T	*eap UNUSED;
+    int		syncing UNUSED;
+{
+#ifdef FEAT_CONCEAL
+    char_u	*arg = eap->arg;
+    char_u	*next;
+
+    eap->nextcmd = find_nextcmd(arg);
+    if (eap->skip)
+	return;
+
+    next = skiptowhite(arg);
+    if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
+	curwin->w_s->b_syn_conceal = TRUE;
+    else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
+	curwin->w_s->b_syn_conceal = FALSE;
+    else
+	EMSG2(_("E390: Illegal argument: %s"), arg);
+#endif
+}
+
+/*
  * Handle ":syntax case" command.
  */
     static void
@@ -3244,9 +3339,9 @@ syn_cmd_case(eap, syncing)
 
     next = skiptowhite(arg);
     if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
-	curbuf->b_syn_ic = FALSE;
+	curwin->w_s->b_syn_ic = FALSE;
     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
-	curbuf->b_syn_ic = TRUE;
+	curwin->w_s->b_syn_ic = TRUE;
     else
 	EMSG2(_("E390: Illegal argument: %s"), arg);
 }
@@ -3268,11 +3363,11 @@ syn_cmd_spell(eap, syncing)
 
     next = skiptowhite(arg);
     if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
-	curbuf->b_syn_spell = SYNSPL_TOP;
+	curwin->w_s->b_syn_spell = SYNSPL_TOP;
     else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
-	curbuf->b_syn_spell = SYNSPL_NOTOP;
+	curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
     else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
-	curbuf->b_syn_spell = SYNSPL_DEFAULT;
+	curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
     else
 	EMSG2(_("E390: Illegal argument: %s"), arg);
 }
@@ -3281,47 +3376,47 @@ syn_cmd_spell(eap, syncing)
  * Clear all syntax info for one buffer.
  */
     void
-syntax_clear(buf)
-    buf_T	*buf;
+syntax_clear(block)
+    synblock_T	*block;
 {
     int i;
 
-    buf->b_syn_error = FALSE;	    /* clear previous error */
-    buf->b_syn_ic = FALSE;	    /* Use case, by default */
-    buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
-    buf->b_syn_containedin = FALSE;
+    block->b_syn_error = FALSE;	    /* clear previous error */
+    block->b_syn_ic = FALSE;	    /* Use case, by default */
+    block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
+    block->b_syn_containedin = FALSE;
 
     /* free the keywords */
-    clear_keywtab(&buf->b_keywtab);
-    clear_keywtab(&buf->b_keywtab_ic);
+    clear_keywtab(&block->b_keywtab);
+    clear_keywtab(&block->b_keywtab_ic);
 
     /* free the syntax patterns */
-    for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
-	syn_clear_pattern(buf, i);
-    ga_clear(&buf->b_syn_patterns);
+    for (i = block->b_syn_patterns.ga_len; --i >= 0; )
+	syn_clear_pattern(block, i);
+    ga_clear(&block->b_syn_patterns);
 
     /* free the syntax clusters */
-    for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
-	syn_clear_cluster(buf, i);
-    ga_clear(&buf->b_syn_clusters);
-    buf->b_spell_cluster_id = 0;
-    buf->b_nospell_cluster_id = 0;
-
-    buf->b_syn_sync_flags = 0;
-    buf->b_syn_sync_minlines = 0;
-    buf->b_syn_sync_maxlines = 0;
-    buf->b_syn_sync_linebreaks = 0;
-
-    vim_free(buf->b_syn_linecont_prog);
-    buf->b_syn_linecont_prog = NULL;
-    vim_free(buf->b_syn_linecont_pat);
-    buf->b_syn_linecont_pat = NULL;
+    for (i = block->b_syn_clusters.ga_len; --i >= 0; )
+	syn_clear_cluster(block, i);
+    ga_clear(&block->b_syn_clusters);
+    block->b_spell_cluster_id = 0;
+    block->b_nospell_cluster_id = 0;
+
+    block->b_syn_sync_flags = 0;
+    block->b_syn_sync_minlines = 0;
+    block->b_syn_sync_maxlines = 0;
+    block->b_syn_sync_linebreaks = 0;
+
+    vim_free(block->b_syn_linecont_prog);
+    block->b_syn_linecont_prog = NULL;
+    vim_free(block->b_syn_linecont_pat);
+    block->b_syn_linecont_pat = NULL;
 #ifdef FEAT_FOLDING
-    buf->b_syn_folditems = 0;
+    block->b_syn_folditems = 0;
 #endif
 
     /* free the stored states */
-    syn_stack_free_all(buf);
+    syn_stack_free_all(block);
     invalidate_current_state();
 }
 
@@ -3334,42 +3429,42 @@ syntax_sync_clear()
     int i;
 
     /* free the syntax patterns */
-    for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
-	if (SYN_ITEMS(curbuf)[i].sp_syncing)
-	    syn_remove_pattern(curbuf, i);
-
-    curbuf->b_syn_sync_flags = 0;
-    curbuf->b_syn_sync_minlines = 0;
-    curbuf->b_syn_sync_maxlines = 0;
-    curbuf->b_syn_sync_linebreaks = 0;
-
-    vim_free(curbuf->b_syn_linecont_prog);
-    curbuf->b_syn_linecont_prog = NULL;
-    vim_free(curbuf->b_syn_linecont_pat);
-    curbuf->b_syn_linecont_pat = NULL;
-
-    syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
+    for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
+	if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
+	    syn_remove_pattern(curwin->w_s, i);
+
+    curwin->w_s->b_syn_sync_flags = 0;
+    curwin->w_s->b_syn_sync_minlines = 0;
+    curwin->w_s->b_syn_sync_maxlines = 0;
+    curwin->w_s->b_syn_sync_linebreaks = 0;
+
+    vim_free(curwin->w_s->b_syn_linecont_prog);
+    curwin->w_s->b_syn_linecont_prog = NULL;
+    vim_free(curwin->w_s->b_syn_linecont_pat);
+    curwin->w_s->b_syn_linecont_pat = NULL;
+
+    syn_stack_free_all(curwin->w_s);		/* Need to recompute all syntax. */
 }
 
 /*
  * Remove one pattern from the buffer's pattern list.
  */
     static void
-syn_remove_pattern(buf, idx)
-    buf_T	*buf;
+syn_remove_pattern(block, idx)
+    synblock_T	*block;
     int		idx;
 {
     synpat_T	*spp;
 
-    spp = &(SYN_ITEMS(buf)[idx]);
+    spp = &(SYN_ITEMS(block)[idx]);
 #ifdef FEAT_FOLDING
     if (spp->sp_flags & HL_FOLD)
-	--buf->b_syn_folditems;
-#endif
-    syn_clear_pattern(buf, idx);
+	--block->b_syn_folditems;
+#endif
+    syn_clear_pattern(block, idx);
     mch_memmove(spp, spp + 1,
-		   sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
-    --buf->b_syn_patterns.ga_len;
+		   sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
+    --block->b_syn_patterns.ga_len;
 }
 
 /*
@@ -3377,18 +3472,18 @@ syn_remove_pattern(buf, idx)
  * last to first!
  */
     static void
-syn_clear_pattern(buf, i)
-    buf_T	*buf;
+syn_clear_pattern(block, i)
+    synblock_T	*block;
     int		i;
 {
-    vim_free(SYN_ITEMS(buf)[i].sp_pattern);
-    vim_free(SYN_ITEMS(buf)[i].sp_prog);
+    vim_free(SYN_ITEMS(block)[i].sp_pattern);
+    vim_free(SYN_ITEMS(block)[i].sp_prog);
     /* Only free sp_cont_list and sp_next_list of first start pattern */
-    if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
-    {
-	vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
-	vim_free(SYN_ITEMS(buf)[i].sp_next_list);
-	vim_free(SYN_ITEMS(buf)[i].sp_syn.cont_in_list);
+    if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
+    {
+	vim_free(SYN_ITEMS(block)[i].sp_cont_list);
+	vim_free(SYN_ITEMS(block)[i].sp_next_list);
+	vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
     }
 }
 
@@ -3396,13 +3491,13 @@ syn_clear_pattern(buf, i)
  * Clear and free one syntax cluster.
  */
     static void
-syn_clear_cluster(buf, i)
-    buf_T	*buf;
+syn_clear_cluster(block, i)
+    synblock_T	*block;
     int		i;
 {
-    vim_free(SYN_CLSTR(buf)[i].scl_name);
-    vim_free(SYN_CLSTR(buf)[i].scl_name_u);
-    vim_free(SYN_CLSTR(buf)[i].scl_list);
+    vim_free(SYN_CLSTR(block)[i].scl_name);
+    vim_free(SYN_CLSTR(block)[i].scl_name_u);
+    vim_free(SYN_CLSTR(block)[i].scl_list);
 }
 
 /*
@@ -3427,7 +3522,7 @@ syn_cmd_clear(eap, syncing)
      * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
      * clear".
      */
-    if (curbuf->b_syn_topgrp != 0)
+    if (curwin->w_s->b_syn_topgrp != 0)
 	return;
 
     if (ends_excmd(*arg))
@@ -3439,8 +3534,12 @@ syn_cmd_clear(eap, syncing)
 	    syntax_sync_clear();
 	else
 	{
-	    syntax_clear(curbuf);
-	    do_unlet((char_u *)"b:current_syntax", TRUE);
+	    syntax_clear(curwin->w_s);
+	    if (curwin->w_s == &curwin->w_buffer->b_s)
+		do_unlet((char_u *)"b:current_syntax", TRUE);
+	    else
+		do_unlet((char_u *)"w:current_syntax", TRUE);
+
 	}
     }
     else
@@ -3468,8 +3567,8 @@ syn_cmd_clear(eap, syncing)
 		     */
 		    short scl_id = id - SYNID_CLUSTER;
 
-		    vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
-		    SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
+		    vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
+		    SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
 		}
 	    }
 	    else
@@ -3487,7 +3586,7 @@ syn_cmd_clear(eap, syncing)
 	}
     }
     redraw_curbuf_later(SOME_VALID);
-    syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
+    syn_stack_free_all(curwin->w_s);		/* Need to recompute all syntax. */
 }
 
 /*
@@ -3504,17 +3603,17 @@ syn_clear_one(id, syncing)
     /* Clear keywords only when not ":syn sync clear group-name" */
     if (!syncing)
     {
-	(void)syn_clear_keyword(id, &curbuf->b_keywtab);
-	(void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
+	(void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
+	(void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
     }
 
     /* clear the patterns for "id" */
-    for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
-    {
-	spp = &(SYN_ITEMS(curbuf)[idx]);
+    for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
+    {
+	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
 	    continue;
-	syn_remove_pattern(curbuf, idx);
+	syn_remove_pattern(curwin->w_s, idx);
     }
 }
 
@@ -3613,7 +3712,7 @@ syn_cmd_list(eap, syncing)
     if (eap->skip)
 	return;
 
-    if (!syntax_present(curbuf))
+    if (!syntax_present(curwin))
     {
 	MSG(_("No Syntax items defined for this buffer"));
 	return;
@@ -3621,30 +3720,30 @@ syn_cmd_list(eap, syncing)
 
     if (syncing)
     {
-	if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
+	if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
 	{
 	    MSG_PUTS(_("syncing on C-style comments"));
 	    syn_lines_msg();
 	    syn_match_msg();
 	    return;
 	}
-	else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
-	{
-	    if (curbuf->b_syn_sync_minlines == 0)
+	else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
+	{
+	    if (curwin->w_s->b_syn_sync_minlines == 0)
 		MSG_PUTS(_("no syncing"));
 	    else
 	    {
 		MSG_PUTS(_("syncing starts "));
-		msg_outnum(curbuf->b_syn_sync_minlines);
+		msg_outnum(curwin->w_s->b_syn_sync_minlines);
 		MSG_PUTS(_(" lines before top line"));
 		syn_match_msg();
 	    }
 	    return;
 	}
 	MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
-	if (curbuf->b_syn_sync_minlines > 0
-		|| curbuf->b_syn_sync_maxlines > 0
-		|| curbuf->b_syn_sync_linebreaks > 0)
+	if (curwin->w_s->b_syn_sync_minlines > 0
+		|| curwin->w_s->b_syn_sync_maxlines > 0
+		|| curwin->w_s->b_syn_sync_linebreaks > 0)
 	{
 	    MSG_PUTS(_("\nsyncing on items"));
 	    syn_lines_msg();
@@ -3660,7 +3759,7 @@ syn_cmd_list(eap, syncing)
 	 */
 	for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
 	    syn_list_one(id, syncing, FALSE);
-	for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
+	for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
 	    syn_list_cluster(id);
     }
     else
@@ -3696,20 +3795,21 @@ syn_cmd_list(eap, syncing)
     static void
 syn_lines_msg()
 {
-    if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
+    if (curwin->w_s->b_syn_sync_maxlines > 0
+				      || curwin->w_s->b_syn_sync_minlines > 0)
     {
 	MSG_PUTS("; ");
-	if (curbuf->b_syn_sync_minlines > 0)
+	if (curwin->w_s->b_syn_sync_minlines > 0)
 	{
 	    MSG_PUTS(_("minimal "));
-	    msg_outnum(curbuf->b_syn_sync_minlines);
-	    if (curbuf->b_syn_sync_maxlines)
+	    msg_outnum(curwin->w_s->b_syn_sync_minlines);
+	    if (curwin->w_s->b_syn_sync_maxlines)
 		MSG_PUTS(", ");
 	}
-	if (curbuf->b_syn_sync_maxlines > 0)
+	if (curwin->w_s->b_syn_sync_maxlines > 0)
 	{
 	    MSG_PUTS(_("maximal "));
-	    msg_outnum(curbuf->b_syn_sync_maxlines);
+	    msg_outnum(curwin->w_s->b_syn_sync_maxlines);
 	}
 	MSG_PUTS(_(" lines before top line"));
     }
@@ -3718,10 +3818,10 @@ syn_lines_msg()
     static void
 syn_match_msg()
 {
-    if (curbuf->b_syn_sync_linebreaks > 0)
+    if (curwin->w_s->b_syn_sync_linebreaks > 0)
     {
 	MSG_PUTS(_("; match "));
-	msg_outnum(curbuf->b_syn_sync_linebreaks);
+	msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
 	MSG_PUTS(_(" line breaks"));
     }
 }
@@ -3759,6 +3859,10 @@ syn_list_one(id, syncing, link_only)
 		    {HL_EXCLUDENL, "excludenl"},
 		    {HL_TRANSP, "transparent"},
 		    {HL_FOLD, "fold"},
+#ifdef FEAT_CONCEAL
+		    {HL_CONCEAL, "conceal"},
+		    {HL_CONCEALENDS, "concealends"},
+#endif
 		    {0, NULL}
 		};
     static struct name_list namelist2[] =
@@ -3774,15 +3878,15 @@ syn_list_one(id, syncing, link_only)
     /* list the keywords for "id" */
     if (!syncing)
     {
-	did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
-	did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
+	did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
+	did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
 							    did_header, attr);
     }
 
     /* list the patterns for "id" */
-    for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
-    {
-	spp = &(SYN_ITEMS(curbuf)[idx]);
+    for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
+    {
+	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
 	    continue;
 
@@ -3796,13 +3900,13 @@ syn_list_one(id, syncing, link_only)
 	}
 	else if (spp->sp_type == SPTYPE_START)
 	{
-	    while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
-		put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
-	    if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
-		put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
-	    while (idx < curbuf->b_syn_patterns.ga_len
-			      && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
-		put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
+	    while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
+		put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
+	    if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
+		put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
+	    while (idx < curwin->w_s->b_syn_patterns.ga_len
+			      && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
+		put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
 	    --idx;
 	    msg_putchar(' ');
 	}
@@ -3828,7 +3932,7 @@ syn_list_one(id, syncing, link_only)
 		msg_puts_attr((char_u *)"groupthere", attr);
 	    msg_putchar(' ');
 	    if (spp->sp_sync_idx >= 0)
-		msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
+		msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
 				   [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
 	    else
 		MSG_PUTS("NONE");
@@ -3873,7 +3977,7 @@ syn_list_cluster(id)
 
     /* slight hack:  roughly duplicate the guts of syn_list_header() */
     msg_putchar('\n');
-    msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
+    msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
 
     if (msg_col >= endcol)	/* output at least one space */
 	endcol = msg_col + 1;
@@ -3881,9 +3985,9 @@ syn_list_cluster(id)
 	endcol = Columns - 1;
 
     msg_advance(endcol);
-    if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
-    {
-	put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
+    if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
+    {
+	put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
 		    hl_attr(HLF_D));
     }
     else
@@ -3925,7 +4029,7 @@ put_id_list(name, list, attr)
 	    short scl_id = *p - SYNID_CLUSTER;
 
 	    msg_putchar('@');
-	    msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
+	    msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
 	}
 	else
 	    msg_outtrans(HL_TABLE()[*p - 1].sg_name);
@@ -4190,12 +4294,13 @@ clear_keywtab(ht)
  * Add a keyword to the list of keywords.
  */
     static void
-add_keyword(name, id, flags, cont_in_list, next_list)
+add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
     char_u	*name;	    /* name of keyword */
     int		id;	    /* group ID for this keyword */
     int		flags;	    /* flags for this keyword */
     short	*cont_in_list; /* containedin for this keyword */
     short	*next_list; /* nextgroup for this keyword */
+    int		conceal_char;
 {
     keyentry_T	*kp;
     hashtab_T	*ht;
@@ -4204,7 +4309,7 @@ add_keyword(name, id, flags, cont_in_lis
     long_u	hash;
     char_u	name_folded[MAXKEYWLEN + 1];
 
-    if (curbuf->b_syn_ic)
+    if (curwin->w_s->b_syn_ic)
 	name_ic = str_foldcase(name, (int)STRLEN(name),
 						 name_folded, MAXKEYWLEN + 1);
     else
@@ -4216,15 +4321,16 @@ add_keyword(name, id, flags, cont_in_lis
     kp->k_syn.id = id;
     kp->k_syn.inc_tag = current_syn_inc_tag;
     kp->flags = flags;
+    kp->k_char = conceal_char;
     kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
     if (cont_in_list != NULL)
-	curbuf->b_syn_containedin = TRUE;
+	curwin->w_s->b_syn_containedin = TRUE;
     kp->next_list = copy_id_list(next_list);
 
-    if (curbuf->b_syn_ic)
-	ht = &curbuf->b_keywtab_ic;
+    if (curwin->w_s->b_syn_ic)
+	ht = &curwin->w_s->b_keywtab_ic;
     else
-	ht = &curbuf->b_keywtab;
+	ht = &curwin->w_s->b_keywtab;
 
     hash = hash_hash(kp->keyword);
     hi = hash_lookup(ht, kp->keyword, hash);
@@ -4275,9 +4381,10 @@ get_group_name(arg, name_end)
  * Return NULL for any error;
  */
     static char_u *
-get_syn_options(arg, opt)
+get_syn_options(arg, opt, conceal_char)
     char_u	    *arg;		/* next argument to be checked */
     syn_opt_arg_T   *opt;		/* various things */
+    int		    *conceal_char UNUSED;
 {
     char_u	*gname_start, *gname;
     int		syn_id;
@@ -4303,6 +4410,9 @@ get_syn_options(arg, opt)
 		    {"gGrRoOuUpPtThHeErReE",	0,	HL_SYNC_THERE},
 		    {"dDiIsSpPlLaAyY",		0,	HL_DISPLAY},
 		    {"fFoOlLdD",		0,	HL_FOLD},
+		    {"cCoOnNcCeEaAlL",		0,	HL_CONCEAL},
+		    {"cCoOnNcCeEaAlLeEnNdDsS",	0,	HL_CONCEALENDS},
+		    {"cCcChHaArR",		11,	0},
 		    {"cCoOnNtTaAiInNsS",	1,	0},
 		    {"cCoOnNtTaAiInNeEdDiInN",	2,	0},
 		    {"nNeExXtTgGrRoOuUpP",	3,	0},
@@ -4312,6 +4422,11 @@ get_syn_options(arg, opt)
     if (arg == NULL)		/* already detected error */
 	return NULL;
 
+#ifdef FEAT_CONCEAL
+    if (curwin->w_s->b_syn_conceal)
+	opt->flags |= HL_CONCEAL;
+#endif
+
     for (;;)
     {
 	/*
@@ -4372,6 +4487,26 @@ get_syn_options(arg, opt)
 	    if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
 		return NULL;
 	}
+	else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
+	{
+#ifdef FEAT_MBYTE
+	    /* cchar=? */
+	    if (has_mbyte)
+	    {
+# ifdef FEAT_CONCEAL
+		*conceal_char = mb_ptr2char(arg + 6);
+# endif
+		arg += mb_ptr2len(arg + 6) - 1;
+	    }
+	    else
+#endif
+#ifdef FEAT_CONCEAL
+		*conceal_char = arg[6];
+#else
+		;
+#endif
+	    arg = skipwhite(arg + 7);
+	}
 	else
 	{
 	    opt->flags |= flagtab[fidx].flags;
@@ -4397,9 +4532,9 @@ get_syn_options(arg, opt)
 		else
 		{
 		    syn_id = syn_name2id(gname);
-		    for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
-			if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
-			      && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
+		    for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
+			if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
+			      && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
 			{
 			    *opt->sync_idx = i;
 			    break;
@@ -4437,20 +4572,20 @@ syn_incl_toplevel(id, flagsp)
     int		id;
     int		*flagsp;
 {
-    if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
+    if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
 	return;
     *flagsp |= HL_CONTAINED;
-    if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
+    if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
     {
 	/* We have to alloc this, because syn_combine_list() will free it. */
 	short	    *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
-	int	    tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
+	int	    tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
 
 	if (grp_list != NULL)
 	{
 	    grp_list[0] = id;
 	    grp_list[1] = 0;
-	    syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
+	    syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
 			 CLUSTER_ADD);
 	}
     }
@@ -4517,12 +4652,12 @@ syn_cmd_include(eap, syncing)
      */
     prev_syn_inc_tag = current_syn_inc_tag;
     current_syn_inc_tag = ++running_syn_inc_tag;
-    prev_toplvl_grp = curbuf->b_syn_topgrp;
-    curbuf->b_syn_topgrp = sgl_id;
+    prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
+    curwin->w_s->b_syn_topgrp = sgl_id;
     if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
 				: source_runtime(eap->arg, TRUE) == FAIL)
 	EMSG2(_(e_notopen), eap->arg);
-    curbuf->b_syn_topgrp = prev_toplvl_grp;
+    curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
     current_syn_inc_tag = prev_syn_inc_tag;
 }
 
@@ -4543,6 +4678,7 @@ syn_cmd_keyword(eap, syncing)
     char_u	*kw;
     syn_opt_arg_T syn_opt_arg;
     int		cnt;
+    int		conceal_char = NUL;
 
     rest = get_group_name(arg, &group_name_end);
 
@@ -4570,7 +4706,7 @@ syn_cmd_keyword(eap, syncing)
 	    p = keyword_copy;
 	    for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
 	    {
-		rest = get_syn_options(rest, &syn_opt_arg);
+		rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
 		if (rest == NULL || ends_excmd(*rest))
 		    break;
 		/* Copy the keyword, removing backslashes, and add a NUL. */
@@ -4599,8 +4735,8 @@ syn_cmd_keyword(eap, syncing)
 			if (p != NULL)
 			    *p = NUL;
 			add_keyword(kw, syn_id, syn_opt_arg.flags,
-						     syn_opt_arg.cont_in_list,
-						       syn_opt_arg.next_list);
+				syn_opt_arg.cont_in_list,
+					 syn_opt_arg.next_list, conceal_char);
 			if (p == NULL)
 			    break;
 			if (p[1] == NUL)
@@ -4644,7 +4780,7 @@ syn_cmd_keyword(eap, syncing)
 	EMSG2(_(e_invarg2), arg);
 
     redraw_curbuf_later(SOME_VALID);
-    syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
+    syn_stack_free_all(curwin->w_s);		/* Need to recompute all syntax. */
 }
 
 /*
@@ -4665,6 +4801,7 @@ syn_cmd_match(eap, syncing)
     int		idx;
     syn_opt_arg_T syn_opt_arg;
     int		sync_idx = 0;
+    int		conceal_char = NUL;
 
     /* Isolate the group name, check for validity */
     rest = get_group_name(arg, &group_name_end);
@@ -4677,7 +4814,7 @@ syn_cmd_match(eap, syncing)
     syn_opt_arg.cont_list = NULL;
     syn_opt_arg.cont_in_list = NULL;
     syn_opt_arg.next_list = NULL;
-    rest = get_syn_options(rest, &syn_opt_arg);
+    rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
 
     /* get the pattern. */
     init_syn_patterns();
@@ -4687,7 +4824,7 @@ syn_cmd_match(eap, syncing)
 	syn_opt_arg.flags |= HL_HAS_EOL;
 
     /* Get options after the pattern */
-    rest = get_syn_options(rest, &syn_opt_arg);
+    rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
 
     if (rest != NULL)		/* all arguments are valid */
     {
@@ -4697,7 +4834,7 @@ syn_cmd_match(eap, syncing)
 	eap->nextcmd = check_nextcmd(rest);
 	if (!ends_excmd(*rest) || eap->skip)
 	    rest = NULL;
-	else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
+	else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
 		&& (syn_id = syn_check_group(arg,
 					   (int)(group_name_end - arg))) != 0)
 	{
@@ -4705,32 +4842,35 @@ syn_cmd_match(eap, syncing)
 	    /*
 	     * Store the pattern in the syn_items list
 	     */
-	    idx = curbuf->b_syn_patterns.ga_len;
-	    SYN_ITEMS(curbuf)[idx] = item;
-	    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
-	    SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
-	    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
-	    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
-	    SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
-	    SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
-	    SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
-	    SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
+	    idx = curwin->w_s->b_syn_patterns.ga_len;
+	    SYN_ITEMS(curwin->w_s)[idx] = item;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
 						     syn_opt_arg.cont_in_list;
+#ifdef FEAT_CONCEAL
+	    SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
+#endif
 	    if (syn_opt_arg.cont_in_list != NULL)
-		curbuf->b_syn_containedin = TRUE;
-	    SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
-	    ++curbuf->b_syn_patterns.ga_len;
+		curwin->w_s->b_syn_containedin = TRUE;
+	    SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
+	    ++curwin->w_s->b_syn_patterns.ga_len;
 
 	    /* remember that we found a match for syncing on */
 	    if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
-		curbuf->b_syn_sync_flags |= SF_MATCH;
+		curwin->w_s->b_syn_sync_flags |= SF_MATCH;
 #ifdef FEAT_FOLDING
 	    if (syn_opt_arg.flags & HL_FOLD)
-		++curbuf->b_syn_folditems;
+		++curwin->w_s->b_syn_folditems;
 #endif
 
 	    redraw_curbuf_later(SOME_VALID);
-	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
+	    syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
 	    return;	/* don't free the progs and patterns now */
 	}
     }
@@ -4785,6 +4925,7 @@ syn_cmd_region(eap, syncing)
     int			success = FALSE;
     int			idx;
     syn_opt_arg_T	syn_opt_arg;
+    int			conceal_char = NUL;
 
     /* Isolate the group name, check for validity */
     rest = get_group_name(arg, &group_name_end);
@@ -4809,7 +4950,7 @@ syn_cmd_region(eap, syncing)
     while (rest != NULL && !ends_excmd(*rest))
     {
 	/* Check for option arguments */
-	rest = get_syn_options(rest, &syn_opt_arg);
+	rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
 	if (rest == NULL || ends_excmd(*rest))
 	    break;
 
@@ -4933,7 +5074,7 @@ syn_cmd_region(eap, syncing)
 	eap->nextcmd = check_nextcmd(rest);
 	if (!ends_excmd(*rest) || eap->skip)
 	    rest = NULL;
-	else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
+	else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
 		&& (syn_id = syn_check_group(arg,
 					   (int)(group_name_end - arg))) != 0)
 	{
@@ -4941,43 +5082,46 @@ syn_cmd_region(eap, syncing)
 	    /*
 	     * Store the start/skip/end in the syn_items list
 	     */
-	    idx = curbuf->b_syn_patterns.ga_len;
+	    idx = curwin->w_s->b_syn_patterns.ga_len;
 	    for (item = ITEM_START; item <= ITEM_END; ++item)
 	    {
 		for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
 		{
-		    SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
-		    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
-		    SYN_ITEMS(curbuf)[idx].sp_type =
+		    SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
+		    SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
+		    SYN_ITEMS(curwin->w_s)[idx].sp_type =
 			    (item == ITEM_START) ? SPTYPE_START :
 			    (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
-		    SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
-		    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
-		    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
-		    SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
+		    SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
+		    SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
+		    SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
+		    SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
 							ppp->pp_matchgroup_id;
+#ifdef FEAT_CONCEAL
+		    SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
+#endif
 		    if (item == ITEM_START)
 		    {
-			SYN_ITEMS(curbuf)[idx].sp_cont_list =
+			SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
 							syn_opt_arg.cont_list;
-			SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
+			SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
 						     syn_opt_arg.cont_in_list;
 			if (syn_opt_arg.cont_in_list != NULL)
-			    curbuf->b_syn_containedin = TRUE;
-			SYN_ITEMS(curbuf)[idx].sp_next_list =
+			    curwin->w_s->b_syn_containedin = TRUE;
+			SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
 							syn_opt_arg.next_list;
 		    }
-		    ++curbuf->b_syn_patterns.ga_len;
+		    ++curwin->w_s->b_syn_patterns.ga_len;
 		    ++idx;
 #ifdef FEAT_FOLDING
 		    if (syn_opt_arg.flags & HL_FOLD)
-			++curbuf->b_syn_folditems;
+			++curwin->w_s->b_syn_folditems;
 #endif
 		}
 	    }
 
 	    redraw_curbuf_later(SOME_VALID);
-	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
+	    syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
 	    success = TRUE;	    /* don't free the progs and patterns now */
 	}
     }
@@ -5169,9 +5313,9 @@ syn_scl_name2id(name)
     name_u = vim_strsave_up(name);
     if (name_u == NULL)
 	return 0;
-    for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
-	if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
-		&& STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
+    for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
+	if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
+		&& STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
 	    break;
     vim_free(name_u);
     return (i < 0 ? 0 : i + SYNID_CLUSTER);
@@ -5237,32 +5381,32 @@ syn_add_cluster(name)
     /*
      * First call for this growarray: init growing array.
      */
-    if (curbuf->b_syn_clusters.ga_data == NULL)
-    {
-	curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
-	curbuf->b_syn_clusters.ga_growsize = 10;
+    if (curwin->w_s->b_syn_clusters.ga_data == NULL)
+    {
+	curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
+	curwin->w_s->b_syn_clusters.ga_growsize = 10;
     }
 
     /*
      * Make room for at least one other cluster entry.
      */
-    if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
+    if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
     {
 	vim_free(name);
 	return 0;
     }
-    len = curbuf->b_syn_clusters.ga_len;
-
-    vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
-    SYN_CLSTR(curbuf)[len].scl_name = name;
-    SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
-    SYN_CLSTR(curbuf)[len].scl_list = NULL;
-    ++curbuf->b_syn_clusters.ga_len;
+    len = curwin->w_s->b_syn_clusters.ga_len;
+
+    vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
+    SYN_CLSTR(curwin->w_s)[len].scl_name = name;
+    SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
+    SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
+    ++curwin->w_s->b_syn_clusters.ga_len;
 
     if (STRICMP(name, "Spell") == 0)
-	curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
+	curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
     if (STRICMP(name, "NoSpell") == 0)
-	curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
+	curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
 
     return len + SYNID_CLUSTER;
 }
@@ -5325,7 +5469,7 @@ syn_cmd_cluster(eap, syncing)
 		EMSG2(_(e_invarg2), rest);
 		break;
 	    }
-	    syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
+	    syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
 			     &clstr_list, list_op);
 	    got_clstr = TRUE;
 	}
@@ -5333,7 +5477,7 @@ syn_cmd_cluster(eap, syncing)
 	if (got_clstr)
 	{
 	    redraw_curbuf_later(SOME_VALID);
-	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
+	    syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
 	}
     }
 
@@ -5349,8 +5493,8 @@ syn_cmd_cluster(eap, syncing)
     static void
 init_syn_patterns()
 {
-    curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
-    curbuf->b_syn_patterns.ga_growsize = 10;
+    curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
+    curwin->w_s->b_syn_patterns.ga_growsize = 10;
 }
 
 /*
@@ -5390,7 +5534,7 @@ get_syn_pattern(arg, ci)
 
     if (ci->sp_prog == NULL)
 	return NULL;
-    ci->sp_ic = curbuf->b_syn_ic;
+    ci->sp_ic = curwin->w_s->b_syn_ic;
 
     /*
      * Check for a match, highlight or region offset.
@@ -5488,17 +5632,17 @@ syn_cmd_sync(eap, syncing)
 	if (STRCMP(key, "CCOMMENT") == 0)
 	{
 	    if (!eap->skip)
-		curbuf->b_syn_sync_flags |= SF_CCOMMENT;
+		curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
 	    if (!ends_excmd(*next_arg))
 	    {
 		arg_end = skiptowhite(next_arg);
 		if (!eap->skip)
-		    curbuf->b_syn_sync_id = syn_check_group(next_arg,
+		    curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
 						   (int)(arg_end - next_arg));
 		next_arg = skipwhite(arg_end);
 	    }
 	    else if (!eap->skip)
-		curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
+		curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
 	}
 	else if (  STRNCMP(key, "LINES", 5) == 0
 		|| STRNCMP(key, "MINLINES", 8) == 0
@@ -5520,24 +5664,24 @@ syn_cmd_sync(eap, syncing)
 	    if (!eap->skip)
 	    {
 		if (key[4] == 'B')
-		    curbuf->b_syn_sync_linebreaks = n;
+		    curwin->w_s->b_syn_sync_linebreaks = n;
 		else if (key[1] == 'A')
-		    curbuf->b_syn_sync_maxlines = n;
+		    curwin->w_s->b_syn_sync_maxlines = n;
 		else
-		    curbuf->b_syn_sync_minlines = n;
+		    curwin->w_s->b_syn_sync_minlines = n;
 	    }
 	}
 	else if (STRCMP(key, "FROMSTART") == 0)
 	{
 	    if (!eap->skip)
 	    {
-		curbuf->b_syn_sync_minlines = MAXLNUM;
-		curbuf->b_syn_sync_maxlines = 0;
+		curwin->w_s->b_syn_sync_minlines = MAXLNUM;
+		curwin->w_s->b_syn_sync_maxlines = 0;
 	    }
 	}
 	else if (STRCMP(key, "LINECONT") == 0)
 	{
-	    if (curbuf->b_syn_linecont_pat != NULL)
+	    if (curwin->w_s->b_syn_linecont_pat != NULL)
 	    {
 		EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
 		finished = TRUE;
@@ -5553,25 +5697,25 @@ syn_cmd_sync(eap, syncing)
 	    if (!eap->skip)
 	    {
 		/* store the pattern and compiled regexp program */
-		if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
+		if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
 				      (int)(arg_end - next_arg - 1))) == NULL)
 		{
 		    finished = TRUE;
 		    break;
 		}
-		curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
+		curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
 
 		/* Make 'cpoptions' empty, to avoid the 'l' flag */
 		cpo_save = p_cpo;
 		p_cpo = (char_u *)"";
-		curbuf->b_syn_linecont_prog =
-			    vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
+		curwin->w_s->b_syn_linecont_prog =
+			    vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
 		p_cpo = cpo_save;
 
-		if (curbuf->b_syn_linecont_prog == NULL)
+		if (curwin->w_s->b_syn_linecont_prog == NULL)
 		{
-		    vim_free(curbuf->b_syn_linecont_pat);
-		    curbuf->b_syn_linecont_pat = NULL;
+		    vim_free(curwin->w_s->b_syn_linecont_pat);
+		    curwin->w_s->b_syn_linecont_pat = NULL;
 		    finished = TRUE;
 		    break;
 		}
@@ -5601,7 +5745,7 @@ syn_cmd_sync(eap, syncing)
     {
 	eap->nextcmd = check_nextcmd(arg_start);
 	redraw_curbuf_later(SOME_VALID);
-	syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
+	syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
     }
 }
 
@@ -5862,8 +6006,8 @@ in_id_list(cur_si, list, ssp, contained)
 	    --cur_si;
 	/* cur_si->si_idx is -1 for keywords, these never contain anything. */
 	if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
-		&(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
-		  SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
+		&(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
+		  SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
 	    return TRUE;
     }
 
@@ -5918,7 +6062,7 @@ in_id_list(cur_si, list, ssp, contained)
 	    return retval;
 	if (item >= SYNID_CLUSTER)
 	{
-	    scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
+	    scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
 	    /* restrict recursiveness to 30 to avoid an endless loop for a
 	     * cluster that includes itself (indirectly) */
 	    if (scl_list != NULL && depth < 30)
@@ -5946,6 +6090,7 @@ static struct subcommand subcommands[] =
     {"case",		syn_cmd_case},
     {"clear",		syn_cmd_clear},
     {"cluster",		syn_cmd_cluster},
+    {"conceal",		syn_cmd_conceal},
     {"enable",		syn_cmd_enable},
     {"include",		syn_cmd_include},
     {"keyword",		syn_cmd_keyword},
@@ -6006,14 +6151,34 @@ ex_syntax(eap)
     }
 }
 
+    void
+ex_ownsyntax(eap)
+    exarg_T	*eap;
+{
+    if (curwin->w_s == &curwin->w_buffer->b_s)
+    {
+	curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
+	memset(curwin->w_s, 0, sizeof(synblock_T));
+#ifdef FEAT_SPELL
+	curwin->w_p_spell = FALSE;	/* No spell checking */
+	clear_string_option(&curwin->w_s->b_p_spc);
+	clear_string_option(&curwin->w_s->b_p_spf);
+	vim_free(curwin->w_s->b_cap_prog);
+	curwin->w_s->b_cap_prog = NULL;
+	clear_string_option(&curwin->w_s->b_p_spl);
+#endif
+    }
+    apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
+}
+
     int
-syntax_present(buf)
-    buf_T	*buf;
-{
-    return (buf->b_syn_patterns.ga_len != 0
-	    || buf->b_syn_clusters.ga_len != 0
-	    || buf->b_keywtab.ht_used > 0
-	    || buf->b_keywtab_ic.ht_used > 0);
+syntax_present(win)
+    win_T	*win;
+{
+    return (win->w_s->b_syn_patterns.ga_len != 0
+	    || win->w_s->b_syn_clusters.ga_len != 0
+	    || win->w_s->b_keywtab.ht_used > 0
+	    || win->w_s->b_keywtab_ic.ht_used > 0);
 }
 
 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
@@ -6124,11 +6289,22 @@ syn_get_id(wp, lnum, col, trans, spellp,
 	    || col < current_col)
 	syntax_start(wp, lnum);
 
-    (void)get_syntax_attr(col, spellp, keep_state);
+    (void)get_syntax_attr(col, NULL, spellp, keep_state);
 
     return (trans ? current_trans_id : current_id);
 }
 
+#if defined(FEAT_CONCEAL) || defined(PROTO)
+/*
+ * Return conceal substitution character
+ */
+    int
+syn_get_sub_char()
+{
+    return current_sub_char;
+}
+#endif
+
 #if defined(FEAT_EVAL) || defined(PROTO)
 /*
  * Return the syntax ID at position "i" in the current stack.
@@ -6164,7 +6340,7 @@ syn_get_foldlevel(wp, lnum)
     int		i;
 
     /* Return quickly when there are no fold items at all. */
-    if (wp->w_buffer->b_syn_folditems != 0)
+    if (wp->w_s->b_syn_folditems != 0)
     {
 	syntax_start(wp, lnum);
 
@@ -6316,6 +6492,10 @@ static char *(highlight_init_light[]) =
 	CENT("CursorLine term=underline cterm=underline",
 	     "CursorLine term=underline cterm=underline guibg=Grey90"),
 #endif
+#ifdef FEAT_CONCEAL
+	CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
+	     "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
+#endif
 #ifdef FEAT_AUTOCMD
 	CENT("MatchParen term=reverse ctermbg=Cyan",
 	     "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
@@ -6400,6 +6580,10 @@ static char *(highlight_init_dark[]) =
 	CENT("MatchParen term=reverse ctermbg=DarkCyan",
 	     "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
 #endif
+#ifdef FEAT_CONCEAL
+	CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
+	     "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
+#endif
 #ifdef FEAT_GUI
 	"Normal gui=NONE",
 #endif
--- a/src/ui.c
+++ b/src/ui.c
@@ -2989,7 +2989,7 @@ mouse_find_win(rowp, colp)
 }
 #endif
 
-#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) || defined (FEAT_GUI_MAC) \
+#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MAC) \
 	|| defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \
 	|| defined(FEAT_GUI_PHOTON) || defined(PROTO)
 /*
--- a/src/version.c
+++ b/src/version.c
@@ -134,6 +134,11 @@ static char *(features[]) =
 #else
 	"-comments",
 #endif
+#ifdef FEAT_CONCEAL
+	"+conceal",
+#else
+	"-conceal",
+#endif
 #ifdef FEAT_CRYPT
 	"+cryptv",
 #else
@@ -144,6 +149,11 @@ static char *(features[]) =
 #else
 	"-cscope",
 #endif
+#ifdef FEAT_CURSORBIND
+	"+cursorbind",
+#else
+	"-cursorbind",
+#endif
 #ifdef CURSOR_SHAPE
 	"+cursorshape",
 #else
@@ -935,13 +945,13 @@ list_version()
 #      if defined(MSWIN)
     MSG_PUTS(_("with GUI."));
 #      else
-#	if defined (TARGET_API_MAC_CARBON) && TARGET_API_MAC_CARBON
+#	if defined(TARGET_API_MAC_CARBON) && TARGET_API_MAC_CARBON
     MSG_PUTS(_("with Carbon GUI."));
 #	else
-#	 if defined (TARGET_API_MAC_OSX) && TARGET_API_MAC_OSX
+#	 if defined(TARGET_API_MAC_OSX) && TARGET_API_MAC_OSX
     MSG_PUTS(_("with Cocoa GUI."));
 #	 else
-#	  if defined (MACOS)
+#	  if defined(MACOS)
     MSG_PUTS(_("with (classic) GUI."));
 #	  endif
 #	 endif
--- a/src/vim.h
+++ b/src/vim.h
@@ -853,6 +853,27 @@ extern char *(*dyn_libintl_textdomain)(c
 # endif
 # define SST_FIX_STATES	 7	/* size of sst_stack[]. */
 # define SST_DIST	 16	/* normal distance between entries */
+# define SST_INVALID	(synstate_T *)-1	/* invalid syn_state pointer */
+
+# define HL_CONTAINED	0x01	/* not used on toplevel */
+# define HL_TRANSP	0x02	/* has no highlighting	*/
+# define HL_ONELINE	0x04	/* match within one line only */
+# define HL_HAS_EOL	0x08	/* end pattern that matches with $ */
+# define HL_SYNC_HERE	0x10	/* sync point after this item (syncing only) */
+# define HL_SYNC_THERE	0x20	/* sync point at current line (syncing only) */
+# define HL_MATCH	0x40	/* use match ID instead of item ID */
+# define HL_SKIPNL	0x80	/* nextgroup can skip newlines */
+# define HL_SKIPWHITE	0x100	/* nextgroup can skip white space */
+# define HL_SKIPEMPTY	0x200	/* nextgroup can skip empty lines */
+# define HL_KEEPEND	0x400	/* end match always kept */
+# define HL_EXCLUDENL	0x800	/* exclude NL from match */
+# define HL_DISPLAY	0x1000	/* only used for displaying, not syncing */
+# define HL_FOLD	0x2000	/* define fold */
+# define HL_EXTEND	0x4000	/* ignore a keepend */
+# define HL_MATCHCONT	0x8000	/* match continued from previous line */
+# define HL_TRANS_CONT	0x10000 /* transparent item without contains arg */
+# define HL_CONCEAL	0x20000 /* can be concealed */
+# define HL_CONCEALENDS	0x40000 /* can be concealed */
 #endif
 
 /* Values for 'options' argument in do_search() and searchit() */
@@ -1292,6 +1313,7 @@ typedef enum
     , HLF_CHD	    /* Changed diff line */
     , HLF_DED	    /* Deleted diff line */
     , HLF_TXD	    /* Text Changed in diff line */
+    , HLF_CONCEAL   /* Concealed text */
     , HLF_SC	    /* Sign column */
     , HLF_SPB	    /* SpellBad */
     , HLF_SPC	    /* SpellCap */
@@ -1313,7 +1335,7 @@ typedef enum
  * When changing this also adjust the default for 'highlight'. */
 #define HL_FLAGS {'8', '@', 'd', 'e', 'h', 'i', 'l', 'm', 'M', \
 		  'n', 'r', 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', \
-		  'f', 'F', 'A', 'C', 'D', 'T', '>', \
+		  'f', 'F', 'A', 'C', 'D', 'T', '-', '>', \
 		  'B', 'P', 'R', 'L', \
 		  '+', '=', 'x', 'X', '*', '#', '_', '!', '.'}
 
--- a/src/window.c
+++ b/src/window.c
@@ -1175,6 +1175,9 @@ win_init(newp, oldp, flags)
     int		i;
 
     newp->w_buffer = oldp->w_buffer;
+#ifdef FEAT_SYN_HL
+    newp->w_s = oldp->w_s;
+#endif
     oldp->w_buffer->b_nwindows++;
     newp->w_cursor = oldp->w_cursor;
     newp->w_valid = 0;
@@ -3294,6 +3297,9 @@ win_alloc_firstwin(oldwin)
 	if (curwin == NULL || curbuf == NULL)
 	    return FAIL;
 	curwin->w_buffer = curbuf;
+#ifdef FEAT_SYN_HL
+	curwin->w_s = &(curbuf->b_s);
+#endif
 	curbuf->b_nwindows = 1;	/* there is one window */
 #ifdef FEAT_WINDOWS
 	curwin->w_alist = &global_alist;
@@ -4401,10 +4407,16 @@ win_free(wp, tp)
     }
 #endif /* FEAT_GUI */
 
+#ifdef FEAT_SYN_HL
+    /* free independent synblock */
+    if (wp->w_s != &wp->w_buffer->b_s)
+	vim_free(wp->w_s);
+#endif
+
 #ifdef FEAT_AUTOCMD
     if (wp != aucmd_win)
 #endif
-	win_remove(wp, tp);
+    win_remove(wp, tp);
     vim_free(wp);
 
 #ifdef FEAT_AUTOCMD
--- a/src/workshop.c
+++ b/src/workshop.c
@@ -1826,7 +1826,8 @@ findYourself(
     else if (*argv0 == '.' || strchr(argv0, '/'))
     {
 	runpath = (char *) malloc(MAXPATHLEN);
-	(void)getcwd(runpath, MAXPATHLEN);
+	if (getcwd(runpath, MAXPATHLEN) == NULL)
+	    runpath[0] = NUL;
 	strcat(runpath, "/");
 	strcat(runpath, argv0);
     }