patch 9.1.0836: The vimtutor can be improved

Commit: 
https://github.com/vim/vim/commit/a54816b884157f6b7973a188f85c708d15cbf72f
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Sun Nov 3 10:49:23 2024 +0100

    patch 9.1.0836: The vimtutor can be improved
    
    Problem:  the vimtutor can be improved
    Solution: port and include the interactive vimtutor plugin from Neovim
              (by Felipe Morales) (Yegappan Lakshmanan)
    
    closes: #6414
    
    Signed-off-by: Christian Brabandt <c...@256bit.org>
    Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com>

diff --git a/Filelist b/Filelist
index 7d2074a04..4fbcc0a2f 100644
--- a/Filelist
+++ b/Filelist
@@ -762,6 +762,10 @@ RT_ALL =   \
                runtime/tools/[a-z]*[a-z0-9] \
                runtime/tutor/README.txt \
                runtime/tutor/tutor \
+               runtime/tutor/en/vim-01-beginner.tutor \
+               runtime/tutor/en/vim-01-beginner.tutor.json \
+               runtime/tutor/tutor.tutor \
+               runtime/tutor/tutor.tutor.json \
                runtime/tutor/tutor.vim \
                runtime/vimrc_example.vim \
                runtime/pack/dist/opt/cfilter/plugin/cfilter.vim \
diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim
new file mode 100644
index 000000000..3265fdde3
--- /dev/null
+++ b/runtime/autoload/tutor.vim
@@ -0,0 +1,219 @@
+" vim: fdm=marker et ts=4 sw=4
+
+" Setup: {{{1
+function! tutor#SetupVim()
+    if !exists('g:did_load_ftplugin') || g:did_load_ftplugin != 1
+        filetype plugin on
+    endif
+    if has('syntax')
+        if !exists('g:syntax_on') || g:syntax_on == 0
+            syntax on
+        endif
+    endif
+endfunction
+
+" Loads metadata file, if available
+function! tutor#LoadMetadata()
+    let b:tutor_metadata = json_decode(join(readfile(expand('%').'.json'), "
"))
+endfunction
+
+" Mappings: {{{1
+
+function! tutor#SetNormalMappings()
+    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#MouseDoubleClick()
+    if foldclosed(line('.')) > -1
+        normal! zo
+    else
+        if match(getline('.'), '^#\{1,} ') > -1
+            silent 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#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
+
+" Marks: {{{1
+
+function! tutor#ApplyMarks()
+    hi! link tutorExpect Special
+    if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
+        let b:tutor_sign_id = 1
+        for expct in keys(b:tutor_metadata['expect'])
+            let lnum = eval(expct)
+            call matchaddpos('tutorExpect', [lnum])
+            call tutor#CheckLine(lnum)
+        endfor
+    endif
+endfunction
+
+function! tutor#ApplyMarksOnChanged()
+    if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
+        let lnum = line('.')
+        if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1
+            call tutor#CheckLine(lnum)
+        endif
+    endif
+endfunction
+
+function! tutor#CheckLine(line)
+    if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
+        let bufn = bufnr('%')
+        let ctext = getline(a:line)
+        if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# 
b:tutor_metadata['expect'][string(a:line)]
+            exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok 
buffer=".bufn
+        else
+            exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad 
buffer=".bufn
+        endif
+        let b:tutor_sign_id+=1
+    endif
+endfunction
+
+" Tutor Cmd: {{{1
+
+function! s:Locale()
+    if exists('v:lang') && v:lang =~ '  '
+        let l:lang = v:lang
+    elseif $LC_ALL =~ '  '
+        let l:lang = $LC_ALL
+    elseif $LANG =~ '  '
+        let l:lang = $LANG
+    else
+        let l:lang = 'en_US'
+    endif
+    return split(l:lang, '_')
+endfunction
+
+function! s:GlobPath(lp, pat)
+    if version >= 704 && has('patch279')
+        return globpath(a:lp, a:pat, 1, 1)
+    else
+        return split(globpath(a:lp, a:pat, 1), '
')
+    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
+
+    call tutor#SetupVim()
+    exe "edit ".l:to_open
+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, "
")
+endfunction
diff --git a/runtime/defaults.vim b/runtime/defaults.vim
index 459841ffc..4e58233ea 100644
--- a/runtime/defaults.vim
+++ b/runtime/defaults.vim
@@ -1,7 +1,7 @@
 " The default vimrc file.
 "
 " Maintainer:  The Vim Project <https://github.com/vim/vim>
-" Last change: 2023 Aug 10
+" Last Change: 2024 Nov 03
 " Former Maintainer:   Bram Moolenaar <b...@vim.org>
 "
 " This is loaded if no vimrc file was found.
@@ -107,11 +107,11 @@ if 1
     " (happens when dropping a file on gvim), for a commit or rebase message
     " (likely a different one than last time), and when using xxd(1) to filter
     " and edit binary files (it transforms input files back and forth, causing
-    " them to have dual nature, so to speak)
+    " them to have dual nature, so to speak) or when running the new tutor
     autocmd BufReadPost *
       \ let line = line("'\"")
       \ | if line >= 1 && line <= line("$") && &filetype !~# 'commit'
-      \      && index(['xxd', 'gitrebase'], &filetype) == -1
+      \      && index(['xxd', 'gitrebase', 'tutor'], &filetype) == -1
       \ |   execute "normal! g`\""
       \ | endif
 
diff --git a/runtime/doc/Make_all.mak b/runtime/doc/Make_all.mak
index f36ad7eca..ccc429b0c 100644
--- a/runtime/doc/Make_all.mak
+++ b/runtime/doc/Make_all.mak
@@ -75,6 +75,7 @@ DOCS = \
        pi_paren.txt \
        pi_spec.txt \
        pi_tar.txt \
+       pi_tutor.txt \
        pi_vimball.txt \
        pi_zip.txt \
        popup.txt \
@@ -228,6 +229,7 @@ HTMLS = \
        pi_paren.html \
        pi_spec.html \
        pi_tar.html \
+       pi_tutor.html \
        pi_vimball.html \
        pi_zip.html \
        popup.html \
diff --git a/runtime/doc/pi_tutor.txt b/runtime/doc/pi_tutor.txt
new file mode 100644
index 000000000..7aaafd19a
--- /dev/null
+++ b/runtime/doc/pi_tutor.txt
@@ -0,0 +1,51 @@
+*pi_tutor.txt*    For Vim version 9.1.  Last change: 2024 Nov 02
+
+INTERACTIVE TUTORIALS FOR VIM                   *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/tags b/runtime/doc/tags
index 79d9dafb9..37a8e266d 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -2177,6 +2177,7 @@ $quote    eval.txt        /*$quote*
 :Termdebug     terminal.txt    /*:Termdebug*
 :TermdebugCommand      terminal.txt    /*:TermdebugCommand*
 :Texplore      pi_netrw.txt    /*:Texplore*
+:Tutor pi_tutor.txt    /*:Tutor*
 :Until terminal.txt    /*:Until*
 :Up    terminal.txt    /*:Up*
 :UseVimball    pi_vimball.txt  /*:UseVimball*
@@ -9438,6 +9439,7 @@ pi_netrw.txt      pi_netrw.txt    /*pi_netrw.txt*
 pi_paren.txt   pi_paren.txt    /*pi_paren.txt*
 pi_spec.txt    pi_spec.txt     /*pi_spec.txt*
 pi_tar.txt     pi_tar.txt      /*pi_tar.txt*
+pi_tutor.txt   pi_tutor.txt    /*pi_tutor.txt*
 pi_vimball.txt pi_vimball.txt  /*pi_vimball.txt*
 pi_zip.txt     pi_zip.txt      /*pi_zip.txt*
 pkzip  options.txt     /*pkzip*
@@ -11244,6 +11246,9 @@ vim-script-intro        usr_41.txt      
/*vim-script-intro*
 vim-script-library     eval.txt        /*vim-script-library*
 vim-security   intro.txt       /*vim-security*
 vim-shebang    various.txt     /*vim-shebang*
+vim-tutor-create       pi_tutor.txt    /*vim-tutor-create*
+vim-tutor-mode pi_tutor.txt    /*vim-tutor-mode*
+vim-tutor-usage        pi_tutor.txt    /*vim-tutor-usage*
 vim-use        intro.txt       /*vim-use*
 vim-variable   eval.txt        /*vim-variable*
 vim.b  if_lua.txt      /*vim.b*
diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt
index fdf1b5386..9902691ba 100644
--- a/runtime/doc/usr_01.txt
+++ b/runtime/doc/usr_01.txt
@@ -1,4 +1,4 @@
-*usr_01.txt*   For Vim version 9.1.  Last change: 2024 May 11
+*usr_01.txt*   For Vim version 9.1.  Last change: 2024 Nov 03
 
                     VIM USER MANUAL - by Bram Moolenaar
 
@@ -107,6 +107,8 @@ For more info see |vimrc| and |compatible-default|.
 ==============================================================================
 *01.3* Using the Vim tutor                             *tutor* *vimtutor*
 
+For the interactive tutor, see |vim-tutor-mode|
+
 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.
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 5f9250348..d016ccfb1 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2024 Nov 02
+*version9.txt*  For Vim version 9.1.  Last change: 2024 Nov 03
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41603,6 +41603,8 @@ Changed~
 - the putty terminal is detected using an |TermResponse| autocommand in
   |defaults.vim| and Vim switches to a dark background
 - the |help-TOC| package is included to ease navigating the documentation.
+- an interactive tutor plugin has been included |vim-tutor-mode|, can be
+  started via |:Tutor|
 
                                                        *added-9.2*
 Added ~
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 36461f69f..f2ddcc676 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -2589,6 +2589,9 @@ au BufNewFile,BufReadPost *.tsscl         setf tsscl
 " TSV Files
 au BufNewFile,BufRead *.tsv                    setf tsv
 
+" 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 000000000..30783d979
--- /dev/null
+++ b/runtime/ftplugin/tutor.vim
@@ -0,0 +1,45 @@
+" vim: fdm=marker
+
+" Base: {{{1
+call tutor#SetupVim()
+
+" Buffer Settings: {{{1
+setlocal noreadonly
+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=@,-,_
+
+" The user will have to enable the folds himself, but we provide the foldexpr
+" function.
+setlocal foldmethod=manual
+setlocal foldexpr=tutor#TutorFolds()
+setlocal foldlevel=4
+
+" Load metadata if it exists: {{{1
+if filereadable(expand('%').'.json')
+    call tutor#LoadMetadata()
+endif
+
+" Mappings: {{{1
+
+call tutor#SetNormalMappings()
+
+" 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#ApplyMarks()
+    autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
+endif
diff --git a/runtime/plugin/tutor.vim b/runtime/plugin/tutor.vim
new file mode 100644
index 000000000..1411b1ac6
--- /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 000000000..83ca547fd
--- /dev/null
+++ b/runtime/syntax/tutor.vim
@@ -0,0 +1,77 @@
+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 contains=tutorInlineNormal
+syn match tutorSectionBullet /#/ contained containedin=tutorSection
+
+syn match tutorTOC /

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1t7XdP-00Aclw-8k%40256bit.org.

Raspunde prin e-mail lui