- 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
Woah, this time already??? I love shipping projects (but hate waiting lol)
If youāre reading this, have fun voting and I wish you the best with your projects!!
txtr is my Vim-style LaTeX editor for the terminal, and this release adds a load of workflow polish on top of the earlier editor/plugin/compiler work.
Highlights:
The thing I am most proud of in this ship is the multi buffer support, it took a while and a lot of pain but its working !! You can open more than one file, keep it open and guess what? When you close the file. txtr even remembers what line and column you were at for when you next open the file!!
The big thing for me is that txtr now feels much less like ājust a cool editor projectā and much more like something you can properly sit in and use.
(thank you so much to the awesome voters on my last ship who recommended me to add or fix a few things - which i have done so!!)

I cut down the README a bit because it was getting cluttered - to those wanting to use this project, go read the docs:
txtr.benji.mom
cursor/session restore
i have written up the entire cursor restore class and also integrated into the main app buffer so that everything works as smoothly as possible
txtr essentially now remembers where you were in a file when you reopen it
for example, if you were editing notes.tex, quit txtr and then reopen that same file later, txtr will restore:
your cursor row
your cursor column
your scroll position
seems like a small update but it honestly makes such a massive difference in terms of workflow and efficiency as a whole - trust me, youāll actually notice it
this works by creating a new persistent state store
txtr stores the state in $XDG_DATA_HOME/txtr/ or in ~/.local/share/txtr if the other var is not set
this is generally where other editors store files such as swap or state files so i thought i would follow that same principle
it is keyed by the fileās absolute path to avoid any duplication or conflicts ;D
i made sure to try and optimise this (although optimization and python dont really mix lol)
it does not save on every key stroke but instead of meaningful file lifecycle moments, this is things like on save. buffer switch. buffer close and app exit.
i also did not want the store become a landfill of old cursor entries over time so old entries get pruned every 30 days (also fully configurable)
wiring cursor state into buffer lifecycle
readme cleanup
docs cleanup
pipx, uv, and venv installs
Log in to leave a comment
bundled defaults
commands.toml or snippets.toml inside of ~/.config/txtr
docs clearup
w/b/e into the docs - with a tiny bit longer explanation stating that they jump between chunks/words just like the binds do in vimreadme tidy

Log in to leave a comment
improved navigation keybinds
w/b/e keybinds\frac{p}{q} are split up more naturally instead of being treated like one big blobhelp menu cleanup
Log in to leave a comment
commands refactor
compiler/build UX refinment
other changes
math aware snippets
outside math those still behave like normal tab snippets, which is the whole
point really
snippets.toml :Dbuild hooks
docs
other bits
Log in to leave a comment
math mode awareness
build post/pre cmds
&&.{file}, {dir}, {stem}, {aux} etc to pass data to those post/pre build commandsother bits
also working on improving snippet matching so it always prefers the longest matching trigger.

Log in to leave a comment
file explorer
:explore / :ex - as commands- in normal mode (a keybind)buffer management commands
:bn - swap to next buffer:bp - swap to previous buffer:buffers / :ls - show all open buffersbuild + buildwatch work
quit/write commands
a modifier that is found in vim-style editorsbuffers panel
:ls / :buffers - which shows your currently open buffers and allows you to also pick files thereother little bits of work
e or from the explorer etc- keybind for explorer
Log in to leave a comment
file explorer
j/k or arrow keys to navigateenter/l to open (a dir or file)h / left to go back to the parent dirq / esc to closethe file explorer also has file previews - which show you the content of the selected file as youre navigating your local directory!
also added some nice icons for directories and files on the actual file tree/ list - very nice if u ask me lol
additional commnands
a modifiera modifier essentially just means that a command applies to all open files instead of just the currently active onewqa etcbug fixes

Log in to leave a comment
file explorer
commands
wqa - save all and quit allqa - quit allwa - save alla is a modifier that means the command w, q etc stops applying to just your active file but every single file open across all buffersw works so it honors single buffer or multi-buffer, depending on the state of txtrq too :Dother bits

Log in to leave a comment
buffer tabs
not much else to really mention in this devlog, a lot of my work has been focused on the file explorer panel and this will continue until its done lol

Log in to leave a comment
multi file support
:e randomfile.tex no longer nukes the current editor state and actually opens it as a new bufferbuffer switching
:bn and :bp- switch to next open buffer and switch to previous buffer:e <already open file> will switch to that file instead of creating a duplicate buffer100j / 100k
docs
:bn / :bp

Log in to leave a comment
just because iāve shipped doesnāt mean the development stops!!
multi file support
:w so it will only save your active file - iāll also add a :a command to signify all - this will allow for commands like :wa (save all)docs and readme changes
pipx to insall txtr system wide
Log in to leave a comment
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