Skip to content

Latest commit

 

History

History
323 lines (250 loc) · 11.7 KB

developer-manual.org

File metadata and controls

323 lines (250 loc) · 11.7 KB

Nyxt Developer’s Manual

Table of contents

Bill of Materials

Source

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

Common Lisp

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.

Web renderers

Nyxt is designed to be web engine agnostic so its dependencies vary.

WebKitGTK

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

Electron

Experimental support for Electron. Further documentation soon.

Other

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).

Development environment

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:

Tests

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)

Functional package managers

If you’re a user of the Guix or Nix package managers, see the sections below.

Guix

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/")))

Nix

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/")))

Installation

Nyxt uses the Make build system. Run make to display the documentation or see the Makefile for more details.

Interact with a running Nyxt binary

At Nyxt’s runtime, it is possible to attach a REPL via SLIME (resp. SLY).

  1. From Nyxt, run the command start-swank (resp. start-slynk). Note the port number in the message buffer (by default, 4006).
  2. From Emacs, run M-x slime-connect RET 127.0.0.1 RET 4006 (resp. sly-connect).

Contributing

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.

Help

Feel free to contact us at any point if you need guidance.

Commit style

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.

Branch management

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.

Programming conventions

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 and rest over car and cdr, respectively.
  • Use define-class instead of defclass.
  • 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.). Prefer uiop: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. Unlike export, 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 and maybe* 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. Use initialize-instance :after or slot-unbound to initialize the slots. Set up the rest of the class in customize-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 over foop).
  • Prefer the term url over uri.
  • URLs should be of type quri:uri. If you need to manipulate a URL string, call it url-string. In case the value contains a URL, but is not quri:url, use url-designator and its url method to normalize into quri:uri.
  • Paths should be of type cl:pathname. Use uiop:native-namestring to “send” to OS-facing functions, uiop:ensure-pathname to “receive” from OS-facing functions or to “trunamize”.
  • Prefer handler-bind over handler-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. Handle error, serious-condition, or exceptionally condition (for instance if you do not control the called code, and some libraries subclass condition instead of error).
  • 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).