- Bill of Materials
- Development environment
- Installation
- Interact with a running Nyxt binary
- Contributing
Either get a tarball (nyxt-<version>-source-with-submodules.tar.xz
) from a
tagged release, or clone as a git repository:
mkdir -p ~/common-lisp
git clone --recurse-submodules https://github.com/atlas-engineer/nyxt ~/common-lisp/nyxt
Nyxt is written in Common Lisp. Currently, we only target one of its implementations - SBCL.
Nyxt also depends on Common Lisp libraries. These are bundled in the tarball
mentioned above or fetched as git submodules (under ./_build
).
Note for advanced users: the single source of truth for CL libraries is dictated
by the git submodules. Any Nyxt build that deviates from it is considered
unofficial. See environment variable NYXT_SUBMODULES
defined in the makefile
to override the default behavior.
Nyxt is designed to be web engine agnostic so its dependencies vary.
Using the latest WebKitGTK version is advised for security concerns. The oldest version that supports all features is 2.36.
The packages that provide the following shared objects are required:
- libwebkit2gtk-4.1.so
- libgobject-2.0.so
- libgirepository-1.0.so
- libglib-2.0.so
- libgthread-2.0.so
- libgio-2.0.so
- libcairo.so
- libpango-1.0.so
- libpangocairo-1.0.so
- libgdk_pixbuf-2.0.so
- libgdk-3.so
- libgtk-3.so
To improve media stream it is recommended to install gst-libav
and the
following plugins:
- gst-plugins-bad
- gst-plugins-base
- gst-plugins-good
- gst-plugins-ugly
Experimental support for Electron. Further documentation soon.
The packages that provide the following shared objects are required:
- libssl.so.3
- libcrypto.so.3
- libfixposix.so.3
- libsqlite3.so
Additionally, the following packages:
- xclip
- when using X system;
- wl-clipboard
- when using Wayland;
- enchant
- spellchecking (optional).
Lisp favors incremental program development meaning that you make some changes and compile them. In other words, there’s no need to compile the whole codebase or even restart the program.
The typical Common Lisp IDE is SLIME (or its fork SLY), which requires being comfortable with Emacs. Add the snippet below to Emacs’ init file.
(setq slime-lisp-implementations
'((nyxt ("sbcl" "--dynamic-space-size 3072")
:env ("CL_SOURCE_REGISTRY=~/common-lisp//:~/common-lisp/nyxt/_build//"))))
Start the REPL by issuing M-- M-x sly RET nyxt RET
and evaluate:
(asdf:load-system :nyxt/gi-gtk)
(nyxt:start)
Note that:
- ASDF must be configured to find the required systems;
cffi
must be configured to find the required shared objects by setting env varLD_LIBRARY_PATH
orcffi:*foreign-library-directories*
.
It is recommended to restart the Lisp image before and after running the tests since some of them are stateful:
(asdf:test-system :nyxt/gi-gtk)
If you’re a user of the Guix or Nix package managers, see the sections below.
See guix.scm.
(setq slime-lisp-implementations
'((nyxt-guix
("guix" "shell" "-D" "-f" "guix.scm"
"--" "bash" "-c" "env LD_LIBRARY_PATH=\"$GUIX_ENVIRONMENT/lib\" sbcl")
:env ("CL_SOURCE_REGISTRY=~/common-lisp//:~/common-lisp/nyxt/_build//")
:directory "~/common-lisp/nyxt/")))
See shell.nix.
(setq slime-lisp-implementations
'((nyxt-nix
("nix-shell" "shell.nix" "--run" "sbcl --dynamic-space-size 3072")
:env ("CL_SOURCE_REGISTRY=~/common-lisp//:~/common-lisp/nyxt/_build//")
:directory "~/common-lisp/nyxt/")))
Nyxt uses the Make
build system. Run make
to display the documentation or
see the Makefile for more details.
At Nyxt’s runtime, it is possible to attach a REPL via SLIME (resp. SLY).
- From Nyxt, run the command
start-swank
(resp.start-slynk
). Note the port number in the message buffer (by default, 4006). - From Emacs, run
M-x slime-connect RET 127.0.0.1 RET 4006
(resp.sly-connect
).
Nyxt is a joint effort and we welcome contributors! You can find tasks on our issue tracker to suit your interests and skills. Please fork the project and open a pull request (PR) on GitHub to undergo the reviewing process. Refer to the branch management section for more detailed information.
Please resist the temptation of discussing changes without drafting its implementation. Currently, we value pragmatism over creativity.
Feel free to contact us at any point if you need guidance.
- To learn Common Lisp, see https://nyxt-browser.com/learn-lisp;
- Open up an issue on GitHub;
- Find Nyxt on Libera IRC: #nyxt;
- Nyxt’s discord;
- Nyxt’s discourse.
Ensure to isolate commits containing whitespace changes (including indentation) or code movements as to avoid noise in the diffs.
Regarding commit messages, we follow the convention of prefixing the title with
the basename when there’s a single modified file. For instance, for changes in
source/mode/blocker.lisp
the commit message would look as per below:
mode/blocker: Short description of the change. Further explanation.
Nyxt uses the following branches:
master
for development;<feature-branches>
for working on particular features;<integer>-series
to backport commits corresponding to specific major versions.
Branch off from the target branch and rebase onto it right before merging as to avoid merge conflicts.
A commit is said to be atomic when it builds and starts Nyxt successfully. At
times, for the sake of readability, it is wise to break the changes down to
smaller non-atomic commits. In that case, a merge commit is required (use merge
option no-ff
). This guarantees that running git bisect
with option
--first-parent
only picks atomic commits, which streamlines the process.
Those with commit access may push trivial changes directly to the target branch.
The usual style guides by Norvig & Pitman’s Tutorial on Good Lisp Programming Style and Google Common Lisp Style Guide are advised.
For symbol naming conventions, see https://www.cliki.net/Naming+conventions.
Some of our conventions include:
- Prefer
first
andrest
overcar
andcdr
, respectively. - Use
define-class
instead ofdefclass
. - Use
nyxt:define-package
for Nyxt-related pacakges. Notice that it features default imports (e.g.export-always
) and package nicknames (e.g.alex
,sera
, etc.). Preferuiop:define-package
for general purpose packages. - Export using
export-always
next to the symbol definition. This helps prevent exports to go out-of-sync, or catch typos. Unlikeexport
,export-always
saves you from surprises upon recompilation. - When sensible, declaim the function types using
->
. Note that there is then no need to mention the type of the arguments and the return value in the docstring. - Use the
maybe
andmaybe*
types instead of(or null ...)
and(or null (array * (0)) ...)
, respectively. - Use the
list-of
type for typed lists. - Use
funcall*
to not error when function does not exist. - Prefer classes over structs.
- Classes should be usable with just a
make-instance
. - Slots classes should be formatted in the following way:
(slot-name
slot-value
...
:documentation "Foo.")
When slot-value
is the only parameter specified then:
(slot-name slot-value)
customize-instance
is reserved for end users. Useinitialize-instance :after
orslot-unbound
to initialize the slots. Set up the rest of the class incustomize-instance :after
. Bear in mind that anything in this last method won’t be customizable for the end user.- Almost all files should be handled via the
nfiles
library. (setf SLOT-WRITER) :after
is reserved for “watchers”, i.e. handlers that are run whenever the slot is set. The:around
method is not used by watchers, and thus the watcher may be overridden.- We use the
%foo%
naming convention for special local variables. - We suffix predicates with
-p
. Unlike the usual convention, we always use a dash (i.e.foo-p
overfoop
). - Prefer the term
url
overuri
. - URLs should be of type
quri:uri
. If you need to manipulate a URL string, call iturl-string
. In case the value contains a URL, but is notquri:url
, useurl-designator
and itsurl
method to normalize intoquri:uri
. - Paths should be of type
cl:pathname
. Useuiop:native-namestring
to “send” to OS-facing functions,uiop:ensure-pathname
to “receive” from OS-facing functions or to “trunamize”. - Prefer
handler-bind
overhandler-case
: when running from the REPL, this triggers the debugger with a full stacktrace; when running the Nyxt binary, all conditions are caught anyway. - Do not handle the
T
condition, this may break everything. Handleerror
,serious-condition
, or exceptionallycondition
(for instance if you do not control the called code, and some libraries subclasscondition
instead oferror
). - Dummy variables are called
_
. - Prefer American spelling.
- Construct
define-command
requires a short one-line docstring without newlines. - Name keyword function parameters as follows
&key (var default-value var-supplied-p)
.