summaryrefslogtreecommitdiff
path: root/developer-manual.org
blob: c4de372df4a6d626f466dba01bf2cb05d1c8bd72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#+TITLE: Nyxt Developer's Manual

# Install org-make-toc so the TOC below will be automatically generated.
# https://github.com/alphapapa/org-make-toc
* Table of contents                                                     :TOC:
:PROPERTIES:
:TOC:      :include all :ignore this
:END:
:CONTENTS:
- [[#bill-of-materials][Bill of Materials]]
  - [[#source][Source]]
  - [[#common-lisp][Common Lisp]]
  - [[#web-renderers][Web renderers]]
    - [[#webkitgtk][WebKitGTK]]
    - [[#electron][Electron]]
  - [[#other][Other]]
- [[#development-environment][Development environment]]
  - [[#tests][Tests]]
  - [[#functional-package-managers][Functional package managers]]
    - [[#guix][Guix]]
    - [[#nix][Nix]]
- [[#installation][Installation]]
- [[#interact-with-a-running-nyxt-binary][Interact with a running Nyxt binary]]
- [[#contributing][Contributing]]
  - [[#help][Help]]
  - [[#commit-style][Commit style]]
  - [[#branch-management][Branch management]]
  - [[#programming-conventions][Programming conventions]]
:END:

* Bill of Materials
** Source

Either get a tarball (=nyxt-<version>-source-with-submodules.tar.xz=) from a
[[https://github.com/atlas-engineer/nyxt/releases][tagged release]], or clone as a git repository:

#+begin_src sh
mkdir -p ~/common-lisp
git clone --recurse-submodules https://github.com/atlas-engineer/nyxt ~/common-lisp/nyxt
#+end_src

** Common Lisp

Nyxt is written in Common Lisp.  Currently, we only target one of its
implementations - [[http://www.sbcl.org/][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 [[https://webkitgtk.org][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 [[https://www.electronjs.org/][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 [[https://github.com/slime/slime][SLIME]] (or its fork [[https://github.com/joaotavora/sly][SLY]]), which requires being
comfortable with Emacs.  Add the snippet below to Emacs' init file.

#+begin_src emacs-lisp
(setq slime-lisp-implementations
      '((nyxt ("sbcl" "--dynamic-space-size 3072")
              :env ("CL_SOURCE_REGISTRY=~/common-lisp//:~/common-lisp/nyxt/_build//"))))
#+end_src

Start the REPL by issuing =M-- M-x sly RET nyxt RET= and evaluate:

#+begin_src lisp
(asdf:load-system :nyxt/gi-gtk)
(nyxt:start)
#+end_src

Note that:

- [[https://asdf.common-lisp.dev/asdf/Configuring-ASDF-to-find-your-systems.html][ASDF must be configured to find the required systems]];
- =cffi= must be configured to find the required shared objects by setting env
  var =LD_LIBRARY_PATH= or =cffi:*foreign-library-directories*=.

** Tests

It is recommended to restart the Lisp image before and after running the tests
since some of them are stateful:

#+begin_src lisp
(asdf:test-system :nyxt/gi-gtk)
#+end_src

** Functional package managers

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

*** Guix

See [[file:~/common-lisp/nyxt/guix.scm][guix.scm]].

#+begin_src emacs-lisp
(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/")))
#+end_src

*** Nix

See [[file:~/common-lisp/nyxt/shell.nix][shell.nix]].

#+begin_src emacs-lisp
(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/")))
#+end_src

* Installation

Nyxt uses the =Make= build system.  Run =make= to display the documentation or
see the [[../makefile][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 [[https://github.com/atlas-engineer/nyxt/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue][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][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.

- To learn Common Lisp, see [[https://nyxt-browser.com/learn-lisp]];
- [[https://github.com/atlas-engineer/nyxt/issues][Open up an issue on GitHub]];
- Find Nyxt on Libera IRC: [[https://kiwiirc.com/nextclient/irc.libera.chat/nyxt][#nyxt]];
- [[https://discord.com/channels/1178074327309099069/1178074327309099072][Nyxt's discord]];
- [[https://discourse.atlas.engineer/][Nyxt's discourse]].

** 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:

#+begin_example
mode/blocker: Short description of the change.

Further explanation.
#+end_example

** 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 [[https://www.cs.umd.edu/~nau/cmsc421/norvig-lisp-style.pdf][Norvig & Pitman's Tutorial on Good Lisp Programming
Style]] and [[https://google.github.io/styleguide/lispguide.xml][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:
#+begin_src lisp
(slot-name
 slot-value
 ...
 :documentation "Foo.")
#+end_src

When =slot-value= is the only parameter specified then:
#+begin_src lisp
(slot-name slot-value)
#+end_src
- =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.

# - Conversion functions =FROM->TO= or =->TO= for generic functions.  The
#   only one that comes to mind is =url= which does not follow this convention...

# - Blocking function should be prefixed with =wait-on-=.

# Local Variables:
# eval: (add-hook 'before-save-hook
#                 (lambda nil (if (fboundp 'org-make-toc)
#                                 (org-make-toc)
#                                 (message-box "Please install org-make-toc.")))
#                 nil
#                 t)
# End: