aboutsummaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2015-08-15 14:56:46 -0400
committerJustin M. Keyes <justinkz@gmail.com>2015-08-15 14:56:46 -0400
commit197a5ad376ed7b5b75fb62a91f00b1eadc2bc2c7 (patch)
treec144054da00b7a3691e7bdf287543d3a22fecb62 /runtime
parentd8f1acbf9d75171d37973e39eceac750f51e4145 (diff)
parent4fc1ab779dd23416af08f4124d5f72c2c0629e56 (diff)
downloadrneovim-197a5ad376ed7b5b75fb62a91f00b1eadc2bc2c7.tar.gz
rneovim-197a5ad376ed7b5b75fb62a91f00b1eadc2bc2c7.tar.bz2
rneovim-197a5ad376ed7b5b75fb62a91f00b1eadc2bc2c7.zip
Merge pull request #2700 from fmoralesc/vim-tutor-mode
runtime: Include vim-tutor-mode
Diffstat (limited to 'runtime')
-rw-r--r--runtime/autoload/tutor.vim350
-rw-r--r--runtime/doc/options.txt2
-rw-r--r--runtime/doc/pi_tutor.txt50
-rw-r--r--runtime/doc/usr_01.txt42
-rw-r--r--runtime/doc/usr_02.txt2
-rw-r--r--runtime/filetype.vim3
-rw-r--r--runtime/ftplugin/tutor.vim43
-rw-r--r--runtime/plugin/tutor.vim6
-rw-r--r--runtime/syntax/tutor.vim83
-rw-r--r--runtime/syntax/vimnormal.vim16
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor955
-rw-r--r--runtime/tutor/tutor.tutor259
12 files changed, 1771 insertions, 40 deletions
diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim
new file mode 100644
index 0000000000..3ec6c7b34f
--- /dev/null
+++ b/runtime/autoload/tutor.vim
@@ -0,0 +1,350 @@
+" vim: fdm=marker et ts=4 sw=4
+
+" Setup: {{{1
+function! tutor#SetupVim()
+ if has('syntax')
+ if !exists('g:syntax_on') || g:syntax_on == 0
+ syntax on
+ endif
+ endif
+endfunction
+
+" Mappings: {{{1
+
+function! s:CheckMaps()
+ nmap
+endfunction
+
+function! s:MapKeyWithRedirect(key, cmd)
+ if maparg(a:key) !=# ''
+ redir => l:keys
+ silent call s:CheckMaps()
+ redir END
+ let l:key_list = split(l:keys, '\n')
+
+ let l:raw_map = filter(copy(l:key_list), "v:val =~# '\\* ".a:key."'")
+ if len(l:raw_map) == 0
+ exe "nnoremap <buffer> <expr> ".a:key." ".a:cmd
+ return
+ endif
+ let l:map_data = split(l:raw_map[0], '\s*')
+
+ exe "nnoremap <buffer> <expr> ".l:map_data[0]." ".a:cmd
+ else
+ exe "nnoremap <buffer> <expr> ".a:key." ".a:cmd
+ endif
+endfunction
+
+function! tutor#MouseDoubleClick()
+ if foldclosed(line('.')) > -1
+ normal! zo
+ else
+ if match(getline('.'), '^#\{1,} ') > -1
+ normal! zc
+ else
+ call tutor#FollowLink(0)
+ endif
+ endif
+endfunction
+
+function! tutor#InjectCommand()
+ let l:cmd = substitute(getline('.'), '^\s*', '', '')
+ exe l:cmd
+ redraw | echohl WarningMsg | echon "tutor: ran" | echohl None | echon " " | echohl Statement | echon l:cmd
+endfunction
+
+function! tutor#SetNormalMappings()
+ call s:MapKeyWithRedirect('l', 'tutor#ForwardSkipConceal(v:count1)')
+ call s:MapKeyWithRedirect('h', 'tutor#BackwardSkipConceal(v:count1)')
+ call s:MapKeyWithRedirect('<right>', 'tutor#ForwardSkipConceal(v:count1)')
+ call s:MapKeyWithRedirect('<left>', 'tutor#BackwardSkipConceal(v:count1)')
+
+ nnoremap <silent> <buffer> <CR> :call tutor#FollowLink(0)<cr>
+ nnoremap <silent> <buffer> <2-LeftMouse> :call tutor#MouseDoubleClick()<cr>
+ nnoremap <buffer> >> :call tutor#InjectCommand()<cr>
+endfunction
+
+function! tutor#SetSampleTextMappings()
+ noremap <silent> <buffer> A :if match(getline('.'), '^--->') > -1 \| call search('\s{\@=', 'Wc') \| startinsert \| else \| startinsert! \| endif<cr>
+ noremap <silent> <buffer> $ :if match(getline('.'), '^--->') > -1 \| call search('.\s{\@=', 'Wc') \| else \| call search('$', 'Wc') \| endif<cr>
+ onoremap <silent> <buffer> $ :if match(getline('.'), '^--->') > -1 \| call search('.\s{\@=', 'Wc') \| else \| call search('$', 'Wc') \| endif<cr>
+ noremap <silent> <buffer> ^ :if match(getline('.'), '^--->') > -1 \| call search('\(--->\s\)\@<=.', 'bcW') \| else \| call search('^', 'bcW') \|endif<cr>
+ onoremap <silent> <buffer> ^ :if match(getline('.'), '^--->') > -1 \| call search('\(--->\s\)\@<=.', 'bcW') \| else \| call search('^', 'bcW') \|endif<cr>
+ nmap <silent> <buffer> 0 ^<esc>
+ nmap <silent> <buffer> <Home> ^<esc>
+ nmap <silent> <buffer> <End> $
+ imap <silent> <buffer> <Home> <esc>^<esc>:startinsert<cr>
+ imap <silent> <buffer> <End> <esc>$:startinsert<cr>
+ noremap <silent> <buffer> I :exe "normal! 0" \| startinsert<cr>
+endfunction
+
+" Navigation: {{{1
+
+" taken from http://stackoverflow.com/a/24224578
+
+function! tutor#ForwardSkipConceal(count)
+ let cnt=a:count
+ let mvcnt=0
+ let c=col('.')
+ let l=line('.')
+ let lc=col('$')
+ let line=getline('.')
+ while cnt
+ if c>=lc
+ let mvcnt+=cnt
+ break
+ endif
+ if stridx(&concealcursor, 'n')==-1
+ let isconcealed=0
+ else
+ let [isconcealed, cchar, group] = synconcealed(l, c)
+ endif
+ if isconcealed
+ let cnt-=strchars(cchar)
+ let oldc=c
+ let c+=1
+ while c < lc
+ let [isconcealed2, cchar2, group2] = synconcealed(l, c)
+ if !isconcealed2 || cchar2 != cchar
+ break
+ endif
+ let c+= 1
+ endwhile
+ let mvcnt+=strchars(line[oldc-1:c-2])
+ else
+ let cnt-=1
+ let mvcnt+=1
+ let c+=len(matchstr(line[c-1:], '.'))
+ endif
+ endwhile
+ return mvcnt.'l'
+endfunction
+
+function! tutor#BackwardSkipConceal(count)
+ let cnt=a:count
+ let mvcnt=0
+ let c=col('.')
+ let l=line('.')
+ let lc=0
+ let line=getline('.')
+ while cnt
+ if c<=1
+ let mvcnt+=cnt
+ break
+ endif
+ if stridx(&concealcursor, 'n')==-1 || c == 0
+ let isconcealed=0
+ else
+ let [isconcealed, cchar, group]=synconcealed(l, c-1)
+ endif
+ if isconcealed
+ let cnt-=strchars(cchar)
+ let oldc=c
+ let c-=1
+ while c>1
+ let [isconcealed2, cchar2, group2] = synconcealed(l, c-1)
+ if !isconcealed2 || cchar2 != cchar
+ break
+ endif
+ let c-=1
+ endwhile
+ let c = max([c, 1])
+ let mvcnt+=strchars(line[c-1:oldc-2])
+ else
+ let cnt-=1
+ let mvcnt+=1
+ let c-=len(matchstr(line[:c-2], '.$'))
+ endif
+ endwhile
+ return mvcnt.'h'
+endfunction
+
+" Hypertext: {{{1
+
+function! tutor#FollowLink(force)
+ let l:stack_s = join(map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")'), '')
+ if l:stack_s =~# 'tutorLink'
+ let l:link_start = searchpairpos('\[', '', ')', 'nbcW')
+ let l:link_end = searchpairpos('\[', '', ')', 'ncW')
+ if l:link_start[0] == l:link_end[0]
+ let l:linkData = getline(l:link_start[0])[l:link_start[1]-1:l:link_end[1]-1]
+ else
+ return
+ endif
+ let l:target = matchstr(l:linkData, '(\@<=.*)\@=')
+ if a:force != 1 && match(l:target, '\*.\+\*') > -1
+ call cursor(l:link_start[0], l:link_end[1])
+ call search(l:target, '')
+ normal! ^
+ elseif a:force != 1 && match(l:target, '^@tutor:') > -1
+ let l:tutor = matchstr(l:target, '@tutor:\zs.*')
+ exe "Tutor ".l:tutor
+ else
+ exe "help ".l:target
+ endif
+ endif
+endfunction
+
+" Folding And Info: {{{1
+
+function! tutor#TutorFolds()
+ if getline(v:lnum) =~# '^#\{1,6}'
+ return ">". len(matchstr(getline(v:lnum), '^#\{1,6}'))
+ else
+ return "="
+ endif
+endfunction
+
+function! tutor#InfoText()
+ let l:info_parts = []
+ if exists('b:tutor_infofunc')
+ call add(l:info_parts, eval(b:tutor_infofunc.'()'))
+ endif
+ return join(l:info_parts, " ")
+endfunction
+
+" Marks {{{1
+function! tutor#PlaceXMarks()
+ call cursor(1, 1)
+ let b:tutor_sign_id = 1
+ while search('^--->', 'W') > 0
+ call tutor#CheckText(getline('.'))
+ let b:tutor_sign_id+=1
+ endwhile
+ call cursor(1, 1)
+endfunction
+
+function! tutor#CheckText(text)
+ if match(a:text, '{expect:ANYTHING}\s*$') == -1
+ if match(getline('.'), '^--->\s*$') > -1
+ exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorbad buffer=".bufnr('%')
+ else
+ if match(getline('.'), '|expect:.\+|') == -1
+ let l:cur_text = matchstr(a:text, '---> \zs.\{-}\ze {expect:')
+ let l:expected_text = matchstr(a:text, '{expect:\zs.*\ze}\s*$')
+ else
+ let l:cur_text = matchstr(a:text, '---> \zs.\{-}\ze |expect:')
+ let l:expected_text = matchstr(a:text, '|expect:\zs.*\ze|\s*$')
+ endif
+ if l:cur_text ==# l:expected_text
+ exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorok buffer=".bufnr('%')
+ else
+ exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorbad buffer=".bufnr('%')
+ endif
+ endif
+ endif
+endfunction
+
+function! tutor#OnTextChanged()
+ let l:text = getline('.')
+ if match(l:text, '^--->') > -1
+ call tutor#CheckText(l:text)
+ endif
+endfunction
+
+" Tutor Cmd: {{{1
+
+function! s:Locale()
+ let l:lang = ""
+ if exists('v:lang') && v:lang =~ '\a\a'
+ let l:lang = v:lang
+ elseif $LC_ALL =~ '\a\a'
+ let l:lang = $LC_ALL
+ elseif $LANG =~ '\a\a'
+ let l:lang = $LANG
+ endif
+ return split(l:lang, '_')
+endfunction
+
+function! s:GlobPath(lp, pat)
+ if version >= 704 && has('patch279')
+ return globpath(a:lp, a:pat, 0, 1)
+ else
+ return split(globpath(a:lp, a:pat, 0), '\n')
+ endif
+endfunction
+
+function! s:Sort(a, b)
+ let mod_a = fnamemodify(a:a, ':t')
+ let mod_b = fnamemodify(a:b, ':t')
+ if mod_a == mod_b
+ let retval = 0
+ elseif mod_a > mod_b
+ if match(mod_a, '^vim-') > -1 && match(mod_b, '^vim-') == -1
+ let retval = -1
+ else
+ let retval = 1
+ endif
+ else
+ if match(mod_b, '^vim-') > -1 && match(mod_a, '^vim-') == -1
+ let retval = 1
+ else
+ let retval = -1
+ endif
+ endif
+ return retval
+endfunction
+
+function! s:GlobTutorials(name)
+ " search for tutorials:
+ " 1. non-localized
+ let l:tutors = s:GlobPath(&rtp, 'tutor/'.a:name.'.tutor')
+ " 2. localized for current locale
+ let l:locale_tutors = s:GlobPath(&rtp, 'tutor/'.s:Locale()[0].'/'.a:name.'.tutor')
+ " 3. fallback to 'en'
+ if len(l:locale_tutors) == 0
+ let l:locale_tutors = s:GlobPath(&rtp, 'tutor/en/'.a:name.'.tutor')
+ endif
+ call extend(l:tutors, l:locale_tutors)
+ return uniq(sort(l:tutors, 's:Sort'), 's:Sort')
+endfunction
+
+function! tutor#TutorCmd(tutor_name)
+ if match(a:tutor_name, '[[:space:]]') > 0
+ echom "Only one argument accepted (check spaces)"
+ return
+ endif
+
+ if a:tutor_name == ''
+ let l:tutor_name = 'vim-01-beginner.tutor'
+ else
+ let l:tutor_name = a:tutor_name
+ endif
+
+ if match(l:tutor_name, '\.tutor$') > 0
+ let l:tutor_name = fnamemodify(l:tutor_name, ':r')
+ endif
+
+ let l:tutors = s:GlobTutorials(l:tutor_name)
+
+ if len(l:tutors) == 0
+ echom "No tutorial with that name found"
+ return
+ endif
+
+ if len(l:tutors) == 1
+ let l:to_open = l:tutors[0]
+ else
+ let l:idx = 0
+ let l:candidates = ['Several tutorials with that name found. Select one:']
+ for candidate in map(copy(l:tutors),
+ \'fnamemodify(v:val, ":h:h:t")."/".s:Locale()[0]."/".fnamemodify(v:val, ":t")')
+ let l:idx += 1
+ call add(l:candidates, l:idx.'. '.candidate)
+ endfor
+ let l:tutor_to_open = inputlist(l:candidates)
+ let l:to_open = l:tutors[l:tutor_to_open-1]
+ endif
+
+ if has('gui') || has('nvim')
+ exe "drop ".l:to_open
+ else
+ exe "edit ".l:to_open
+ endif
+endfunction
+
+function! tutor#TutorCmdComplete(lead,line,pos)
+ let l:tutors = s:GlobTutorials('*')
+ let l:names = uniq(sort(map(l:tutors, 'fnamemodify(v:val, ":t:r")'), 's:Sort'))
+ return join(l:names, "\n")
+endfunction
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index a79903b358..e8c844f7d2 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -5163,7 +5163,7 @@ A jump table for the options with a short description can be found at |Q_op|.
print/ files for printing |postscript-print-encoding|
spell/ spell checking files |spell|
syntax/ syntax files |mysyntaxfile|
- tutor/ files for vimtutor |tutor|
+ tutor/ tutorial files |:Tutor|
And any other file searched for with the |:runtime| command.
diff --git a/runtime/doc/pi_tutor.txt b/runtime/doc/pi_tutor.txt
new file mode 100644
index 0000000000..7ed47b24dd
--- /dev/null
+++ b/runtime/doc/pi_tutor.txt
@@ -0,0 +1,50 @@
+*pi_tutor.txt* Interactive tutorials.
+*vim-tutor-mode*
+
+vim-tutor-mode provides a system to follow and create interactive tutorials
+for vim and third party plugins. It replaces the venerable `vimtutor` system.
+
+==============================================================================
+1. Usage *vim-tutor-usage*
+
+vim-tutor-mode tutorials are hypertext documents, they have rich text and
+contain links. To stand out from the rest of the text, links are underlined.
+You can follow them by placing the cursor over them and pressing <Enter>, or
+by double-clicking them.
+
+1.1 Commands
+------------
+ *:Tutor*
+:Tutor {tutorial} Opens a tutorial. Command-line completion for
+ {tutorial} is provided, the candidates are a list of
+ '.tutor' files found in the 'tutor/' folder in
+ the 'runtimepath'. Tutorials prefixed with 'vim-' will
+ always be shown first.
+
+ If no {tutorial} is provided, the command starts the
+ 'vim-01-beginner' tutorial, which is equivalent to
+ Vim's `vimtutor`.
+
+=============================================================================
+2. Creating tutorials *vim-tutor-create*
+
+Writing vim-tutor-mode tutorials is easy. For an overview of the format used,
+please consult the 'tutor.tutor' file: >
+
+ :Tutor tutor
+<
+New tutorials must be placed in the 'tutor/' folder in the 'runtimepath'
+to be detected by the :Tutor command.
+
+It is recommended to use a less formal style when writing tutorials than in
+regular documentation (unless the content requires it).
+
+============================================================================
+3. Contributing
+
+Development of the plugin is done over at github [1]. Feel free to report
+issues and make suggestions.
+
+[1]: https://github.com/fmoralesc/vim-tutor-mode
+
+" vim: set ft=help :
diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt
index cdb3035a40..c6d363ad5f 100644
--- a/runtime/doc/usr_01.txt
+++ b/runtime/doc/usr_01.txt
@@ -70,52 +70,18 @@ If the file already exists you probably want to keep it.
For more info see |vimrc|.
==============================================================================
-*01.3* Using the Vim tutor *tutor* *vimtutor*
+*01.3* Using the Vim tutor *tutor* *vimtutor*
Instead of reading the text (boring!) you can use the vimtutor to learn your
first Vim commands. This is a 30 minute tutorial that teaches the most basic
Vim functionality hands-on.
-On Unix, if Vim has been properly installed, you can start it from the shell:
->
- vimtutor
+To start the tutorial, execute >
-On MS-Windows you can find it in the Program/Vim menu. Or execute
-vimtutor.bat in the $VIMRUNTIME directory.
-
-This will make a copy of the tutor file, so that you can edit it without
-the risk of damaging the original.
- There are a few translated versions of the tutor. To find out if yours is
-available, use the two-letter language code. For French: >
-
- vimtutor fr
-
-On Unix, if you prefer using the GUI version of Vim, use "gvimtutor" or
-"vimtutor -g", optionally with a two-letter language code.
-
-
-On other systems, you have to do a little work:
-
-1. Copy the tutor file. You can do this with Vim (it knows where to find it):
->
- vim -u NONE -c 'e $VIMRUNTIME/tutor/tutor' -c 'w! TUTORCOPY' -c 'q'
-<
- This will write the file "TUTORCOPY" in the current directory. To use a
-translated version of the tutor, append the two-letter language code to the
-filename. For French:
->
- vim -u NONE -c 'e $VIMRUNTIME/tutor/tutor.fr' -c 'w! TUTORCOPY' -c 'q'
+ :Tutor
<
-2. Edit the copied file with Vim:
->
- vim -u NONE TUTORCOPY
-<
- The extra arguments make sure Vim is started in a good mood.
+from within nvim. The tutorial will lead you from that point. Have fun!
-3. Delete the copied file when you are finished with it:
->
- del TUTORCOPY
-<
==============================================================================
*01.4* Copyright *manual-copyright*
diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt
index ebb918d7ec..cd25b14e32 100644
--- a/runtime/doc/usr_02.txt
+++ b/runtime/doc/usr_02.txt
@@ -164,7 +164,7 @@ right and j points down. In a picture: >
The best way to learn these commands is by using them. Use the "i" command to
insert some more lines of text. Then use the hjkl keys to move around and
insert a word somewhere. Don't forget to press <Esc> to go back to Normal
-mode. The |vimtutor| is also a nice way to learn by doing.
+mode. |:Tutor| is also a nice way to learn by doing.
For Japanese users, Hiroshi Iwatani suggested using this:
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 71d6b0b25a..66e747a06f 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -2259,6 +2259,9 @@ au BufNewFile,BufReadPost *.tssop setf tssop
" TSS - Command Line (temporary)
au BufNewFile,BufReadPost *.tsscl setf tsscl
+" Tutor mode
+au BufNewFile,BufReadPost *.tutor setf tutor
+
" TWIG files
au BufNewFile,BufReadPost *.twig setf twig
diff --git a/runtime/ftplugin/tutor.vim b/runtime/ftplugin/tutor.vim
new file mode 100644
index 0000000000..237176f5eb
--- /dev/null
+++ b/runtime/ftplugin/tutor.vim
@@ -0,0 +1,43 @@
+" vim: fdm=marker
+
+" Base: {{{1
+call tutor#SetupVim()
+
+" Buffer Settings: {{{1
+if !exists('g:tutor_debug') || g:tutor_debug == 0
+ setlocal buftype=nofile
+ setlocal concealcursor+=inv
+ setlocal conceallevel=2
+else
+ setlocal buftype=
+ setlocal concealcursor&
+ setlocal conceallevel=0
+endif
+setlocal noundofile
+
+setlocal keywordprg=:help
+setlocal iskeyword=@,-,_
+
+setlocal foldmethod=expr
+setlocal foldexpr=tutor#TutorFolds()
+setlocal foldcolumn=3
+setlocal foldlevel=4
+
+setlocal statusline=%{toupper(expand('%:t:r'))}\ tutorial%=
+setlocal statusline+=%{tutor#InfoText()}
+
+" Mappings: {{{1
+
+call tutor#SetNormalMappings()
+call tutor#SetSampleTextMappings()
+
+" Checks: {{{1
+
+sign define tutorok text=✓ texthl=tutorOK
+sign define tutorbad text=✗ texthl=tutorX
+
+if !exists('g:tutor_debug') || g:tutor_debug == 0
+ call tutor#PlaceXMarks()
+ autocmd! TextChanged <buffer> call tutor#OnTextChanged()
+ autocmd! TextChangedI <buffer> call tutor#OnTextChanged()
+endif
diff --git a/runtime/plugin/tutor.vim b/runtime/plugin/tutor.vim
new file mode 100644
index 0000000000..1411b1ac63
--- /dev/null
+++ b/runtime/plugin/tutor.vim
@@ -0,0 +1,6 @@
+if exists('g:loaded_tutor_mode_plugin') || &compatible
+ finish
+endif
+let g:loaded_tutor_mode_plugin = 1
+
+command! -nargs=? -complete=custom,tutor#TutorCmdComplete Tutor call tutor#TutorCmd(<q-args>)
diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim
new file mode 100644
index 0000000000..99bcb78270
--- /dev/null
+++ b/runtime/syntax/tutor.vim
@@ -0,0 +1,83 @@
+if exists("b:current_syntax")
+ finish
+endif
+
+syn include @VIM syntax/vim.vim
+unlet b:current_syntax
+syn include @TUTORSHELL syntax/sh.vim
+unlet b:current_syntax
+syn include @VIMNORMAL syntax/vimnormal.vim
+
+syn match tutorLink /\[.\{-}\](.\{-})/ contains=tutorInlineNormal
+syn match tutorLinkBands /\[\|\]\|(\|)/ contained containedin=tutorLink,tutorLinkAnchor conceal
+syn match tutorLinkAnchor /(.\{-})/ contained containedin=tutorLink conceal
+syn match tutorURL /\(https\?\|file\):\/\/[[:graph:]]\+\>\/\?/
+syn match tutorEmail /\<[[:graph:]]\+@[[:graph:]]\+\>/
+syn match tutorInternalAnchor /\*[[:alnum:]-]\+\*/ contained conceal containedin=tutorSection
+
+syn match tutorSection /^#\{1,6}\s.\+$/ fold
+syn match tutorSectionBullet /#/ contained containedin=tutorSection
+
+syn match tutorTOC /\ctable of contents:/
+
+syn match tutorConcealedEscapes /\\[`*!\[\]()«»:$-]\@=/ conceal
+
+syn region tutorEmphasis matchgroup=Delimiter start=/[\*]\@<!\*\*\@!/ end=/[\*]\@<!\*\*\@!/
+ \ concealends contains=tutorInlineCommand,tutorInlineNormal
+syn region tutorBold matchgroup=Delimiter start=/\*\{2}/ end=/\*\{2}/
+ \ concealends contains=tutorInlineCommand,tutorInlineNormal
+
+syn keyword tutorMarks TODO NOTE IMPORTANT TIP ATTENTION EXERCISE
+syn keyword tutorMarks todo note tip attention exercise
+syn keyword tutorMarks Todo Note Tip Excersise
+
+syn match tutorTextMark /\\\@<!--->/ conceal cchar=→
+syn region tutorSampleText start=/^\(--->\)\@=/ end=/$/ keepend contains=@SPELL
+syn match tutorSampleTextMark /^--->/ contained containedin=tutorSampleText conceal cchar=→
+syn match tutorSampleTextExpect /\}\@<! {expect:.\+}\s*$/ contained containedin=tutorSampleText conceal
+syn match tutorSampleTextExpect /|\@<! |expect:.\+|\s*$/ contained containedin=tutorSampleText conceal
+
+syn region tutorCodeblock matchgroup=Delimiter start=/^\~\{3}.*$/ end=/^\~\{3}/
+
+syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL concealends
+syn match tutorShellPrompt /\(^\s*\)\@<=[$#]/ contained containedin=tutorShell
+
+syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/ concealends
+
+syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM concealends
+syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM concealends
+
+syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL concealends
+syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL concealends
+
+syn match tutorInlineType /{\(normal\|vim\)}/ contained conceal
+
+syn match tutorInlineOK /✓/
+syn match tutorInlineX /✗/
+
+hi! tutorLink cterm=underline gui=underline ctermfg=lightblue guifg=#0088ff
+hi! link tutorLinkBands Delimiter
+hi! link tutorLinkAnchor Underlined
+hi! link tutorInternalAnchor Underlined
+hi! link tutorURL tutorLink
+hi! link tutorEmail tutorLink
+
+hi! link tutorSection Title
+hi! link tutorSectionBullet Delimiter
+
+hi! link tutorTOC Directory
+
+hi! tutorMarks cterm=bold gui=bold
+
+hi! tutorEmphasis gui=italic cterm=italic
+hi! tutorBold gui=bold cterm=bold
+
+hi! link tutorSampleText Special
+hi! tutorOK ctermfg=green guifg=#00ff88 cterm=bold gui=bold
+hi! tutorX ctermfg=red guifg=#ff2000 cterm=bold gui=bold
+hi! link tutorInlineOK tutorOK
+hi! link tutorInlineX tutorX
+
+hi! link tutorShellPrompt Delimiter
+
+let b:current_syntax = "tutor"
diff --git a/runtime/syntax/vimnormal.vim b/runtime/syntax/vimnormal.vim
new file mode 100644
index 0000000000..df672c5236
--- /dev/null
+++ b/runtime/syntax/vimnormal.vim
@@ -0,0 +1,16 @@
+syn match normalOp /[dcrypoaxv!"#%&.-\/:<>=?@ABCDGHIJKLMNOPQRSUVWXYZgmqstz~iu]/ nextgroup=normalMod
+syn match normalMod /m\@<![ia]/
+syn match normalObject /["'()<>BW\[\]`bstweE{}ftFT;,$]/
+syn match normalCount /[0-9]/
+syn region normalSearch start=/[/?]\@<=./ end=/.<CR>\@=/ contains=normalKey keepend
+syn region normalChange start=/\([cr][wWbBeE()\[\]{}pst]\)\@<=./ end=/.\@=/ contains=normalKey keepend
+syn match normalCharSearch /\c[ftr]\@<=\w/
+syn match normalMark /\(f\@<!m\)\@<=[a-zA-Z0-9]/
+syn match normalKey /<'\@!.\{-}>'\@!/
+
+hi! link normalOp Operator
+hi! link normalMod PreProc
+hi! link normalObject Structure
+hi! link normalCount Number
+hi! link normalMark Identifier
+hi! link normalKey Special
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
new file mode 100644
index 0000000000..bda4e3537e
--- /dev/null
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -0,0 +1,955 @@
+# Welcome to the VIM Tutor
+
+Vim is a very powerful editor that has many commands, too many to explain in a
+tutor such as this. This tutor is designed to describe enough of the commands
+that you will be able to easily use Vim as an all-purpose editor. It is
+IMPORTANT to remember that this tutor is set up to teach by use. That means
+that you need to do the exercises to learn them properly. If you only read
+the text, you will soon forget what is most important!
+
+NOTE: The commands in the lessons will modify the text, but those changes won't
+be saved. Don't worry about messing things up; just remember that pressing
+[<Esc>](<Esc>) and then [u](u) will undo the latest change.
+
+This tutorial is interactive, and there are a few things you should know.
+Pressing [<Enter>](<Enter>) over text highlighted [like this](holy-grail) will take you to some relevant
+help (hopefully), and pressing K over any word will try to do so too. Sometimes
+you will be required to modify text like
+---> this here {expect:this here}
+Once you have done the changes correctly, the ✗ sign at the left will change
+to ✓. I imagine you can already see how neat Vim can be ;)
+Other times, you'll be prompted to run a command (I'll explain this later):
+~~~ cmd
+ :help <Enter>
+~~~
+or press a sequence of keys
+~~~ normal
+ <Esc>0f<Space>d3wP$P
+~~~
+
+Text within <'s and >'s (like `<Enter>`{normal}) describes a key to press instead of text
+to type.
+
+Now, make sure that your Shift-Lock key is NOT depressed and press the `j`{normal}
+key enough times to move the cursor so that Lesson 1.1 completely fills the
+screen.
+
+## Lesson 1.1: MOVING THE CURSOR
+
+** To move the cursor, press the `h`, `j`, `k`, `l` keys as indicated. **
+
+ ↑
+ k Hint: The h key is at the left and moves left.
+ ← h l → The l key is at the right and moves right.
+ j The j key looks like a down arrow.
+ ↓
+
+ 1. Move the cursor around the screen until you are comfortable.
+
+ 2. Hold down the down key (j) until it repeats.
+ Now you know how to move to the next lesson.
+
+ 3. Using the down key, move to Lesson 1.2.
+
+NOTE: If you are ever unsure about something you typed, press <Esc> to place
+ you in Normal mode. Then retype the command you wanted.
+
+NOTE: The cursor keys should also work. But using hjkl you will be able to
+ move around much faster, once you get used to it. Really!
+
+# Lesson 1.2: EXITING VIM
+
+!! NOTE: Before executing any of the steps below, read this entire lesson !!
+
+ 1. Press the <Esc> key (to make sure you are in [Normal mode]().
+
+ 2. Type:
+
+ `:q!`{vim} `<Enter>`{normal}.
+
+ This exits the editor, DISCARDING any changes you have made.
+
+ 3. Open vim and get back here by executing the command that got you into this
+ tutor. That might be:
+
+ :Tutor <Enter>
+
+ 4. If you have these steps memorized and are confident, execute steps
+ 1 through 3 to exit and re-enter the editor.
+
+NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you
+ will learn how to save the changes to a file.
+
+ 5. Move the cursor down to Lesson 1.3.
+
+## Lesson 1.3: TEXT EDITING - DELETION
+
+** Press `x`{normal} to delete the character under the cursor. **
+
+1. Move the cursor to the line below marked --->.
+
+2. To fix the errors, move the cursor until it is on top of the
+ character to be deleted.
+
+3. Press [the x key](x) to delete the unwanted character.
+
+4. Repeat steps 2 through 4 until the sentence is correct.
+
+---> The ccow jumpedd ovverr thhe mooon. {expect:The cow jumped over the moon.}
+
+5. Now that the line is correct, go on to Lesson 1.4.
+
+NOTE: As you go through this tutor, do not try to memorize, learn by usage.
+
+# Lesson 1.4: TEXT EDITING: INSERTION
+
+** Press `i`{normal} to insert text. **
+
+ 1. Move the cursor to the first line below marked --->.
+
+ 2. To make the first line the same as the second, move the cursor on top
+ of the first character AFTER where the text is to be inserted.
+
+ 3. Press i and type in the necessary additions.
+
+ 4. As each error is fixed press <Esc> to return to Normal mode.
+ Repeat steps 2 through 4 to correct the sentence.
+
+---> There is text misng this . {expect:There is some text missing from this line.}
+---> There is some text missing from this line. {expect:There is some text missing from this line.}
+
+ 5. When you are comfortable inserting text move to lesson 1.5.
+
+# Lesson 1.5: TEXT EDITING: APPENDING
+
+** Press `A`{normal} to append text. **
+
+ 1. Move the cursor to the first line below marked --->.
+ It does not matter on what character the cursor is in that line.
+
+ 2. Press [A](A) and type in the necessary additions.
+
+ 3. As the text has been appended press <Esc> to return to Normal mode.
+
+ 4. Move the cursor to the second line marked ---> and repeat
+ steps 2 and 3 to correct this sentence.
+
+---> There is some text missing from th {expect:There is some text missing from this line.}
+---> There is some text missing from this line. {expect:There is some text missing from this line.}
+---> There is also some text miss {expect:There is also some text missing here.}
+---> There is also some text missing here. {expect:There is also some text missing here.}
+
+ 5. When you are comfortable appending text move to lesson 1.6.
+
+# Lesson 1.6: EDITING A FILE
+
+** Use `:wq`{vim} to save a file and exit. **
+
+!! NOTE: Before executing any of the steps below, read this entire lesson !!
+
+ 1. Exit this tutor as you did in lesson 1.2: :q!
+ Or, if you have access to another terminal, do the following there.
+
+ 2. At the shell prompt type this command:
+~~~ sh
+ $ vim tutor
+~~~
+ 'vim' is the command to start the Vim editor, 'tutor' is the name of the
+ file you wish to edit. Use a file that may be changed.
+
+ 3. Insert and delete text as you learned in the previous lessons.
+
+ 4. Save the file with changes and exit Vim with:
+~~~ cmd
+ :wq
+~~~
+
+ Note you'll need to press `<Enter>` to execute the command.
+
+ 5. If you have quit vimtutor in step 1 restart the vimtutor and move down to
+ the following summary.
+
+ 6. After reading the above steps and understanding them: do it.
+
+# Lesson 1 SUMMARY
+
+ 1. The cursor is moved using either the arrow keys or the hjkl keys.
+ h (left) j (down) k (up) l (right)
+
+ 2. To start Vim from the shell prompt type:
+
+~~~ sh
+ $ vim FILENAME
+~~~
+
+ 3. To exit Vim type: <Esc> :q! <Enter> to trash all changes.
+ OR type: <Esc> :wq <Enter> to save the changes.
+
+ 4. To delete the character at the cursor type: `x`{normal}
+
+ 5. To insert or append text type:
+ `i`{normal} type inserted text `<Esc>`{normal} insert before the cursor
+ `A`{normal} type appended text `<Esc>`{normal} append after the line
+
+NOTE: Pressing `<Esc>`{normal} will place you in Normal mode or will cancel
+ an unwanted and partially completed command.
+
+Now continue with Lesson 2.
+
+# Lesson 2.1: DELETION COMMANDS
+
+** Type `dw`{normal} to delete a word. **
+
+ 1. Press `<Esc>`{normal} to make sure you are in Normal mode.
+
+ 2. Move the cursor to the line below marked --->.
+
+ 3. Move the cursor to the beginning of a word that needs to be deleted.
+
+ 4. Type [d](d)[w](w) to make the word disappear.
+
+---> There are a some words fun that don't belong paper in this sentence. {expect:There are some words that don't belong in this sen