diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1077a3c..0f020a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,28 +2,36 @@ name: Test on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] workflow_dispatch: jobs: - test: - + fmt: + name: Check Formatting runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@v6.0.2 + - name: Checkout Code + uses: actions/checkout@v6.0.2 - - name: Install system dependencies - run: sudo apt install vim-gtk3 xvfb + - name: Set up tools via mise + uses: jdx/mise-action@v4.0.1 - - name: Set up Ruby via mise - uses: jdx/mise-action@v4.0.1 + - name: Check formatting + run: mise run fmt:check + + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v6.0.2 - - name: Install gems - run: bundle install + - name: Set up tools via mise + uses: jdx/mise-action@v4.0.1 - - name: Run headless tests - run: xvfb-run -a bundle exec rspec + - name: Run Lua tests + run: mise run test diff --git a/.gitignore b/.gitignore index 0d1204a..4ac6386 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ doc/tags vendor/ -spec/examples.txt doc/tags diff --git a/.rspec b/.rspec deleted file mode 100644 index 83e16f8..0000000 --- a/.rspec +++ /dev/null @@ -1,2 +0,0 @@ ---color ---require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 0fbebed..0000000 --- a/.rubocop.yml +++ /dev/null @@ -1,5 +0,0 @@ -Metrics/AbcSize: - Enabled: false - -Metrics/BlockLength: - Enabled: false diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2b5bdb7..0000000 --- a/.travis.yml +++ /dev/null @@ -1,52 +0,0 @@ -os: osx -osx_image: xcode11.3 - -branches: - only: - - master - -language: ruby - -env: - HOMEBREW_NO_INSTALL_CLEANUP=1 - HOMEBREW_NO_AUTO_UPDATE=1 - PYTHON_VERSION=3.8.1 - -cache: - bundler: true - pip: true - directories: - - $HOME/.rvm - - $HOME/Library/Caches/Homebrew # https://stackoverflow.com/questions/39930171/cache-brew-builds-with-travis-ci - - /usr/local/Homebrew - - $HOME/.cache/pip - - $HOME/.cache/pyenv - -rvm: - - '2.7.0' - -before_install: - - gem install bundler - - gem install rubocop - - brew install macvim - -install: - - | - which pyenv - whereis pyenv - eval "$(pyenv init -)" - pyenv install --skip-existing "$PYTHON_VERSION" - pyenv global "$PYTHON_VERSION" - pyenv shell "$PYTHON_VERSION" - pyenv local "$PYTHON_VERSION" - pyenv rehash - pip3 --version - pip3 install vim-vint - - bundle install - - -script: - - vint -s --enable-neovim plugin/*.vim - - rubocop - - bundle exec rspec --format documentation diff --git a/Gemfile b/Gemfile deleted file mode 100644 index d5d68a1..0000000 --- a/Gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -group :test do - gem 'rake', '~> 12.3.3' - gem 'rspec' - gem 'rspec-retry' - gem 'vimrunner' -end diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 3002717..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,46 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - diff-lcs (1.6.2) - rake (12.3.3) - rspec (3.13.2) - rspec-core (~> 3.13.0) - rspec-expectations (~> 3.13.0) - rspec-mocks (~> 3.13.0) - rspec-core (3.13.6) - rspec-support (~> 3.13.0) - rspec-expectations (3.13.5) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-mocks (3.13.8) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-retry (0.6.2) - rspec-core (> 3.3) - rspec-support (3.13.7) - vimrunner (0.3.6) - -PLATFORMS - arm64-darwin-23 - ruby - -DEPENDENCIES - rake (~> 12.3.3) - rspec - rspec-retry - vimrunner - -CHECKSUMS - bundler (4.0.12) sha256=7f8b757d28dfb636e7b24fba2344ac6dd13b5b24f4b46d62573d483f211825ac - diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 - rake (12.3.3) sha256=f7694adb4fe638da35452300cee6c545e9c377a0e3190018ac04d590b3c26ab3 - rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587 - rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d - rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 - rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47 - rspec-retry (0.6.2) sha256=6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858 - rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c - vimrunner (0.3.6) sha256=b6c695eb6b6fa4ad28187a07219ef52726d952678d8fd7a9c7c90e88c5d828d2 - -BUNDLED WITH - 4.0.12 diff --git a/README.md b/README.md index 2ca2e49..d209b32 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ ![Bullets.vim](img/bullets-vim-logo.svg) + [![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors-) + > :information_source: Looking for help/maintainers https://github.com/dkarter/bullets.vim/issues/126 @@ -23,6 +25,7 @@ Renumbering lines: # Installation ### With Vim 8.1+ native package manager: + Clone into `.vim/pack/plugins/start` @@ -35,8 +38,7 @@ Make sure to include `packloadall` in your `vimrc`. Plug 'bullets-vim/bullets.vim' ``` -Then source your bundle file and run `:PlugInstall`. - +Then source your Vim config and run `:PlugInstall`. # Usage @@ -306,20 +308,20 @@ let g:bullets_checkbox_partials_toggle = 0 # Mappings -* Insert new bullet in INSERT mode: `` (Return key) -* Same as in case you want to unmap in INSERT mode (compatibility depends on your terminal emulator): `` -* Insert new bullet in NORMAL mode: `o` -* Renumber current visual selection: `gN` -* Renumber entire bullet list containing the cursor in NORMAL mode: gN -* Toggle a checkbox in NORMAL mode: `x` -* Demote a bullet (indent it, decrease bullet level, and make it a child of the previous bullet): - + NORMAL mode: `>>` - + INSERT mode: `` - + VISUAL mode: `>` -* Promote a bullet (unindent it and increase the bullet level): - + NORMAL mode: `<<` - + INSERT mode: `` - + VISUAL mode: `<` +- Insert new bullet in INSERT mode: `` (Return key) +- Same as in case you want to unmap in INSERT mode (compatibility depends on your terminal emulator): `` +- Insert new bullet in NORMAL mode: `o` +- Renumber current visual selection: `gN` +- Renumber entire bullet list containing the cursor in NORMAL mode: gN +- Toggle a checkbox in NORMAL mode: `x` +- Demote a bullet (indent it, decrease bullet level, and make it a child of the previous bullet): + - NORMAL mode: `>>` + - INSERT mode: `` + - VISUAL mode: `>` +- Promote a bullet (unindent it and increase the bullet level): + - NORMAL mode: `<<` + - INSERT mode: `` + - VISUAL mode: `<` Disable default mappings: @@ -345,34 +347,25 @@ Just add above to your .vimrc # Testing -The test suite is written using vimrunner. It is known to run on macOS with MacVim installed, and on travis. Your vim must have `+clientserver` and either have its own GUI or in a virtual X11 window. +The test suite is written in Lua and runs under Neovim with [Plenary](https://github.com/nvim-lua/plenary.nvim). ## Local Development Setup -This project uses [mise](https://mise.jdx.dev) to manage the Ruby version. Install it, then run: +This project uses [mise](https://mise.jdx.dev) to install Neovim. Install mise, then run: ```sh mise install -bundle install ``` ## Running Tests -On your mac run: - -```sh -bundle exec rspec -``` - -On linux: +Run the full test suite with: ```sh -bundle install -xvfb-run bundle exec rspec +mise run test ``` -You should see a Vim window open which will run each test, same general idea as -Capybara integration testing. ❤️ +The test task runs Neovim headlessly and downloads Plenary to `/tmp/plenary.nvim` on first run. # TODO @@ -385,11 +378,11 @@ Capybara integration testing. ❤️ - [x] reset numbers (user selects numbered bullets 3-5 and copies to middle of document, then reselects and resets them to 1-3) - [x] check if plugin initialized and don't load if it did - [x] allow for return without creating a bullet (only possible in GuiVim - unfortunately) + unfortunately) - [x] check if user is at EOL before appending auto-bullet - they may just want to - [x] attempt to keep the same total bullet width even as number width varies (right padding) - [x] detect lists that have multiline bullets (should have no empty lines between - lines). + lines). - [x] add alphabetic list - [x] support for intelligent alphanumeric indented bullets e.g. 1. \t a. \t 1. - [x] change nested outline levels in visual mode @@ -406,9 +399,7 @@ Capybara integration testing. ❤️ [![Hashrocket logo](https://hashrocket.com/hashrocket_logo.svg)](https://hashrocket.com) Bullets.vim is kindly supported by [Hashrocket, a multidisciplinary design and -development consultancy](https://hashrocket.com). If you'd like to [work with -us](https://hashrocket.com/contact-us/hire-us) or [join our -team](https://hashrocket.com/contact-us/jobs), don't hesitate to get in touch. +development consultancy](https://hashrocket.com). If you'd like to [work with us](https://hashrocket.com/contact-us/hire-us) or [join our team](https://hashrocket.com/contact-us/jobs), don't hesitate to get in touch. ## Contributors ✨ diff --git a/Rakefile b/Rakefile deleted file mode 100644 index cffdd09..0000000 --- a/Rakefile +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rspec/core/rake_task' - -RSpec::Core::RakeTask.new(:spec) - -task default: :spec diff --git a/committed.toml b/committed.toml new file mode 100644 index 0000000..65d4a36 --- /dev/null +++ b/committed.toml @@ -0,0 +1,3 @@ +subject_capitalized = false +style = "conventional" +subject_length = 72 diff --git a/dprint.json b/dprint.json new file mode 100644 index 0000000..8fe2ffc --- /dev/null +++ b/dprint.json @@ -0,0 +1,28 @@ +{ + "json": { + }, + "markdown": { + }, + "toml": { + }, + "yaml": { + }, + "excludes": [ + "**/*-lock.json" + ], + "includes": [ + "**/*.lua", + "**/*.yml", + "**/*.yaml", + "**/*.md", + "**/*.json", + "**/*.toml" + ], + "plugins": [ + "https://plugins.dprint.dev/RubixDev/stylua-v0.2.1.wasm", + "https://plugins.dprint.dev/json-0.21.3.wasm", + "https://plugins.dprint.dev/markdown-0.22.1.wasm", + "https://plugins.dprint.dev/toml-0.7.0.wasm", + "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.6.0.wasm" + ] +} diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..b9134e7 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/evilmartians/lefthook/master/schema.json + +output: + - summary + +commit-msg: + jobs: + - name: conventional commits + run: mise exec -- committed --commit-file {1} + +pre-commit: + parallel: true + jobs: + - name: check formatting + run: mise run fmt:check + + - name: check secrets + run: mise exec -- gitleaks git --staged --pre-commit --no-banner --redact diff --git a/mise.toml b/mise.toml index 96bdb30..20498a7 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,34 @@ [tools] -ruby = "4" +"github:crate-ci/committed" = "latest" +dprint = "latest" +gitleaks = "latest" +lefthook = "latest" +neovim = "latest" + +[hooks] +postinstall = "lefthook install" + +[tasks.fmt] +description = "Format code with dprint" +run = "dprint fmt" + +[tasks."fmt:check"] +description = "Check code formatting with dprint" +run = "dprint check" + +[tasks.test] +description = "Run Neovim test suite via plenary" +run = """ +PLENARY_PATH=/tmp/plenary.nvim +if [ ! -d "$PLENARY_PATH" ]; then + git clone --depth=1 https://github.com/nvim-lua/plenary.nvim "$PLENARY_PATH" +fi +status=0 +for spec in test/*_spec.lua; do + nvim --headless \ + -c "set rtp+=.,${PLENARY_PATH} | runtime plugin/plenary.vim | runtime plugin/bullets.vim" \ + --noplugin \ + -c "lua require('plenary.busted').run('${spec}')" || status=1 +done +exit $status +""" diff --git a/spec/alphabetic_bullets_spec.rb b/spec/alphabetic_bullets_spec.rb deleted file mode 100644 index 06c96df..0000000 --- a/spec/alphabetic_bullets_spec.rb +++ /dev/null @@ -1,231 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Bullets.vim' do - describe 'alphabetic bullets' do - it 'adds a new upper case bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - A. this is the first bullet - TEXT - - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.feedkeys '\' - vim.type 'sixth bullet' - vim.feedkeys '\' - vim.type 'seventh bullet' - vim.feedkeys '\' - vim.type 'eighth bullet' - vim.feedkeys '\' - vim.type 'ninth bullet' - vim.feedkeys '\' - vim.type 'tenth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - A. this is the first bullet - B. second bullet - C. third bullet - D. fourth bullet - E. fifth bullet - F. sixth bullet - G. seventh bullet - H. eighth bullet - I. ninth bullet - J. tenth bullet - - TEXT - end - - it 'adds a new lower case bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - a. this is the first bullet - TEXT - - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.feedkeys '\' - vim.type 'sixth bullet' - vim.feedkeys '\' - vim.type 'seventh bullet' - vim.feedkeys '\' - vim.type 'eighth bullet' - vim.feedkeys '\' - vim.type 'ninth bullet' - vim.feedkeys '\' - vim.type 'tenth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - a. this is the first bullet - b. second bullet - c. third bullet - d. fourth bullet - e. fifth bullet - f. sixth bullet - g. seventh bullet - h. eighth bullet - i. ninth bullet - j. tenth bullet - - TEXT - end - - it 'adds a new bullet and loops at z' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - y. this is the first bullet - TEXT - - vim.edit filename - vim.command('let g:bullets_renumber_on_change=0') - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'AY. fourth bullet' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.feedkeys '\' - vim.type 'sixth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - y. this is the first bullet - z. second bullet - aa. third bullet - AY. fourth bullet - AZ. fifth bullet - BA. sixth bullet\n - TEXT - end - - it 'does not add a new bullet when mixed case' do - test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) - # Hello there - Ab. this is the first bullet - INIT - # Hello there - Ab. this is the first bullet - not a bullet - EXPECTED - end - - # it 'correctly numbers after wrapped lines starting with short words' do - # # TODO: maybe take guidance from Pandoc and require two spaces after the - # closure to allow us to differentiate between bullets and abbreviations - # and words. Might also consider only allowing single letters. - # test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - # # Hello there - # a. first bullet might not catch - # me. second line. - # INIT - # # Hello there - # a. first bullet might not catch - # \tme. second line. - # b. second bullet - # EXPECTED - # end - - # it 'correctly numbers after lines beginning with initialized names' do - # # TODO: maybe take guidance from Pandoc and require two spaces after the - # closure to allow us to differentiate between bullets and abbreviations - # and words. Might also consider only allowing single letters. - # test_bullet_inserted('Second bullet', <<-INIT, <<-EXPECTED) - # # Hello there - # I. The first president of the USA was - # G. Washington. - # INIT - # # Hello there - # I. The first president of the USA was - # G. Washington. - # II. Second bullet - # EXPECTED - # end - - describe 'g:bullets_max_alpha_characters' do - it 'stops adding items after configured max (default 2)' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - zy. this is the first bullet - TEXT - - vim.edit filename - vim.command('let g:bullets_renumber_on_change=0') - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'not a bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - zy. this is the first bullet - zz. second bullet - not a bullet\n - TEXT - end - - it 'does not bullets if configured as 0' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - a. this is the first bullet - TEXT - - vim.command 'let g:bullets_max_alpha_characters = 0' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'not a bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - a. this is the first bullet - not a bullet\n - TEXT - end - end - end -end diff --git a/spec/asciidoc_spec.rb b/spec/asciidoc_spec.rb deleted file mode 100644 index 18d7981..0000000 --- a/spec/asciidoc_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'AsciiDoc' do - it 'maintains indentation in ascii doc bullets' do - test_bullet_inserted('rats', <<-INIT, <<-EXPECTED) - = Pets! - * dogs - ** cats - INIT - = Pets! - * dogs - ** cats - ** rats - EXPECTED - end - - it 'supports dot bullets' do - test_bullet_inserted('cats', <<-INIT, <<-EXPECTED) - = Pets! - . dogs - INIT - = Pets! - . dogs - . cats - EXPECTED - end - - it 'supports nested dot bullets' do - pending('FIXME: this test fails, but the functionality works') - test_bullet_inserted('rats', <<-INIT, <<-EXPECTED) - = Pets! - . dogs - .. cats - INIT - = Pets! - . dogs - .. cats - .. rats - EXPECTED - end -end diff --git a/spec/bullets_spec.rb b/spec/bullets_spec.rb deleted file mode 100644 index cf370f0..0000000 --- a/spec/bullets_spec.rb +++ /dev/null @@ -1,358 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Bullets.vim' do - describe 'inserting new bullets' do - context 'on return key when cursor is not at EOL' do - it 'splits the line and does not add a bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - this is the first bullet - TEXT - - vim.edit filename - vim.type 'G$i' - vim.feedkeys '\' - vim.type 'second bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - this is the first bulle - second bullett\n - TEXT - end - end - - context 'on return key when cursor is at EOL' do - it 'adds a new bullet if the previous line had a known bullet type' do - test_bullet_inserted('do that', <<-INIT, <<-EXPECTED) - # Hello there - - do this - INIT - # Hello there - - do this - - do that - EXPECTED - end - - it 'adds a new latex bullet' do - test_bullet_inserted('Second item', <<-INIT, <<-EXPECTED) - \\documentclass{article} - \\begin{document} - - \\begin{itemize} - \\item First item - INIT - \\documentclass{article} - \\begin{document} - - \\begin{itemize} - \\item First item - \\item Second item - EXPECTED - end - - it 'adds a pandoc bullet if the prev line had one' do - test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - Hello there - #. this is the first bullet - INIT - Hello there - #. this is the first bullet - #. second bullet - EXPECTED - end - - it 'adds an Org mode bullet if the prev line had one' do - test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - Hello there - **** this is the first bullet - INIT - Hello there - **** this is the first bullet - **** second bullet - EXPECTED - end - - it 'adds a new numeric bullet if the previous line had numeric bullet' do - test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - # Hello there - 1) this is the first bullet - INIT - # Hello there - 1) this is the first bullet - 2) second bullet - EXPECTED - end - - it 'adds a new numeric bullet with right padding' do - test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - # Hello there - 1. this is the first bullet - INIT - # Hello there - 1. this is the first bullet - 2. second bullet - EXPECTED - end - - it 'maintains total bullet width from 9. to 10. with reduced padding' do - vim.command('let g:bullets_renumber_on_change=0') - test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - # Hello there - 9. this is the first bullet - INIT - # Hello there - 9. this is the first bullet - 10. second bullet - EXPECTED - end - - it 'adds a new - bullet with right padding' do - test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) - # Hello there - - this is the first bullet - INIT - # Hello there - - this is the first bullet - - second bullet - EXPECTED - end - - it 'does not insert a new numeric bullet for decimal numbers' do - test_bullet_inserted('second line', <<-INIT, <<-EXPECTED) - # Hello there - 3.14159 is an approximation of pi. - INIT - # Hello there - 3.14159 is an approximation of pi. - second line - EXPECTED - end - - it 'adds a new roman numeral bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - TEXT - - vim.command 'let g:bullets_pad_right = 0' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - II. second bullet - III. third bullet - IV. fourth bullet - V. fifth bullet\n - TEXT - end - - it 'adds a new lowercase roman numeral bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - i. this is the first bullet - TEXT - - vim.command 'let g:bullets_pad_right = 0' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - i. this is the first bullet - ii. second bullet - iii. third bullet - iv. fourth bullet - v. fifth bullet\n - TEXT - end - - it 'does not confuse with the "ignorecase" option' do - vim.command 'set ignorecase' - test_bullet_inserted('second line', <<-INIT, <<-EXPECTED) - # Hello there - Vi. this is the first line - INIT - # Hello there - Vi. this is the first line - second line - EXPECTED - end - - it 'does not insert a new roman bullets without following spaces' do - test_bullet_inserted('second line', <<-INIT, <<-EXPECTED) - # Hello there - m.example.com is a site. - INIT - # Hello there - m.example.com is a site. - second line - EXPECTED - end - - it 'does not insert a new roman bullets for invalid roman numbers' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - LID. the first line - TEXT - - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second line' - vim.feedkeys '\' - vim.type 'vim. third line' - vim.feedkeys '\' - vim.type 'fourth line' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - LID. the first line - second line - vim. third line - fourth line\n - TEXT - end - - it 'deletes the last bullet if it is empty' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - this is the first bullet - TEXT - - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents.strip).to eq normalize_string_indent(<<-TEXT) - # Hello there - - this is the first bullet - TEXT - end - - it 'promote the last bullet when configured to' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - this is the first bullet - - this is the second bullet - TEXT - - vim.command 'let g:bullets_delete_last_bullet_if_empty = 2' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents.strip).to eq normalize_string_indent(<<-TEXT) - # Hello there - - this is the first bullet - - this is the second bullet - - - TEXT - end - - it 'does not delete the last bullet when configured not to' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - this is the first bullet - TEXT - - vim.command 'let g:bullets_delete_last_bullet_if_empty = 0' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents.strip).to eq normalize_string_indent(<<-TEXT) - # Hello there - - this is the first bullet - - - TEXT - end - - it 'toggles roman numeral bullets with g:bullets_enable_roman_list' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - i. this is the first bullet - TEXT - - # Disable alpha lists to isolate test to roman numerals - vim.command 'let g:bullets_max_alpha_characters = 0' - vim.command 'let g:bullets_enable_roman_list = 1' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.command 'let g:bullets_enable_roman_list = 0' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - i. this is the first bullet - ii. second bullet - iii. third bullet - fourth bullet - fifth bullet\n - TEXT - end - end - end -end diff --git a/spec/checkboxes_spec.rb b/spec/checkboxes_spec.rb deleted file mode 100644 index 1dc5b82..0000000 --- a/spec/checkboxes_spec.rb +++ /dev/null @@ -1,386 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'checkboxes' do - it 'inserts another checkbox after the previous one' do - test_bullet_inserted('do that', <<-INIT, <<-EXPECTED) - # Hello there - - [ ] do this - INIT - # Hello there - - [ ] do this - - [ ] do that - EXPECTED - end - - it 'inserts a * checkbox after the previous one' do - test_bullet_inserted('do that', <<-INIT, <<-EXPECTED) - # Hello there - * [ ] do this - INIT - # Hello there - * [ ] do this - * [ ] do that - EXPECTED - end - - it 'inserts an empty checkbox even if prev line was checked' do - test_bullet_inserted('do that', <<-INIT, <<-EXPECTED) - # Hello there - - [x] do this - INIT - # Hello there - - [x] do this - - [ ] do that - EXPECTED - end - - it 'toggle a bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [ ] first bullet - - [X] second bullet - - [x] third bullet - - [.] fourth bullet - - [o] fifth bullet - - [O] sixth bullet - - not a checkbox - TEXT - - vim.edit filename - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [X] first bullet - - [ ] second bullet - - [ ] third bullet - - [X] fourth bullet - - [X] fifth bullet - - [X] sixth bullet - - not a checkbox - - TEXT - end - - it 'toggle a bullet and adjust parent' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [ ] first bullet - - [ ] second bullet - - [ ] third bullet - TEXT - - vim.edit filename - vim.normal 'G' - vim.command 'ToggleCheckbox' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [X] first bullet - - [X] second bullet - - [X] third bullet - - TEXT - end - - it 'toggle a bullet and adjust children' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [ ] first bullet - - [ ] second bullet - - [ ] third bullet - TEXT - - vim.edit filename - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [X] first bullet - - [X] second bullet - - [X] third bullet - - TEXT - end - - it 'toggle a bullet and calculate completion' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [ ] first bullet - - [ ] second bullet - - [ ] third bullet - - [ ] fourth bullet - - [ ] fifth bullet - - [ ] sixth bullet - - [ ] seventh bullet - - [ ] eighth bullet - - [ ] ninth bullet - - [ ] tenth bullet - - [ ] eleventh bullet - - [ ] twelfth bullet - - [ ] thirteenth bullet - - [ ] fourteenth bullet - - [ ] fifteenth bullet - - [ ] sixteenth bullet - - [X] seventeenth bullet - - [X] eighteenth bullet - - [X] ninteenth bullet - - [X] twentieth bullet - - [X] twenty-first bullet - TEXT - - vim.edit filename - vim.normal '3j' - vim.command 'ToggleCheckbox' - vim.normal '6j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal '2j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.normal '2j' - vim.command 'ToggleCheckbox' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [.] first bullet - - [.] second bullet - - [X] third bullet - - [ ] fourth bullet - - [ ] fifth bullet - - [ ] sixth bullet - - [O] seventh bullet - - [ ] eighth bullet - - [X] ninth bullet - - [X] tenth bullet - - [X] eleventh bullet - - [X] twelfth bullet - - [X] thirteenth bullet - - [X] fourteenth bullet - - [X] fifteenth bullet - - [X] sixteenth bullet - - [O] seventeenth bullet - - [ ] eighteenth bullet - - [X] ninteenth bullet - - [X] twentieth bullet - - [X] twenty-first bullet - - TEXT - end - - it 'adds and toggles bullets using UTF characters' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [ ] first bullet - TEXT - - vim.edit filename - vim.command 'let g:bullets_checkbox_markers="✗○◐●✓"' - vim.normal 'j' - vim.command 'ToggleCheckbox' - vim.feedkeys 'o' - vim.type 'second bullet' - vim.feedkeys '\\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.command 'ToggleCheckbox' - vim.feedkeys 'o\' - vim.type 'fifth bullet' - vim.command 'ToggleCheckbox' - vim.feedkeys 'o' - vim.type 'sixth bullet' - vim.command 'ToggleCheckbox' - vim.command 'ToggleCheckbox' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [✓] first bullet - - [◐] second bullet - \t- [✗] third bullet - \t- [✓] fourth bullet - - [✓] fifth bullet - - [✗] sixth bullet - - TEXT - end - - it 'recomputes checkboxes recursively on RecomputeCheckboxes' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [ ] EXPECTED: ¼ - - [X] checkbox leaf - - [ ] EXPECTED: CHECKED - - [ ] EXPECTED: CHECKED - - [ ] EXPECTED: CHECKED - - [X] checkbox leaf - - [X] checkbox leaf - - [X] EXPECTED: ¾ - - [X] checkbox leaf - - [X] checkbox leaf - - [X] checkbox leaf - - [ ] checkbox leaf - - [X] EXPECTED: ½ - - [ ] EXPECTED: CHECKED - - [ ] EXPECTED: CHECKED - - [X] checkbox leaf - - [½] checkbox leaf (EXPECTED: UNCHECKED) - - [½] EXPECTED: UNCHECKED - - [ ] checkbox leaf - - [½] checkbox leaf (EXPECTED: UNCHECKED) - TEXT - - vim.edit filename - vim.command 'let g:bullets_checkbox_markers=" .¼½¾X"' - vim.type '9j' - vim.command 'RecomputeCheckboxes' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [¼] EXPECTED: ¼ - - [X] checkbox leaf - - [X] EXPECTED: CHECKED - - [X] EXPECTED: CHECKED - - [X] EXPECTED: CHECKED - - [X] checkbox leaf - - [X] checkbox leaf - - [¾] EXPECTED: ¾ - - [X] checkbox leaf - - [X] checkbox leaf - - [X] checkbox leaf - - [ ] checkbox leaf - - [½] EXPECTED: ½ - - [X] EXPECTED: CHECKED - - [X] EXPECTED: CHECKED - - [X] checkbox leaf - - [ ] checkbox leaf (EXPECTED: UNCHECKED) - - [ ] EXPECTED: UNCHECKED - - [ ] checkbox leaf - - [ ] checkbox leaf (EXPECTED: UNCHECKED) - - TEXT - end - - it 'recomputes checkboxes correctly on reindents' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [X] parent bullet - - [X] first child bullet - TEXT - - vim.edit filename - vim.command 'let g:bullets_checkbox_markers=" /X"' - vim.type 'GA' - vim.feedkeys '\' - vim.command 'RecomputeCheckboxes' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [/] parent bullet - - [X] first child bullet - - [ ] - - TEXT - - vim.command 'let g:bullets_delete_last_bullet_if_empty = 2' - vim.feedkeys '\' - vim.command 'RecomputeCheckboxes' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [X] parent bullet - - [X] first child bullet - - [ ] - - TEXT - end - - it 'handles skip-level checkbox trees' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - - [X] parent bullet (EXPECTED: /) - - skip: not checkbox content - - [ ] new root bullet (EXPECTED: /) - - [ ] first child bullet - - [X] first child bullet - - [X] first child bullet - - [ ] first child bullet - TEXT - - vim.edit filename - vim.command 'let g:bullets_checkbox_markers=" /X"' - vim.type '2j' - vim.command 'RecomputeCheckboxes' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - - [/] parent bullet (EXPECTED: /) - - skip: not checkbox content - - [/] new root bullet (EXPECTED: /) - - [ ] first child bullet - - [X] first child bullet - - [X] first child bullet - - [ ] first child bullet - - TEXT - end -end diff --git a/spec/filetypes_spec.rb b/spec/filetypes_spec.rb deleted file mode 100644 index 6fd330e..0000000 --- a/spec/filetypes_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'filetypes' do - it 'creates mapping for bullets on empty buffer if configured' do - vim.command 'new' - vim.insert '# Hello there' - vim.feedkeys '\' - vim.type '- this is the first bullet' - vim.feedkeys '\' - vim.type 'this is the second bullet' - - buffer_content = vim.echo("join(getbufline(bufname(''), 1, '$'), '\n')") - - expect(buffer_content).to eq normalize_string_indent(<<-TEXT) - # Hello there - - this is the first bullet - this is the second bullet - TEXT - end - - it 'should have text filetype for .txt' do - # bullets.vim is triggered by particular filetypes; - # if somehow your vim is recognising .txt and setting - # filetype to something that isn't text or markdown, - # the rest of the tests are gonna fail. - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, '') - - vim.edit filename - vim.type 'i' - vim.feedkeys '\\' - vim.type '=&filetype' - vim.feedkeys '\\' - vim.write - - file_contents = normalize_string_indent(IO.read(filename)).strip - - expect(%w[markdown text]).to include(file_contents) - end -end diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb deleted file mode 100644 index 7cdc97a..0000000 --- a/spec/nested_bullets_spec.rb +++ /dev/null @@ -1,625 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Bullets.vim' do - describe 'nested bullets' do - it 'demotes an existing bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - II. second bullet - III. third bullet - IV. fourth bullet - V. fifth bullet - VI. sixth bullet - VII. seventh bullet - VIII. eighth bullet - IX. ninth bullet - TEXT - - vim.edit filename - vim.normal '2ji' - vim.feedkeys '\' - vim.normal 'j' - vim.feedkeys '>>>>>>' - vim.normal 'j' - vim.feedkeys '>>>>>>>>' - vim.normal 'j' - vim.feedkeys '>>>>>>>>>>' - vim.normal 'j' - vim.feedkeys '>>>>>>>>' - vim.feedkeys '>>>>' - vim.normal 'j' - vim.feedkeys '>>>>>>>>' - vim.feedkeys '>>>>>>' - vim.normal 'j' - vim.feedkeys '>>>>>>>>' - vim.feedkeys '>>>>>>>>' - vim.normal 'j' - vim.feedkeys '>>>>>>>>' - vim.feedkeys '>>>>>>>>>>' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t\t1. third bullet - \t\t\t\ta. fourth bullet - \t\t\t\t\ti. fifth bullet - \t\t\t\t\t\t- sixth bullet - \t\t\t\t\t\t\t* seventh bullet - \t\t\t\t\t\t\t\t+ eighth bullet - \t\t\t\t\t\t\t\t\t+ ninth bullet - - TEXT - end - - it 'promotes an existing bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t\t1. third bullet - \t\t\t\ta. fourth bullet - \t\t\t\t\ti. fifth bullet - \t\t\t\t\t\t- sixth bullet - \t\t\t\t\t\t\t* seventh bullet - \t\t\t\t\t\t\t\t+ eighth bullet - TEXT - - vim.edit filename - vim.normal '2j' - vim.feedkeys '<<' - vim.normal 'ji' - vim.feedkeys '\' - vim.feedkeys '\' - vim.normal 'j' - vim.feedkeys '<<<<<<' - vim.normal 'j' - vim.feedkeys '<<<<<<' - vim.feedkeys '<<<<' - vim.normal 'j' - vim.feedkeys '<<<<<<' - vim.feedkeys '<<<<<<' - vim.normal 'j' - vim.feedkeys '<<<<<<' - vim.feedkeys '<<<<<<<<' - vim.normal 'j' - vim.feedkeys '<<<<<<' - vim.feedkeys '<<<<<<' - vim.feedkeys '<<<<' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - II. second bullet - \tA. third bullet - \tB. fourth bullet - III. fifth bullet - IV. sixth bullet - V. seventh bullet - VI. eighth bullet - - TEXT - end - - it 'demotes an empty bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - TEXT - - vim.edit filename - vim.normal 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'second bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - - TEXT - end - - it 'promotes an empty bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - TEXT - - vim.edit filename - vim.normal 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'third bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - II. third bullet - - TEXT - end - - it 'restarts numbering with multiple outlines' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - TEXT - - vim.edit filename - vim.normal 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'A. first bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'second bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - - A. first bullet - \t1. second bullet - - TEXT - end - - it 'works with custom outline level definitions' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - TEXT - - vim.edit filename - vim.command "let g:bullets_outline_levels=['num','ABC','std*']" - vim.normal 'GA' - vim.feedkeys '\' - vim.type '1. first bullet' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.feedkeys '\' - vim.type 'sixth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'seventh bullet' - vim.feedkeys '\' - vim.type 'eighth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'ninth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'tenth bullet' - vim.feedkeys '\' - vim.type 'eleventh bullet' - - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. first bullet - 2. second bullet - \tA. third bullet - \tB. fourth bullet - \t\t* fifth bullet - \t\t* sixth bullet - \t\t\t* seventh bullet - \t\t\t* eighth bullet - \tC. ninth bullet - 3. tenth bullet - 4. eleventh bullet - - TEXT - end - - it 'promotes and demotes from different starting levels' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 1. this is the first bullet - \ta. second bullet - TEXT - - vim.edit filename - vim.normal 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type '+ fourth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type '* sixth bullet' - vim.feedkeys '\' - vim.type 'seventh bullet' - vim.feedkeys '\' - - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. this is the first bullet - 2. second bullet - \ta. third bullet - + fourth bullet - \t+ fifth bullet - * sixth bullet - \t+ seventh bullet - - TEXT - end - - it 'does not nest beyond defined levels' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - \t\t\ta. fourth bullet - \t\t\t\ti. fifth bullet - \t\t\t\tii. sixth bullet - \t\t\t\t\t- seventh bullet - \t\t\t\t\t\t* eighth bullet - \t\t\t\t\t\t\t+ ninth bullet - TEXT - - vim.edit filename - vim.normal 'GA' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'tenth bullet' - vim.feedkeys '\' - vim.type 'eleventh bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - \t\t\ta. fourth bullet - \t\t\t\ti. fifth bullet - \t\t\t\tii. sixth bullet - \t\t\t\t\t- seventh bullet - \t\t\t\t\t\t* eighth bullet - \t\t\t\t\t\t\t+ ninth bullet - \t\t\t\t\t\t\t\t+ tenth bullet - \t\t\t\t\t\t\t\t+ eleventh bullet - - TEXT - end - - it 'removes bullet when promoting top level bullet' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - A. this is the first bullet - - I. second bullet - \tA. third bullet - TEXT - - vim.edit filename - vim.normal 'j' - vim.feedkeys '<<' - vim.normal '3ji' - vim.feedkeys '\' - vim.feedkeys '\' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - this is the first bullet - - I. second bullet - third bullet - - TEXT - end - - it 'handle standard bullets when they are not in outline list' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 1. this is the first bullet - \t- standard bullet - TEXT - - vim.edit filename - vim.command "let g:bullets_outline_levels=['num','ABC']" - vim.normal 'GA' - vim.feedkeys '\' - vim.type 'second standard bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.type 'third bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. this is the first bullet - \t- standard bullet - \t- second standard bullet - 2. second bullet - 3. third bullet - - TEXT - end - - it 'adds new nested bullets with correct alpha/roman numerals' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - TEXT - - vim.edit filename - vim.normal 'GA' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'fifth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'sixth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'seventh bullet' - vim.feedkeys '\' - vim.type 'eighth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'ninth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'tenth bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'eleventh bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'twelfth bullet' - vim.feedkeys '\' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - \t\t\ta. fourth bullet - \t\t\t\ti. fifth bullet - \t\t\t\t\t- sixth bullet - \t\t\t\t\t- seventh bullet - \t\t\t\tii. eighth bullet - \t\t\tb. ninth bullet - \t\t2. tenth bullet - \tB. eleventh bullet - II. twelfth bullet - - TEXT - end - - it 'changes levels in visual mode' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 1. first bullet - \ta. second bullet - \tb. third bullet - \t\t* fourth bullet - \t\t* fifth bullet - \t\t\tsixth bullet - \t\t* seventh bullet - 2. eighth bullet - \t\ta. ninth bullet - \ta. tenth bullet - \tb. eleventh bullet - 3. twelfth bullet - \t thirteenth bullet - \ta. fourteenth bullet - \t\t* fifteenth bullet - 4. sixteenth bullet - TEXT - - vim.edit filename - vim.command "let g:bullets_outline_levels=['num','abc','std*']" - vim.normal '3jv' - vim.feedkeys '<' - vim.normal 'jv2j' - vim.feedkeys '<' - vim.normal 'jvj' - vim.feedkeys '>' - vim.normal 'jvj' - vim.feedkeys '<' - vim.feedkeys '<' - vim.normal 'jv' - vim.feedkeys '>' - vim.normal '3jv2j' - vim.feedkeys '>' - vim.feedkeys '>' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. first bullet - \ta. second bullet - 2. third bullet - \ta. fourth bullet - \tb. fifth bullet - \t\tsixth bullet - \t\t\t* seventh bullet - \tc. eighth bullet - 3. ninth bullet - tenth bullet - \t\ta. eleventh bullet - 4. twelfth bullet - \t thirteenth bullet - \t\t\ta. fourteenth bullet - \t\t\t\t* fifteenth bullet - \t\ta. sixteenth bullet - - TEXT - end - - it 'add and change bullets with multiple line spacing and wrapped lines' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - TEXT - - vim.edit filename - vim.command 'let g:bullets_line_spacing=2' - vim.normal 'GA' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' - vim.normal 'dd' - vim.insert ' wrapped bullet' - vim.feedkeys '\' - vim.type 'fourth bullet' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - - II. second bullet - - \tA. third bullet - \twrapped bullet - - \tB. fourth bullet - - TEXT - end - - it 'indents after a line ending in a colon' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - a. this is the first bullet - TEXT - - vim.command 'let g:bullets_auto_indent_after_colon = 1' - vim.edit filename - vim.type 'GA' - vim.feedkeys '\' - vim.type 'this is the second bullet:' - vim.feedkeys '\' - vim.type 'this bullet is indented' - vim.feedkeys '\' - vim.type 'this bullet is also indented' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents.strip).to eq normalize_string_indent(<<-TEXT) - # Hello there - a. this is the first bullet - b. this is the second bullet: - \ti. this bullet is indented - \tii. this bullet is also indented - TEXT - - write_file(filename, <<-TEXT) - # Hello there - a. this is the first bullet - TEXT - - vim.command 'let g:bullets_auto_indent_after_colon = 1' - vim.edit filename - vim.feedkeys '\' - vim.type 'GA' - vim.feedkeys '\' - vim.type 'this is the second bullet that ends with fullwidth colon:' - vim.feedkeys '\' - vim.type 'this bullet is indented' - vim.feedkeys '\' - vim.type 'this bullet is also indented' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents.strip).to eq normalize_string_indent(<<-TEXT) - # Hello there - a. this is the first bullet - b. this is the second bullet that ends with fullwidth colon: - \ti. this bullet is indented - \tii. this bullet is also indented - TEXT - end - end -end diff --git a/spec/renumber_bullets_spec.rb b/spec/renumber_bullets_spec.rb deleted file mode 100644 index 37b5506..0000000 --- a/spec/renumber_bullets_spec.rb +++ /dev/null @@ -1,327 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 're-numbering' do - it 'renumbers a selected list correctly' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 33. this is the first bullet - 2. this is the second bullet - 1. this is the third bullet - 4. this is the fourth bullet - TEXT - - vim.edit filename - vim.type 'ggVG' - vim.feedkeys 'gN' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. this is the first bullet - 2. this is the second bullet - 3. this is the third bullet - 4. this is the fourth bullet\n - TEXT - end - - it 'renumbers a list containing checkboxes' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 33. this is the first bullet - - [x] this is the second bullet - 1. this is the third bullet - - [ ] this is the fourth bullet - 4. this is the fifth bullet - - - [o] second bullet list - b. second item - - [x] third item - TEXT - - vim.edit filename - vim.type 'j' - vim.feedkeys 'gN' - vim.type '}j' - vim.feedkeys 'gN' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. this is the first bullet - - [x] this is the second bullet - 2. this is the third bullet - - [ ] this is the fourth bullet - 3. this is the fifth bullet - - - [o] second bullet list - a. second item - - [x] third item\n - TEXT - end - - it 'renumbers a nested list' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 0. zero bullet - - X. first bullet - - - second bullet - \twrapped line - - a. third bullet - - I. fourth bullet - - \tV. fifth bullet - - \t\tB. sixth bullet - - \t* seventh bullet - - \t\ti. eighth bullet - - \t\tx. ninth bullet - \t\t\t wrapped line - - \t\t\ta. tenth bullet - wrapped line without indent - - \tC. eleventh bullet - \t\t0. twelfth bullet - - \t\t\t* thirteenth bullet - - \t\t\t\t+ fourteenth bullet - - \t\t\tb. fifteenth bullet - - \t\ta. sixteenth bullet - - \t\t\t8. seventeenth bullet - - \t\t\t0. eighteenth bullet - \t\t\t\t wrapped line - - \tnormal indented line - next normal line - - 1. nineteenth bullet - - x. twentieth bullet - - - v. twenty-first bullet - - twenty-second bullet - - - v. twenty-third bullet - TEXT - - vim.edit filename - vim.command 'let g:bullets_line_spacing=2' - vim.normal '3j' - vim.feedkeys 'gN' - vim.normal 'G3k' - vim.feedkeys 'gN' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. zero bullet - - 2. first bullet - - - second bullet - \twrapped line - - 3. third bullet - - 4. fourth bullet - - \tI. fifth bullet - - \t\tA. sixth bullet - - \t* seventh bullet - - \t\ti. eighth bullet - - \t\tii. ninth bullet - \t\t\t wrapped line - - \t\t\ta. tenth bullet - wrapped line without indent - - \tII. eleventh bullet - \t\t1. twelfth bullet - - \t\t\t* thirteenth bullet - - \t\t\t\t+ fourteenth bullet - - \t\t\ta. fifteenth bullet - - \t\t2. sixteenth bullet - - \t\t\t1. seventeenth bullet - - \t\t\t2. eighteenth bullet - \t\t\t\t wrapped line - - \tnormal indented line - next normal line - - 1. nineteenth bullet - - x. twentieth bullet - - - i. twenty-first bullet - - twenty-second bullet - - - v. twenty-third bullet - - TEXT - end - - it 'visually renumbers a nested list' do - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, <<-TEXT) - # Hello there - 0. zero bullet - - X. first bullet - - - second bullet - \twrapped line - - a. third bullet - - I. fourth bullet - - \tV. fifth bullet - - \t\tB. sixth bullet - - \t* seventh bullet - - \t\ti. eighth bullet - - \t\tx. ninth bullet - \t\t\t wrapped line - - \t\t\ta. tenth bullet - wrapped line without indent - - \tC. eleventh bullet - \t\t0. twelfth bullet - - \t\t\t* thirteenth bullet - - \t\t\t\t+ fourteenth bullet - - \t\t\td. fifteenth bullet - - \t\ta. sixteenth bullet - - \t\t\t8. seventeenth bullet - - \t\t\t0. eighteenth bullet - \t\t\t\t wrapped line - - \tnormal indented line - next normal line - - 1. nineteenth bullet - - x. twentieth bullet - - - v. twenty-first bullet - - twenty-second bullet - - - v. twenty-third bullet - TEXT - - vim.edit filename - vim.command 'let g:bullets_line_spacing=2' - vim.type '2jVGk' - vim.feedkeys 'gN' - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 0. zero bullet - - I. first bullet - - - second bullet - \twrapped line - - II. third bullet - - III. fourth bullet - - \tI. fifth bullet - - \t\tA. sixth bullet - - \t* seventh bullet - - \t\ti. eighth bullet - - \t\tii. ninth bullet - \t\t\t wrapped line - - \t\t\ta. tenth bullet - wrapped line without indent - - \tII. eleventh bullet - \t\t1. twelfth bullet - - \t\t\t* thirteenth bullet - - \t\t\t\t+ fourteenth bullet - - \t\t\ti. fifteenth bullet - - \t\t2. sixteenth bullet - - \t\t\t1. seventeenth bullet - - \t\t\t2. eighteenth bullet - \t\t\t\t wrapped line - - \tnormal indented line - next normal line - - IV. nineteenth bullet - - V. twentieth bullet - - - VI. twenty-first bullet - - twenty-second bullet - - - v. twenty-third bullet - - TEXT - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index 58f3c15..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,135 +0,0 @@ -# frozen_string_literal: true - -require 'vimrunner' -require 'vimrunner/rspec' -require 'securerandom' -require 'rspec/retry' - -Vimrunner::RSpec.configure do |config| - # Use a single Vim instance for the test suite. Set to false to use an - # instance per test (slower, but can be easier to manage). - config.reuse_server = false - - # Decide how to start a Vim instance. In this block, an instance should be - # spawned and set up with anything project-specific. - config.start_vim do - vim = Vimrunner.start - - # Or, start a GUI instance: - # vim = Vimrunner.start_gvim - - # Setup your plugin in the Vim instance - plugin_path = File.expand_path('..', __dir__) - vim.add_plugin(plugin_path, 'plugin/bullets.vim') - - # The returned value is the Client available in the tests. - vim - end -end - -RSpec.configure do |config| - - # RSpec Retry - # =========== - # show retry status in spec process - config.verbose_retry = true - - # show exception that triggers a retry if verbose_retry is set to true - config.display_try_failure_messages = true - - # retry each spec 3 times - config.around :each do |ex| - ex.run_with_retry retry: 3 - end - # ============ - - config.around do |example| - Dir.mktmpdir do |dir| - Dir.chdir(dir) do - vim.command("cd #{dir}") - example.call - end - end - end - - config.expect_with :rspec do |expectations| - # This option will default to `true` in RSpec 4. It makes the `description` - # and `failure_message` of custom matchers include text for helper methods - # defined using `chain`, e.g.: - # be_bigger_than(2).and_smaller_than(4).description - # # => "be bigger than 2 and smaller than 4" - # ...rather than: - # # => "be bigger than 2" - expectations.include_chain_clauses_in_custom_matcher_descriptions = true - end - - # rspec-mocks config goes here. You can use an alternate test double - # library (such as bogus or mocha) by changing the `mock_with` option here. - config.mock_with :rspec do |mocks| - # Prevents you from mocking or stubbing a method that does not exist on - # a real object. This is generally recommended, and will default to - # `true` in RSpec 4. - mocks.verify_partial_doubles = true - end - - config.filter_run :focus - config.run_all_when_everything_filtered = true - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = 'spec/examples.txt' - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ - # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode - config.disable_monkey_patching! - - # This setting enables warnings. It's recommended, but in some cases may - # be too noisy due to issues in dependencies. - config.warnings = true - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = 'doc' - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -end - -def test_bullet_inserted(second_bullet_text, initial_text, expected_text) - filename = "#{SecureRandom.hex(6)}.txt" - write_file(filename, initial_text) - - vim.edit(filename) - vim.type('GA') - vim.feedkeys('\\') - vim.type(second_bullet_text) - vim.write - - file_contents = IO.read(filename) - - expect(file_contents).to eq normalize_string_indent("#{expected_text}\n") -end diff --git a/spec/wrapping_bullets_spec.rb b/spec/wrapping_bullets_spec.rb deleted file mode 100644 index 391037b..0000000 --- a/spec/wrapping_bullets_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'wrapped bullets' do - it 'inserts a new bullet after a wrapped bullet' do - test_bullet_inserted('do that', <<-INIT, <<-EXPECTED) - # Hello there - - do this - this is the second line of the first bullet - INIT - # Hello there - - do this - this is the second line of the first bullet - - do that - EXPECTED - end - - it 'does not insert wrapped bullets unnecessarily' do - test_bullet_inserted('do that', <<-INIT, <<-EXPECTED) - # Hello there - - do this - this is the second line of the first bullet - - no bullets after this line - INIT - # Hello there - - do this - this is the second line of the first bullet - - no bullets after this line - do that - EXPECTED - end -end diff --git a/test/alphabetic_bullets_spec.lua b/test/alphabetic_bullets_spec.lua new file mode 100644 index 0000000..675cd6d --- /dev/null +++ b/test/alphabetic_bullets_spec.lua @@ -0,0 +1,140 @@ +local helpers = require("test.helpers") + +describe("Bullets.vim", function() + describe("alphabetic bullets", function() + before_each(function() + helpers.reset_config() + end) + + it("adds a new upper case bullet", function() + helpers.new_buffer({ + "# Hello there", + "A. this is the first bullet", + }) + helpers.feedkeys( + "Asecond bulletthird bulletfourth bulletfifth bullet" + .. "sixth bulletseventh bulleteighth bulletninth bullettenth bullet" + ) + assert.are.same({ + "# Hello there", + "A. this is the first bullet", + "B. second bullet", + "C. third bullet", + "D. fourth bullet", + "E. fifth bullet", + "F. sixth bullet", + "G. seventh bullet", + "H. eighth bullet", + "I. ninth bullet", + "J. tenth bullet", + }, helpers.get_lines()) + end) + + it("adds a new lower case bullet", function() + helpers.new_buffer({ + "# Hello there", + "a. this is the first bullet", + }) + helpers.feedkeys( + "Asecond bulletthird bulletfourth bulletfifth bullet" + .. "sixth bulletseventh bulleteighth bulletninth bullettenth bullet" + ) + assert.are.same({ + "# Hello there", + "a. this is the first bullet", + "b. second bullet", + "c. third bullet", + "d. fourth bullet", + "e. fifth bullet", + "f. sixth bullet", + "g. seventh bullet", + "h. eighth bullet", + "i. ninth bullet", + "j. tenth bullet", + }, helpers.get_lines()) + end) + + it("adds a new bullet and loops at z", function() + vim.g.bullets_renumber_on_change = 0 + helpers.new_buffer({ + "# Hello there", + "y. this is the first bullet", + }) + -- Type through "third bullet", then press CR twice: + -- first CR inserts "ab. " (next bullet after "aa."), + -- second CR on empty bullet line triggers delete-last-bullet-if-empty, + -- leaving an empty line in normal mode. + helpers.feedkeys("Asecond bulletthird bullet") + -- Now in normal mode on empty line 5. Use 'A' to enter insert at end, + -- type the override bullet, then continue with remaining bullets. + helpers.feedkeys("AAY. fourth bulletfifth bulletsixth bullet") + assert.are.same({ + "# Hello there", + "y. this is the first bullet", + "z. second bullet", + "aa. third bullet", + "AY. fourth bullet", + "AZ. fifth bullet", + "BA. sixth bullet", + }, helpers.get_lines()) + end) + + it("does not add a new bullet when mixed case", function() + -- "Ab." is mixed case so the plugin doesn't recognise it as a bullet. + -- CR is therefore deferred via feedkeys('n'); the 'tx' flag in our outer + -- feedkeys drains that deferred CR, leaving normal mode on a new empty line. + -- Use the two-step non-bullet-line pattern. + helpers.new_buffer({ + "# Hello there", + "Ab. this is the first bullet", + }) + helpers.feedkeys("A") + helpers.feedkeys("inot a bullet") + assert.are.same({ + "# Hello there", + "Ab. this is the first bullet", + "not a bullet", + }, helpers.get_lines()) + end) + + describe("g:bullets_max_alpha_characters", function() + it("stops adding items after configured max (default 2)", function() + vim.g.bullets_renumber_on_change = 0 + helpers.new_buffer({ + "# Hello there", + "zy. this is the first bullet", + }) + -- "A" inserts "zz. " (within max), type "second bullet", + -- then on the "zz. second bullet" line: next would be "aaa." (3 + -- chars) which exceeds the default max of 2, so no bullet is inserted; + -- instead a plain CR is deferred. The 'tx' flag drains it, leaving + -- normal mode on a new empty line. + helpers.feedkeys("Asecond bullet") + helpers.feedkeys("inot a bullet") + assert.are.same({ + "# Hello there", + "zy. this is the first bullet", + "zz. second bullet", + "not a bullet", + }, helpers.get_lines()) + end) + + it("does not bullets if configured as 0", function() + vim.g.bullets_max_alpha_characters = 0 + helpers.new_buffer({ + "# Hello there", + "a. this is the first bullet", + }) + -- With max=0, alpha bullets are disabled entirely so "a." is not + -- recognized as a bullet → CR defers via feedkeys('n') + helpers.feedkeys("A") + helpers.feedkeys("inot a bullet") + assert.are.same({ + "# Hello there", + "a. this is the first bullet", + "not a bullet", + }, helpers.get_lines()) + end) + end) + end) +end) diff --git a/test/asciidoc_spec.lua b/test/asciidoc_spec.lua new file mode 100644 index 0000000..d15fcd7 --- /dev/null +++ b/test/asciidoc_spec.lua @@ -0,0 +1,23 @@ +local helpers = require("test.helpers") + +describe("AsciiDoc", function() + it("maintains indentation in ascii doc bullets", function() + helpers.test_bullet_inserted( + "rats", + { "= Pets!", "* dogs", "** cats" }, + { "= Pets!", "* dogs", "** cats", "** rats" } + ) + end) + + it("supports dot bullets", function() + helpers.test_bullet_inserted("cats", { "= Pets!", ". dogs" }, { "= Pets!", ". dogs", ". cats" }) + end) + + it("supports nested dot bullets", function() + helpers.test_bullet_inserted( + "rats", + { "= Pets!", ". dogs", ".. cats" }, + { "= Pets!", ". dogs", ".. cats", ".. rats" } + ) + end) +end) diff --git a/test/bullets_spec.lua b/test/bullets_spec.lua new file mode 100644 index 0000000..6b625fa --- /dev/null +++ b/test/bullets_spec.lua @@ -0,0 +1,310 @@ +local helpers = require("test.helpers") + +describe("Bullets.vim", function() + describe("inserting new bullets", function() + before_each(function() + helpers.reset_config() + vim.o.ignorecase = false + end) + + describe("on return key when cursor is not at EOL", function() + it("splits the line and does not add a bullet", function() + -- G$i places cursor on last char 't' in "- this is the first bullet" + -- i inserts before 't', CR is deferred (not at EOL), splits the line + -- then "second bullet" is typed before 't': "second bullett" + -- Temporarily disable formatoptions 'r' to avoid comment-leader insertion + local saved_fo = vim.o.formatoptions + vim.cmd("set formatoptions-=r") + helpers.new_buffer({ + "# Hello there", + "- this is the first bullet", + }) + -- Position cursor on 't' (last char, 0-indexed col 25), enter insert before it + -- CR is deferred by plugin (not at EOL); 'x' flag drains the deferred CR → split + -- We end up in normal mode on new line with 't' + helpers.feedkeys("G$i") + -- Now in normal mode at col 0 of "t"; insert before 't' and type + helpers.feedkeys("isecond bullet") + vim.o.formatoptions = saved_fo + assert.are.same({ + "# Hello there", + "- this is the first bulle", + "second bullett", + }, helpers.get_lines()) + end) + end) + + describe("on return key when cursor is at EOL", function() + it("adds a new bullet if the previous line had a known bullet type", function() + helpers.test_bullet_inserted( + "do that", + { "# Hello there", "- do this" }, + { "# Hello there", "- do this", "- do that" } + ) + end) + + it("adds a new latex bullet", function() + helpers.test_bullet_inserted("Second item", { + "\\documentclass{article}", + " \\begin{document}", + "", + " \\begin{itemize}", + " \\item First item", + }, { + "\\documentclass{article}", + " \\begin{document}", + "", + " \\begin{itemize}", + " \\item First item", + " \\item Second item", + }) + end) + + it("adds a pandoc bullet if the prev line had one", function() + helpers.test_bullet_inserted( + "second bullet", + { "Hello there", "#. this is the first bullet" }, + { "Hello there", "#. this is the first bullet", "#. second bullet" } + ) + end) + + it("adds an Org mode bullet if the prev line had one", function() + helpers.test_bullet_inserted( + "second bullet", + { "Hello there", "**** this is the first bullet" }, + { "Hello there", "**** this is the first bullet", "**** second bullet" } + ) + end) + + it("adds a new numeric bullet if the previous line had numeric bullet", function() + helpers.test_bullet_inserted( + "second bullet", + { "# Hello there", "1) this is the first bullet" }, + { "# Hello there", "1) this is the first bullet", "2) second bullet" } + ) + end) + + it("adds a new numeric bullet with right padding", function() + helpers.test_bullet_inserted( + "second bullet", + { "# Hello there", "1. this is the first bullet" }, + { "# Hello there", "1. this is the first bullet", "2. second bullet" } + ) + end) + + it("maintains total bullet width from 9. to 10. with reduced padding", function() + vim.g.bullets_renumber_on_change = 0 + helpers.test_bullet_inserted( + "second bullet", + { "# Hello there", "9. this is the first bullet" }, + { "# Hello there", "9. this is the first bullet", "10. second bullet" } + ) + end) + + it("adds a new - bullet with right padding", function() + helpers.test_bullet_inserted( + "second bullet", + { "# Hello there", "- this is the first bullet" }, + { "# Hello there", "- this is the first bullet", "- second bullet" } + ) + end) + + it("does not insert a new numeric bullet for decimal numbers", function() + -- "3.14159 is an approximation of pi." is not a bullet line + -- CR on non-bullet line is deferred, use two-call pattern + helpers.new_buffer({ + "# Hello there", + "3.14159 is an approximation of pi.", + }) + helpers.feedkeys("A") + helpers.feedkeys("isecond line") + assert.are.same({ + "# Hello there", + "3.14159 is an approximation of pi.", + "second line", + }, helpers.get_lines()) + end) + + it("adds a new roman numeral bullet", function() + vim.g.bullets_pad_right = 0 + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + }) + helpers.feedkeys("Asecond bulletthird bulletfourth bulletfifth bullet") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "II. second bullet", + "III. third bullet", + "IV. fourth bullet", + "V. fifth bullet", + }, helpers.get_lines()) + end) + + it("adds a new lowercase roman numeral bullet", function() + vim.g.bullets_pad_right = 0 + helpers.new_buffer({ + "# Hello there", + "i. this is the first bullet", + }) + helpers.feedkeys("Asecond bulletthird bulletfourth bulletfifth bullet") + assert.are.same({ + "# Hello there", + "i. this is the first bullet", + "ii. second bullet", + "iii. third bullet", + "iv. fourth bullet", + "v. fifth bullet", + }, helpers.get_lines()) + end) + + it("does not confuse with the 'ignorecase' option", function() + vim.cmd("set ignorecase") + -- "Vi." is mixed case / not a valid roman numeral bullet → non-bullet CR + helpers.new_buffer({ + "# Hello there", + "Vi. this is the first line", + }) + helpers.feedkeys("A") + helpers.feedkeys("isecond line") + assert.are.same({ + "# Hello there", + "Vi. this is the first line", + "second line", + }, helpers.get_lines()) + end) + + it("does not insert a new roman bullets without following spaces", function() + -- "m.example.com is a site." has no space after the dot → not a bullet + helpers.new_buffer({ + "# Hello there", + "m.example.com is a site.", + }) + helpers.feedkeys("A") + helpers.feedkeys("isecond line") + assert.are.same({ + "# Hello there", + "m.example.com is a site.", + "second line", + }, helpers.get_lines()) + end) + + it("does not insert a new roman bullets for invalid roman numbers", function() + -- "LID." is not a valid roman numeral, so no bullet continuation + -- However lines typed after non-bullet lines also get no bullet + helpers.new_buffer({ + "# Hello there", + "LID. the first line", + }) + -- First CR after "LID. the first line" (non-bullet) → deferred + helpers.feedkeys("A") + helpers.feedkeys("isecond line") + -- Now on "second line" (non-bullet) → deferred CR + helpers.feedkeys("A") + helpers.feedkeys("ivim. third line") + -- Now on "vim. third line" (non-bullet) → deferred CR + helpers.feedkeys("A") + helpers.feedkeys("ifourth line") + assert.are.same({ + "# Hello there", + "LID. the first line", + "second line", + "vim. third line", + "fourth line", + }, helpers.get_lines()) + end) + + it("deletes the last bullet if it is empty", function() + helpers.new_buffer({ + "# Hello there", + "- this is the first bullet", + }) + -- First CR creates "- " empty bullet, second CR on empty bullet deletes it + helpers.feedkeys("A") + local lines = helpers.get_lines() + -- Strip trailing empty lines before comparison. + while #lines > 0 and lines[#lines] == "" do + table.remove(lines) + end + assert.are.same({ + "# Hello there", + "- this is the first bullet", + }, lines) + end) + + it("promote the last bullet when configured to", function() + vim.g.bullets_delete_last_bullet_if_empty = 2 + helpers.new_buffer({ + "# Hello there", + "- this is the first bullet", + " - this is the second bullet", + }) + -- First CR creates new child bullet " - ", second CR promotes (dedents) + helpers.feedkeys("A") + local lines = helpers.get_lines() + -- strip trailing empty lines + while #lines > 0 and lines[#lines] == "" do + table.remove(lines) + end + -- The promoted bullet has a trailing space ("- ") matching plugin output + assert.are.same({ + "# Hello there", + "- this is the first bullet", + " - this is the second bullet", + "- ", + }, lines) + end) + + it("does not delete the last bullet when configured not to", function() + vim.g.bullets_delete_last_bullet_if_empty = 0 + helpers.new_buffer({ + "# Hello there", + "- this is the first bullet", + }) + -- First CR creates "- " empty bullet, second CR on empty bullet + -- with config=0 does NOT delete it; a plain CR fires leaving "- " intact + helpers.feedkeys("A") + local lines = helpers.get_lines() + -- strip trailing empty lines + while #lines > 0 and lines[#lines] == "" do + table.remove(lines) + end + -- The non-deleted bullet retains trailing space ("- ") matching plugin output + assert.are.same({ + "# Hello there", + "- this is the first bullet", + "- ", + }, lines) + end) + + it("toggles roman numeral bullets with g:bullets_enable_roman_list", function() + -- Disable alpha lists to isolate test to roman numerals + vim.g.bullets_max_alpha_characters = 0 + vim.g.bullets_enable_roman_list = 1 + helpers.new_buffer({ + "# Hello there", + "i. this is the first bullet", + }) + -- Type second and third bullets (roman numeral bullets) + helpers.feedkeys("Asecond bulletthird bullet") + -- Disable roman list mid-test + vim.g.bullets_enable_roman_list = 0 + -- Type fourth and fifth (no roman numeral prefix now) + -- We're in normal mode, need to append and continue + helpers.feedkeys("A") + helpers.feedkeys("ifourth bullet") + helpers.feedkeys("A") + helpers.feedkeys("ififth bullet") + assert.are.same({ + "# Hello there", + "i. this is the first bullet", + "ii. second bullet", + "iii. third bullet", + "fourth bullet", + "fifth bullet", + }, helpers.get_lines()) + end) + end) + end) +end) diff --git a/test/checkboxes_spec.lua b/test/checkboxes_spec.lua new file mode 100644 index 0000000..d67991e --- /dev/null +++ b/test/checkboxes_spec.lua @@ -0,0 +1,325 @@ +local helpers = require("test.helpers") + +describe("checkboxes", function() + describe("inserting checkboxes", function() + it("inserts another checkbox after the previous one", function() + helpers.test_bullet_inserted( + "do that", + { "# Hello there", "- [ ] do this" }, + { "# Hello there", "- [ ] do this", "- [ ] do that" } + ) + end) + + it("inserts a * checkbox after the previous one", function() + helpers.test_bullet_inserted( + "do that", + { "# Hello there", "* [ ] do this" }, + { "# Hello there", "* [ ] do this", "* [ ] do that" } + ) + end) + + it("inserts an empty checkbox even if prev line was checked", function() + helpers.test_bullet_inserted( + "do that", + { "# Hello there", "- [x] do this" }, + { "# Hello there", "- [x] do this", "- [ ] do that" } + ) + end) + end) + + describe("toggling checkboxes", function() + before_each(function() + helpers.reset_config() + end) + + it("toggle a bullet", function() + helpers.new_buffer({ + "# Hello there", + "- [ ] first bullet", + "- [X] second bullet", + "- [x] third bullet", + "- [.] fourth bullet", + "- [o] fifth bullet", + "- [O] sixth bullet", + "- not a checkbox", + }) + -- Move to line 2 (first bullet line), then toggle each + helpers.feedkeys("gg") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + assert.are.same({ + "# Hello there", + "- [X] first bullet", + "- [ ] second bullet", + "- [ ] third bullet", + "- [X] fourth bullet", + "- [X] fifth bullet", + "- [X] sixth bullet", + "- not a checkbox", + }, helpers.get_lines()) + end) + + it("toggle a bullet and adjust parent", function() + helpers.new_buffer({ + "# Hello there", + "- [ ] first bullet", + " - [ ] second bullet", + " - [ ] third bullet", + }) + helpers.feedkeys("G") + vim.cmd("ToggleCheckbox") + assert.are.same({ + "# Hello there", + "- [X] first bullet", + " - [X] second bullet", + " - [X] third bullet", + }, helpers.get_lines()) + end) + + it("toggle a bullet and adjust children", function() + helpers.new_buffer({ + "# Hello there", + "- [ ] first bullet", + " - [ ] second bullet", + " - [ ] third bullet", + }) + helpers.feedkeys("ggj") + vim.cmd("ToggleCheckbox") + assert.are.same({ + "# Hello there", + "- [X] first bullet", + " - [X] second bullet", + " - [X] third bullet", + }, helpers.get_lines()) + end) + + it("toggle a bullet and calculate completion", function() + helpers.new_buffer({ + "# Hello there", + "- [ ] first bullet", + " - [ ] second bullet", + " - [ ] third bullet", + " - [ ] fourth bullet", + " - [ ] fifth bullet", + " - [ ] sixth bullet", + " - [ ] seventh bullet", + " - [ ] eighth bullet", + " - [ ] ninth bullet", + " - [ ] tenth bullet", + " - [ ] eleventh bullet", + " - [ ] twelfth bullet", + " - [ ] thirteenth bullet", + " - [ ] fourteenth bullet", + " - [ ] fifteenth bullet", + " - [ ] sixteenth bullet", + " - [X] seventeenth bullet", + " - [X] eighteenth bullet", + " - [X] ninteenth bullet", + " - [X] twentieth bullet", + " - [X] twenty-first bullet", + }) + -- cursor starts at last line (line 21), go to line 4 (3j from top = line 4) + -- new_buffer places cursor at last line, so we need to go to line 4 + helpers.feedkeys("gg") + helpers.feedkeys("3j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("6j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("2j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + helpers.feedkeys("2j") + vim.cmd("ToggleCheckbox") + assert.are.same({ + "# Hello there", + "- [.] first bullet", + " - [.] second bullet", + " - [X] third bullet", + " - [ ] fourth bullet", + " - [ ] fifth bullet", + " - [ ] sixth bullet", + " - [O] seventh bullet", + " - [ ] eighth bullet", + " - [X] ninth bullet", + " - [X] tenth bullet", + " - [X] eleventh bullet", + " - [X] twelfth bullet", + " - [X] thirteenth bullet", + " - [X] fourteenth bullet", + " - [X] fifteenth bullet", + " - [X] sixteenth bullet", + " - [O] seventeenth bullet", + " - [ ] eighteenth bullet", + " - [X] ninteenth bullet", + " - [X] twentieth bullet", + " - [X] twenty-first bullet", + }, helpers.get_lines()) + end) + + it("adds and toggles bullets using UTF characters", function() + vim.g.bullets_checkbox_markers = "✗○◐●✓" + -- Ensure produces tabs (not spaces) regardless of user config + vim.opt.expandtab = false + helpers.new_buffer({ + "# Hello there", + "- [ ] first bullet", + }) + -- Toggle first bullet (cursor at line 2 already via new_buffer) + helpers.feedkeys("j") + vim.cmd("ToggleCheckbox") + -- Open new line and type "second bullet" + helpers.feedkeys("osecond bullet") + -- Open new line with indent and type "third bullet" + helpers.feedkeys("othird bullet") + -- Open new line (same indent) and type "fourth bullet", then toggle + helpers.feedkeys("ofourth bullet") + vim.cmd("ToggleCheckbox") + -- Open new line with dedent and type "fifth bullet", then toggle + helpers.feedkeys("ofifth bullet") + vim.cmd("ToggleCheckbox") + -- Open new line and type "sixth bullet", toggle twice + helpers.feedkeys("osixth bullet") + vim.cmd("ToggleCheckbox") + vim.cmd("ToggleCheckbox") + assert.are.same({ + "# Hello there", + "- [✓] first bullet", + "- [◐] second bullet", + "\t- [✗] third bullet", + "\t- [✓] fourth bullet", + "- [✓] fifth bullet", + "- [✗] sixth bullet", + }, helpers.get_lines()) + end) + + it("recomputes checkboxes recursively on RecomputeCheckboxes", function() + vim.g.bullets_checkbox_markers = " .¼½¾X" + helpers.new_buffer({ + "# Hello there", + "- [ ] EXPECTED: ¼", + " - [X] checkbox leaf", + " - [ ] EXPECTED: CHECKED", + " - [ ] EXPECTED: CHECKED", + " - [ ] EXPECTED: CHECKED", + " - [X] checkbox leaf", + " - [X] checkbox leaf", + " - [X] EXPECTED: ¾", + " - [X] checkbox leaf", + " - [X] checkbox leaf", + " - [X] checkbox leaf", + " - [ ] checkbox leaf", + " - [X] EXPECTED: ½", + " - [ ] EXPECTED: CHECKED", + " - [ ] EXPECTED: CHECKED", + " - [X] checkbox leaf", + " - [½] checkbox leaf (EXPECTED: UNCHECKED)", + " - [½] EXPECTED: UNCHECKED", + " - [ ] checkbox leaf", + " - [½] checkbox leaf (EXPECTED: UNCHECKED)", + }) + helpers.feedkeys("gg") + helpers.feedkeys("9j") + vim.cmd("RecomputeCheckboxes") + assert.are.same({ + "# Hello there", + "- [¼] EXPECTED: ¼", + " - [X] checkbox leaf", + " - [X] EXPECTED: CHECKED", + " - [X] EXPECTED: CHECKED", + " - [X] EXPECTED: CHECKED", + " - [X] checkbox leaf", + " - [X] checkbox leaf", + " - [¾] EXPECTED: ¾", + " - [X] checkbox leaf", + " - [X] checkbox leaf", + " - [X] checkbox leaf", + " - [ ] checkbox leaf", + " - [½] EXPECTED: ½", + " - [X] EXPECTED: CHECKED", + " - [X] EXPECTED: CHECKED", + " - [X] checkbox leaf", + " - [ ] checkbox leaf (EXPECTED: UNCHECKED)", + " - [ ] EXPECTED: UNCHECKED", + " - [ ] checkbox leaf", + " - [ ] checkbox leaf (EXPECTED: UNCHECKED)", + }, helpers.get_lines()) + end) + + it("recomputes checkboxes correctly on reindents", function() + vim.g.bullets_checkbox_markers = " /X" + helpers.new_buffer({ + "# Hello there", + "- [X] parent bullet", + " - [X] first child bullet", + }) + -- Press CR at end of last line to add a new child bullet + helpers.feedkeys("GA") + vim.cmd("RecomputeCheckboxes") + assert.are.same({ + "# Hello there", + "- [/] parent bullet", + " - [X] first child bullet", + " - [ ] ", + }, helpers.get_lines()) + + -- Phase 2: press CR on the new empty bullet, which should dedent/remove it + vim.g.bullets_delete_last_bullet_if_empty = 2 + helpers.feedkeys("A") + vim.cmd("RecomputeCheckboxes") + assert.are.same({ + "# Hello there", + "- [X] parent bullet", + " - [X] first child bullet", + "- [ ] ", + }, helpers.get_lines()) + end) + + it("handles skip-level checkbox trees", function() + vim.g.bullets_checkbox_markers = " /X" + helpers.new_buffer({ + "# Hello there", + "- [X] parent bullet (EXPECTED: /)", + " - skip: not checkbox content", + " - [ ] new root bullet (EXPECTED: /)", + " - [ ] first child bullet", + " - [X] first child bullet", + " - [X] first child bullet", + " - [ ] first child bullet", + }) + helpers.feedkeys("gg") + helpers.feedkeys("2j") + vim.cmd("RecomputeCheckboxes") + assert.are.same({ + "# Hello there", + "- [/] parent bullet (EXPECTED: /)", + " - skip: not checkbox content", + " - [/] new root bullet (EXPECTED: /)", + " - [ ] first child bullet", + " - [X] first child bullet", + " - [X] first child bullet", + " - [ ] first child bullet", + }, helpers.get_lines()) + end) + end) +end) diff --git a/test/filetypes_spec.lua b/test/filetypes_spec.lua new file mode 100644 index 0000000..b3cae5a --- /dev/null +++ b/test/filetypes_spec.lua @@ -0,0 +1,20 @@ +local helpers = require("test.helpers") + +describe("filetypes", function() + it("creates mapping for bullets on empty buffer if configured", function() + -- g:bullets_enable_in_empty_buffers defaults to 0, so a new buffer with + -- no filetype does NOT get the bullet mapping. + vim.cmd("enew") + vim.cmd("setlocal formatoptions= comments=") -- prevent '#' comment-continuation + helpers.feedkeys("i# Hello there- this is the first bulletthis is the second bullet") + assert.are.same({ "# Hello there", "- this is the first bullet", "this is the second bullet" }, helpers.get_lines()) + end) + + it("should have text filetype for .txt", function() + local tmpfile = vim.fn.tempname() .. ".txt" + vim.cmd("edit " .. tmpfile) + local ft = vim.bo.filetype + assert.is_true(ft == "text" or ft == "markdown", "Expected 'text' or 'markdown' filetype, got: " .. tostring(ft)) + vim.cmd("bdelete!") + end) +end) diff --git a/test/helpers.lua b/test/helpers.lua new file mode 100644 index 0000000..ae1ee52 --- /dev/null +++ b/test/helpers.lua @@ -0,0 +1,53 @@ +local M = {} + +-- Replaces termcodes and feeds keys synchronously (including mapped keys) +function M.feedkeys(keys) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), "tx", false) +end + +-- Opens a fresh buffer with the given lines and the 'text' filetype, +-- positions the cursor at the end of the last line, and returns the bufnr. +function M.new_buffer(lines) + vim.cmd("enew") + vim.bo.filetype = "text" + vim.api.nvim_buf_set_lines(0, 0, -1, false, lines) + local last = #lines + vim.api.nvim_win_set_cursor(0, { last, #lines[last] }) + return vim.api.nvim_get_current_buf() +end + +-- Returns all lines of the current buffer. +function M.get_lines() + return vim.api.nvim_buf_get_lines(0, 0, -1, false) +end + +-- Sets up a buffer with initial_lines, appends second_bullet via , +-- then asserts the buffer matches expected_lines. +function M.test_bullet_inserted(second_bullet, initial_lines, expected_lines) + M.new_buffer(initial_lines) + M.feedkeys("A" .. second_bullet) + assert.are.same(expected_lines, M.get_lines()) +end + +-- Resets all bullets.vim globals to their plugin defaults. +-- Call in before_each for any describe block that mutates config. +function M.reset_config() + vim.g.bullets_enabled_file_types = { "markdown", "text", "gitcommit", "scratch" } + vim.g.bullets_enable_in_empty_buffers = 1 + vim.g.bullets_set_mappings = 1 + vim.g.bullets_mapping_leader = "" + vim.g.bullets_custom_mappings = {} + vim.g.bullets_max_alpha_characters = 2 + vim.g.bullets_auto_indent_after_colon = 1 + vim.g.bullets_line_spacing = 1 + vim.g.bullets_renumber_on_change = 1 + vim.g.bullets_nested_checkboxes = 1 + vim.g.bullets_checkbox_markers = " .oOX" + vim.g.bullets_checkbox_partials_toggle = 1 + vim.g.bullets_outline_levels = { "ROM", "ABC", "num", "abc", "rom", "std-", "std*", "std+" } + vim.g.bullets_enable_roman_list = 1 + vim.g.bullets_pad_right = 1 + vim.g.bullets_delete_last_bullet_if_empty = 1 +end + +return M diff --git a/test/minimal_init.lua b/test/minimal_init.lua new file mode 100644 index 0000000..ecdb160 --- /dev/null +++ b/test/minimal_init.lua @@ -0,0 +1,16 @@ +-- Resolve the repo root relative to this file's location +local script_path = debug.getinfo(1, "S").source:sub(2) -- strip leading '@' +local repo_root = vim.fn.fnamemodify(script_path, ":p:h:h") + +local plenary_path = "/tmp/plenary.nvim" +if not vim.uv.fs_stat(plenary_path) then + vim.fn.system({ "git", "clone", "--depth=1", "https://github.com/nvim-lua/plenary.nvim", plenary_path }) +end + +vim.opt.rtp:prepend(plenary_path) +vim.opt.rtp:prepend(repo_root) + +vim.cmd("filetype plugin on") +vim.cmd("runtime plugin/bullets.vim") +vim.cmd("set formatoptions=") +vim.cmd("set noexpandtab") diff --git a/test/nested_bullets_spec.lua b/test/nested_bullets_spec.lua new file mode 100644 index 0000000..8dc6aab --- /dev/null +++ b/test/nested_bullets_spec.lua @@ -0,0 +1,470 @@ +local helpers = require("test.helpers") + +describe("Bullets.vim", function() + describe("nested bullets", function() + before_each(function() + helpers.reset_config() + -- Plugin uses `normal! >>` / `normal! <<` internally which respect shiftwidth/expandtab. + -- Set noexpandtab + shiftwidth=tabstop=4 so one indent level = one tab character. + vim.opt.expandtab = false + vim.opt.shiftwidth = 4 + vim.opt.tabstop = 4 + end) + + it("demotes an existing bullet", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + "II. second bullet", + "III. third bullet", + "IV. fourth bullet", + "V. fifth bullet", + "VI. sixth bullet", + "VII. seventh bullet", + "VIII. eighth bullet", + "IX. ninth bullet", + }) + -- Go to line 3 (gg + 2j), enter insert, demote with + helpers.feedkeys("gg2ji") + -- Back to normal mode, go down 1 line, demote 3 times with >> + helpers.feedkeys("j>>>>>>") + -- Continue demoting subsequent lines + helpers.feedkeys("j>>>>>>>>") + helpers.feedkeys("j>>>>>>>>>>") + helpers.feedkeys("j>>>>>>>>") + helpers.feedkeys(">>>>") + helpers.feedkeys("j>>>>>>>>") + helpers.feedkeys(">>>>>>") + helpers.feedkeys("j>>>>>>>>") + helpers.feedkeys(">>>>>>>>") + helpers.feedkeys("j>>>>>>>>") + helpers.feedkeys(">>>>>>>>>>") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "\t\t\t1. third bullet", + "\t\t\t\ta. fourth bullet", + "\t\t\t\t\ti. fifth bullet", + "\t\t\t\t\t\t- sixth bullet", + "\t\t\t\t\t\t\t* seventh bullet", + "\t\t\t\t\t\t\t\t+ eighth bullet", + "\t\t\t\t\t\t\t\t\t+ ninth bullet", + }, helpers.get_lines()) + end) + + it("promotes an existing bullet", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "\t\t\t1. third bullet", + "\t\t\t\ta. fourth bullet", + "\t\t\t\t\ti. fifth bullet", + "\t\t\t\t\t\t- sixth bullet", + "\t\t\t\t\t\t\t* seventh bullet", + "\t\t\t\t\t\t\t\t+ eighth bullet", + }) + -- Go to line 3 (gg + 2j), promote with << + helpers.feedkeys("gg2j<<") + -- Go to line 4, enter insert, demote twice with + helpers.feedkeys("ji") + -- Continue promoting subsequent lines + helpers.feedkeys("j<<<<<<") + helpers.feedkeys("j<<<<<<") + helpers.feedkeys("<<<<") + helpers.feedkeys("j<<<<<<") + helpers.feedkeys("<<<<<<") + helpers.feedkeys("j<<<<<<") + helpers.feedkeys("<<<<<<<<") + helpers.feedkeys("j<<<<<<") + helpers.feedkeys("<<<<<<") + helpers.feedkeys("<<<<") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "II. second bullet", + "\tA. third bullet", + "\tB. fourth bullet", + "III. fifth bullet", + "IV. sixth bullet", + "V. seventh bullet", + "VI. eighth bullet", + }, helpers.get_lines()) + end) + + it("demotes an empty bullet", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + }) + -- Enter insert at end, press CR (on bullet line), demote with , type + helpers.feedkeys("GAsecond bullet") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + }, helpers.get_lines()) + end) + + it("promotes an empty bullet", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + }) + -- Enter insert at end, press CR (on bullet line), promote with , type + helpers.feedkeys("GAthird bullet") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "II. third bullet", + }, helpers.get_lines()) + end) + + it("restarts numbering with multiple outlines", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + }) + -- GA enters insert at end, on bullet line creates new bullet, then + -- again on bullet line (empty), which should delete the empty bullet + -- (delete_last_bullet_if_empty), leaving normal mode. Then another does + -- the same. Then we type a new bullet manually. + helpers.feedkeys("GA") + -- Now on a new empty bullet line. CR again triggers delete-last-bullet + helpers.feedkeys("A") + -- Again on empty line + helpers.feedkeys("A") + -- Now type the manual bullet header + helpers.feedkeys("iA. first bullet") + -- Enter insert at end, CR on bullet line, demote, type + helpers.feedkeys("Asecond bullet") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "", + "A. first bullet", + "\t1. second bullet", + }, helpers.get_lines()) + end) + + it("works with custom outline level definitions", function() + vim.g.bullets_outline_levels = { "num", "ABC", "std*" } + helpers.new_buffer({ + "# Hello there", + }) + -- Enter insert at end, CR (non-bullet line header, deferred CR) + helpers.feedkeys("GA") + -- Now in normal mode on new empty line - type the first bullet + helpers.feedkeys("i1. first bullet") + -- CR on bullet line - stays in insert after plugin fires + helpers.feedkeys("Asecond bullet") + -- CR on bullet line, then demote, then type + helpers.feedkeys("Athird bullet") + helpers.feedkeys("Afourth bullet") + helpers.feedkeys("Afifth bullet") + helpers.feedkeys("Asixth bullet") + helpers.feedkeys("Aseventh bullet") + helpers.feedkeys("Aeighth bullet") + -- demote twice, then type + helpers.feedkeys("Aninth bullet") + -- demote once, then type + helpers.feedkeys("Atenth bullet") + helpers.feedkeys("Aeleventh bullet") + assert.are.same({ + "# Hello there", + "1. first bullet", + "2. second bullet", + "\tA. third bullet", + "\tB. fourth bullet", + "\t\t* fifth bullet", + "\t\t* sixth bullet", + "\t\t\t* seventh bullet", + "\t\t\t* eighth bullet", + "\tC. ninth bullet", + "3. tenth bullet", + "4. eleventh bullet", + }, helpers.get_lines()) + end) + + it("promotes and demotes from different starting levels", function() + helpers.new_buffer({ + "# Hello there", + "1. this is the first bullet", + "\ta. second bullet", + }) + -- In insert mode at end, promote with (still in insert after plugin's :BulletPromote) + helpers.feedkeys("GA") + -- Now "2. second bullet" in normal mode - CR on bullet line, demote, type + helpers.feedkeys("Athird bullet") + -- Two CRs: first creates empty \tb. bullet, second deletes it (delete_last_bullet_if_empty) + helpers.feedkeys("A") + helpers.feedkeys("A") + -- Type the non-bullet manually on the blank line + helpers.feedkeys("i+ fourth bullet") + -- CR on + bullet line, demote, type + helpers.feedkeys("Afifth bullet") + -- Two CRs: first creates empty \t+ bullet, second deletes it + helpers.feedkeys("A") + helpers.feedkeys("A") + -- Type the non-bullet manually + helpers.feedkeys("i* sixth bullet") + -- CR on * bullet line creates * seventh bullet, type it + helpers.feedkeys("Aseventh bullet") + -- Re-enter insert at end and demote with . + helpers.feedkeys("A") + assert.are.same({ + "# Hello there", + "1. this is the first bullet", + "2. second bullet", + "\ta. third bullet", + "+ fourth bullet", + "\t+ fifth bullet", + "* sixth bullet", + "\t+ seventh bullet", + }, helpers.get_lines()) + end) + + it("does not nest beyond defined levels", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "\t\t1. third bullet", + "\t\t\ta. fourth bullet", + "\t\t\t\ti. fifth bullet", + "\t\t\t\tii. sixth bullet", + "\t\t\t\t\t- seventh bullet", + "\t\t\t\t\t\t* eighth bullet", + "\t\t\t\t\t\t\t+ ninth bullet", + }) + -- GA enters insert at end, CR on bullet line, demote with , type + helpers.feedkeys("GAtenth bullet") + helpers.feedkeys("Aeleventh bullet") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "\t\t1. third bullet", + "\t\t\ta. fourth bullet", + "\t\t\t\ti. fifth bullet", + "\t\t\t\tii. sixth bullet", + "\t\t\t\t\t- seventh bullet", + "\t\t\t\t\t\t* eighth bullet", + "\t\t\t\t\t\t\t+ ninth bullet", + "\t\t\t\t\t\t\t\t+ tenth bullet", + "\t\t\t\t\t\t\t\t+ eleventh bullet", + }, helpers.get_lines()) + end) + + it("removes bullet when promoting top level bullet", function() + helpers.new_buffer({ + "# Hello there", + "A. this is the first bullet", + "", + "I. second bullet", + "\tA. third bullet", + }) + -- Go to line 2 (gg + j), promote with << + helpers.feedkeys("ggj<<") + -- Go to line 5 (3j from line 2 = line 5), enter insert, promote twice + helpers.feedkeys("3ji") + assert.are.same({ + "# Hello there", + "this is the first bullet", + "", + "I. second bullet", + "third bullet", + }, helpers.get_lines()) + end) + + it("handle standard bullets when they are not in outline list", function() + vim.g.bullets_outline_levels = { "num", "ABC" } + helpers.new_buffer({ + "# Hello there", + "1. this is the first bullet", + "\t- standard bullet", + }) + -- GA enters insert at end, CR on bullet line (standard bullet), type + helpers.feedkeys("GAsecond standard bullet") + -- CR on bullet line, promote with , type + helpers.feedkeys("Asecond bullet") + -- CR on bullet line, type + helpers.feedkeys("Athird bullet") + assert.are.same({ + "# Hello there", + "1. this is the first bullet", + "\t- standard bullet", + "\t- second standard bullet", + "2. second bullet", + "3. third bullet", + }, helpers.get_lines()) + end) + + it("adds new nested bullets with correct alpha/roman numerals", function() + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + }) + -- All CRs are on bullet lines. After , insert mode remains active + -- so the sequence can continue by typing text and pressing for the next line. + helpers.feedkeys( + "GAthird bulletfourth bulletfifth bulletsixth bulletseventh bullet" + ) + helpers.feedkeys( + "Aeighth bulletninth bullettenth bulleteleventh bullettwelfth bullet" + ) + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "\tA. second bullet", + "\t\t1. third bullet", + "\t\t\ta. fourth bullet", + "\t\t\t\ti. fifth bullet", + "\t\t\t\t\t- sixth bullet", + "\t\t\t\t\t- seventh bullet", + "\t\t\t\tii. eighth bullet", + "\t\t\tb. ninth bullet", + "\t\t2. tenth bullet", + "\tB. eleventh bullet", + "II. twelfth bullet", + }, helpers.get_lines()) + end) + + it("changes levels in visual mode", function() + vim.g.bullets_outline_levels = { "num", "abc", "std*" } + helpers.new_buffer({ + "# Hello there", + "1. first bullet", + "\ta. second bullet", + "\tb. third bullet", + "\t\t* fourth bullet", + "\t\t* fifth bullet", + "\t\t\tsixth bullet", + "\t\t* seventh bullet", + "2. eighth bullet", + "\t\ta. ninth bullet", + "\ta. tenth bullet", + "\tb. eleventh bullet", + "3. twelfth bullet", + "\t thirteenth bullet", + "\ta. fourteenth bullet", + "\t\t* fifteenth bullet", + "4. sixteenth bullet", + }) + -- After each visual < or > operation, the plugin re-enters visual mode (via s:set_selection). + -- Exit visual mode before starting each fresh visual selection. + helpers.feedkeys("gg3jv<") + helpers.feedkeys("jv2j<") + helpers.feedkeys("jvj>") + helpers.feedkeys("jvj<") + -- The plugin leaves us in visual mode with the same selection. + helpers.feedkeys("<") + helpers.feedkeys("jv>") + helpers.feedkeys("3jv2j>") + -- Repeat the operation on the same visual selection. + helpers.feedkeys(">") + assert.are.same({ + "# Hello there", + "1. first bullet", + "\ta. second bullet", + "2. third bullet", + "\ta. fourth bullet", + "\tb. fifth bullet", + "\t\tsixth bullet", + "\t\t\t* seventh bullet", + "\tc. eighth bullet", + "3. ninth bullet", + "tenth bullet", + "\t\ta. eleventh bullet", + "4. twelfth bullet", + "\t thirteenth bullet", + "\t\t\ta. fourteenth bullet", + "\t\t\t\t* fifteenth bullet", + "\t\ta. sixteenth bullet", + }, helpers.get_lines()) + end) + + it("add and change bullets with multiple line spacing and wrapped lines", function() + vim.g.bullets_line_spacing = 2 + helpers.new_buffer({ + "# Hello there", + "I. this is the first bullet", + }) + -- GA enters insert at end, CR on bullet line (with line_spacing=2, creates empty line too) + -- Then type 'second bullet', then CR again, demote, type 'third bullet' + helpers.feedkeys("GAsecond bullet") + helpers.feedkeys("Athird bullet") + -- After CR on bullet line with line_spacing=2, cursor is on the new empty line after the bullet + -- dd deletes that line, then inserts '\twrapped bullet'. + helpers.feedkeys("A") + helpers.feedkeys("dd") + helpers.feedkeys("i\twrapped bullet") + -- Then CR, type 'fourth bullet' + helpers.feedkeys("Afourth bullet") + assert.are.same({ + "# Hello there", + "I. this is the first bullet", + "", + "II. second bullet", + "", + "\tA. third bullet", + "\twrapped bullet", + "", + "\tB. fourth bullet", + }, helpers.get_lines()) + end) + + it("indents after a line ending in a colon", function() + vim.g.bullets_auto_indent_after_colon = 1 + helpers.new_buffer({ + "# Hello there", + "a. this is the first bullet", + }) + -- GA enters insert at end, CR on bullet line, type second bullet ending with colon + helpers.feedkeys("GAthis is the second bullet:") + -- CR after colon should auto-indent + helpers.feedkeys("Athis bullet is indented") + helpers.feedkeys("Athis bullet is also indented") + -- Check first phase + local lines1 = helpers.get_lines() + -- Remove trailing empty lines before comparison. + while #lines1 > 0 and lines1[#lines1] == "" do + table.remove(lines1) + end + assert.are.same({ + "# Hello there", + "a. this is the first bullet", + "b. this is the second bullet:", + "\ti. this bullet is indented", + "\tii. this bullet is also indented", + }, lines1) + + -- Phase 2: reset buffer with same content, test fullwidth colon + helpers.new_buffer({ + "# Hello there", + "a. this is the first bullet", + }) + -- Use GA to enter insert at end of the last line. + helpers.feedkeys("GAthis is the second bullet that ends with fullwidth colon:") + helpers.feedkeys("Athis bullet is indented") + helpers.feedkeys("Athis bullet is also indented") + local lines2 = helpers.get_lines() + while #lines2 > 0 and lines2[#lines2] == "" do + table.remove(lines2) + end + assert.are.same({ + "# Hello there", + "a. this is the first bullet", + "b. this is the second bullet that ends with fullwidth colon:", + "\ti. this bullet is indented", + "\tii. this bullet is also indented", + }, lines2) + end) + end) +end) diff --git a/test/poc_spec.lua b/test/poc_spec.lua new file mode 100644 index 0000000..9e2f9c8 --- /dev/null +++ b/test/poc_spec.lua @@ -0,0 +1,21 @@ +local function feedkeys(keys) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), "tx", false) +end + +describe("Bullets.vim", function() + it("loads the plugin", function() + assert.equals(2, vim.fn.exists(":InsertNewBullet")) + end) + + it("inserts a new bullet on ", function() + vim.cmd("enew") + vim.bo.filetype = "text" + vim.api.nvim_buf_set_lines(0, 0, -1, false, { "- first item" }) + vim.api.nvim_win_set_cursor(0, { 1, #"- first item" }) + + feedkeys("A") + + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + assert.equals("- ", lines[2]) + end) +end) diff --git a/test/renumber_bullets_spec.lua b/test/renumber_bullets_spec.lua new file mode 100644 index 0000000..0f25c5f --- /dev/null +++ b/test/renumber_bullets_spec.lua @@ -0,0 +1,301 @@ +local helpers = require("test.helpers") + +describe("re-numbering", function() + before_each(function() + helpers.reset_config() + end) + + it("renumbers a selected list correctly", function() + helpers.new_buffer({ + "# Hello there", + "33. this is the first bullet", + "2. this is the second bullet", + "1. this is the third bullet", + "4. this is the fourth bullet", + }) + helpers.feedkeys("ggVGgN") + assert.are.same({ + "# Hello there", + "1. this is the first bullet", + "2. this is the second bullet", + "3. this is the third bullet", + "4. this is the fourth bullet", + }, helpers.get_lines()) + end) + + it("renumbers a list containing checkboxes", function() + helpers.new_buffer({ + "# Hello there", + "33. this is the first bullet", + "- [x] this is the second bullet", + "1. this is the third bullet", + " - [ ] this is the fourth bullet", + "4. this is the fifth bullet", + "", + "- [o] second bullet list", + "b. second item", + "- [x] third item", + }) + -- cursor is at line 10; move to line 2 (j from line 1 = line 2) + helpers.feedkeys("ggj") + helpers.feedkeys("gN") + helpers.feedkeys("}j") + helpers.feedkeys("gN") + assert.are.same({ + "# Hello there", + "1. this is the first bullet", + "- [x] this is the second bullet", + "2. this is the third bullet", + " - [ ] this is the fourth bullet", + "3. this is the fifth bullet", + "", + "- [o] second bullet list", + "a. second item", + "- [x] third item", + }, helpers.get_lines()) + end) + + it("renumbers a nested list", function() + vim.g.bullets_line_spacing = 2 + helpers.new_buffer({ + "# Hello there", + "0. zero bullet", + "", + "X. first bullet", + "", + "- second bullet", + "\twrapped line", + "", + "a. third bullet", + "", + "I. fourth bullet", + "", + "\tV. fifth bullet", + "", + "\t\tB. sixth bullet", + "", + "\t* seventh bullet", + "", + "\t\ti. eighth bullet", + "", + "\t\tx. ninth bullet", + "\t\t\t wrapped line", + "", + "\t\t\ta. tenth bullet", + "wrapped line without indent", + "", + "\tC. eleventh bullet", + "\t\t0. twelfth bullet", + "", + "\t\t\t* thirteenth bullet", + "", + "\t\t\t\t+ fourteenth bullet", + "", + "\t\t\tb. fifteenth bullet", + "", + "\t\ta. sixteenth bullet", + "", + "\t\t\t8. seventeenth bullet", + "", + "\t\t\t0. eighteenth bullet", + "\t\t\t\t wrapped line", + "", + "\tnormal indented line", + "next normal line", + "", + "1. nineteenth bullet", + "", + "x. twentieth bullet", + "", + "", + "v. twenty-first bullet", + "- twenty-second bullet", + "", + "", + "v. twenty-third bullet", + }) + -- Go to line 4 (gg then 3j from line 1) and renumber from there + helpers.feedkeys("gg3j") + helpers.feedkeys("gN") + -- Go to last line then 3k up and renumber + helpers.feedkeys("G3k") + helpers.feedkeys("gN") + assert.are.same({ + "# Hello there", + "1. zero bullet", + "", + "2. first bullet", + "", + "- second bullet", + "\twrapped line", + "", + "3. third bullet", + "", + "4. fourth bullet", + "", + "\tI. fifth bullet", + "", + "\t\tA. sixth bullet", + "", + "\t* seventh bullet", + "", + "\t\ti. eighth bullet", + "", + "\t\tii. ninth bullet", + "\t\t\t wrapped line", + "", + "\t\t\ta. tenth bullet", + "wrapped line without indent", + "", + "\tII. eleventh bullet", + "\t\t1. twelfth bullet", + "", + "\t\t\t* thirteenth bullet", + "", + "\t\t\t\t+ fourteenth bullet", + "", + "\t\t\ta. fifteenth bullet", + "", + "\t\t2. sixteenth bullet", + "", + "\t\t\t1. seventeenth bullet", + "", + "\t\t\t2. eighteenth bullet", + "\t\t\t\t wrapped line", + "", + "\tnormal indented line", + "next normal line", + "", + "1. nineteenth bullet", + "", + "x. twentieth bullet", + "", + "", + "i. twenty-first bullet", + "- twenty-second bullet", + "", + "", + "v. twenty-third bullet", + }, helpers.get_lines()) + end) + + it("visually renumbers a nested list", function() + vim.g.bullets_line_spacing = 2 + helpers.new_buffer({ + "# Hello there", + "0. zero bullet", + "", + "X. first bullet", + "", + "- second bullet", + "\twrapped line", + "", + "a. third bullet", + "", + "I. fourth bullet", + "", + "\tV. fifth bullet", + "", + "\t\tB. sixth bullet", + "", + "\t* seventh bullet", + "", + "\t\ti. eighth bullet", + "", + "\t\tx. ninth bullet", + "\t\t\t wrapped line", + "", + "\t\t\ta. tenth bullet", + "wrapped line without indent", + "", + "\tC. eleventh bullet", + "\t\t0. twelfth bullet", + "", + "\t\t\t* thirteenth bullet", + "", + "\t\t\t\t+ fourteenth bullet", + "", + "\t\t\td. fifteenth bullet", + "", + "\t\ta. sixteenth bullet", + "", + "\t\t\t8. seventeenth bullet", + "", + "\t\t\t0. eighteenth bullet", + "\t\t\t\t wrapped line", + "", + "\tnormal indented line", + "next normal line", + "", + "1. nineteenth bullet", + "", + "x. twentieth bullet", + "", + "", + "v. twenty-first bullet", + "- twenty-second bullet", + "", + "", + "v. twenty-third bullet", + }) + -- From line 1, go down 2 (to line 3), select to last line minus 1 (VGk), then renumber + helpers.feedkeys("gg2jVGkgN") + assert.are.same({ + "# Hello there", + "0. zero bullet", + "", + "I. first bullet", + "", + "- second bullet", + "\twrapped line", + "", + "II. third bullet", + "", + "III. fourth bullet", + "", + "\tI. fifth bullet", + "", + "\t\tA. sixth bullet", + "", + "\t* seventh bullet", + "", + "\t\ti. eighth bullet", + "", + "\t\tii. ninth bullet", + "\t\t\t wrapped line", + "", + "\t\t\ta. tenth bullet", + "wrapped line without indent", + "", + "\tII. eleventh bullet", + "\t\t1. twelfth bullet", + "", + "\t\t\t* thirteenth bullet", + "", + "\t\t\t\t+ fourteenth bullet", + "", + "\t\t\ti. fifteenth bullet", + "", + "\t\t2. sixteenth bullet", + "", + "\t\t\t1. seventeenth bullet", + "", + "\t\t\t2. eighteenth bullet", + "\t\t\t\t wrapped line", + "", + "\tnormal indented line", + "next normal line", + "", + "IV. nineteenth bullet", + "", + "V. twentieth bullet", + "", + "", + "VI. twenty-first bullet", + "- twenty-second bullet", + "", + "", + "v. twenty-third bullet", + }, helpers.get_lines()) + end) +end) diff --git a/test/wrapping_bullets_spec.lua b/test/wrapping_bullets_spec.lua new file mode 100644 index 0000000..08ba345 --- /dev/null +++ b/test/wrapping_bullets_spec.lua @@ -0,0 +1,45 @@ +local helpers = require("test.helpers") + +describe("wrapped bullets", function() + it("inserts a new bullet after a wrapped bullet", function() + helpers.test_bullet_inserted("do that", { + "# Hello there", + "- do this", + " this is the second line of the first bullet", + }, { + "# Hello there", + "- do this", + " this is the second line of the first bullet", + "- do that", + }) + end) + + it("does not insert wrapped bullets unnecessarily", function() + -- When is pressed on a non-bullet line the plugin defers the actual + -- newline via feedkeys('n'). Using two separate feedkeys calls ensures the + -- deferred CR fires (the 'x' flag drains it) before we type the next text. + vim.cmd("enew") + vim.bo.filetype = "text" + vim.api.nvim_buf_set_lines(0, 0, -1, false, { + "# Hello there", + "- do this", + " this is the second line of the first bullet", + "", + "no bullets after this line", + }) + vim.api.nvim_win_set_cursor(0, { 5, #"no bullets after this line" }) + -- feedkeys 'tx' exits insert mode after draining typeahead. After the first + -- call the plugin's deferred '\' has fired (new empty line 6) and we are + -- in normal mode. The second call uses 'i' to re-enter insert before typing. + helpers.feedkeys("A") + helpers.feedkeys("ido that") + assert.are.same({ + "# Hello there", + "- do this", + " this is the second line of the first bullet", + "", + "no bullets after this line", + "do that", + }, helpers.get_lines()) + end) +end)