Initial commit

This commit is contained in:
Steven Stallion 2021-04-18 16:15:17 -05:00
commit 6b0eda9724
24 changed files with 2782 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
*.egg-info/
*.py[co]
__pycache__/
/.coverage
/.pytest_cache
/build
/dist
/venv

41
.travis.yml Normal file
View file

@ -0,0 +1,41 @@
language: python
python:
- 2.7
- 3.6
- 3.7
- 3.8
- 3.9
install:
- pip install -e .[test]
script:
- pycodestyle
- pytest
jobs:
include:
- stage: deploy
if: tag ~= /^v(\d+\.){2}\d+(-\S+)?$/
python: 3.6
install:
- pip install .[deploy]
script:
- export RELEASE_NOTES=$(scripts/release_notes.py $TRAVIS_TAG)
deploy:
- provider: pypi
edge: true
username: $PYPI_USERNAME
password: $PYPI_PASSWORD
distributions: sdist bdist_wheel
remove_build_dir: false
on:
tags: true
- provider: releases
edge: true
token: $GITHUB_TOKEN
file: dist/*
release_notes: $RELEASE_NOTES
on:
tags: true

10
CHANGELOG.md Normal file
View file

@ -0,0 +1,10 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
[Unreleased]: https://github.com/sstallion/vimwiki-cli/compare/HEAD

71
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,71 @@
# Contributing
If you have an idea or feature request please open an [issue], even if you don't
have time to contribute!
## Making Changes
> **Note**: This guide assumes you have a working Python installation (most
> versions are supported) and have the Python Package Installer available in
> your PATH.
To get started, [fork] this repository on GitHub and clone a working copy for
development:
$ git clone git@github.com:YOUR-USERNAME/vimwiki-cli.git
Once cloned, change the directory to your working copy, create a new virtualenv,
and activate the environment:
$ virtualenv venv
$ source venv/bin/activate
> **Note**: If the `virtualenv` command is not available, it can be installed
> locally by issuing `pip install --user virtualenv`.
Dependencies are managed via `setuptools`; to set up the environment for
development, issue:
$ pip install -e ".[test]"
Once you are finished making changes, be sure to check the output of
`pycodestyle` and `pytest`. At a minimum, there should be no test regressions
and additional tests should be added for new functionality. If user-facing
changes are introduced, be sure add an entry to the `Unreleased` section in
[CHANGELOG.md] as well.
With that out of the way, you can now commit your changes and create a [pull
request] against the `master` branch for review!
## Making New Releases
Making new releases is automated by the Travis CI deployment pipeline. Releases
should only be created from the `master` branch; as such `master` should be
passing tests at all times.
To make a new release, follow these steps:
1. Install dependencies by issuing `pip install -e ".[deploy]"` in an existing
virtualenv (see above).
2. Verify `pycodestyle` and `pytest` are passing locally.
3. Increment the version number by issuing `bump2version <major|minor|patch>`.
4. Create a new section in [CHANGELOG.md] for the new version, and move items
from `Unreleased` to this section. Links should also be updated to point to
the correct tags for comparisons.
5. Verify release notes by issuing `scripts/release_notes.py v<version>` and
make adjustments as needed.
6. Commit changes by issuing `git commit -a -m "Release v<version>"`.
7. Create a release tag by issuing `git tag -a -m "Release v<version>"
v<version>`.
8. Push changes to `master` by issuing `git push origin master --tags`.
9. Verify Travis CI deploys successfully to PyPI and GitHub.
## License
By contributing to this repository, you agree that your contributions will be
licensed under its Simplified BSD License.
[CHANGELOG.md]: https://github.com/sstallion/vimwiki-cli/blob/master/CHANGELOG.md
[fork]: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo
[issue]: https://github.com/sstallion/vimwiki-cli/issues
[pull request]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request

23
LICENSE Normal file
View file

@ -0,0 +1,23 @@
Copyright (c) 2021 Steven Stallion
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

3
MANIFEST.in Normal file
View file

@ -0,0 +1,3 @@
include CHANGELOG.md
include CONTRIBUTING.md
include LICENSE

117
README.md Normal file
View file

@ -0,0 +1,117 @@
# Vimwiki Command-Line Interface
[![](https://img.shields.io/travis/com/sstallion/vimwiki-cli)][Travis]
[![](https://img.shields.io/pypi/v/vimwiki-cli)][PyPI]
[![](https://img.shields.io/pypi/pyversions/vimwiki-cli)][PyPI]
[![](https://img.shields.io/github/license/sstallion/vimwiki-cli)][LICENSE]
`vimwiki-cli` is a command-line interface to [Vimwiki], a plugin for the [Vim]
text editor. It provides a front-end for interactive editor commands and can be
used to automate repetitive tasks such as rebuilding tag metadata and generating
links, all from the command line.
[![](https://asciinema.org/a/411893.svg)](https://asciinema.org/a/411893?autoplay=1)
## Installation
`vimwiki-cli` is available on [PyPI] and can be installed locally using the
Python Package Installer:
$ pip install --user vimwiki-cli
Once installed, issue `vimwiki --help` to see usage.
## Configuration
For most installations no configuration is needed, however several environment
variables are available to modify default behavior without the need to pass
global options on the command line:
| Environment Variable | Global Option | Description |
|------------------------|------------------|---------------------------------------------------|
| `VIMWIKI_EDITOR` | `--editor` | Editor to launch, defaults to `$EDITOR` or `vim`. |
| `VIMWIKI_COUNT` | `--count` | Index of wiki to open. |
| `VIMWIKI_SELECT` | `--select` | Select wiki from interactive list. |
| `VIMWIKI_OPEN_MATCHES` | `--open-matches` | Open search results by default. |
| `VIMWIKI_OPEN_TABS` | `--open-tabs` | Open pages in a new tab by default. |
## Advanced
### Supported Commands
Each CLI command corresponds to one or more Ex commands executed in the editor.
The following table details the mapping between these commands:
| CLI Command | Ex Commands |
|-----------------------------------------|---------------------------------------------------------------------|
| `vimwiki` | `:VimwikiIndex` |
| `vimwiki check-links` | `:VimwikiIndex \| VimwikiCheckLinks` |
| `vimwiki diary` | `:VimwikiDiaryIndex` |
| `vimwiki diary generate-links` | `:VimwikiDiaryIndex \| VimwikiDiaryGenerateLinks` |
| `vimwiki diary today` | `:VimwikiMakeDiaryNote` |
| `vimwiki diary tomorrow` | `:VimwikiMakeTomorrowDiaryNote` |
| `vimwiki diary yesterday` | `:VimwikiMakeYesterdayDiaryNote` |
| `vimwiki generate-links PAGE PATTERN` | `:VimwikiIndex \| VimwikiGoto PAGE \| VimwikiGenerateLinks PATTERN` |
| `vimwiki goto PAGE` | `:VimwikiIndex \| VimwikiGoto PAGE` |
| `vimwiki help` | `:help vimwiki.txt \| only` |
| `vimwiki search PATTERN` | `:VimwikiIndex \| VimwikiSearch PATTERN` |
| `vimwiki tags generate-links PAGE TAGS` | `:VimwikiIndex \| VimwikiGoto PAGE \| VimwikiGenerateTagLinks TAGS` |
| `vimwiki tags rebuild` | `:VimwikiIndex \| VimwikiRebuildTags` |
| `vimwiki tags search PATTERN` | `:VimwikiIndex \| VimwikiSearchTags PATTERN` |
### Shell Completion
Shell completion is available for `bash`, `fish`, and `zsh` shells. To generate
an activation script, issue:
$ env _VIMWIKI_COMPLETE=source_$(basename $SHELL) vimwiki >/path/to/vimwiki-complete.sh
Once generated, the activation script may be sourced directly or from the shell
startup file to provide completion:
$ . /path/to/vimwiki-complete.sh
### Git Integration
For wikis managed with git, a pre-commit hook script is available that executes
non-interactive commands to rebuild tag metadata and generate links before
commit.
The pre-commit hook relies on the following configuration options:
| Configuration Option | Description |
|-------------------------------|------------------------------------------------|
| `vimwiki.options` | Extra options to pass to the `vimwiki` command |
| `vimwiki.linkspage` | Page which contains generated links |
| `vimwiki.taglinkspage` | Page which contains generated tag links |
| `vimwiki.generatelinks` | Generate links before commit (bool) |
| `vimwiki.generatediarylinks` | Generate diary links before commit (bool) |
| `vimwiki.generatetaglinks` | Generate tag links before commit (bool) |
| `vimwiki.rebuildtags` | Rebuild tag metadata before commit (bool) |
For example, to configure the hook to rebuild tag metadata and generate tag
links in the `Tags` page before commit, issue:
$ git config vimwiki.taglinkspage Tags
$ git config vimwiki.generatetaglinks true
$ git config vimwiki.rebuildtags true
To enable the hook, copy or link [pre-commit.sh] to `.git/hooks/pre-commit` in
the wiki directory.
## Contributing
Pull requests are welcome! See [CONTRIBUTING.md] for more details.
## License
Source code in this repository is licensed under a Simplified BSD License. See
[LICENSE] for more details.
[CONTRIBUTING.md]: https://github.com/sstallion/vimwiki-cli/blob/master/CONTRIBUTING.md
[LICENSE]: https://github.com/sstallion/vimwiki-cli/blob/master/LICENSE
[pre-commit.sh]: https://github.com/sstallion/vimwiki-cli/blob/master/scripts/pre-commit.sh
[PyPI]: https://pypi.org/project/vimwiki-cli/
[Travis]: https://travis-ci.com/sstallion/vimwiki-cli
[Vim]: https://www.vim.org/
[Vimwiki]: https://vimwiki.github.io/

982
docs/demo.cast Normal file
View file

@ -0,0 +1,982 @@
{"version": 2, "width": 100, "height": 30, "timestamp": 1620184051, "env": {"SHELL": "/bin/bash", "TERM": "screen-256color"}}
[0.0, "o", "> "]
[0.60998, "o", "p"]
[0.745986, "o", "i"]
[0.852597, "o", "p"]
[0.966771, "o", " "]
[1.177992, "o", "i"]
[1.288622, "o", "n"]
[1.406953, "o", "s"]
[1.504404, "o", "t"]
[1.638533, "o", "a"]
[1.775429, "o", "l"]
[1.91585, "o", "l"]
[2.047486, "o", " "]
[2.180287, "o", "v"]
[2.256772, "o", "i"]
[2.356053, "o", "m"]
[2.480626, "o", "w"]
[2.597993, "o", "i"]
[2.777366, "o", "k"]
[2.936913, "o", "i"]
[3.123016, "o", "-"]
[3.309962, "o", "c"]
[3.391171, "o", "l"]
[3.503335, "o", "i"]
[3.730464, "o", "\r\n"]
[4.565348, "o", "Collecting vimwiki-cli\r\n"]
[5.374813, "o", " Downloading https://files.pythonhosted.org/packages/b8/91/ac220c4c811b268515d55550fdd2dde0bba7db646735b31c6ac4f27cf46d/vimwiki_cli-1.0.0-py2.py3-none-any.whl\r\n"]
[5.504618, "o", "Collecting click~=7.1 (from vimwiki-cli)\r\n"]
[5.886197, "o", "\u001b[?25l"]
[5.88637, "o", " Downloading https://files.pythonhosted.org/packages/d2/3d/fa76db83bf75c4f8d338c2fd15c8d33fdd7ad23a9b5e57eb6c5de26b430e/click-7.1.2-py2.py3-none-any.whl (82kB)\r\n"]
[5.889762, "o", "\r\u001b[K |████ | 10kB 2.9MB/s eta 0:00:01"]
[5.893345, "o", "\r\u001b[K |████████ | 20kB 2.9MB/s eta 0:00:01"]
[5.897592, "o", "\r\u001b[K |███████████▉ | 30kB 2.7MB/s eta 0:00:01"]
[5.901686, "o", "\r\u001b[K |███████████████▉ | 40kB 2.7MB/s eta 0:00:01"]
[5.905329, "o", "\r\u001b[K |███████████████████▉ | 51kB 2.7MB/s eta 0:00:01"]
[5.909076, "o", "\r\u001b[K |███████████████████████▊ | 61kB 2.7MB/s eta 0:00:01"]
[5.913712, "o", "\r\u001b[K |███████████████████████████▊ | 71kB 2.6MB/s eta 0:00:01"]
[5.917773, "o", "\r\u001b[K |███████████████████████████████▊| 81kB 2.6MB/s eta 0:00:01"]
[5.918975, "o", "\r\u001b[K |████████████████████████████████| 92kB 2.8MB/s \r\n"]
[6.03118, "o", "\u001b[?25hInstalling collected packages: click, vimwiki-cli\r\n"]
[6.073511, "o", "Successfully installed click-7.1.2 vimwiki-cli-1.0.0\r\n"]
[6.142408, "o", "\u001bk> "]
[7.176374, "o", "v"]
[7.230336, "o", "\bvi"]
[7.326891, "o", "m"]
[7.414424, "o", "w"]
[7.541765, "o", "i"]
[7.70334, "o", "k"]
[7.867321, "o", "i"]
[7.998455, "o", " "]
[8.153415, "o", "s"]
[8.271593, "o", "e"]
[8.38011, "o", "a"]
[8.476485, "o", "r"]
[8.660425, "o", "c"]
[8.744664, "o", "h"]
[8.811553, "o", " "]
[8.952174, "o", "\""]
[9.142498, "o", "R"]
[9.321398, "o", "e"]
[9.418041, "o", "l"]
[9.52775, "o", "a"]
[9.606224, "o", "t"]
[9.699445, "o", "e"]
[9.869113, "o", "d"]
[10.027922, "o", " "]
[10.187112, "o", "T"]
[10.327914, "o", "o"]
[10.464903, "o", "o"]
[10.595828, "o", "l"]
[10.684412, "o", "s"]
[11.161151, "o", "\""]
[11.532045, "o", "\u001b[?2004l\r\r\n"]
[11.744421, "o", "\u001b[?1006h\u001b[?1002h\u001b[?1049h\u001b[22;0;0t\u001b[?1004h\u001b[?2004h\u001b[>4;2m\u001b[?1h\u001b=\u001b[?2004h\u001b[1;30r\u001b[?12h\u001b[?12l\u001b[22;2t\u001b[22;1t"]
[11.745131, "o", "\u001b[27m\u001b[23m\u001b[29m\u001b[m\u001b[37m\u001b[H\u001b[2J"]
[11.754708, "o", "\u001b[?25l\u001b[30;1H\"~/.vimwiki/wiki/index.wiki\" 44L, 1532C"]
[11.802697, "o", "\u001b[2 q"]
[11.886064, "o", "\u001b[2;1H▽\u001b[6n\u001b[2;1H \u001b[1;1H\u001b[>c\u001b]10;?\u0007\u001b]11;?\u0007"]
[11.891861, "o", "\u001b[1;1H\u001b[1m\u001b[97m\u001b[44m 1 Related Tools.wiki \u001b[m\u001b[37m\u001b[34m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[1;24H\u001b[38;5;254m\u001b[48;5;234m \u001b[m\u001b[37m\u001b[1;98H\u001b[38;5;234m\u001b[48;5;254m X \u001b[m\u001b[37m\u001b[2;1H\u001b[37m 1 \u001b[m\u001b[37m\u001b[48;5;234m= Related Tools = \u001b[m\u001b[37m\u001b[3;1H\u001b[90m 2 \r\n 3 \u001b[m\u001b[37mThis page contains Vim plugins and external tools that can be used with Vimwiki.\r\n\u001b[90m 4 \u001b[m\u001b[37mThese lists are incomplete so please _\u001b[3mhelp update them\u001b[23m\u001b[37m_ if you know of something\r\n\u001b[90m 5 \u001b[m\u001b[37mthat is missing!\r\n\u001b[90m 6 \r\n 7 \u001b[m\u001b[37m== Vim Plugins ==\r\n\u001b[90m 8 \r\n 9 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/mattn/calendar-vim|calendar-vim]]\u001b[m\u001b[37m\r\n\u001b[90m 10 \u001b[m\u001b[37m - Select a date to open a diary page.\r\n\u001b[90m 11 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/tbabej/taskwiki|taskwiki]]\u001b[m\u001b[37m\r\n\u001b[90m 12 \u001b[m\u001b[37m - Integration with \u001b[4m\u001b[94m[[https://taskwarrior.org/|taskwarrior]]\u001b[m\u001b[37m for task\r\n\u001b[90"]
[11.891884, "o", "m 13 \u001b[m\u001b[37m management. This only supports the default syntax.\r\n\u001b[90m 14 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/majutsushi/tagbar|Tagbar]]\u001b[m\u001b[37m\r\n\u001b[90m 15 \u001b[m\u001b[37m - Creates a sidebar to view generated tags. For Vimwiki this can be\r\n\u001b[90m 16 \u001b[m\u001b[37m used to display links to headers.\r\n\u001b[90m 17 \u001b[m\u001b[37m - For this to work an \u001b[4m\u001b[94m[[https://raw.githubusercontent.com/vimwiki/utils/master/vwtags.py|add\u001b[m\u001b[37m\u001b[19;1H\u001b[90m 18 \u001b[m\u001b[37m is needed which is available in the \u001b[4m\u001b[94m[[https://github.com/vimwiki/utils|utility repository]\u001b[m\u001b[37m\u001b[20;1H\u001b[90m 19 \u001b[m\u001b[37m Additional instructions are in the comments of the script.\r\n\u001b[90m 20 \u001b[m\u001b[37m - If using Markdown syntax `\u001b[34m#\u001b[m\u001b[37m` symbols used within code blocks are\r\n\u001b[90m 21 \u001b[m\u001b[37m recognized as a header i.e. in a shell script snippet. An alternative\r\n\u001b[90m 22 \u001b[m\u001b[37m version that works for Markdown can be found\r\n\u001b[90m 23 \u001b[m\u001b[37m "]
[11.892855, "o", "\u001b[4m\u001b[94m[[https://raw.githubusercontent.com/jszakmeister/markdown2ctags/master/markdown2ctags.py|h\u001b[m\u001b[37m\u001b[25;1H\u001b[90m 24 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/teranex/vimwiki-tasks|vimwiki-tasks]]\u001b[m\u001b[37m\r\n\u001b[90m 25 \u001b[m\u001b[37m - Another integration with \u001b[4m\u001b[94m[[https://taskwarrior.org/|taskwarrior]]\u001b[m\u001b[37m. This\r\n\u001b[90m 26 \u001b[m\u001b[37m plugin does not seem to be maintained any longer.\r\n\u001b[90m 27 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/matt-snider/vim-tagquery|vim-tagquery]]"]
[11.893512, "o", "\u001b[m\u001b[37m\r\n\u001b[1m\u001b[97m\u001b[44m NORMAL \u001b[m\u001b[37m\u001b[34m\u001b[48;5;238m\u001b[m\u001b[37m\u001b[29;10H\u001b[38;5;254m\u001b[48;5;238m \u001b[29;12H master \u001b[m\u001b[37m\u001b[38;5;238m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;21H\u001b[38;5;254m\u001b[48;5;234m wiki/Related Tools.wiki \u001b[m\u001b[37m\u001b[38;5;234m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;47H\u001b[38;5;254m\u001b[48;5;234m \u001b[m\u001b[37m\u001b[38;5;234m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;83H\u001b[38;5;238m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;84H\u001b[38;5;254m\u001b[48;5;238m 1% \u001b[m\u001b[37m\u001b[29;91H\u001b[38;5;234m\u001b[48;5;254m \u001b[29;93H 1:3 \u001b]2;Related Tools.wiki (~/.vimwiki/wiki) - VIM\u0007"]
[11.8942, "o", "\u001b[2;7H\u001b[?25h"]
[11.900501, "o", "\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[11.901584, "o", "\u001b[?2004h\u001b[?1006h\u001b[?1002h\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[11.903685, "o", "\u001b[?2004h\u001b[?1006h\u001b[?1002h\u001b[?2004h"]
[11.904302, "o", "\u001b]2;Related Tools.wiki (~/.vimwiki/wiki) - VIM\u0007"]
[12.799674, "o", "\u001b[?25l\u001b[m\u001b[37m\r\u001b[90m 38 \u001b[m\u001b[37m - Convert Vimwiki to various other formats such as HTML, PDF, etc.\u001b[2;75H\u001b[K\u001b[3;2H\u001b[90m39\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/patrickdavey/vimwiki_markdown|vimwiki_markdown]]\u001b[m\u001b[37m\u001b[4;2H\u001b[90m40\u001b[m\u001b[37m\u001b[1C - A Ruby gem to convert vimwiki Markdown files to HTML. Still being actively\u001b[5;2H\u001b[90m41\u001b[m\u001b[37m\u001b[1C developed.\u001b[5;19H\u001b[K\u001b[6;2H\u001b[90m42\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/WnP/vimwiki_markdown|vimwiki_markdown Python]]\u001b[m\u001b[37m\u001b[7;2H\u001b[90m43\u001b[m\u001b[37m\u001b[5C- A Python script to convert Markdown files to HTML.\u001b[8;2H\u001b[90m44\u001b[m\u001b[37m\u001b[1C - Also see \u001b[4m\u001b[94m[[https://github.com/vimwiki/vimwiki/issues/578|Issue #578]]\u001b[m\u001b[37m\u001b[9;2H\u001b[90m45\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/maqiv/vimwiki-godown#todo|vimwiki-godown]]\u001b[m\u001b[37m\u001b[10;2H\u001b[90m46\u001b[m\u001b[37m\u001b[1C - HTML converter that adds the ability to prefix relative links to other\u001b[11;2H\u001b[90m47\u001b[m\u001b[37m\u001b[5C Vimwiki pages\u001b[11;25H\u001b[K\u001b[12;2H\u001b[90m48\u001b[m\u001b[37m\u001b[1C - See \u001b[4m\u001b[94m[[https://github.com/vimwiki/vimwi"]
[12.799954, "o", "ki/issues/284|Issue #284]]\u001b[m\u001b[37m\u001b[13;2H\u001b[90m49\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/sstallion/vimwiki-skel|vimwiki-skel]]\u001b[m\u001b[37m\u001b[13;65H\u001b[K\u001b[14;2H\u001b[90m50\u001b[m\u001b[37m\u001b[5C- Uses \u001b[4m\u001b[94m[[https://dynalon.github.io/mdwiki/#!index.md|MDWiki]]\u001b[m\u001b[37m to convert\u001b[15;2H\u001b[90m51\u001b[m\u001b[37m\u001b[1C Markdown files to HTML.\u001b[15;32H\u001b[K\u001b[16;2H\u001b[90m52\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://gist.github.com/maikeldotuk/54a91c21ed9623705fdce7bab2989742|wiki2html.sh Gist]]\u001b[m\u001b[37m\u001b[17;2H\u001b[90m53\u001b[m\u001b[37m\u001b[5C- Uses Pandoc to convert Markdown files to HTML.\u001b[18;2H\u001b[90m54\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/fasheng/vimwiki2org|vimwiki2org]]\u001b[m\u001b[37m\u001b[18;61H\u001b[K\u001b[19;2H\u001b[90m55\u001b[m\u001b[37m\u001b[5C- Convert vimwiki to \u001b[4m\u001b[94m[[https://orgmode.org/|Emacs orgmode]]\u001b[m\u001b[37m. Has not been\u001b[19;82H\u001b[K\u001b[20;2H\u001b[90m56\u001b[m\u001b[37m\u001b[5Cupdated in 6 years.\u001b[20;28H\u001b[K\u001b[21;2H\u001b[90m57\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/ycpei/vwweb|vwweb]]\u001b[m\u001b[37m\u001b[21;48H\u001b[K\u001b[22;2H\u001b[90m58\u001b[m\u001b[37m"]
[12.804394, "o", "\u001b[5C- Python script to generate a website from Vimwiki files.\u001b[22;66H\u001b[K\u001b[23;2H\u001b[90m59\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://box.matto.nl/vimwikijabberbot.html|vimwiki-todo-jabberbot]]\u001b[m\u001b[37m\u001b[24;2H\u001b[90m60\u001b[m\u001b[37m\u001b[5C- Todo management using Jabberbot. The linked GitHub repository seems to\u001b[24;81H\u001b[K\u001b[25;2H\u001b[90m61\u001b[m\u001b[37m\u001b[1C have been removed.\u001b[25;27H\u001b[K\u001b[26;2H\u001b[90m62\u001b[m\u001b[37m\u001b[1C- \u001b[4m\u001b[94m[[https://github.com/zweifisch/vimwiki-tools|vimwiki-tools]]\u001b[m\u001b[37m\u001b[26;67H\u001b[K\u001b[27;2H\u001b[90m63\u001b[m\u001b[37m\u001b[5C- Python tool to generate an index and convert wiki files to Markdown\r\n\u001b[37m 64 \u001b[m\u001b[37m\u001b[48;5;234m format. This has not been updated in 6 years. "]
[12.805283, "o", "\u001b[m\u001b[37m\u001b[29;85H\u001b[38;5;254m\u001b[48;5;238m100\u001b[m\u001b[37m\u001b[7C\u001b[38;5;234m\u001b[48;5;254m64:5\u001b[28;9H\u001b[?25h"]
[13.082274, "o", "\u001b[?2004h"]
[13.095707, "o", "\u001b[?25l\u001b[6 q\u001b[2 q\u001b[6 q"]
[13.120049, "o", "\u001b[m\u001b[37m\u001b[1;23H\u001b[1m\u001b[97m\u001b[44m[+] \u001b[m\u001b[37m\u001b[34m\u001b[48;5;234m\u001b[2;28r\u001b[m\u001b[37m\u001b[28;1H\r\n\u001b[1;30r\u001b[27;1H\u001b[90m 64 \u001b[m\u001b[37m format. This has not been updated in 6 years.\u001b[27;54H\u001b[K\u001b[28;1H\u001b[37m 65 \u001b[m\u001b[37m\u001b[48;5;234m- "]
[13.122213, "o", "\u001b[m\u001b[37m\u001b[29;1H\u001b[1m\u001b[97m\u001b[42m INSERT \u001b[m\u001b[37m\u001b[32m\u001b[48;5;238m\u001b[m\u001b[37m\u001b[29;46H\u001b[38;5;254m\u001b[48;5;234m[+]\u001b[m\u001b[37m\u001b[1C\u001b[38;5;234m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;96H\u001b[38;5;234m\u001b[48;5;254m5:3\u001b[m\u001b[37m\u001b[30;1H\u001b[K"]
[13.123258, "o", "\u001b]2;Related Tools.wiki + (~/.vimwiki/wiki) - VIM\u0007\u001b[28;7H\u001b[?25h"]
[13.254309, "o", "\u001b[?25l\u001b[2;2H\u001b[90m 39 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/patrickdavey/vimwiki_markdown|vimwiki_markdown]]\u001b[m\u001b[37m\u001b[3;2H\u001b[90m 40 \u001b[m\u001b[37m - A Ruby gem to convert vimwiki Markdown files to HTML. Still being actively\u001b[4;2H\u001b[90m 41 \u001b[m\u001b[37m developed.\u001b[5;2H\u001b[90m 42 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/WnP/vimwiki_markdown|vimwiki_markdown Python]]\u001b[m\u001b[37m\u001b[6;2H\u001b[90m 43 \u001b[m\u001b[37m - A Python script to convert Markdown files to HTML.\u001b[7;2H\u001b[90m 44 \u001b[m\u001b[37m - Also see \u001b[4m\u001b[94m[[https://github.com/vimwiki/vimwiki/issues/578|Issue #578]]\u001b[m\u001b[37m\u001b[8;2H\u001b[90m 45 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/maqiv/vimwiki-godown#todo|vimwiki-godown]]\u001b[m\u001b[37m\u001b[9;2H\u001b[90m 46 \u001b[m\u001b[37m - HTML converter that adds the ability to prefix relative links to other\u001b[10;2H\u001b[90m 47 \u001b[m\u001b[37m Vimwiki pages\u001b[11;2H\u001b[90m 48 \u001b[m\u001b[37m - See \u001b[4m\u001b[94m[[https://github.com/vimwiki/vimwiki/issues/284|Issue #284]]\u001b[m\u001b[37m\u001b[12;2H\u001b[90m 49 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/sstallion/vimwiki-skel|vimwiki-skel]]\u001b["]
[13.254557, "o", "m\u001b[37m\u001b[13;2H\u001b[90m 50 \u001b[m\u001b[37m - Uses \u001b[4m\u001b[94m[[https://dynalon.github.io/mdwiki/#!index.md|MDWiki]]\u001b[m\u001b[37m to convert\u001b[14;2H\u001b[90m 51 \u001b[m\u001b[37m Markdown files to HTML.\u001b[15;2H\u001b[90m 52 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://gist.github.com/maikeldotuk/54a91c21ed9623705fdce7bab2989742|wiki2html.sh Gist]]\u001b[m\u001b[37m\u001b[16;2H\u001b[90m 53 \u001b[m\u001b[37m - Uses Pandoc to convert Markdown files to HTML.\u001b[17;2H\u001b[90m 54 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/fasheng/vimwiki2org|vimwiki2org]]\u001b[m\u001b[37m\u001b[18;2H\u001b[90m 55 \u001b[m\u001b[37m - Convert vimwiki to \u001b[4m\u001b[94m[[https://orgmode.org/|Emacs orgmode]]\u001b[m\u001b[37m. Has not been\u001b[19;2H\u001b[90m 56 \u001b[m\u001b[37m updated in 6 years.\u001b[20;2H\u001b[90m 57 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/ycpei/vwweb|vwweb]]\u001b[m\u001b[37m\u001b[21;2H\u001b[90m 58 \u001b[m\u001b[37m - Python script to generate a website from Vimwiki files.\u001b[22;2H\u001b[90m 59 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://box.matto.nl/vimwikijabberbot.html|vimwiki-todo-jabberbot]]\u001b[m\u001b[37m\u001b[23;2H\u001b[90m 60 "]
[13.257274, "o", "\u001b[m\u001b[37m - Todo management using Jabberbot. The linked GitHub repository seems to\u001b[24;2H\u001b[90m 61 \u001b[m\u001b[37m have been removed.\u001b[25;2H\u001b[90m 62 \u001b[m\u001b[37m- \u001b[4m\u001b[94m[[https://github.com/zweifisch/vimwiki-tools|vimwiki-tools]]\u001b[m\u001b[37m\u001b[26;2H\u001b[90m 63 \u001b[m\u001b[37m - Python tool to generate an index and convert wiki files to Markdown\u001b[27;2H\u001b[90m 64 \u001b[m\u001b[37m format. This has not been updated in 6 years.\r\n\u001b[32m+ \u001b[m\u001b[37m\u001b[37m 65 \u001b[m\u001b[37m\u001b[48;5;234m-"]
[13.257845, "o", " \u001b[?25h"]
[13.718683, "o", "\u001b[?25l["]
[13.719307, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;10H\u001b[?25h"]
[13.833413, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m["]
[13.83416, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;11H\u001b[?25h"]
[13.858612, "o", "\u001b[?25l"]
[13.85918, "o", "\u001b[?25h"]
[14.101624, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mh"]
[14.102287, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;12H\u001b[?25h"]
[14.239848, "o", "\u001b[?25l"]
[14.240646, "o", "\u001b[?25h"]
[14.243203, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[14.244016, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;13H\u001b[?25h"]
[14.380137, "o", "\u001b[?25l"]
[14.380978, "o", "\u001b[?25h"]
[14.383572, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[14.384283, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;14H\u001b[?25h"]
[14.483423, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mp"]
[14.484288, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;15H\u001b[?25h"]
[14.596427, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[14.597193, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m10\u001b[28;16H\u001b[?25h"]
[14.627297, "o", "\u001b[?25l"]
[14.627851, "o", "\u001b[?25h"]
[14.788174, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m:"]
[14.788897, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;17H\u001b[?25h"]
[14.923514, "o", "\u001b[?25l"]
[14.924367, "o", "\u001b[?25h"]
[14.982904, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m/"]
[14.983874, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;18H\u001b[?25h"]
[15.104467, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m/"]
[15.105608, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;19H\u001b[?25h"]
[15.129053, "o", "\u001b[?25l"]
[15.129979, "o", "\u001b[?25h"]
[15.226079, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[28;11H\u001b[4m\u001b[94m\u001b[48;5;234mhttps://g"]
[15.227135, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;20H\u001b[?25h"]
[15.339252, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[15.340174, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;21H\u001b[?25h"]
[15.369903, "o", "\u001b[?25l"]
[15.370549, "o", "\u001b[?25h"]
[15.417769, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mt"]
[15.418601, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;22H\u001b[?25h"]
[15.518466, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mh"]
[15.519577, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;23H\u001b[?25h"]
[15.664213, "o", "\u001b[?25l"]
[15.665147, "o", "\u001b[?25h"]
[15.685513, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mu"]
[15.686286, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;24H\u001b[?25h"]
[15.822897, "o", "\u001b[?25l"]
[15.823797, "o", "\u001b[?25h"]
[15.874037, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mb"]
[15.875094, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;25H\u001b[?25h"]
[16.018851, "o", "\u001b[?25l"]
[16.01973, "o", "\u001b[?25h"]
[16.02268, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m."]
[16.023755, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m20\u001b[28;26H\u001b[?25h"]
[16.168403, "o", "\u001b[?25l"]
[16.169318, "o", "\u001b[?25h"]
[16.172196, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mc"]
[16.173121, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;27H\u001b[?25h"]
[16.248369, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mo"]
[16.249411, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;28H\u001b[?25h"]
[16.359219, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mm"]
[16.360317, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;29H\u001b[?25h"]
[16.392307, "o", "\u001b[?25l"]
[16.39292, "o", "\u001b[?25h"]
[16.529773, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m/"]
[16.530903, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;30H\u001b[?25h"]
[16.647357, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ms"]
[16.648303, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;31H\u001b[?25h"]
[16.674333, "o", "\u001b[?25l"]
[16.674899, "o", "\u001b[?25h"]
[16.775362, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ms"]
[16.776053, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;32H\u001b[?25h"]
[16.916445, "o", "\u001b[?25l"]
[16.917188, "o", "\u001b[?25h"]
[16.952969, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mt"]
[16.953932, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;33H\u001b[?25h"]
[17.042838, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ma"]
[17.043856, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;34H\u001b[?25h"]
[17.113866, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ml"]
[17.114902, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;35H\u001b[?25h"]
[17.260385, "o", "\u001b[?25l"]
[17.261291, "o", "\u001b[?25h"]
[17.264581, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ml"]
[17.265628, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m30\u001b[28;36H\u001b[?25h"]
[17.379073, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[17.379894, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;37H\u001b[?25h"]
[17.400611, "o", "\u001b[?25l"]
[17.401187, "o", "\u001b[?25h"]
[17.514106, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mo"]
[17.515067, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;38H\u001b[?25h"]
[17.617374, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mn"]
[17.618372, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;39H\u001b[?25h"]
[17.76534, "o", "\u001b[?25l"]
[17.766312, "o", "\u001b[?25h"]
[17.770618, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m/"]
[17.771801, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;40H\u001b[?25h"]
[17.90684, "o", "\u001b[?25l"]
[17.907615, "o", "\u001b[?25h"]
[17.910602, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mv"]
[17.911383, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;41H\u001b[?25h"]
[18.032803, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[18.033755, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;42H\u001b[?25h"]
[18.057936, "o", "\u001b[?25l"]
[18.058859, "o", "\u001b[?25h"]
[18.109076, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mm"]
[18.109917, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;43H\u001b[?25h"]
[18.200535, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mw"]
[18.201645, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;44H\u001b[?25h"]
[18.342655, "o", "\u001b[?25l"]
[18.343328, "o", "\u001b[?25h"]
[18.345604, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[18.346392, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;45H\u001b[?25h"]
[18.481116, "o", "\u001b[?25l"]
[18.481999, "o", "\u001b[?25h"]
[18.514306, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mk"]
[18.51506, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m40\u001b[28;46H\u001b[?25h"]
[18.65003, "o", "\u001b[?25l"]
[18.650641, "o", "\u001b[?25h"]
[18.667918, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[18.668703, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;47H\u001b[?25h"]
[18.812119, "o", "\u001b[?25l"]
[18.812931, "o", "\u001b[?25h"]
[18.869526, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m-"]
[18.870426, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;48H\u001b[?25h"]
[19.014003, "o", "\u001b[?25l"]
[19.014768, "o", "\u001b[?25h"]
[19.101556, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mc"]
[19.102269, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;49H\u001b[?25h"]
[19.202537, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ml"]
[19.203514, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;50H\u001b[?25h"]
[19.274439, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[19.275394, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;51H\u001b[?25h"]
[19.420001, "o", "\u001b[?25l"]
[19.420785, "o", "\u001b[?25h"]
[19.553207, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m|"]
[19.55409, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;52H\u001b[?25h"]
[19.699703, "o", "\u001b[?25l"]
[19.700532, "o", "\u001b[?25h"]
[19.766485, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mv"]
[19.767299, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;53H\u001b[?25h"]
[19.863475, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[19.864491, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;54H\u001b[?25h"]
[19.962483, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mm"]
[19.963459, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;55H\u001b[?25h"]
[20.037153, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mw"]
[20.038167, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m50\u001b[28;56H\u001b[?25h"]
[20.182541, "o", "\u001b[?25l"]
[20.183353, "o", "\u001b[?25h"]
[20.186509, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[20.187328, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;57H\u001b[?25h"]
[20.326716, "o", "\u001b[?25l"]
[20.327351, "o", "\u001b[?25h"]
[20.332072, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mk"]
[20.332729, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;58H\u001b[?25h"]
[20.465183, "o", "\u001b[?25l"]
[20.465752, "o", "\u001b[?25h"]
[20.471257, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[20.47191, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;59H\u001b[?25h"]
[20.601798, "o", "\u001b[?25l"]
[20.602446, "o", "\u001b[?25h"]
[20.65048, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m-"]
[20.651204, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;60H\u001b[?25h"]
[20.786104, "o", "\u001b[?25l"]
[20.786777, "o", "\u001b[?25h"]
[20.825157, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mc"]
[20.826096, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;61H\u001b[?25h"]
[20.926158, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234ml"]
[20.927096, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;62H\u001b[?25h"]
[21.036513, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234mi"]
[21.037601, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;63H\u001b[?25h"]
[21.069801, "o", "\u001b[?25l"]
[21.070747, "o", "\u001b[?25h"]
[21.451898, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m]"]
[21.452853, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;10H\u001b[?25h"]
[21.584144, "o", "\u001b[?25l\u001b[m\u001b[37m\b\u001b[4m\u001b[94m\u001b[48;5;234m[[\u001b[53C]"]
[21.585104, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;9H\u001b[?25h"]
[21.821301, "o", "\u001b[2 q\u001b[?25l\u001b[m\u001b[37m\u001b[4m\u001b[38;5;202m\u001b[48;5;234m[\u001b[54C]"]
[21.822184, "o", "\u001b[m\u001b[37m\r\n\u001b[1m\u001b[97m\u001b[44m NORMAL \u001b[m\u001b[37m\u001b[34m\u001b[48;5;238m\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;64H\u001b[?25h\u001b[?2004h"]
[21.845278, "o", "\u001b[?25l\u001b[6 q\u001b[2 q\u001b[6 q\u001b[2;28r\u001b[m\u001b[37m\u001b[28;1H\r\n\u001b[1;30r\u001b[27;3H\u001b[90m 65 \u001b[m\u001b[37m- \u001b[4m\u001b[38;5;202m[\u001b[m\u001b[37m\u001b[4m\u001b[94m[https://github.com/sstallion/vimwiki-cli|vimwiki-cli]\u001b[m\u001b[37m\u001b[4m\u001b[38;5;202m]\u001b[m\u001b[37m\u001b[27;65H\u001b[K\u001b[28;1H\u001b[90m \u001b[m\u001b[37m\u001b[37m 66 \u001b[m\u001b[37m\u001b[48;5;234m- \u001b[m\u001b[37m\u001b[27;9H\u001b[4m\u001b[94m[\u001b[54C]"]
[21.857314, "o", "\u001b[m\u001b[37m\r\n\r\n\u001b[1m\u001b[97m\u001b[42m INSERT \u001b[m\u001b[37m\u001b[32m\u001b[48;5;238m\u001b[m\u001b[37m\u001b[29;96H\u001b[38;5;234m\u001b[48;5;254m6:3 \u001b[28;9H\u001b[?25h"]
[21.985458, "o", "\u001b[?25l\u001b[m\u001b[37m\r\u001b[32m+ "]
[21.985892, "o", "\u001b[6C\u001b[?25h"]
[22.158851, "o", "\u001b[?25l"]
[22.159435, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;8H\u001b[?25h"]
[22.282468, "o", "\u001b[?25l"]
[22.282935, "o", "\u001b[?25h"]
[22.291777, "o", "\u001b[?25l\u001b[m\u001b[37m\b\u001b[48;5;234m "]
[22.292338, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;7H\u001b[?25h"]
[22.417065, "o", "\u001b[?25l"]
[22.417635, "o", "\u001b[?25h"]
[22.647053, "o", "\u001b[?25l"]
[22.647749, "o", "\u001b[29;98H2\u001b[28;8H\u001b[?25h"]
[22.773459, "o", "\u001b[?25l"]
[22.77395, "o", "\u001b[?25h"]
[22.799658, "o", "\u001b[?25l"]
[22.800116, "o", "\u001b[29;98H3\u001b[28;9H\u001b[?25h"]
[22.928828, "o", "\u001b[?25l"]
[22.929281, "o", "\u001b[?25h"]
[22.973683, "o", "\u001b[?25l"]
[22.974481, "o", "\u001b[29;98H4\u001b[28;10H\u001b[?25h"]
[23.108678, "o", "\u001b[?25l"]
[23.109193, "o", "\u001b[?25h"]
[23.116138, "o", "\u001b[?25l"]
[23.116776, "o", "\u001b[29;98H5\u001b[28;11H\u001b[?25h"]
[23.251007, "o", "\u001b[?25l"]
[23.251647, "o", "\u001b[?25h"]
[23.26059, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m-"]
[23.261317, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;12H\u001b[?25h"]
[23.392222, "o", "\u001b[?25l"]
[23.392803, "o", "\u001b[?25h"]
[23.430321, "o", "\u001b[?25l"]
[23.430991, "o", "\u001b[29;98H7\u001b[28;13H\u001b[?25h"]
[23.564002, "o", "\u001b[?25l"]
[23.56455, "o", "\u001b[?25h"]
[23.721707, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mP"]
[23.72243, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;14H\u001b[?25h"]
[23.858413, "o", "\u001b[?25l"]
[23.859288, "o", "\u001b[?25h"]
[23.91229, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234my"]
[23.913147, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;15H\u001b[?25h"]
[24.052934, "o", "\u001b[?25l"]
[24.053566, "o", "\u001b[?25h"]
[24.055655, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[24.05628, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m10\u001b[28;16H\u001b[?25h"]
[24.142255, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mh"]
[24.143143, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;17H\u001b[?25h"]
[24.287582, "o", "\u001b[?25l"]
[24.288341, "o", "\u001b[?25h"]
[24.300208, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[24.301059, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;18H\u001b[?25h"]
[24.368743, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[24.369768, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;19H\u001b[?25h"]
[24.442869, "o", "\u001b[?25l"]
[24.443772, "o", "\u001b[29;99H4\u001b[28;20H\u001b[?25h"]
[24.586744, "o", "\u001b[?25l"]
[24.587483, "o", "\u001b[?25h"]
[24.634367, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mC"]
[24.635223, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;21H\u001b[?25h"]
[24.706561, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mL"]
[24.707477, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;22H\u001b[?25h"]
[24.785985, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mI"]
[24.786876, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;23H\u001b[?25h"]
[24.931715, "o", "\u001b[?25l"]
[24.93248, "o", "\u001b[?25h"]
[25.004825, "o", "\u001b[?25l"]
[25.005795, "o", "\u001b[29;99H8\u001b[28;24H\u001b[?25h"]
[25.152195, "o", "\u001b[?25l"]
[25.152968, "o", "\u001b[?25h"]
[25.155793, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[25.156612, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;25H\u001b[?25h"]
[25.301767, "o", "\u001b[?25l"]
[25.302571, "o", "\u001b[?25h"]
[25.683864, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mh"]
[25.684754, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m20\u001b[28;26H\u001b[?25h"]
[25.826425, "o", "\u001b[?25l"]
[25.827484, "o", "\u001b[?25h"]
[25.879085, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[25.880277, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;27H\u001b[?25h"]
[25.997462, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[25.998498, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;28H\u001b[?25h"]
[26.024159, "o", "\u001b[?25l"]
[26.02497, "o", "\u001b[?25h"]
[26.094299, "o", "\u001b[?25l"]
[26.095371, "o", "\u001b[29;99H3\u001b[28;29H\u001b[?25h"]
[26.240471, "o", "\u001b[?25l"]
[26.241175, "o", "\u001b[?25h"]
[26.246653, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mp"]
[26.247571, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;30H\u001b[?25h"]
[26.319458, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[26.320309, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;31H\u001b[?25h"]
[26.413461, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[26.414434, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;32H\u001b[?25h"]
[26.556753, "o", "\u001b[?25l"]
[26.557557, "o", "\u001b[?25h"]
[26.5599, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mv"]
[26.560537, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;33H\u001b[?25h"]
[26.612955, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[26.613839, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;34H\u001b[?25h"]
[26.759828, "o", "\u001b[?25l"]
[26.760584, "o", "\u001b[?25h"]
[26.766508, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[26.767376, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;35H\u001b[?25h"]
[26.912092, "o", "\u001b[?25l"]
[26.912923, "o", "\u001b[?25h"]
[26.94998, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[26.951015, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m30\u001b[28;36H\u001b[?25h"]
[27.096894, "o", "\u001b[?25l"]
[27.097748, "o", "\u001b[?25h"]
[27.170984, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[27.171915, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;37H\u001b[?25h"]
[27.318116, "o", "\u001b[?25l"]
[27.319019, "o", "\u001b[?25h"]
[27.321883, "o", "\u001b[?25l"]
[27.322834, "o", "\u001b[29;99H2\u001b[28;38H\u001b[?25h"]
[27.457875, "o", "\u001b[?25l"]
[27.458718, "o", "\u001b[?25h"]
[27.481838, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[27.482593, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;39H\u001b[?25h"]
[27.620538, "o", "\u001b[?25l"]
[27.621348, "o", "\u001b[?25h"]
[27.624368, "o", "\u001b[?25l"]
[27.625126, "o", "\u001b[29;99H4\u001b[28;40H\u001b[?25h"]
[27.706153, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mf"]
[27.707131, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;41H\u001b[?25h"]
[27.852986, "o", "\u001b[?25l"]
[27.85375, "o", "\u001b[?25h"]
[27.891957, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[27.892895, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;42H\u001b[?25h"]
[27.972714, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[27.973609, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;43H\u001b[?25h"]
[28.043884, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[28.04486, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;44H\u001b[?25h"]
[28.118409, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[28.119425, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;45H\u001b[?25h"]
[28.262478, "o", "\u001b[?25l"]
[28.263441, "o", "\u001b[?25h"]
[28.266435, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m-"]
[28.267342, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m40\u001b[28;46H\u001b[?25h"]
[28.3326, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[28.333419, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;47H\u001b[?25h"]
[28.468519, "o", "\u001b[?25l"]
[28.469294, "o", "\u001b[?25h"]
[28.471794, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[28.472674, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;48H\u001b[?25h"]
[28.541486, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[28.5423, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;49H\u001b[?25h"]
[28.681787, "o", "\u001b[?25l"]
[28.682849, "o", "\u001b[?25h"]
[28.685888, "o", "\u001b[?25l"]
[28.686908, "o", "\u001b[29;99H4\u001b[28;50H\u001b[?25h"]
[28.77196, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mf"]
[28.773391, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;51H\u001b[?25h"]
[28.88365, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[28.884614, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;52H\u001b[?25h"]
[28.918772, "o", "\u001b[?25l"]
[28.919746, "o", "\u001b[?25h"]
[28.95416, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[28.955052, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;53H\u001b[?25h"]
[29.025358, "o", "\u001b[?25l"]
[29.026269, "o", "\u001b[29;99H8\u001b[28;54H\u001b[?25h"]
[29.088469, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[29.089403, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;55H\u001b[?25h"]
[29.154716, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[29.155791, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m50\u001b[28;56H\u001b[?25h"]
[29.208804, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[29.209864, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;57H\u001b[?25h"]
[29.328828, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[29.329821, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;58H\u001b[?25h"]
[29.35604, "o", "\u001b[?25l"]
[29.356944, "o", "\u001b[?25h"]
[29.571991, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[29.573086, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;59H\u001b[?25h"]
[29.718752, "o", "\u001b[?25l"]
[29.719466, "o", "\u001b[?25h"]
[29.880976, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[29.881945, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;60H\u001b[?25h"]
[30.027332, "o", "\u001b[?25l"]
[30.028128, "o", "\u001b[?25h"]
[30.047658, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mc"]
[30.048337, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;61H\u001b[?25h"]
[30.184138, "o", "\u001b[?25l"]
[30.184944, "o", "\u001b[?25h"]
[30.258261, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[30.258934, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;62H\u001b[?25h"]
[30.359586, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[30.360575, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;63H\u001b[?25h"]
[30.454171, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mv"]
[30.455156, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;64H\u001b[?25h"]
[30.538143, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[30.539117, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;65H\u001b[?25h"]
[30.621041, "o", "\u001b[?25l"]
[30.622113, "o", "\u001b[29;98H60\u001b[28;66H\u001b[?25h"]
[30.732961, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[30.73399, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;67H\u001b[?25h"]
[30.766997, "o", "\u001b[?25l"]
[30.767974, "o", "\u001b[?25h"]
[30.890765, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[30.891687, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;68H\u001b[?25h"]
[30.972655, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[30.973352, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;69H\u001b[?25h"]
[31.120276, "o", "\u001b[?25l"]
[31.121108, "o", "\u001b[?25h"]
[31.124288, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[31.125357, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;70H\u001b[?25h"]
[31.190374, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[31.191513, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;71H\u001b[?25h"]
[31.286738, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[31.287792, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;72H\u001b[?25h"]
[31.402397, "o", "\u001b[?25l"]
[31.403286, "o", "\u001b[29;99H7\u001b[28;73H\u001b[?25h"]
[31.433071, "o", "\u001b[?25l"]
[31.433862, "o", "\u001b[?25h"]
[31.49811, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mc"]
[31.498947, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;74H\u001b[?25h"]
[31.566619, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[31.56732, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;75H\u001b[?25h"]
[31.657161, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[31.65798, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m70\u001b[28;76H\u001b[?25h"]
[31.804636, "o", "\u001b[?25l"]
[31.805629, "o", "\u001b[?25h"]
[31.808729, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[31.809611, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;77H\u001b[?25h"]
[31.919386, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[31.920401, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;78H\u001b[?25h"]
[31.954934, "o", "\u001b[?25l"]
[31.955721, "o", "\u001b[?25h"]
[31.985772, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[31.98672, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;79H\u001b[?25h"]
[32.045565, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[32.046519, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;80H\u001b[?25h"]
[32.156088, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[32.157134, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;81H\u001b[?25h"]
[32.192162, "o", "\u001b[?25l"]
[32.193012, "o", "\u001b[?25h"]
[32.256181, "o", "\u001b[?25l"]
[32.257195, "o", "\u001b[29;99H6\u001b[28;82H\u001b[?25h"]
[32.363555, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[32.364208, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;83H\u001b[?25h"]
[32.387466, "o", "\u001b[?25l"]
[32.388088, "o", "\u001b[?25h"]
[32.453689, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[32.454388, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;84H\u001b[?25h"]
[32.527992, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[32.528977, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;85H\u001b[?25h"]
[32.627327, "o", "\u001b[?25l"]
[32.62818, "o", "\u001b[29;98H80\u001b[28;86H\u001b[?25h"]
[32.756322, "o", "\u001b[?25l\u001b[2;28r\u001b[m\u001b[37m\u001b[28;1H\r\n\u001b[1;30r\u001b[27;3H\u001b[90m 66 \u001b[m\u001b[37m - Python CLI that provides a front-end for interactive editor commands and\u001b[27;85H\u001b[K\u001b[28;1H\u001b[90m \u001b[m\u001b[37m\u001b[37m 67 \u001b[m\u001b[37m\u001b[48;5;234m c "]
[32.757193, "o", "\u001b[m\u001b[37m\u001b[29;96H\u001b[38;5;234m\u001b[48;5;254m7:8 \u001b[28;14H\u001b[?25h"]
[32.774865, "o", "\u001b[?25l"]
[32.775418, "o", "\u001b[?25h"]
[32.800643, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[32.801274, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;15H\u001b[?25h"]
[32.862862, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[32.863858, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m10\u001b[28;16H\u001b[?25h"]
[32.974627, "o", "\u001b[?25l"]
[32.975511, "o", "\u001b[29;99H1\u001b[28;17H\u001b[?25h"]
[33.008573, "o", "\u001b[?25l\u001b[m\u001b[37m\r\u001b[32m+ "]
[33.009078, "o", "\u001b[14C\u001b[?25h"]
[33.073381, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mb"]
[33.074136, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;18H\u001b[?25h"]
[33.137584, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[33.138272, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;19H\u001b[?25h"]
[33.232708, "o", "\u001b[?25l"]
[33.233365, "o", "\u001b[29;99H4\u001b[28;20H\u001b[?25h"]
[33.344077, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mu"]
[33.344914, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;21H\u001b[?25h"]
[33.37419, "o", "\u001b[?25l"]
[33.374843, "o", "\u001b[?25h"]
[33.423348, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[33.424081, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;22H\u001b[?25h"]
[33.515425, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[33.51633, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;23H\u001b[?25h"]
[33.655082, "o", "\u001b[?25l"]
[33.655912, "o", "\u001b[?25h"]
[33.658731, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[33.659589, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;24H\u001b[?25h"]
[33.743565, "o", "\u001b[?25l"]
[33.744307, "o", "\u001b[29;99H9\u001b[28;25H\u001b[?25h"]
[33.88971, "o", "\u001b[?25l"]
[33.89061, "o", "\u001b[?25h"]
[33.893384, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[33.89488, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m20\u001b[28;26H\u001b[?25h"]
[33.934558, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[33.935524, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;27H\u001b[?25h"]
[34.035885, "o", "\u001b[?25l"]
[34.036824, "o", "\u001b[29;99H2\u001b[28;28H\u001b[?25h"]
[34.099026, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[34.100225, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;29H\u001b[?25h"]
[34.21684, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mu"]
[34.217698, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;30H\u001b[?25h"]
[34.241434, "o", "\u001b[?25l"]
[34.242069, "o", "\u001b[?25h"]
[34.336887, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[34.337857, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;31H\u001b[?25h"]
[34.414496, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[34.415494, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;32H\u001b[?25h"]
[34.559326, "o", "\u001b[?25l"]
[34.560215, "o", "\u001b[?25h"]
[34.61467, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[34.61583, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;33H\u001b[?25h"]
[34.691256, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[34.692503, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;34H\u001b[?25h"]
[34.780385, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[34.7818, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;35H\u001b[?25h"]
[34.864086, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[34.865391, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m30\u001b[28;36H\u001b[?25h"]
[34.953578, "o", "\u001b[?25l"]
[34.954868, "o", "\u001b[29;99H1\u001b[28;37H\u001b[?25h"]
[35.100679, "o", "\u001b[?25l"]
[35.101826, "o", "\u001b[?25h"]
[35.14399, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[35.145364, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;38H\u001b[?25h"]
[35.261474, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[35.26273, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;39H\u001b[?25h"]
[35.29037, "o", "\u001b[?25l"]
[35.291301, "o", "\u001b[?25h"]
[35.457246, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mp"]
[35.458263, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;40H\u001b[?25h"]
[35.595301, "o", "\u001b[?25l"]
[35.596011, "o", "\u001b[?25h"]
[35.61606, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[35.616914, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;41H\u001b[?25h"]
[35.762954, "o", "\u001b[?25l"]
[35.764032, "o", "\u001b[?25h"]
[35.920713, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[35.922072, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;42H\u001b[?25h"]
[36.066113, "o", "\u001b[?25l"]
[36.06695, "o", "\u001b[?25h"]
[36.521298, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[36.522091, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;43H\u001b[?25h"]
[36.657939, "o", "\u001b[?25l"]
[36.658751, "o", "\u001b[?25h"]
[36.791548, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[36.792446, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;44H\u001b[?25h"]
[36.933075, "o", "\u001b[?25l"]
[36.933794, "o", "\u001b[?25h"]
[37.041085, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[37.041769, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;45H\u001b[?25h"]
[37.174299, "o", "\u001b[?25l"]
[37.175056, "o", "\u001b[?25h"]
[37.207559, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mv"]
[37.208148, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m40\u001b[28;46H\u001b[?25h"]
[37.354923, "o", "\u001b[?25l"]
[37.355749, "o", "\u001b[?25h"]
[37.413162, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[37.414224, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;47H\u001b[?25h"]
[37.55719, "o", "\u001b[?25l"]
[37.558318, "o", "\u001b[?25h"]
[37.577037, "o", "\u001b[?25l"]
[37.578396, "o", "\u001b[29;99H2\u001b[28;48H\u001b[?25h"]
[37.72376, "o", "\u001b[?25l"]
[37.724844, "o", "\u001b[?25h"]
[37.814478, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[37.815767, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;49H\u001b[?25h"]
[37.963287, "o", "\u001b[?25l"]
[37.964292, "o", "\u001b[?25h"]
[37.968918, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[37.970018, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;50H\u001b[?25h"]
[38.046656, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[38.047654, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;51H\u001b[?25h"]
[38.111703, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mk"]
[38.112678, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;52H\u001b[?25h"]
[38.221292, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[38.222342, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;53H\u001b[?25h"]
[38.251582, "o", "\u001b[?25l"]
[38.252353, "o", "\u001b[?25h"]
[38.342932, "o", "\u001b[?25l"]
[38.343911, "o", "\u001b[29;99H8\u001b[28;54H\u001b[?25h"]
[38.458516, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[38.459832, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;55H\u001b[?25h"]
[38.48929, "o", "\u001b[?25l"]
[38.490336, "o", "\u001b[?25h"]
[38.594979, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mu"]
[38.595964, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m50\u001b[28;56H\u001b[?25h"]
[38.732763, "o", "\u001b[?25l"]
[38.733534, "o", "\u001b[?25h"]
[38.739392, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mc"]
[38.740299, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;57H\u001b[?25h"]
[38.826583, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mh"]
[38.827539, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;58H\u001b[?25h"]
[38.91907, "o", "\u001b[?25l"]
[38.920218, "o", "\u001b[29;99H3\u001b[28;59H\u001b[?25h"]
[38.957774, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[38.958956, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;60H\u001b[?25h"]
[39.044488, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[39.045654, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;61H\u001b[?25h"]
[39.134906, "o", "\u001b[?25l"]
[39.135988, "o", "\u001b[29;99H6\u001b[28;62H\u001b[?25h"]
[39.283332, "o", "\u001b[?25l"]
[39.284256, "o", "\u001b[?25h"]
[39.287577, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[39.288657, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;63H\u001b[?25h"]
[39.36209, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[39.363122, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;64H\u001b[?25h"]
[39.415685, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mb"]
[39.416556, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;65H\u001b[?25h"]
[39.560642, "o", "\u001b[?25l"]
[39.561596, "o", "\u001b[?25h"]
[39.599806, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mu"]
[39.600636, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m60\u001b[28;66H\u001b[?25h"]
[39.680406, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[39.681541, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;67H\u001b[?25h"]
[39.773368, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ml"]
[39.774596, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;68H\u001b[?25h"]
[39.919325, "o", "\u001b[?25l"]
[39.920257, "o", "\u001b[?25h"]
[39.923432, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[39.924452, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;69H\u001b[?25h"]
[39.974142, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[39.975046, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;70H\u001b[?25h"]
[40.063146, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[40.063975, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;71H\u001b[?25h"]
[40.154803, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mg"]
[40.155698, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;72H\u001b[?25h"]
[40.246864, "o", "\u001b[?25l"]
[40.247767, "o", "\u001b[29;99H7\u001b[28;73H\u001b[?25h"]
[40.385426, "o", "\u001b[?25l"]
[40.386525, "o", "\u001b[?25h"]
[40.39003, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[40.39115, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;74H\u001b[?25h"]
[40.48997, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[40.491175, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;75H\u001b[?25h"]
[40.614718, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mg"]
[40.615719, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m70\u001b[28;76H\u001b[?25h"]
[40.637613, "o", "\u001b[?25l"]
[40.638399, "o", "\u001b[?25h"]
[40.68564, "o", "\u001b[?25l"]
[40.68645, "o", "\u001b[29;99H1\u001b[28;77H\u001b[?25h"]
[40.780558, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[40.781618, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;78H\u001b[?25h"]
[40.854055, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[40.855156, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;79H\u001b[?25h"]
[41.001458, "o", "\u001b[?25l"]
[41.002301, "o", "\u001b[?25h"]
[41.019126, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[41.019976, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;80H\u001b[?25h"]
[41.138065, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[41.139192, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;81H\u001b[?25h"]
[41.167684, "o", "\u001b[?25l"]
[41.168504, "o", "\u001b[?25h"]
[41.335272, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[41.336028, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;82H\u001b[?25h"]
[41.47234, "o", "\u001b[?25l"]
[41.473162, "o", "\u001b[?25h"]
[41.508526, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[41.509429, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;83H\u001b[?25h"]
[41.584993, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[41.586026, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;84H\u001b[?25h"]
[41.702766, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[41.703706, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;85H\u001b[?25h"]
[41.730804, "o", "\u001b[?25l"]
[41.731427, "o", "\u001b[?25h"]
[41.837398, "o", "\u001b[?25l"]
[41.838435, "o", "\u001b[29;98H80\u001b[28;86H\u001b[?25h"]
[41.974874, "o", "\u001b[?25l\u001b[2;28r\u001b[m\u001b[37m\u001b[28;1H\r\n\u001b[1;30r\u001b[27;3H\u001b[90m 67 \u001b[m\u001b[37m can be used to automate repetitive tasks such as rebuilding tag metadata\u001b[27;85H\u001b[K\u001b[28;1H\u001b[90m \u001b[m\u001b[37m\u001b[37m 68 \u001b[m\u001b[37m\u001b[48;5;234m a "]
[41.975811, "o", "\u001b[m\u001b[37m\u001b[29;96H\u001b[38;5;234m\u001b[48;5;254m8:8 \u001b[28;14H\u001b[?25h"]
[41.993712, "o", "\u001b[?25l"]
[41.994336, "o", "\u001b[?25h"]
[42.047005, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[42.047645, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;15H\u001b[?25h"]
[42.137218, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[42.137991, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m10\u001b[28;16H\u001b[?25h"]
[42.213117, "o", "\u001b[?25l"]
[42.214164, "o", "\u001b[29;99H1\u001b[28;17H\u001b[?25h"]
[42.340034, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mg"]
[42.341355, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;18H\u001b[?25h"]
[42.365934, "o", "\u001b[?25l\u001b[m\u001b[37m\r\u001b[32m+ "]
[42.366796, "o", "\u001b[15C\u001b[?25h"]
[42.404093, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[42.405069, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;19H\u001b[?25h"]
[42.551789, "o", "\u001b[?25l"]
[42.552888, "o", "\u001b[?25h"]
[42.559314, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[42.560541, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;20H\u001b[?25h"]
[42.705835, "o", "\u001b[?25l"]
[42.706809, "o", "\u001b[?25h"]
[42.72486, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[42.726168, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;21H\u001b[?25h"]
[42.869905, "o", "\u001b[?25l"]
[42.870718, "o", "\u001b[?25h"]
[42.873565, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[42.874482, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;22H\u001b[?25h"]
[43.014694, "o", "\u001b[?25l"]
[43.015509, "o", "\u001b[?25h"]
[43.022193, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[43.023122, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;23H\u001b[?25h"]
[43.125865, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[43.126786, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;24H\u001b[?25h"]
[43.234107, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[43.234854, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;25H\u001b[?25h"]
[43.263644, "o", "\u001b[?25l"]
[43.264453, "o", "\u001b[?25h"]
[43.295809, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[43.296805, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m20\u001b[28;26H\u001b[?25h"]
[43.372896, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mg"]
[43.374056, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;27H\u001b[?25h"]
[43.45383, "o", "\u001b[?25l"]
[43.454888, "o", "\u001b[29;99H2\u001b[28;28H\u001b[?25h"]
[43.552608, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ml"]
[43.553643, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;29H\u001b[?25h"]
[43.649564, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[43.650637, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;30H\u001b[?25h"]
[43.7467, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[43.747761, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;31H\u001b[?25h"]
[43.86212, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mk"]
[43.863321, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;32H\u001b[?25h"]
[43.887125, "o", "\u001b[?25l"]
[43.887964, "o", "\u001b[?25h"]
[43.950905, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ms"]
[43.951914, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;33H\u001b[?25h"]
[44.073351, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m,"]
[44.074454, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;34H\u001b[?25h"]
[44.099344, "o", "\u001b[?25l"]
[44.10022, "o", "\u001b[?25h"]
[44.184955, "o", "\u001b[?25l"]
[44.185961, "o", "\u001b[29;99H9\u001b[28;35H\u001b[?25h"]
[44.244327, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[44.245529, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m30\u001b[28;36H\u001b[?25h"]
[44.363092, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ml"]
[44.364029, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;37H\u001b[?25h"]
[44.389896, "o", "\u001b[?25l"]
[44.390556, "o", "\u001b[?25h"]
[44.478631, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ml"]
[44.479409, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;38H\u001b[?25h"]
[44.578042, "o", "\u001b[?25l"]
[44.578798, "o", "\u001b[29;99H3\u001b[28;39H\u001b[?25h"]
[44.621906, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mf"]
[44.622908, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;40H\u001b[?25h"]
[44.767686, "o", "\u001b[?25l"]
[44.768467, "o", "\u001b[?25h"]
[44.801401, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mr"]
[44.80251, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;41H\u001b[?25h"]
[44.865767, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[44.866761, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;42H\u001b[?25h"]
[44.930382, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[44.93145, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;43H\u001b[?25h"]
[45.002999, "o", "\u001b[?25l"]
[45.004089, "o", "\u001b[29;99H8\u001b[28;44H\u001b[?25h"]
[45.066857, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mt"]
[45.067907, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;45H\u001b[?25h"]
[45.154543, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mh"]
[45.155553, "o", "\u001b[m\u001b[37m\u001b[29;98H\u001b[38;5;234m\u001b[48;5;254m40\u001b[28;46H\u001b[?25h"]
[45.219308, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[45.220292, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;47H\u001b[?25h"]
[45.27361, "o", "\u001b[?25l"]
[45.274581, "o", "\u001b[29;99H2\u001b[28;48H\u001b[?25h"]
[45.420821, "o", "\u001b[?25l"]
[45.421644, "o", "\u001b[?25h"]
[45.427495, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mc"]
[45.428436, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;49H\u001b[?25h"]
[45.485035, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mo"]
[45.486004, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;50H\u001b[?25h"]
[45.535923, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[45.536864, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;51H\u001b[?25h"]
[45.67832, "o", "\u001b[?25l"]
[45.678958, "o", "\u001b[?25h"]
[45.681262, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mm"]
[45.681905, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m6\u001b[28;52H\u001b[?25h"]
[45.781754, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ma"]
[45.78298, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m7\u001b[28;53H\u001b[?25h"]
[45.867023, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[45.86838, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m8\u001b[28;54H\u001b[?25h"]
[45.947653, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234md"]
[45.949036, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m9\u001b[28;55H\u001b[?25h"]
[45.996746, "o", "\u001b[?25l"]
[45.99802, "o", "\u001b[29;98H50\u001b[28;56H\u001b[?25h"]
[46.111751, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234ml"]
[46.112995, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m1\u001b[28;57H\u001b[?25h"]
[46.143361, "o", "\u001b[?25l"]
[46.144204, "o", "\u001b[?25h"]
[46.176066, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mi"]
[46.177009, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m2\u001b[28;58H\u001b[?25h"]
[46.290998, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234mn"]
[46.292134, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m3\u001b[28;59H\u001b[?25h"]
[46.322155, "o", "\u001b[?25l"]
[46.322854, "o", "\u001b[?25h"]
[46.35816, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234me"]
[46.359055, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;60H\u001b[?25h"]
[46.501581, "o", "\u001b[?25l"]
[46.502707, "o", "\u001b[?25h"]
[46.507489, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[48;5;234m."]
[46.50863, "o", "\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m5\u001b[28;61H\u001b[?25h"]
[46.6442, "o", "\u001b[?25l"]
[46.644989, "o", "\u001b[?25h"]
[47.863226, "o", "\b"]
[47.879544, "o", "\u001b[2 q\u001b[?25l"]
[47.880345, "o", "\u001b[m\u001b[37m\r\n\u001b[1m\u001b[97m\u001b[44m NORMAL \u001b[m\u001b[37m\u001b[34m\u001b[48;5;238m\u001b[m\u001b[37m\u001b[29;99H\u001b[38;5;234m\u001b[48;5;254m4\u001b[28;60H\u001b[?25h"]
[47.993999, "o", "\u001b[?25l"]
[47.994637, "o", "\u001b[?25h"]
[48.657375, "o", "\u001b[?25l\r\n\r\n\u001b[m\u001b[37m:\u001b[?2004h\u001b[?25h"]
[48.901196, "o", "w"]
[49.143303, "o", "q"]
[49.637485, "o", "\r"]
[49.637936, "o", "\u001b[?25l\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[49.638343, "o", "\"wiki/Related Tools.wiki\""]
[49.644714, "o", " 68L, 3882C written"]
[49.662295, "o", "\r\u001b]2;Related Tools.wiki + (~/.vimwiki/wiki) - VIM\u0007\u001b[23;2t\u001b[23;1t\u001b[22;2t\u001b[22;1t\u001b[23;2t\u001b[23;1t\r\r\n\u001b[39;49m\u001b[?2004l\u001b[?1l\u001b>\u001b[?25h\u001b[>4;m\u001b[?2004l\u001b[?1004l\u001b[?1049l\u001b[23;0;0t"]
[49.668616, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J> \u001b[K\u001b[67C \u001b[68D"]
[49.66948, "o", "\r\r\u001b[?2004h\u001b[0m\u001b[27m\u001b[24m\u001b[J> \u001b[K\u001b[67C \u001b[68D"]
[50.265778, "o", "v"]
[50.354279, "o", "\bvi"]
[50.453211, "o", "m"]
[50.591559, "o", "w"]
[50.73511, "o", "i"]
[50.912015, "o", "k"]
[51.067609, "o", "i"]
[51.170407, "o", " "]
[51.318616, "o", "d"]
[51.403, "o", "i"]
[51.522321, "o", "a"]
[51.621358, "o", "r"]
[51.721855, "o", "y"]
[51.79326, "o", " "]
[51.915283, "o", "t"]
[52.014835, "o", "o"]
[52.148808, "o", "d"]
[52.221807, "o", "a"]
[52.289313, "o", "y"]
[52.765627, "o", "\u001b[?2004l\r\r\n"]
[52.977471, "o", "\u001b[?1006h\u001b[?1002h\u001b[?1049h\u001b[22;0;0t\u001b[?1004h\u001b[?2004h\u001b[>4;2m\u001b[?1h\u001b=\u001b[?2004h\u001b[1;30r\u001b[?12h\u001b[?12l\u001b[22;2t\u001b[22;1t"]
[52.978207, "o", "\u001b[27m\u001b[23m\u001b[29m\u001b[m\u001b[37m\u001b[H\u001b[2J"]
[52.988064, "o", "\u001b[?25l\u001b[30;1H\"~/.vimwiki/wiki/diary/2021-05-04.wiki\" 8L, 182C"]
[53.025928, "o", "\u001b[2 q\u001b[2;1H▽\u001b[6n\u001b[2;1H \u001b[1;1H\u001b[>c\u001b]10;?\u0007\u001b]11;?\u0007"]
[53.031171, "o", "\u001b[1;1H\u001b[1m\u001b[97m\u001b[44m 1 2021-05-04.wiki \u001b[m\u001b[37m\u001b[34m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[1;21H\u001b[38;5;254m\u001b[48;5;234m \u001b[m\u001b[37m\u001b[1;98H\u001b[38;5;234m\u001b[48;5;254m X \u001b[m\u001b[37m\u001b[2;1H\u001b[90m 1 \u001b[m\u001b[37m= 2021-05-04 Notes =\r\n\u001b[90m 2 \r\n 3 \u001b[m\u001b[37m== vimwiki-cli ==\r\n\u001b[90m 4 \r\n 5 \u001b[m\u001b[37m- [X] Finish writing documentation.\r\n\u001b[90m 6 \u001b[m\u001b[37m- [X] Add CONTRIBUTORS.md.\r\n\u001b[90m 7 \u001b[m\u001b[37m- [ ] Record a *\u001b[1mgreat\u001b[m\u001b[37m* demo!\r\n\u001b[37m 8 \u001b[m\u001b[37m\u001b[48;5;234m - [ ] Add `\u001b[m\u001b[37m\u001b[34m\u001b[48;5;234mvimwiki-cli\u001b[m\u001b[37m\u001b[48;5;234m` to \u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m[[Related Tools]]\u001b[m\u001b[37m\u001b[48;5;234m \u001b[m\u001b[37m\u001b[10;1H\u001b[94m~ \u001b[11;1H~ \u001b[12;1H~ "]
[53.031215, "o", " \u001b[13;1H~ \u001b[14;1H~ \u001b[15;1H~ \u001b[16;1H~ \u001b[17;1H~ \u001b[18;1H~ \u001b[19;1H~ \u001b[20;1H~ \u001b[21;1H~ "]
[53.031755, "o", "\u001b[22;1H~ \u001b[23;1H~ \u001b[24;1H~ \u001b[25;1H~ \u001b[26;1H~ \u001b[27;1H~ \u001b[28;1H~ "]
[53.032326, "o", "\u001b[m\u001b[37m\u001b[29;1H\u001b[1m\u001b[97m\u001b[44m NORMAL \u001b[m\u001b[37m\u001b[34m\u001b[48;5;238m\u001b[m\u001b[37m\u001b[29;10H\u001b[38;5;254m\u001b[48;5;238m \u001b[29;12H master \u001b[m\u001b[37m\u001b[38;5;238m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;21H\u001b[38;5;254m\u001b[48;5;234m wiki/diary/2021-05-04.wiki \u001b[m\u001b[37m\u001b[38;5;234m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;50H\u001b[38;5;254m\u001b[48;5;234m \u001b[m\u001b[37m\u001b[38;5;234m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;83H\u001b[38;5;238m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[29;84H\u001b[38;5;254m\u001b[48;5;238m 100% \u001b[m\u001b[37m\u001b[29;91H\u001b[38;5;234m\u001b[48;5;254m \u001b[29;93H 8:5 \u001b]2;2021-05-04.wiki (~/.vimwiki/wiki/diary) - VIM\u0007"]
[53.033219, "o", "\u001b[9;9H\u001b[?25h"]
[53.090079, "o", "\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[53.091114, "o", "\u001b[?2004h\u001b[?1006h\u001b[?1002h\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[53.093338, "o", "\u001b[?2004h\u001b[?1006h\u001b[?1002h\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[53.096335, "o", "\u001b[?2004h\u001b[?1006h\u001b[?1002h\u001b[?2004h"]
[53.096634, "o", "\u001b]2;2021-05-04.wiki (~/.vimwiki/wiki/diary) - VIM\u0007"]
[54.665725, "o", "\u001b[?2004h"]
[54.67551, "o", "\u001b[?25l"]
[54.678659, "o", "\u001b[m\u001b[37m\u001b[1;20H\u001b[1m\u001b[97m\u001b[44m[+] \u001b[m\u001b[37m\u001b[34m\u001b[48;5;234m\u001b[m\u001b[37m\u001b[8;8HX\u001b[9;12H\u001b[48;5;234mX"]
[54.679382, "o", "\u001b[m\u001b[37m\u001b[29;49H\u001b[38;5;254m\u001b[48;5;234m[+]\u001b[m\u001b[37m\u001b[1C\u001b[38;5;234m\u001b[48;5;234m\u001b]2;2021-05-04.wiki + (~/.vimwiki/wiki/diary) - VIM\u0007"]
[54.68113, "o", "\u001b[9;9H\u001b[?25h"]
[54.803825, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[2;3H\u001b[90m 1 \u001b[m\u001b[37m= 2021-05-04 Notes =\u001b[3;3H\u001b[90m 2 \r\n 3 \u001b[m\u001b[37m== vimwiki-cli ==\u001b[5;3H\u001b[90m 4 \r\n 5 \u001b[m\u001b[37m- [X] Finish writing documentation.\u001b[7;3H\u001b[90m 6 \u001b[m\u001b[37m- [X] Add CONTRIBUTORS.md.\r\n\u001b[33m~ \u001b[m\u001b[37m\u001b[90m 7 \u001b[m\u001b[37m- [X] Record a *\u001b[1mgreat\u001b[m\u001b[37m* demo!\r\n\u001b[33m~ \u001b[m\u001b[37m\u001b[37m 8 \u001b[m\u001b[37m\u001b[2C\u001b[48;5;234m - [X] Add `\u001b[m\u001b[37m\u001b[34m\u001b[48;5;234mvimwiki-cli\u001b[m\u001b[37m\u001b[48;5;234m` to \u001b[m\u001b[37m\u001b[4m\u001b[94m\u001b[48;5;234m[[Related Tools]]"]
[54.804284, "o", "\u001b[9;11H\u001b[?25h"]
[56.059347, "o", "\u001b[?25l\u001b[m\u001b[37m\u001b[30;1H\u001b[K\u001b[30;1H:\u001b[?2004h"]
[56.059701, "o", "\u001b[?25h"]
[56.307923, "o", "w"]
[56.523436, "o", "q"]
[56.731317, "o", "\r"]
[56.731699, "o", "\u001b[?25l\u001b[?1006l\u001b[?1002l\u001b[?2004l"]
[56.731804, "o", "\"wiki/diary/2021-05-04.wiki\""]
[56.7415, "o", " 8L, 182C written"]
[56.758986, "o", "\r\u001b]2;Related Tools.wiki + (~/.vimwiki/wiki) - VIM\u0007\u001b[23;2t\u001b[23;1t\u001b[22;2t\u001b[22;1t\u001b[23;2t\u001b[23;1t\r\r\n\u001b[39;49m\u001b[?2004l\u001b[?1l\u001b>\u001b[?25h\u001b[>4;m\u001b[?2004l\u001b[?1004l\u001b[?1049l\u001b[23;0;0t"]
[56.763887, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J> \u001b[K\u001b[67C \u001b[68D"]
[56.764349, "o", "\r\r\u001b[?2004h\u001b[0m\u001b[27m\u001b[24m\u001b[J> \u001b[K\u001b[67C \u001b[68D"]
[57.138927, "o", "g"]
[57.235826, "o", "\bgi"]
[57.332261, "o", "t"]
[57.380013, "o", " "]
[57.552776, "o", "c"]
[57.618149, "o", "o"]
[57.695046, "o", "m"]
[57.853982, "o", "m"]
[57.943164, "o", "i"]
[58.053934, "o", "t"]
[58.134827, "o", " "]
[58.395612, "o", "-"]
[58.478227, "o", "a"]
[58.637724, "o", " "]
[58.778272, "o", "-"]
[58.993868, "o", "m"]
[59.10482, "o", " "]
[59.268853, "o", "'"]
[59.548293, "o", "U"]
[59.708867, "o", "p"]
[59.796098, "o", "d"]
[59.900954, "o", "a"]
[59.994491, "o", "t"]
[60.089628, "o", "e"]
[60.146289, "o", " "]
[60.322568, "o", "v"]
[60.428998, "o", "i"]
[60.510147, "o", "m"]
[60.638009, "o", "w"]
[60.774885, "o", "i"]
[60.948263, "o", "k"]
[61.103887, "o", "i"]
[61.256502, "o", "w"]
[61.361821, "o", "i"]
[61.520288, "o", "k"]
[61.695677, "o", "i"]
[62.05812, "o", "!"]
[62.318457, "o", "'"]
[63.567055, "o", "\u001b[?2004l\r\r\n"]
[63.653316, "o", "\u001b[90mvim\u001b[92m|\u001b[37mwiki\u001b[0m: Generating links..."]
[66.050112, "o", "done.\r\n"]
[66.051653, "o", "\u001b[90mvim\u001b[92m|\u001b[37mwiki\u001b[0m: Generating diary links..."]
[68.361937, "o", "done.\r\n"]
[68.363488, "o", "\u001b[90mvim\u001b[92m|\u001b[37mwiki\u001b[0m: Rebuilding tag metadata..."]
[70.680921, "o", "done.\r\n"]
[70.683838, "o", "\u001b[90mvim\u001b[92m|\u001b[37mwiki\u001b[0m: Generating tag links..."]
[73.022411, "o", "done.\r\n"]
[73.035142, "o", "[master 593e5ec] Update vimwikiwiki!\r\n 2 files changed, 6 insertions(+), 2 deletions(-)\r\n"]
[73.035556, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J> \u001b[K\u001b[67C \u001b[68D"]
[75.268685, "o", "\u001b[?2004l\r\r\n"]

2
pyproject.toml Normal file
View file

@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel"]

81
scripts/pre-commit.sh Executable file
View file

@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# A pre-commit hook script to execute non-interactive vimwiki commands to
# rebuild tag metadata and generate links before commit.
#
# This hook relies on the following configuration options:
#
# vimwiki.options -- Extra options to pass to the vimwiki command
# vimwiki.linkspage -- Page which contains generated links
# vimwiki.taglinkspage -- Page which contains generated tag links
# vimwiki.generatelinks -- Generate links before commit (bool)
# vimwiki.generatediarylinks -- Generate diary links before commit (bool)
# vimwiki.generatetaglinks -- Generate tag links before commit (bool)
# vimwiki.rebuildtags -- Rebuild tag metadata before commit (bool)
#
# To enable this hook, copy or link this file to ".git/hooks/pre-commit".
if git config --get-colorbool color.interactive
then
# See https://github.com/vimwiki/vimwiki/blob/master/doc/logo.svg
say_prefix='\e[90mvim\e[92m|\e[37mwiki\e[0m'
else
say_prefix='vimwiki'
fi
say () {
printf '%b: %s' "$say_prefix" "$*"
}
say_done () {
printf 'done.\n'
}
# Exit if no changes staged for commit
if git diff --cached --diff-filter=tuxb --quiet
then
exit 0
fi
# Stash untracked changes
git stash push --keep-index --include-untracked --quiet >/dev/null 2>&1
trap 'git stash pop --quiet >/dev/null 2>&1' 0
trap 'exit 1' 1 2 3 15
options=$(git config vimwiki.options)
if test "$(git config --bool vimwiki.generatelinks || echo false)" = true
then
page=$(git config vimwiki.linkspage || echo index)
say 'Generating links...'
vimwiki $options generate-links "$page" || exit
say_done
fi
if test "$(git config --bool vimwiki.generatediarylinks || echo false)" = true
then
say 'Generating diary links...'
vimwiki $options diary generate-links || exit
say_done
fi
if test "$(git config --bool vimwiki.rebuildtags || echo false)" = true
then
say 'Rebuilding tag metadata...'
vimwiki $options tags rebuild || exit
say_done
fi
if test "$(git config --bool vimwiki.generatetaglinks || echo false)" = true
then
page=$(git config vimwiki.taglinkspage || echo index)
say 'Generating tag links...'
vimwiki $options tags generate-links "$page" || exit
say_done
fi
# Add changes to index
git add --all

86
scripts/release_notes.py Executable file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env python3
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import argparse
import logging
import sys
from pprint import pformat
from textwrap import indent
import keepachangelog
logger = logging.getLogger(__name__)
def main():
parser = argparse.ArgumentParser(description="""
Generate release notes from a changelog that conforms to Keep a
Changelog v1.0.0 (https://keepachangelog.com/en/1.0.0/).
""")
parser.add_argument('-f', '--file', nargs='?', default='CHANGELOG.md',
help='read changes from file (default: CHANGELOG.md)')
parser.add_argument('-o', '--output', nargs='?', default=sys.stdout,
type=argparse.FileType('w'),
help='write output to file (default: <stdout>)', metavar='FILE')
parser.add_argument('-v', '--verbose', action='store_true',
help='increase output verbosity')
parser.add_argument('version', help='version to generate')
args = parser.parse_args()
logging.basicConfig(format='%(levelname)s: %(message)s',
level=logging.DEBUG if args.verbose else logging.INFO)
logger.debug('Reading changes from %s', args.file)
changes = keepachangelog.to_raw_dict(args.file)
logger.debug('Found changes:\n%s', indent(pformat(changes), ' '))
if args.version not in changes:
logger.error('Unable to find version %s', args.version)
sys.exit(1)
logger.debug('Writing output to %s', args.output.name)
with args.output as f:
input = changes[args.version]['raw']
for index, line in enumerate(input.splitlines(True)):
# keepachangelog will strip bare newlines, which can impact
# readability. Newlines are added around headings to minimally
# conform to the Google Markdown style guide.
if line.startswith('#'):
if index > 0:
f.write('\n')
f.write('%s\n' % line)
else:
f.write(line)
return 0
if __name__ == '__main__':
sys.exit(main())

27
setup.cfg Normal file
View file

@ -0,0 +1,27 @@
[bumpversion]
current_version = 0.0.0
[bumpversion:file:setup.py]
search = version='{current_version}'
replace = version='{new_version}'
[bumpversion:file:vimwiki_cli/__init__.py]
search = __version__ = '{current_version}'
replace = __version__ = '{new_version}'
[bdist_wheel]
universal = 1
[coverage:report]
fail_under = 90
show_missing = True
[pycodestyle]
exclude = .git,__pycache__,build,dist,venv
select = E,W
max-line-length = 99
show-source = True
[tool:pytest]
addopts = --cov=vimwiki_cli
testpaths = tests

60
setup.py Normal file
View file

@ -0,0 +1,60 @@
import os.path
from setuptools import find_packages, setup
dirname = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(dirname, 'README.md')) as f:
long_description = f.read()
setup(name='vimwiki-cli',
version='0.0.0',
description='Vimwiki Command-Line Interface',
long_description=long_description,
long_description_content_type='text/markdown',
author='Steven Stallion',
author_email='sstallion@gmail.com',
url='https://github.com/sstallion/vimwiki-cli',
packages=find_packages(),
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
'Intended Audience :: End Users/Desktop',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Topic :: Text Editors',
'Topic :: Text Editors :: Documentation'
],
license='BSD-2-Clause',
keywords='cli vim vimwiki wiki',
install_requires=[
'click~=7.1',
],
entry_points={
'console_scripts': [
'vimwiki=vimwiki_cli.__main__:cli'
]
},
extras_require={
'build': [
'build>=0.3.0',
'virtualenv>=20.0.35'
],
'test': [
'coverage~=5.5',
'mock~=3.0',
'pycodestyle~=2.7',
'pytest~=4.6',
'pytest-cov~=2.11'
],
'deploy': [
'bump2version>=1.0.0',
'keepachangelog>=0.5.0'
]
})

49
tests/conftest.py Normal file
View file

@ -0,0 +1,49 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import os
import pytest
from vimwiki_cli.context import CONTEXT_SETTINGS
from vimwiki_cli.wiki import Wiki
# Remove conflicting environment variables:
@pytest.fixture(scope='session', autouse=True)
def setup_env():
prefix = CONTEXT_SETTINGS.get('auto_envvar_prefix')
for key in os.environ:
if key.startswith(prefix):
del os.environ[key]
@pytest.fixture(autouse=True)
def wiki_options():
return {}
@pytest.fixture
def wiki(wiki_options):
return Wiki(**wiki_options)

271
tests/test_cli.py Normal file
View file

@ -0,0 +1,271 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import mock
import pytest
from click.testing import CliRunner
from vimwiki_cli.__main__ import *
from vimwiki_cli.wiki import Wiki
@pytest.fixture
def runner():
return CliRunner()
@mock.patch('vimwiki_cli.__main__.make_wiki')
@mock.patch('vimwiki_cli.__main__.index')
@pytest.mark.parametrize('args,expected', [
('--editor EDITOR', {'editor': 'EDITOR'}),
('--count 42', {'count': 42}),
('--select', {'select': True}),
('--open-matches', {'open_matches': True}),
('--open-tabs', {'open_tabs': True})
])
def test_options(_, mock_make_wiki, runner, args, expected):
result = runner.invoke(cli, args=args)
assert result.exit_code == 0
for key, value in expected.items():
assert key in mock_make_wiki.call_args.kwargs and \
mock_make_wiki.call_args.kwargs[key] == value
@mock.patch('vimwiki_cli.__main__.make_wiki')
@mock.patch('vimwiki_cli.__main__.index')
@pytest.mark.parametrize('expected', [
({'editor': Wiki.DEFAULT_EDITOR}),
({'count': Wiki.DEFAULT_COUNT}),
({'select': Wiki.DEFAULT_SELECT}),
({'open_matches': Wiki.DEFAULT_OPEN_MATCHES}),
({'open_tabs': Wiki.DEFAULT_OPEN_TABS})
])
def test_options_with_defaults(_, mock_make_wiki, runner, expected):
result = runner.invoke(cli)
assert result.exit_code == 0
for key, value in expected.items():
assert key in mock_make_wiki.call_args.kwargs and \
mock_make_wiki.call_args.kwargs[key] == value
@mock.patch('vimwiki_cli.__main__.make_wiki')
@mock.patch('vimwiki_cli.__main__.index')
@pytest.mark.parametrize('env,expected', [
({'VIMWIKI_EDITOR': 'EDITOR'}, {'editor': 'EDITOR'}),
({'VIMWIKI_COUNT': '42'}, {'count': 42}),
({'VIMWIKI_SELECT': '1'}, {'select': True}),
({'VIMWIKI_OPEN_MATCHES': '1'}, {'open_matches': True}),
({'VIMWIKI_OPEN_TABS': '1'}, {'open_tabs': True})
])
def test_options_with_env(_, mock_make_wiki, runner, env, expected):
result = runner.invoke(cli, env=env)
assert result.exit_code == 0
for key, value in expected.items():
assert key in mock_make_wiki.call_args.kwargs and \
mock_make_wiki.call_args.kwargs[key] == value
@mock.patch('vimwiki_cli.__main__.index')
def test_default(mock_index, runner):
result = runner.invoke(cli)
assert result.exit_code == 0
# index() should be invoked if no command is specified:
mock_index.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.check_links')
def test_check_links(mock_check_links, runner):
result = runner.invoke(cli, 'check-links')
assert result.exit_code == 0
mock_check_links.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.generate_links')
@pytest.mark.parametrize('args,expected', [
('PAGE', ('PAGE', '')),
('PAGE PATTERN', ('PAGE', 'PATTERN'))
])
def test_generate_links(mock_generate_links, runner, args, expected):
result = runner.invoke(cli, 'generate-links ' + args)
assert result.exit_code == 0
mock_generate_links.assert_called_with(*expected)
@mock.patch('vimwiki_cli.wiki.Wiki.generate_links')
def test_generate_links_with_empty_page(mock_generate_links, runner):
result = runner.invoke(cli, 'generate-links ""')
assert result.exit_code != 0
mock_generate_links.assert_not_called()
@mock.patch('vimwiki_cli.wiki.Wiki.goto')
def test_goto(mock_goto, runner):
result = runner.invoke(cli, 'goto PAGE')
assert result.exit_code == 0
mock_goto.assert_called_with('PAGE')
@mock.patch('vimwiki_cli.wiki.Wiki.goto')
def test_goto_with_empty_page(mock_goto, runner):
result = runner.invoke(cli, 'goto ""')
assert result.exit_code != 0
mock_goto.assert_not_called()
@mock.patch('vimwiki_cli.wiki.Wiki.help')
def test_help(mock_help, runner):
result = runner.invoke(cli, 'help')
assert result.exit_code == 0
mock_help.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.index')
def test_index(mock_index, runner):
result = runner.invoke(cli, 'index')
assert result.exit_code == 0
mock_index.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.search')
def test_search(mock_search, runner):
result = runner.invoke(cli, 'search PATTERN')
assert result.exit_code == 0
mock_search.assert_called_with('PATTERN')
@mock.patch('vimwiki_cli.wiki.Wiki.search')
def test_search_with_empty_pattern(mock_search, runner):
result = runner.invoke(cli, 'search ""')
assert result.exit_code != 0
mock_search.assert_not_called()
@mock.patch('vimwiki_cli.diary.index')
def test_diary_default(mock_index, runner):
result = runner.invoke(cli, 'diary')
assert result.exit_code == 0
# index() should be invoked if no command is specified:
mock_index.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.diary_generate_links')
def test_diary_generate_links(mock_diary_generate_links, runner):
result = runner.invoke(cli, 'diary generate-links')
assert result.exit_code == 0
mock_diary_generate_links.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.diary_index')
def test_diary_index(mock_diary_index, runner):
result = runner.invoke(cli, 'diary index')
assert result.exit_code == 0
mock_diary_index.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.make_diary_note')
def test_diary_today(mock_make_diary_note, runner):
result = runner.invoke(cli, 'diary today')
assert result.exit_code == 0
mock_make_diary_note.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.make_tomorrow_diary_note')
def test_diary_tomorrow(mock_make_tomorrow_diary_note, runner):
result = runner.invoke(cli, 'diary tomorrow')
assert result.exit_code == 0
mock_make_tomorrow_diary_note.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.make_yesterday_diary_note')
def test_diary_yesterday(mock_make_yesterday_diary_note, runner):
result = runner.invoke(cli, 'diary yesterday')
assert result.exit_code == 0
mock_make_yesterday_diary_note.assert_called_with()
@mock.patch('vimwiki_cli.wiki.Wiki.generate_tag_links')
@pytest.mark.parametrize('args,expected', [
('PAGE', ('PAGE', ())),
('PAGE TAG1 TAG2', ('PAGE', ('TAG1', 'TAG2')))
])
def test_tags_generate_links(mock_generate_tag_links, runner, args, expected):
result = runner.invoke(cli, 'tags generate-links ' + args)
assert result.exit_code == 0
mock_generate_tag_links.assert_called_with(*expected)
@mock.patch('vimwiki_cli.wiki.Wiki.generate_tag_links')
def test_tags_generate_links_with_empty_page(mock_generate_tag_links, runner):
result = runner.invoke(cli, 'tags generate-links ""')
assert result.exit_code != 0
mock_generate_tag_links.assert_not_called()
@mock.patch('vimwiki_cli.wiki.Wiki.rebuild_tags')
@pytest.mark.parametrize('args,expected', [
('', False),
('--all', True)
])
def test_tags_rebuild(mock_rebuild_tags, runner, args, expected):
result = runner.invoke(cli, 'tags rebuild ' + args)
assert result.exit_code == 0
mock_rebuild_tags.assert_called_with(expected)
@mock.patch('vimwiki_cli.wiki.Wiki.search_tags')
def test_search_tags(mock_search_tags, runner):
result = runner.invoke(cli, 'tags search PATTERN')
assert result.exit_code == 0
mock_search_tags.assert_called_with('PATTERN')
@mock.patch('vimwiki_cli.wiki.Wiki.search_tags')
def test_search_tags_with_empty_pattern(mock_search_tags, runner):
result = runner.invoke(cli, 'tags search ""')
assert result.exit_code != 0
mock_search_tags.assert_not_called()

188
tests/test_editor.py Normal file
View file

@ -0,0 +1,188 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import subprocess
import mock
import pytest
from vimwiki_cli.editor import *
@pytest.fixture(autouse=True)
def cmd_options():
return {}
@pytest.fixture
def cmd(wiki, cmd_options):
return Command(wiki, 'COMMAND', **cmd_options)
@pytest.fixture
def cmd_global(wiki, cmd_options):
return GlobalCommand(wiki, 'GLOBAL_COMMAND', **cmd_options)
@pytest.fixture
def cmd_local(wiki, cmd_options):
return LocalCommand(wiki, 'LOCAL_COMMAND', **cmd_options)
@pytest.fixture
def cmd_diary(wiki, cmd_options):
return DiaryCommand(wiki, 'DIARY_COMMAND', **cmd_options)
@pytest.mark.parametrize('wiki_options', [{'open_matches': True}])
@pytest.mark.parametrize('cmd_options', [{'interactive': True, 'open_matches': True}])
def test_interactive_with_matches(cmd):
assert cmd._args == ['COMMAND', 'lopen']
@pytest.mark.parametrize('wiki_options', [{'open_tabs': True}])
@pytest.mark.parametrize('cmd_options', [{'interactive': True}])
def test_interactive_with_tabs(cmd):
assert cmd._args == ['$tabnew', 'COMMAND']
@pytest.mark.parametrize('cmd_options', [{'interactive': True, 'quit': True}])
def test_interactive_with_quit(cmd):
assert cmd._args == ['COMMAND', 'q!']
@pytest.mark.parametrize('cmd_options', [{'interactive': True, 'write_quit': True}])
def test_interactive_with_write_quit(cmd):
assert cmd._args == ['COMMAND', 'wq!']
@mock.patch('os.execvp')
@pytest.mark.parametrize('wiki_options', [{'editor': 'EDITOR'}])
@pytest.mark.parametrize('cmd_options', [{'interactive': True}])
def test_interactive_run(mock_execvp, cmd):
cmd.run()
mock_execvp.assert_called_with('EDITOR', ['EDITOR', '-c', 'COMMAND'])
@mock.patch('os.execvp', side_effect=OSError)
@pytest.mark.parametrize('wiki_options', [{'editor': 'EDITOR'}])
@pytest.mark.parametrize('cmd_options', [{'interactive': True}])
def test_interactive_run_with_error(mock_execvp, cmd):
with pytest.raises(OSError):
cmd.run()
@pytest.mark.parametrize('wiki_options', [{'open_matches': True}])
@pytest.mark.parametrize('cmd_options', [{'interactive': False, 'open_matches': True}])
def test_noninteractive_with_matches(cmd):
assert 'lopen' not in cmd._args
@pytest.mark.parametrize('wiki_options', [{'open_tabs': True}])
@pytest.mark.parametrize('cmd_options', [{'interactive': False}])
def test_noninteractive_with_tabs(cmd):
assert '$tabnew' not in cmd._args
@pytest.mark.parametrize('cmd_options', [{'interactive': False, 'quit': True}])
def test_noninteractive_with_quit(cmd):
assert cmd._args == ['COMMAND', 'q!']
@pytest.mark.parametrize('cmd_options', [{'interactive': False, 'write_quit': True}])
def test_noninteractive_with_write_quit(cmd):
assert cmd._args == ['COMMAND', 'wq!']
@mock.patch('sys.exit')
@mock.patch('subprocess.Popen')
@pytest.mark.parametrize('wiki_options', [{'editor': 'EDITOR'}])
@pytest.mark.parametrize('cmd_options', [{'interactive': False}])
def test_noninteractive_run(mock_Popen, mock_exit, cmd):
mock_Popen.return_value.communicate.return_value = (b'', b'')
mock_Popen.return_value.returncode = 0
cmd.run()
mock_Popen.assert_called_with(['EDITOR', '-c', 'COMMAND'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
mock_exit.assert_called_with(mock_Popen.return_value.returncode)
@mock.patch('subprocess.Popen', side_effect=OSError)
@pytest.mark.parametrize('wiki_options', [{'editor': 'EDITOR'}])
@pytest.mark.parametrize('cmd_options', [{'interactive': False}])
def test_noninteractive_run_with_error(mock_Popen, cmd):
with pytest.raises(OSError):
cmd.run()
@mock.patch('sys.exit')
@mock.patch('subprocess.Popen')
@pytest.mark.parametrize('wiki_options', [{'editor': 'EDITOR'}])
@pytest.mark.parametrize('cmd_options', [{'interactive': False}])
def test_noninteractive_run_with_returncode(mock_Popen, mock_exit, cmd):
mock_Popen.return_value.communicate.return_value = (b'', b'')
mock_Popen.return_value.returncode = ~0
cmd.run()
mock_exit.assert_called_with(mock_Popen.return_value.returncode)
def test_global_with_defaults(cmd_global):
assert cmd_global._args == ['GLOBAL_COMMAND']
@pytest.mark.parametrize('wiki_options', [{'count': 42}])
def test_global_with_count(cmd_global):
assert cmd_global._args == ['GLOBAL_COMMAND 42']
@pytest.mark.parametrize('wiki_options', [{'count': 42, 'select': True}])
def test_global_with_count_and_select(cmd_global):
assert 'VimwikiUISelect' not in cmd_global._args
@pytest.mark.parametrize('wiki_options', [{'select': True}])
def test_global_select(cmd_global):
assert cmd_global._args == ['VimwikiUISelect', 'GLOBAL_COMMAND']
@pytest.mark.parametrize('wiki_options', [{'select': True}])
@pytest.mark.parametrize('cmd_options', [{'interactive': False}])
def test_global_with_select_forces_interactive(cmd_global):
assert cmd_global.interactive is True
def test_local_with_defaults(cmd_local):
assert cmd_local._args == ['VimwikiIndex', 'LOCAL_COMMAND']
def test_diary_with_defaults(cmd_diary):
assert cmd_diary._args == ['VimwikiDiaryIndex', 'DIARY_COMMAND']

144
tests/test_wiki.py Normal file
View file

@ -0,0 +1,144 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import mock
import pytest
from vimwiki_cli.wiki import *
@mock.patch('vimwiki_cli.wiki.Command')
def test_help(mock_cmd, wiki):
wiki.help()
mock_cmd.assert_called_with(wiki, 'help vimwiki.txt', 'only')
@mock.patch('vimwiki_cli.wiki.GlobalCommand')
def test_index(mock_cmd, wiki):
wiki.index()
mock_cmd.assert_called_with(wiki, 'VimwikiIndex')
@mock.patch('vimwiki_cli.wiki.GlobalCommand')
def test_diary_index(mock_cmd, wiki):
wiki.diary_index()
mock_cmd.assert_called_with(wiki, 'VimwikiDiaryIndex')
@mock.patch('vimwiki_cli.wiki.GlobalCommand')
def test_make_diary_note(mock_cmd, wiki):
wiki.make_diary_note()
mock_cmd.assert_called_with(wiki, 'VimwikiMakeDiaryNote')
@mock.patch('vimwiki_cli.wiki.GlobalCommand')
def test_make_yesterday_diary_note(mock_cmd, wiki):
wiki.make_yesterday_diary_note()
mock_cmd.assert_called_with(wiki, 'VimwikiMakeYesterdayDiaryNote')
@mock.patch('vimwiki_cli.wiki.GlobalCommand')
def test_make_tomorrow_diary_note(mock_cmd, wiki):
wiki.make_tomorrow_diary_note()
mock_cmd.assert_called_with(wiki, 'VimwikiMakeTomorrowDiaryNote')
@mock.patch('vimwiki_cli.wiki.LocalCommand')
def test_goto(mock_cmd, wiki):
wiki.goto('PAGE')
mock_cmd.assert_called_with(wiki, 'VimwikiGoto PAGE')
@mock.patch('vimwiki_cli.wiki.LocalCommand')
def test_search(mock_cmd, wiki):
wiki.search('PATTERN')
mock_cmd.assert_called_with(wiki, 'silent! VimwikiSearch PATTERN',
open_matches=True)
@mock.patch('vimwiki_cli.wiki.LocalCommand')
@pytest.mark.parametrize('args,expected', [
(('PAGE',), ('VimwikiGoto PAGE', 'VimwikiGenerateLinks ')),
(('PAGE', 'PATTERN'), ('VimwikiGoto PAGE', 'VimwikiGenerateLinks PATTERN'))
])
def test_generate_links(mock_cmd, wiki, args, expected):
wiki.generate_links(*args)
mock_cmd.assert_called_with(wiki, *expected,
interactive=False, write_quit=True)
@mock.patch('vimwiki_cli.wiki.DiaryCommand')
def test_diary_generate_links(mock_cmd, wiki):
wiki.diary_generate_links()
mock_cmd.assert_called_with(wiki, 'VimwikiDiaryGenerateLinks',
interactive=False, write_quit=True)
@mock.patch('vimwiki_cli.wiki.LocalCommand')
def test_check_links(mock_cmd, wiki):
wiki.check_links()
mock_cmd.assert_called_with(wiki, 'VimwikiCheckLinks')
@mock.patch('vimwiki_cli.wiki.LocalCommand')
@pytest.mark.parametrize('args,expected', [
(False, 'VimwikiRebuildTags'),
(True, 'VimwikiRebuildTags!')
])
def test_rebuild_tags(mock_cmd, wiki, args, expected):
wiki.rebuild_tags(args)
mock_cmd.assert_called_with(wiki, expected,
interactive=False, quit=True)
@mock.patch('vimwiki_cli.wiki.LocalCommand')
def test_search_tags(mock_cmd, wiki):
wiki.search_tags('PATTERN')
mock_cmd.assert_called_with(wiki, 'silent! VimwikiSearchTags PATTERN',
open_matches=True)
@mock.patch('vimwiki_cli.wiki.LocalCommand')
@pytest.mark.parametrize('args,expected', [
(('PAGE',), ('VimwikiGoto PAGE', 'VimwikiGenerateTagLinks ')),
(('PAGE', ('TAG1', 'TAG2')), ('VimwikiGoto PAGE', 'VimwikiGenerateTagLinks TAG1 TAG2'))
])
def test_generate_tag_links(mock_cmd, wiki, args, expected):
wiki.generate_tag_links(*args)
mock_cmd.assert_called_with(wiki, *expected,
interactive=False, write_quit=True)

25
vimwiki_cli/__init__.py Normal file
View file

@ -0,0 +1,25 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
__version__ = '0.0.0'

139
vimwiki_cli/__main__.py Normal file
View file

@ -0,0 +1,139 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import logging
import sys
import click
from . import __version__
from .context import *
from .diary import diary
from .tags import tags
logger = logging.getLogger(__name__)
@click.group(context_settings=CONTEXT_SETTINGS, invoke_without_command=True,
epilog='Please report issues to https://github.com/sstallion/vimwiki-cli/issues.')
@click.option('--editor',
help='Editor to launch, defaults to $EDITOR or vim.')
@click.option('--count', type=int,
help='Index of wiki to open.')
@click.option('--select', is_flag=True,
help='Select wiki from interactive list.')
@click.option('--open-matches', is_flag=True,
help='Open search results by default.')
@click.option('--open-tabs', is_flag=True,
help='Open pages in a new tab by default.')
@click.option('-v', '--verbose', is_flag=True,
help='Increase output verbosity.')
@click.version_option(message='%(prog)s %(version)s')
@click.pass_context
def cli(ctx, *args, **kwargs):
"""Vimwiki Command-Line Interface
vimwiki is a command-line interface to Vimwiki, a plugin for the Vim text
editor. It provides a front-end for interactive editor commands and can
be used to automate repetitive tasks such as rebuilding tag metadata and
generating links, all from the command line.
Global options may also be configured using environment variables:
\b
VIMWIKI_EDITOR See --editor.
VIMWIKI_COUNT See --count.
VIMWIKI_SELECT See --select.
VIMWIKI_OPEN_MATCHES See --open-matches.
VIMWIKI_OPEN_TABS See --open-tabs.
If no command is specified, the wiki index will be opened by default.
"""
verbose = kwargs.pop('verbose', False)
logging.basicConfig(format='%(levelname)s: %(message)s',
level=logging.DEBUG if verbose else logging.INFO)
logger.debug('Python %s', sys.version)
logger.debug('Version %s', __version__)
make_wiki(ctx, *args, **kwargs)
if ctx.invoked_subcommand is None:
ctx.invoke(index)
@cli.command()
@pass_wiki
def check_links(wiki):
"""Search files and check reachability of links."""
wiki.check_links()
@cli.command()
@click.argument('page', callback=validate_nonempty)
@click.argument('pattern', required=False, default='')
@pass_wiki
def generate_links(wiki, page, pattern):
"""Create or update an overview of all pages in PAGE.
An optional PATTERN may be specified to indicate which files to search for
using a glob path.
"""
wiki.generate_links(page, pattern)
@cli.command()
@click.argument('page', callback=validate_nonempty)
@pass_wiki
def goto(wiki, page):
"""Open or create PAGE."""
wiki.goto(page)
@cli.command()
@pass_wiki
def help(wiki):
"""Open plugin help file."""
wiki.help()
@cli.command(hidden=True)
@pass_wiki
def index(wiki):
"""Open wiki index."""
wiki.index()
@cli.command()
@click.argument('pattern', callback=validate_nonempty)
@pass_wiki
def search(wiki, pattern):
"""Search wiki for text matching PATTERN."""
wiki.search(pattern)
cli.add_command(diary)
cli.add_command(tags)
if __name__ == '__main__': # pragma: no cover
cli()

53
vimwiki_cli/context.py Normal file
View file

@ -0,0 +1,53 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import click
from .wiki import Wiki
CONTEXT_SETTINGS = {
'auto_envvar_prefix': 'VIMWIKI',
'default_map': {
'editor': Wiki.DEFAULT_EDITOR,
'count': Wiki.DEFAULT_COUNT,
'select': Wiki.DEFAULT_SELECT,
'open_matches': Wiki.DEFAULT_OPEN_MATCHES,
'open_tabs': Wiki.DEFAULT_OPEN_TABS
}
}
pass_wiki = click.make_pass_decorator(Wiki, ensure=True)
def make_wiki(ctx, *args, **kwargs):
"""Create Wiki instance as user data and add to context."""
ctx.obj = Wiki(**kwargs)
def validate_nonempty(ctx, param, value):
"""Validate parameter is not an empty string."""
if not value.strip():
raise click.BadParameter('value cannot be empty')
return value

73
vimwiki_cli/diary.py Normal file
View file

@ -0,0 +1,73 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import click
from .context import *
@click.group(invoke_without_command=True)
@click.pass_context
def diary(ctx):
"""Command group for interacting with the diary.
If no command is specified, the diary index will be opened by default.
"""
if ctx.invoked_subcommand is None:
ctx.invoke(index)
@diary.command()
@pass_wiki
def generate_links(wiki):
"""Create or update an overview of diary pages."""
wiki.diary_generate_links()
@diary.command(hidden=True)
@pass_wiki
def index(wiki):
"""Open diary index."""
wiki.diary_index()
@diary.command()
@pass_wiki
def today(wiki):
"""Open diary page for today."""
wiki.make_diary_note()
@diary.command()
@pass_wiki
def tomorrow(wiki):
"""Open diary page for tomorrow."""
wiki.make_tomorrow_diary_note()
@diary.command()
@pass_wiki
def yesterday(wiki):
"""Open diary page for yesterday."""
wiki.make_yesterday_diary_note()

132
vimwiki_cli/editor.py Normal file
View file

@ -0,0 +1,132 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import logging
import os
import subprocess
import sys
logger = logging.getLogger(__name__)
class Command(object):
DEFAULT_INTERACTIVE = True
DEFAULT_OPEN_MATCHES = False
DEFAULT_QUIT = False
DEFAULT_WRITE_QUIT = False
def __init__(self, wiki, *args, **options):
self._wiki = wiki
self._options = options
args = list(args)
if self.interactive:
if wiki.open_tabs:
args.insert(0, '$tabnew')
if self.open_matches and wiki.open_matches:
args.append('lopen')
if self.quit or self.write_quit:
args.append('%sq!' % ('w' if self.write_quit else ''))
self._args = args
def __repr__(self):
return '%s(%r, %r, %r)' % (self.__class__.__name__,
self._wiki,
self._args,
self._options)
@property
def interactive(self):
return self._options.get('interactive', Command.DEFAULT_INTERACTIVE)
@property
def open_matches(self):
return self._options.get('open_matches', Command.DEFAULT_OPEN_MATCHES)
@property
def quit(self):
return self._options.get('quit', Command.DEFAULT_QUIT)
@property
def write_quit(self):
return self._options.get('write_quit', Command.DEFAULT_WRITE_QUIT)
def run(self):
"""Run command in the editor. This method does not return as
interactive commands replace the runing process with the editor and
non-interactive commands exit with the resulting return code.
"""
logger.debug('Running %r' % self)
args = [self._wiki.editor]
for arg in self._args:
args.extend(['-c', arg.strip()])
logger.debug('Launching %r' % args)
if self.interactive:
os.execvp(args[0], args)
else:
process = subprocess.Popen(args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(_, stderr) = process.communicate()
if process.returncode != 0:
logger.error(stderr.decode(sys.stderr.encoding))
sys.exit(process.returncode)
class GlobalCommand(Command):
def __init__(self, wiki, *args, **options):
assert len(args) > 0
args = list(args)
if wiki.count is not None:
args.insert(0, '%s %d' % (args.pop(0), wiki.count))
elif wiki.select:
args.insert(0, 'VimwikiUISelect')
# Force interactive if VimwikiUISelect executed:
options['interactive'] = True
Command.__init__(self, wiki, *args, **options)
class LocalCommand(GlobalCommand):
def __init__(self, wiki, *args, **options):
GlobalCommand.__init__(self, wiki, 'VimwikiIndex', *args, **options)
class DiaryCommand(GlobalCommand):
def __init__(self, wiki, *args, **options):
GlobalCommand.__init__(self, wiki, 'VimwikiDiaryIndex', *args, **options)

62
vimwiki_cli/tags.py Normal file
View file

@ -0,0 +1,62 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import click
from .context import *
@click.group()
def tags():
"""Command group for interacting with tags."""
@tags.command()
@click.argument('page', callback=validate_nonempty)
@click.argument('tags', nargs=-1)
@pass_wiki
def generate_links(wiki, page, tags):
"""Create or update an overview of all tags in PAGE.
An optional list of TAGS may be specified to restrict output. This
command requires tag metadata built using the rebuild subcommand.
"""
wiki.generate_tag_links(page, tags)
@tags.command()
@click.option('--all', is_flag=True,
help='Rebuild all files, not just those that are newer.')
@pass_wiki
def rebuild(wiki, all):
"""Rebuild tag metadata."""
wiki.rebuild_tags(all)
@tags.command()
@click.argument('pattern', callback=validate_nonempty)
@pass_wiki
def search(wiki, pattern):
"""Search wiki for tags matching PATTERN."""
wiki.search_tags(pattern)

135
vimwiki_cli/wiki.py Normal file
View file

@ -0,0 +1,135 @@
# Copyright (c) 2021 Steven Stallion
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import os
from .editor import Command, GlobalCommand, LocalCommand, DiaryCommand
class Wiki(object):
DEFAULT_EDITOR = os.getenv('EDITOR', 'vim')
DEFAULT_COUNT = None
DEFAULT_SELECT = False
DEFAULT_OPEN_MATCHES = False
DEFAULT_OPEN_TABS = False
def __init__(self, **options):
self._options = options
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__,
self._options)
@property
def editor(self):
return self._options.get('editor', Wiki.DEFAULT_EDITOR)
@property
def count(self):
return self._options.get('count', Wiki.DEFAULT_COUNT)
@property
def select(self):
return self._options.get('select', Wiki.DEFAULT_SELECT)
@property
def open_matches(self):
return self._options.get('open_matches', Wiki.DEFAULT_OPEN_MATCHES)
@property
def open_tabs(self):
return self._options.get('open_tabs', Wiki.DEFAULT_OPEN_TABS)
# Help commands:
def help(self):
"""Open plugin help file."""
Command(self, 'help vimwiki.txt', 'only').run()
# Global commands:
def index(self):
"""Open wiki index."""
GlobalCommand(self, 'VimwikiIndex').run()
def diary_index(self):
"""Open diary index."""
GlobalCommand(self, 'VimwikiDiaryIndex').run()
def make_diary_note(self):
"""Open diary page for today."""
GlobalCommand(self, 'VimwikiMakeDiaryNote').run()
def make_yesterday_diary_note(self):
"""Open diary page for yesterday."""
GlobalCommand(self, 'VimwikiMakeYesterdayDiaryNote').run()
def make_tomorrow_diary_note(self):
"""Open diary page for tomorrow."""
GlobalCommand(self, 'VimwikiMakeTomorrowDiaryNote').run()
# Local commands:
def goto(self, page):
"""Open or create page."""
assert page.strip()
LocalCommand(self, 'VimwikiGoto ' + page).run()
def search(self, pattern):
"""Search wiki for text matching pattern."""
assert pattern.strip()
LocalCommand(self, 'silent! VimwikiSearch ' + pattern,
open_matches=True).run()
def generate_links(self, page, pattern=''):
"""Create or update an overview of all pages in page."""
assert page.strip()
LocalCommand(self, 'VimwikiGoto ' + page, 'VimwikiGenerateLinks ' + pattern,
interactive=False, write_quit=True).run()
def diary_generate_links(self):
"""Create or update an overview of diary pages."""
DiaryCommand(self, 'VimwikiDiaryGenerateLinks',
interactive=False, write_quit=True).run()
def check_links(self):
"""Search files and check reachability of links."""
LocalCommand(self, 'VimwikiCheckLinks').run()
def rebuild_tags(self, all=False):
"""Rebuild tag metadata."""
LocalCommand(self, 'VimwikiRebuildTags' + ('!' if all else ''),
interactive=False, quit=True).run()
def search_tags(self, pattern):
"""Search wiki for tags matching pattern."""
assert pattern.strip()
LocalCommand(self, 'silent! VimwikiSearchTags ' + pattern,
open_matches=True).run()
def generate_tag_links(self, page, tags=()):
"""Create or update an overview of all tags in page."""
assert page.strip()
LocalCommand(self, 'VimwikiGoto ' + page, 'VimwikiGenerateTagLinks ' + ' '.join(tags),
interactive=False, write_quit=True).run()