Extending xettel

xettel is built in such a way that it is agnostic as to the way you store your Zettels. By default, it only provides a single class, xettel.impl.markdown.ZettelMD.ZettelMD that provides a very elementary way of handling Markdown Zettels. End users should, and are encouraged to, define their own classes fitting their own needs.

In the extra/config/impl/ folder of the source code, there is the class that I use, which, for instance, provides support for LaTeX snippets and even tikz-cd snippets.

How to make xettel aware of my implementation

Before entering into the details of how to code your own implementation, let us detail how xettel will be aware of your new code.

First, all your extra code should reside in the impl subfolder of your configuration folder (usually ~/.config/xettel). At runtime, xettel imports all the files within this folder.

xettel keeps a registry of all available implementations in xettel.implRegister.

In order to register your class, you should decorate it with xettel.implRegister.register_Z(). The name you give it as a parameter is the name you should give in your configuration to the field Zettelkasten.zk_impl in order to use it.

Should you wish to not use the folder/file metaphor, you will also have to register a Zettelkasten class using xettel.implRegister.register_ZK().

How to code a new implementation

We only describe how to make your own implementation using the folder/file metaphor. Other use-cases are exotic and require a more in-depth understanding of xettel that you can achieve by reading the source code.

Basically, your new implementation should be a subclass of xettel.base.ZettelFile.ZettelFile. This class only reads the file and some of its attributes. From there, there are several ways you can go.

Note

If you just need to work with Markdown Zettels, you may as well make your class a subclass of xettel.impl.markdown.ZettelMD.ZettelMD. Even if you don’t want to use Markdown, this class is a good example to see what to implement and how to do it.

In order to make things more portable, at runtime, the class has access to the config file which is stored as a dictionnary in xettel.config.config.

Parsing a file

If you want to have your Zettels stored in a particular format, the two methods you should overload are xettel.base.ZettelFile.ZettelFile.parse_attributes() and xettel.base.ZAbstract.ZettelAbstract.parse_links().

When overloading parse_attributes(), the raw text is available at the 'bare_contents' key of the xettel.base.ZAbstract.ZettelAbstract.attributes dictionnary.

You may store the attributes in this dictionnary under any name you want, however, there are some conventions that are used when entering the data in xapian.

First of all, the entries at 'uid', 'backlinks' and 'links' are reserved and should not be used. The contents of the Zettel (i.e. everything that is not metadata) should be stored under the key 'text', the title under 'title', tags under 'tags' as a list of strings, the abstract under 'abstract'.

When overloading this method, you should NOT parse links.

Next, when overloading parse_links(), you may safely ignore the links parameter or do nothing with it. In the ZettelMD class, it’s only there for typechecking purposes. This method should find links within the text, convert them to int and pass them to the xettel.base.ZAbstract.ZettelAbstract.parse_links() method.

Exporting

Probably the major reason for having built xettel modularly is to allow end users to have a large control over the export process.

The three methods involved in this process are xettel.base.ZettelFile.ZettelFile.export(), xettel.base.ZettelFile.issue_export_cmd() and xettel.base.ZettelFile.ZettelFile.tag_export(), the latter being there to generate tag pages. All these methods are static methods.

The role of the export() method is to prepare the metadata to be passed to issue_export_cmd() and, check if the destination file is newer than the source and call issue_export_cmd() otherwise, or if force is true. The actual command is executed from issue_export_cmd().

There are two kinds of metadata that is passed to issue_export_cmd(): 'env' is a dictionnary of strings that consists of the environement variables; 'zinfo' is a dictionnary of strings that consists of parameters to pass to the command.

When using, for instance, pandoc, you may specify filters, templates, etc., in the issue_export_cmd() method.

When overloading tag_export(), you have to generate a complete document before sending it to issue_export_cmd().

Adding new search prefixes

Modifying the search process involves dealing directly with xapian. Please read the documentation and the documentation on Python bidings to understand how all of this works.

xapian stores data in two different ways: the document and the terms associated to it. The document consists of arbitrary data and is not a priori searchable, its main purpose is to allow for retreiving data after having found it. The terms are what xapian searches.

Saving data and setting search terms is done by the xettel.base.ZettelFile.ZettelFile.to_xapian() method.

Setting the document

xettel stores the data of a Zettel as a JSON dictionnary containing the attributes. This is done in the method xettel.base.ZettelFile.ZettelFile.prepare_data_for_xapian(), which adds the data of the links, backlinks and UID and prunes the bare content.

Setting the search terms

The method to_xapian() individually sets some universal search terms, such as links, uids, backlinks. For less universal terms, such as titles, abstract, etc., it calls the method xettel.base.ZettelFile.ZettelFile.index_attributes().

This methods iterates over the keys of attributes and works in conjuction with get_indexing_function() to get a function that sets the terms using the provided TermGenerator. Note that throughout the whole process of creating terms for the Zettelkasten, a single term generator is used.

Be careful, when creating new prefixes, that it is expected that they follow some conventions, as explicited in xapian documentation.