- A vim style terminal-based LaTeX editor
- Docs here: https://txtr.benji.mom
- Existing editors make writing LaTeX too slow, txtr exists to change that!!
- A continuation of a previous project
I built txtr, a modal LaTeX editor that uses Vim keybinds. I’ve spent a long time on this project (~ half my hours not tracked on flavortown) and have made something pretty cool if I say so myself.
My main motivation for making this was down to a couple things:
txtr ships with quite a few interesting features, some of which took a lot longer than others:
To those reviewing this project, i hope you like it ! I’ve spent long time writing the documentation for this too so it should have everything you need to get going:
txtr.benji.mom
the time has come - to ship txtr

splash page versions
readme cleanup
other bits
Log in to leave a comment
zathura pdf viewer plugin
:pdf - forward-search the current cursor position in Zathura:pdf sync - same as :pdf:pdf open - open the current file’s PDF in Zathura:pdf close - close the Zathura window launched by txtrdocs pages
Log in to leave a comment
zathura plugin
wordcount plugin fixes
other stuff
Log in to leave a comment
compiler synctex
zathura plugin
:pdf, :pdf sync, :pdf open, ``pdf close
both :pdf` and `:pdf sync` - willl do the forward search (what i was talking about above w/synctex)other stuff
Log in to leave a comment
panel resize bug fix
plugin install/update output
other little bits
Log in to leave a comment
Log in to leave a comment
custom keybinds
~/.config/txtr/keybinds.toml when you first run txtr (just like the rest of the config files):w or :build - this means you can create keybinds to build your latex file even faster etc:keybinds reload so you can tweak binds and reload them without restarting txtr:keybinds path too so its obvious what file txtr is reading ur binds frommouse drag selection
other stuff
docs
Log in to leave a comment
custom keybinds
autocomplete popup
tab keytab remains dedicated to indents and snippet expansionplugin installation
plugin info panel

Log in to leave a comment
lots of proper polishing in this devlog tbf - starting to work on making txtr feel more configurable and ready for a proper release :)
custom keybinds [WIP]
:keybinds reload and :keybinds path.~/.config.txtr.keybinds.toml file for all your key bind needs !!clipboard shortcuts
Also working on mouse drag selection - so you can select text by clicking and dragging with your mouse if you prefer that.
Log in to leave a comment
: command.Log in to leave a comment
~/.config/txtr/plugins in order to avoid spamming the user with notifications saying they have got new plugins found in that dir that have not been activatedLog in to leave a comment
Log in to leave a comment
:wordcount an actually useful command
Log in to leave a comment
engine chooses the built-in compiler preset
aux_dir controls where aux and log files go when the engine supports it
custom_cmd overrides the preset completely
autocompile supports:
“off” - requires manual compilation
“save” - compiles on file save but must be initiated with :build at least once
“always” - compiles on file save without needing to initiate with :build
build_log_autohide keeps the log closed unless a build fails
build_log_autoclose closes the build log after successful builds
watch_interval controls :buildwatch debounce timing
Log in to leave a comment
Plugin Development
This page is about writing plugins, not just installing them.
Choose a format
Use a single-file plugin when the plugin is small and self-contained.
use a package plugin if its more complex and needs plenty of helper functions/methods 
The built-in freeze plugin is a package plugin and is a good reference example.
minimal workflow:
subclass PluginBase
set metadata like name, description, version, author
register commands in on_load
unregister your command section in on_unload
recommended metadata
name
description
author
version
commands
commands is worth filling out even for installed-but-not-loaded plugins because txtr can show it in :plugin info.
Log in to leave a comment
Log in to leave a comment
Log in to leave a comment
Log in to leave a comment
~/.config/txtr/plugins to avoid having to edit the pip package code wherever its stored in your device)Log in to leave a comment
:e <directory>.:build actually saves ur file too - its a weird bug that needs fixingLog in to leave a comment
[pluginname]
setting = ""
there is a helper to access config:
Log in to leave a comment
Added some more real authoring helpers - self.config reads from [pluginname] in config - so plugins can set their own config options !
plugins no longer need to poke private panel wiring directly, now working on full support for plugins to render plugins and also textual widgets
Log in to leave a comment

:plugin install logs::plugin update command for manual updates (no arg will update all, arg will update only the passed plugin):plugin uninstall - clue is in the name, it unloads the plugin and removes it completely from diskLog in to leave a comment
:plugin command
:plugin update:plugin uninstallLog in to leave a comment
:plugin update and :plugin uninstall
Log in to leave a comment
:plugin info <plugin> you will actually see all the commands that plugin has without it actually having to be enabledLog in to leave a comment
:plugin info or plugin list you see a much nicer panel with all the plugin details:plugin list - you can select a plugin with arrow keys, hitting enter on a selected plugin will then open the :plugin info panel for that plugin!!Log in to leave a comment
:plugin enable now warns for already-enabled / not-installed cases.:plugin disable now warns for already-disabled cases and removes the plugin from config cleanly.Log in to leave a comment
:plugin list or :plugin info is ran - the only issue with this is that the headers from the build panel are still there (i.e. “compiler”, “success/fail” etc):plugin info panel for that specific plugin!Log in to leave a comment
texitor/core/builtins - builtin plugins (shipped with package)~/.config/txtr/plugins - user installed plugins class MyPlugin(PluginBase):
name = "myplugin
description = "does something cool"
version = "1.0.0"
author = "you"
manifest.toml which contains all the metadata for the pluginon_load - call when enabledon_unload - call on disable or exiton_save - when file savedon_cursor_move - after cursor moveson_mode_change - normal/visual/insert changestatusbar_segment - returns (text, color) or NoneLog in to leave a comment
:plugin list - opens a panel with all the available plugins (currently is using the build panel but with diff content - will update this to a standalone panel when i get a chance):plugin info <plugin> - opens a panel with the information on a requested plugin - this is things like author, description, version etc:plugin enable - enable a plugin that is currently stored in ~/.config/txtr or a built-in one:plugin install - fetch a plugin from the registry and install it:plugin disable - unload from current session and remove from config!!Log in to leave a comment
:plugin list, :plugin enable/disable, :plugin info, plugin install/uninstall
:plugin list command which essentially renders a panel on the screen that shows all available plugins (installed ones and ones that are enabled / disabled):plugin info command too which will essentially pull plugin metadata (version, author etc) - and draw a panel on the screen showing thisLog in to leave a comment
on top of this, i have written the quickstart guide which details how to get familiar with using txtr to write LaTeX (briefly) - covering things like snippets, the help menu and the very basic keybinds you need to use txtr as an editor.
go read the documentation !!
txtr.benji.mom 
Log in to leave a comment
nothing you’ll notice yet if you are using txtr lol
i have started to implement the plugin system
this is a pretty big feature so will definitely take me a while
i have started off by planning out exactly how its gonna work and how the main plugin loading + registry system works:
two plugin formats will be supported, single file and package
single file plugins will literally just be a single file and when you install, txtr will pull the raw file fron the url provided in the plugin registry for that command
package plugins will have a format along the following lines:
mainfest.toml - stores plugin info ie desc, version, author
__init__.py / main.py / plugin.py - main plugin file
and any other helper files that might be needed
there will be a basic plugin registry system - literally just a json file on the repo which is fetched by txtr when you want to install or browse plugins - this will also be checked when you launch the program to see if version numbers still match (if not then update plugin)
the plugin registry system mainly relies on plugin authors supplying a link to the repo where the plugin is stored - which will be git cloned into ~/.config/txtr/plugin if you choose to install that plugin (will also support a plain zip file)
will also have plugin enable/disable so you can have plugins installed that arent actually enabled if needed
this is a lot of work lol and so far all i have done is begin to get my head around all this, made a decent plan (in the comments of the plugin.py file lol) and started to implement a generic plugin loader.
will be working on the docs a bit now so there isnt loads to put on the site when this is finished
Log in to leave a comment
:bw or :buildwatch
citations.bib_files option - this means if you ran config set ... it would override any additional directories, so i made config append where you can edit config and append additional options to the item in config if it is a list !im now starting to work on the plugin system - this is quite a big thing so expect a few devlogs before a working system is in place lol
Log in to leave a comment
\cite{ in insert mode, the autocomplete popup now shows matching entries from your projects .bib files instead of LaTeX commands. (also works with all cite variants like \citep{
:bib command which maually rescans .bib files (use if you added something to your bib)citations.bib_files in config :)Log in to leave a comment
bibparser.py - which holds all the regex for extracting citations/source entries from any passed bibliography files (.lib files)citecompleter.py - which scans the local directory for all .bib files (also checks the config file if any additional dirs have been passed), and proceeds to parse them using bibparser.py before then providing the completions for these - which will show up inside of the auto complete popup (only when typing \cite{}.)see the new installation guide @ https://txtr.benji.mom/installation
Log in to leave a comment
check out the timelapse of me doing all this here
Log in to leave a comment
also added a cool animation at the bottom of the splash screen (worked on it a couple devlogs back too) - its a latex ticker that is essentially a scrolling strip of latex snippets that runs along the bottom of the splash page.
there was a lot of code rewriting in this one but got there in the end :)
Log in to leave a comment
txtr without a filetxtr written in some funky ascii fonts that rotate every time u launch txtr :)q to quit etc)Log in to leave a comment
very peak devlog today
check out the docs at https://txtr.benji.mom
Log in to leave a comment
txtr instead of txtr <filename.tex>
~/.config/txtr/recents.json and can store a maximum of 10 recently opened files!there are no actual visible changes yet since ive done backend work for it only and havent integrated into the main loop but heres some other stuff ive worked on and not demoed fully yet :)
Log in to leave a comment
jump to error
the build panel that appears whenever you compile a latex file now has two different views:
i’ve also finished up the compiler logic to make this all possible lol
Log in to leave a comment
VggGy and you will copy the entire file !Log in to leave a comment
:imstuckintxtrpleasehelpme - which exists txtr :)checkout the timelapse of this session here
i am now working on a better error processing system for the compiler - it will be able to read error logs from your chosen compiler and then output them in a nicer format (perhaps line jumping later??)
Log in to leave a comment
Log in to leave a comment
@register(command="..." ... ) etc
def command_function(self) .....
much nicer and way easier to add commands, also means i’m not typing if statements constantly lmao
Log in to leave a comment
woohoo!! finished the refactor
__init__.py - the core app class, storing the key dispatch loop, autocomplete helpers, and everything else that makes up the core of the app itself.actions.py - this mixin stores every single action method, this is things like mode transitions, cursor movement, yank/paste/delete and all other editing operationscommands.py - the commands mixin which is the core logic for all commands sent to the command line - this is things like write, quit, build, clean etcno behaviour changes (only some very minimal ones regarding warnings sent on build fail) - purely just a structural refactor
i am now working on getting rid of the massive if statement chain that is currently used by the command dispatch and instead making it a much more maintainable system that works directly with the command register :)
esc, u, dd etcLog in to leave a comment
| to identify where a tab stop lies inside a snippet, I have moved to using ${N} where N is the nth tab stop - this also allows for placeholder values by doing ${N:placeholder} which is pretty cool :)Log in to leave a comment