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.