diff --git a/README.md b/README.md
index 1b055f3..de842f0 100644
--- a/README.md
+++ b/README.md
@@ -67,13 +67,14 @@ Thank you for making this plugin better!
Table of Contents
Install
Quick start
- Examplesshow 22 items
+ - Examples
show 23 items
- g:mkdx#settingsshow 43 items
+ g:mkdx#settingsshow 44 items
g:mkdx#settings.gf_on_steroids
g:mkdx#settings.links.external.enable
g:mkdx#settings.links.external.timeout
@@ -117,8 +118,9 @@ Thank you for making this plugin better!
g:mkdx#settings.table.header_divider
g:mkdx#settings.table.divider
g:mkdx#settings.table.align
- g:mkdx#settings.enter.shift
+ g:mkdx#settings.tab.enable
g:mkdx#settings.enter.enable
+ g:mkdx#settings.enter.shift
g:mkdx#settings.enter.o
g:mkdx#settings.enter.shifto
g:mkdx#settings.enter.malformed
@@ -342,6 +344,19 @@ previous indentation level.
" :h mkdx-function-shift-enter-handler
```
+## Indenting / unindenting numbered list items
+
+
+
+Numbered list items will be renumbered upon indenting / unindenting. The algorithm for this is still pretty basic
+but attempts to ensure correct nesting. This feature uses tab and shift+tab
+to indent and unindent respectively.
+
+```viml
+" :h mkdx-mapping-list-items
+" :h mkdx-function-indent-handler
+```
+
## Toggling lines from / to task items

@@ -1085,6 +1100,25 @@ let g:mkdx#settings = { 'table': { 'align': {
\ 'default': 'center' } } }
```
+## `g:mkdx#settings.tab.enable`
+
+This setting is enabled by default, when enabled, pressing tab and shift+tab on numbered
+list items will indent / unindent them and also renumber the list accordingly.
+
+## `g:mkdx#settings.enter.enable`
+
+This setting enables auto-appending list items when you are editing a markdown list.
+When enter is pressed, a function is executed to detect wether or not to insert a new list item
+or just do a regular enter. unordered lists and numbered lists are both handled correctly.
+
+**NOTE:** When this setting is enabled, mkdx will execute a `setlocal formatoptions-=r` to prevent duplicate list markers from being inserted.
+This setting is [auto updated](#gmkdxsettingsauto_updateenable) when available.
+
+```viml
+" :h mkdx-setting-enter-enable
+let g:mkdx#settings = { 'enter': { 'enable': 1 } }
+```
+
## `g:mkdx#settings.enter.shift`
When enabled, pressing shift+enter will indent the next line upto the level of the text on the current line.
@@ -1139,20 +1173,6 @@ This can usually be fixed by setting them manually for your emulator, included f
:let g:mkdx#settings = { 'enter': { 'shift': 0 } }
```
-## `g:mkdx#settings.enter.enable`
-
-This setting enables auto-appending list items when you are editing a markdown list.
-When enter is pressed, a function is executed to detect wether or not to insert a new list item
-or just do a regular enter. unordered lists and numbered lists are both handled correctly.
-
-**NOTE:** When this setting is enabled, mkdx will execute a `setlocal formatoptions-=r` to prevent duplicate list markers from being inserted.
-This setting is [auto updated](#gmkdxsettingsauto_updateenable) when available.
-
-```viml
-" :h mkdx-setting-enter-enable
-let g:mkdx#settings = { 'enter': { 'enable': 1 } }
-```
-
## `g:mkdx#settings.enter.o`
This setting overwrites normal mode `o` in markdown files and causes `o` to work like pressing `` at the end of the line
@@ -1424,6 +1444,8 @@ To prevent mapping of a key from happening, see: [unmapping functionality](#unma
|# handler|insert|#|`(mkdx-link-compl)`|
|Jump to file|normal|gf|`(mkdx-gf)`|
|Open external file|normal|gx|`(mkdx-gx)`|
+|Indent numbered list item|tab|`(mkdx-indent)`|
+|Unindent numbered list item|shift+tab|`(mkdx-unindent)`|
## Remapping functionality
@@ -1471,6 +1493,8 @@ nmap =
vmap =
```
+If you only want to do this for markdown files, refer to the previous snippet using the `au` command.
+
The mappings are checked using the value of [`g:mkdx#settings.map.prefix`](#gmkdxsettingsmapprefix) so you may need to check its value first
by running the following: `:echo g:mkdx#settings.map.prefix`. A better way to prevent mkdx from mapping keys is by remapping `` mappings.
diff --git a/autoload/mkdx.vim b/autoload/mkdx.vim
index e1372ff..881b244 100644
--- a/autoload/mkdx.vim
+++ b/autoload/mkdx.vim
@@ -1686,6 +1686,113 @@ fun! mkdx#ShiftOHandler()
startinsert!
endfun
+fun! s:util.UpdateNumberedList()
+ let lnum = line('.')
+ let num_pat = '^>\? *[0-9.]\+'
+ let result = []
+
+ while (indent(lnum) > 0)
+ let lnum -= 1
+ endwhile
+
+ if (match(getline(lnum), num_pat) > -1)
+ let start_lnum = lnum
+ let end_lnum = lnum
+ let indent_size = s:sw()
+
+ while (nextnonblank(start_lnum - 1) == start_lnum - 1)
+ let start_lnum -= 1
+ endwhile
+
+ while (nextnonblank(end_lnum + 1) == end_lnum + 1)
+ let end_lnum += 1
+ endwhile
+
+ for lnum in range(start_lnum, end_lnum)
+ let raw_line = getline(lnum)
+ let line = substitute(raw_line, '^ *>', '', '')
+ let depth = len(get(matchlist(line, '^ *'), 0, '')) / indent_size
+ let line = substitute(line, '^ *', '', '')
+ let prev = get(result, -1)
+ let has_prev = type(prev) == type({})
+ let nums = []
+
+ if (match(line, num_pat) > -1)
+ if (has_prev)
+ let depth = min([depth, prev.depth + 1])
+ let nums = copy(prev.nums)
+
+ if depth > prev.depth
+ call add(nums, 1)
+ elseif depth < prev.depth
+ let nums = nums[0:depth]
+ let nums[depth] += 1
+ elseif depth == prev.depth
+ let nums[depth] += 1
+ endif
+ else
+ call add(nums, 1)
+ endif
+
+ call add(result, {
+ \ 'quoted': match(raw_line, '^ *>') > -1,
+ \ 'lnum': lnum,
+ \ 'depth': depth,
+ \ 'lines': [line],
+ \ 'nums': nums
+ \ })
+ elseif (has_prev)
+ call add(prev.lines, line)
+ endif
+ endfor
+ endif
+
+ for item in result
+ let indent = repeat(' ', s:sw() * item.depth)
+ let quote = item.quoted ? '> ' : ''
+ let lnum = item.lnum
+ let line = substitute(item.lines[0], num_pat, join(item.nums, '.') . '.', '')
+
+ call setline(lnum, quote . indent . line)
+
+ if (len(item.lines) > 1)
+ let sp_pat = '^>\? *\(\([0-9.]\+\|[' . join(g:mkdx#settings.tokens.enter, '') . ']\)\( \[.\]\)\? \|\[.\]\)'
+ let item_syms = get(matchlist(line, sp_pat), 1, '')
+ let inline_indent = repeat(' ', len(item_syms))
+
+ for inline_line in item.lines[1:]
+ let lnum += 1
+ call setline(lnum, quote . indent . inline_indent . inline_line)
+ endfor
+ endif
+ endfor
+
+ return result
+endfun
+
+fun! mkdx#IndentHandler(nested)
+ let line = getline('.')
+ let quote = get(matchlist(line, '^ *>'), 0, '')
+
+ if (!empty(quote))
+ let line = substitute(line, quote, '', '')
+ call setline('.', line)
+ endif
+
+ if a:nested
+ normal! >>
+ else
+ normal! <<
+ endif
+
+ if (!empty(quote))
+ let line = quote . getline('.')
+ call setline('.', line)
+ endif
+
+ call s:util.UpdateNumberedList()
+endfun
+
fun! mkdx#ShiftEnterHandler()
if (!g:mkdx#settings.enter.shift) | return "\n" | endif
let rem = matchlist(getline('.'), '^\(> *\)\? *\(\%([0-9.]\+\|[' . join(g:mkdx#settings.tokens.enter, '') . ']\)\%( \+\[.\]\)\? *\|\[.\] *\)')
diff --git a/doc/gifs/indent-unindent-list-items.gif b/doc/gifs/indent-unindent-list-items.gif
new file mode 100644
index 0000000..5836fe2
Binary files /dev/null and b/doc/gifs/indent-unindent-list-items.gif differ
diff --git a/doc/mkdx.txt b/doc/mkdx.txt
index cf4e849..9c6b17a 100644
--- a/doc/mkdx.txt
+++ b/doc/mkdx.txt
@@ -50,6 +50,7 @@ Settings `.......................................................` |mkdx-setting
tokens.list `.....................................` |mkdx-setting-tokens-list|
table.header_divider `...................` |mkdx-setting-table-header-divider|
table.divider `.................................` |mkdx-setting-table-divider|
+ tab.enable `.......................................` |mkdx-setting-tab-enable|
enter.enable `...................................` |mkdx-setting-enter-enable|
enter.shift `.....................................` |mkdx-setting-enter-shift|
enter.o `.............................................` |mkdx-setting-enter-o|
@@ -153,6 +154,8 @@ Plugs `.............................................................` |mkdx-plug
(mkdx-fence-backtick) `.....................` |mkdx-plug-fence-backtick|
(mkdx-gf) `.............................................` |mkdx-plug-gf|
(mkdx-gx) `.............................................` |mkdx-plug-gx|
+ (mkdx-indent) `.....................................` |mkdx-plug-indent|
+ (mkdx-unindent) `.................................` |mkdx-plug-unindent|
*mkdx-toc-mappings*
Mappings `.......................................................` |mkdx-mappings|
@@ -213,6 +216,7 @@ Functions `.....................................................` |mkdx-function
mkdx#WrapStrike() `..............................` |mkdx-function-wrap-strike|
mkdx#gf() `...............................................` |mkdx-function-gf|
mkdx#MaybeRestoreVisual() `.............` |mkdx-function-maybe-restore-visual|
+ mkdx#IndentHandler() `........................` |mkdx-function-indent-handler|
*mkdx-toc-errors*
Errors `...........................................................` |mkdx-errors|
@@ -637,7 +641,7 @@ being compared. This means column names are case-insensitive.
==============================================================================
`g:mkdx#settings.enter.enable = 1` *mkdx-setting-enter-enable*
-When pressing in insert mode, this handles appending of new list items
+When pressing in insert mode, this handles appending of new list items
in lists. See |mkdx-setting-tokens-list| for a list of supported list tokens.
Examples can be found at |mkdx-function-enter-handler|.
@@ -647,6 +651,12 @@ The overrides include:
`setlocal formatoptions-=r`
`setlocal autoindent`
+==============================================================================
+`g:mkdx#settings.tab.enable = 1` *mkdx-setting-tab-enable*
+
+When enabled, pressing and will indent / unindent lines and
+additionally renumber numbered list items.
+
==============================================================================
`g:mkdx#settings.enter.shift = 0` *mkdx-setting-enter-shift*
@@ -1262,6 +1272,16 @@ using {repeat.vim} by Tim Pope (https://github.com/tpope/vim-repeat).
`:call mkdx#gf('x')`
+==============================================================================
+(mkdx-indent) *mkdx-plug-indent*
+
+ `:call mkdx#IndentHandler(1)`
+
+==============================================================================
+(mkdx-unindent) *mkdx-plug-unindent*
+
+ `:call mkdx#IndentHandler(0)`
+
==============================================================================
MAPPINGS *mkdx-mappings*
@@ -1463,10 +1483,11 @@ visual selection. This function restores visual selection if
==============================================================================
List items *mkdx-mapping-list-items*
- `o` and
+ `o`, `O`, , , and
When editing a list in markdown, pressing either in insert mode or `o`
-in normal mode will insert a new list item below the current item. All items
+in normal mode will insert a new list item below the current item. When
+`O` is pressed, an item will be added above the current list item. All items
following the newly added item are incremented accordingly. The following
patterns are identified as list tokens:
@@ -1474,6 +1495,12 @@ patterns are identified as list tokens:
- Any token present in |mkdx-setting-tokens-list|
- All of the above, but inside a quoted block e.g: `> 1. list item`
+When pressing with |mkdx-setting-enter-shift| enabled, a new line
+of the current list item is created. This allows creating inline list items.
+
+When pressing or on a numbered list, the list will be reindented
+accordingly.
+
For examples, see |mkdx-function-enter-handler| and
|mkdx-function-shift-enter-handler|.
@@ -2343,6 +2370,12 @@ mkdx#MaybeRestoreVisual() *mkdx-function-maybe-restore-visual*
When |mkdx-setting-restore-visual| is enabled, this function restores the last
visual selection.
+mkdx#IndentHandler({indent}) *mkdx-function-indent-handler*
+
+When {indent} is nonzero, indents as usual, otherwise unindents as usual.
+Additionally, when indenting / unindenting within a numbered list, the
+list will be renumbered accordingly.
+
==============================================================================
|ERRORS| *mkdx-errors*
diff --git a/ftplugin/markdown/mkdx.vim b/ftplugin/markdown/mkdx.vim
index 648daa4..2f39d59 100644
--- a/ftplugin/markdown/mkdx.vim
+++ b/ftplugin/markdown/mkdx.vim
@@ -4,6 +4,7 @@ let s:defaults = {
\ 'restore_visual': 1,
\ 'gf_on_steroids': 0,
\ 'enter': { 'enable': 1, 'shift': 0, 'malformed': 1, 'o': 1, 'shifto': 1 },
+ \ 'tab': { 'enable': 1 },
\ 'map': { 'prefix': '', 'enable': 1 },
\ 'tokens': { 'enter': ['-', '*', '>'], 'bold': '**', 'italic': '*',
\ 'list': '-', 'fence': '', 'header': '#', 'strike': '' },
@@ -101,6 +102,8 @@ inoremap (mkdx-fence-backtick) =mkdx#InsertFenced
inoremap (mkdx-ctrl-n-compl) =mkdx#InsertCtrlNHandler()
inoremap (mkdx-ctrl-p-compl) =mkdx#InsertCtrlPHandler()
inoremap (mkdx-link-compl) mkdx#CompleteLink()
+noremap (mkdx-indent) :call mkdx#IndentHandler(1)
+noremap (mkdx-unindent) :call mkdx#IndentHandler(0)
if (g:mkdx#settings.links.fragment.complete)
setlocal completefunc=mkdx#Complete
@@ -187,6 +190,21 @@ if g:mkdx#settings.map.enable == 1
endif
endif
+ if (g:mkdx#settings.tab.enable)
+ if (!hasmapto('(mkdx-indent)', 'n'))
+ nmap (mkdx-indent)
+ endif
+ if (!hasmapto('(mkdx-indent', 'v'))
+ vmap (mkdx-indent)
+ endif
+ if (!hasmapto('(mkdx-unindent)', 'n'))
+ nmap (mkdx-unindent)
+ endif
+ if (!hasmapto('(mkdx-unindent', 'v'))
+ vmap (mkdx-unindent)
+ endif
+ endif
+
if (g:mkdx#settings.enter.enable)
setlocal formatoptions-=r
setlocal autoindent
diff --git a/test/components/lists.vader b/test/components/lists.vader
index 32e2eae..997b02d 100644
--- a/test/components/lists.vader
+++ b/test/components/lists.vader
@@ -310,3 +310,60 @@ Expect (List items are correctly incremented and new empty list item):
2.2. subtask
2.3. subtask
3. hello
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given (A nested numbered list):
+ 1. task
+ 2. task
+ 2.1. subtask
+ 2.2. subtask
+ 3. hello
+
+Do (Press on the second nested list item makes it a child of the first):
+ 3j\
+
+Expect (List items are correctly indented):
+ 1. task
+ 2. task
+ 2.1. subtask
+ 2.1.1. subtask
+ 3. hello
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given (A deeply nested numbered list):
+ 1. task
+ 2. task
+ 2.1. subtask
+ 2.1.1. subtask
+ 3. hello
+
+Do (Press on the first nested list item unindents all its children):
+ 2j\
+
+Expect (List items are correctly indented):
+ 1. task
+ 2. task
+ 3. subtask
+ 3.1. subtask
+ 4. hello
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given (A deeply nested numbered list):
+ 1. task
+ 2. task
+ 2.1. subtask
+ 2.1.1. subtask
+ 3. hello
+
+Do (Press on the last list item sets its number correctly):
+ G\
+
+Expect (List items are correctly indented):
+ 1. task
+ 2. task
+ 2.1. subtask
+ 2.1.1. subtask
+ 2.2. hello