main
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4<!-- Oct 07, 2022 -->
  5<meta charset="utf-8" />
  6<meta name="viewport" content="width=device-width, initial-scale=1" />
  7<title>emacs: Managing projects</title>
  8<meta name="author" content="Vincent Demeester" />
  9<meta name="generator" content="Org Mode" />
 10<link rel='icon' type='image/x-icon' href='/images/favicon.ico'/>
 11<meta name='viewport' content='width=device-width, initial-scale=1'>
 12<link rel='stylesheet' href='/css/new.css' type='text/css'/>
 13<link rel='stylesheet' href='/css/syntax.css' type='text/css'/>
 14<link href='/index.xml' rel='alternate' type='application/rss+xml' title='Vincent Demeester' />
 15</head>
 16<body>
 17<main id="content" class="content">
 18<header>
 19<h1 class="title">emacs: Managing projects</h1>
 20</header><p>
 21Working on <b>project</b> a key part of my workflow when using <a href="emacs.html">GNU/Emacs</a>. Almost everything I
 22work on can be part of a project. It might be simpler to give examples:
 23<a href="https://github.com/tektoncd/pipeline"><code>tektoncd/pipeline</code></a> checked out is a project, my <code>~/desktop/org</code> is another project. There
 24is only a handful of buffer in Emacs that I do not consider of any project, one example is
 25the <a href="org_mode.html">org-mode</a> agenda.
 26</p>
 27
 28<p>
 29In a <b>project</b>, I want to be able to:
 30</p>
 31
 32<ul class="org-ul">
 33<li>List <b>all</b> files in that project
 34<ul class="org-ul">
 35<li><i>optionally</i> spiking the files from <code>.gitignore</code></li>
 36</ul></li>
 37<li>Search in all <b>project</b> files
 38<ul class="org-ul">
 39<li>using <code>ripgrep</code> or something else</li>
 40<li>possibly to a search and replace</li>
 41</ul></li>
 42<li>Run commands on the project <span class="underline">root</span> folder
 43<ul class="org-ul">
 44<li>could be a compilation, some tests, some random commands</li>
 45</ul></li>
 46<li>Manage the version control (using <code>magit</code>)
 47<ul class="org-ul">
 48<li>adding files, switch branches, …</li>
 49</ul></li>
 50<li>Switch between project buffers</li>
 51</ul>
 52
 53<p>
 54Emacs 27.1 ships with a <code>project</code> library that has some useful function. But prior to this,
 55the <a href="https://github.com/bbatsov/projectile"><code>projectile</code></a> project has been the way to go for managing projects in
 56<a href="emacs.html">GNU/Emacs</a>. <a href="https://github.com/bbatsov/projectile"><code>projectile</code></a> is also quite extensible and integrates relatively well with a
 57bunch of other libraries.
 58</p>
 59
 60<p>
 61The <i>mnemonics</i> key for the <b>project</b> is <code>C-c p</code>, and thus, any project command will start with
 62that prefix.
 63</p>
 64
 65
 66<nav id="table-of-contents" role="doc-toc">
 67<h2>Table of Contents</h2>
 68<div id="text-table-of-contents" role="doc-toc">
 69<ul>
 70<li><a href="#Projectile">Projectile</a>
 71<ul>
 72<li><a href="#Custom%20project%20types">Custom project types</a>
 73<ul>
 74<li><a href="#%3Dko%3D"><span class="todo TODO">TODO</span> <code>ko</code></a></li>
 75<li><a href="#Others"><span class="todo TODO">TODO</span> Others</a></li>
 76</ul>
 77</li>
 78</ul>
 79</li>
 80<li><a href="#Configuration%20layout">Configuration layout</a></li>
 81</ul>
 82</div>
 83</nav>
 84
 85<section id="outline-container-Projectile" class="outline-2">
 86<h2 id="Projectile">Projectile</h2>
 87<div class="outline-text-2" id="text-Projectile">
 88<p>
 89Let&rsquo;s configure <code>projectile</code> using <code>use-package</code>.
 90</p>
 91
 92<div class="org-src-container">
 93<pre class="src src-emacs-lisp" id="org10b55c6">(use-package projectile
 94  <span class="org-builtin">:commands</span>
 95  (projectile-ack
 96   projectile-ag
 97   projectile-compile-project
 98   projectile-configure-project
 99   projectile-package-project
100   projectile-install-project
101   projectile-test-project
102   projectile-run-project
103   projectile-dired
104   projectile-find-dir
105   projectile-find-file
106   projectile-find-file-dwim
107   projectile-find-file-in-directory
108   projectile-find-tag
109   projectile-test-project
110   projectile-grep
111   projectile-invalidate-cache
112   projectile-kill-buffers
113   projectile-multi-occur
114   projectile-project-p
115   projectile-project-root
116   projectile-recentf
117   projectile-regenerate-tags
118   projectile-replace
119   projectile-replace-regexp
120   projectile-run-async-shell-command-in-root
121   projectile-run-shell-command-in-root
122   projectile-switch-project
123   projectile-switch-to-buffer
124   projectile-vc
125   projectile-commander)
126  <span class="org-builtin">:bind-keymap</span> (<span class="org-string">"C-c p"</span> . projectile-command-map)
127  <span class="org-builtin">:config</span>
128  &lt;&lt;projectile-completion&gt;&gt;
129  &lt;&lt;projectile-variables&gt;&gt;
130  &lt;&lt;projectile-compilation&gt;&gt;
131  &lt;&lt;projectile-known-projects&gt;&gt;
132  &lt;&lt;projectile-commander-methods&gt;&gt;
133  &lt;&lt;projectile-custom-types&gt;&gt;
134  (projectile-mode))
135</pre>
136</div>
137
138<p>
139First thing first, let&rsquo;s tell <code>projectile</code> to use the default completion system instead of
140<code>ivy</code>, or <code>helm</code>, or …
141</p>
142
143<div class="org-src-container">
144<pre class="src src-emacs-lisp" id="orgde12c8e">(<span class="org-keyword">setq-default</span> projectile-completion-system 'default)
145</pre>
146</div>
147
148<p>
149Let&rsquo;s also configure some <code>projectile</code> behavior.
150</p>
151
152<ul class="org-ul">
153<li>The default action when switch to a project should be the <code>commander</code>, as it allows to do
154different actions. This is done by setting <code>projectile-command</code> to
155<code>project-switch-project-action</code>.</li>
156<li>When switching to tests (<code>C-c p t</code>), if there is no test files, create one. This is done
157by setting <code>projectile-create-missing-test-files</code>.</li>
158</ul>
159
160<div class="org-src-container">
161<pre class="src src-emacs-lisp" id="orgfa1a302">(<span class="org-keyword">setq-default</span> projectile-switch-project-action #'projectile-commander
162              projectile-create-missing-test-files t)
163</pre>
164</div>
165
166<p>
167In order to make sure we can have a <i>living</i> compilation per project, we need to modify the
168buffer name to include the project name. This is easily do-able by writing a function for
169<code>compilation-buffer-name-function</code>.
170</p>
171
172<div class="org-src-container">
173<pre class="src src-emacs-lisp" id="org7dccd1f">(<span class="org-keyword">setq-default</span> compilation-buffer-name-function (<span class="org-keyword">lambda</span> (mode) (concat <span class="org-string">"*"</span> (downcase mode) <span class="org-string">": "</span> (projectile-project-name) <span class="org-string">"*"</span>)))
174</pre>
175</div>
176
177<p>
178Do not track known projects automatically, instead call projectile-add-known-project
179Remove dead projects when Emacs is idle
180</p>
181
182<div class="org-src-container">
183<pre class="src src-emacs-lisp" id="orgc9691a9">(<span class="org-keyword">setq-default</span> projectile-track-known-projects-automatically nil)
184(run-with-idle-timer 10 nil #'projectile-cleanup-known-projects)
185</pre>
186</div>
187
188<div class="org-src-container">
189<pre class="src src-emacs-lisp" id="org9967408">(def-projectile-commander-method ?s
190  <span class="org-string">"Open a *shell* buffer for the project"</span>
191  (projectile-run-eshell nil))
192(def-projectile-commander-method ?c
193  <span class="org-string">"Run `</span><span class="org-string"><span class="org-constant">compile</span></span><span class="org-string">' in the project"</span>
194  (projectile-compile-project nil))
195</pre>
196</div>
197</div>
198
199<div id="outline-container-Custom%20project%20types" class="outline-3">
200<h3 id="Custom%20project%20types">Custom project types</h3>
201<div class="outline-text-3" id="text-Custom%20project%20types">
202<p>
203 <a href="https://github.com/bbatsov/projectile"><code>projectile</code></a> allows to add custom project type, in addition to the built-in project
204types. See <a href="https://projectile.readthedocs.io/en/latest/projects/">Projects - Projectile: The Project Interaction Library for Emacs</a> for a little
205bit more detail. It should be possible to configure the <code>configure</code>, <code>compile</code>, <code>package</code>,
206<code>install</code> and <code>test</code> commands. One a the hope of this section is to be able to define highly
207customized project types so that doing <code>C-p u</code> on, let&rsquo;s say, <code>tektoncd/pipeline</code> does the
208right thing by default.
209</p>
210
211<p>
212An example of custom project type is the following.
213</p>
214
215<div class="org-src-container">
216<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Ruby + RSpec</span>
217(projectile-register-project-type 'ruby-rspec '(<span class="org-string">"Gemfile"</span> <span class="org-string">"lib"</span> <span class="org-string">"spec"</span>)
218                                  <span class="org-builtin">:project-file</span> <span class="org-string">"Gemfile"</span>
219                                  <span class="org-builtin">:compile</span> <span class="org-string">"bundle exec rake"</span>
220                                  <span class="org-builtin">:src-dir</span> <span class="org-string">"lib/"</span>
221                                  <span class="org-builtin">:test</span> <span class="org-string">"bundle exec rspec"</span>
222                                  <span class="org-builtin">:test-dir</span> <span class="org-string">"spec/"</span>
223                                  <span class="org-builtin">:test-suffix</span> <span class="org-string">"_spec"</span>)
224</pre>
225</div>
226
227<p>
228One nice aspect of <code>:compile</code> (and some others) is that it can take a symbolic reference to
229a function, which means, you can define dynamic behavior. Based on the doc this works for
230<code>:compile</code>, <code>:configure</code>, <code>:compilation-dir</code> and <code>:run</code> (but <i>my hope is it would work for <code>:test</code>
231and that a <code>:package</code> and an <code>:install</code> would exist</i>).
232</p>
233</div>
234
235<div id="outline-container-%3Dko%3D" class="outline-4">
236<h4 id="%3Dko%3D"><span class="todo TODO">TODO</span> <code>ko</code></h4>
237<div class="outline-text-4" id="text-%3Dko%3D">
238<p>
239First thing first, what makes a <code>ko</code> project. In most cases, a <code>.ko.yaml</code> will be present (at
240the root folder of the project). Let&rsquo;s also define a function do detect if a it&rsquo;s a <code>ko</code>
241project that uses the <i>standard</i> <code>config</code> folder for yamls.
242</p>
243
244<div class="org-src-container">
245<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-project-p</span> ()
246  <span class="org-doc">"Check if a project contains a .ko.yaml file."</span>
247  (projectile-verify-file <span class="org-string">".ko.yaml"</span>))
248(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-with-config-project-p</span> ()
249  <span class="org-doc">"Check if a project is a ko project and has a config/ folder full of yaml"</span>
250  (<span class="org-keyword">and</span> (projectile-ko-project-p)
251       (projectile-verify-file-wildcard <span class="org-string">"config/*.yaml"</span>)))
252</pre>
253</div>
254
255<p>
256Let&rsquo;s register the <code>ko</code> project (with config). Long-term, the idea is to make different
257function for <code>ko</code> and <code>ko-with-config</code> projects.
258</p>
259
260<div class="org-src-container">
261<pre class="src src-emacs-lisp">(projectile-register-project-type 'ko-with-config #'projectile-ko-with-config-project-p
262                                  <span class="org-builtin">:project-file</span> <span class="org-string">".ko.yaml"</span> <span class="org-comment-delimiter">; </span><span class="org-comment">might not be required</span>
263                                  <span class="org-builtin">:configure</span> 'projectile-ko-configure-command
264                                  <span class="org-builtin">:compile</span> 'projectile-ko-compile-command
265                                  <span class="org-builtin">:test</span> 'projectile-ko-test-command
266                                  <span class="org-builtin">:run</span> 'projectile-ko-run-command
267                                  <span class="org-builtin">:package</span> 'projectile-ko-package-command
268                                  <span class="org-builtin">:install</span> 'projectile-ko-install-command)
269</pre>
270</div>
271
272
273<p>
274Let&rsquo;s now dig a little bit more into the configure, compile, test, run, package and
275install commands. As we can pass it a function, we can define behaviour depending on the
276current opened buffer, etc. One assumption that we can make is that a <code>ko</code> project is also a
277<code>go</code> project.
278</p>
279
280<dl class="org-dl">
281<dt>configure</dt><dd><p>
282configure stands for <code>./configure</code> scripts usually. Let&rsquo;s see what it could
283be for <code>ko</code> project. Most likely related to file generations.
284</p>
285<ul class="org-ul">
286<li>default to <code>./hack/update-codegen.sh</code> if it is present.</li>
287</ul>
288<div class="org-src-container">
289<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-configure-command</span> ()
290  <span class="org-doc">"define a configure command for a ko project, depending on the opened file"</span>
291  (<span class="org-keyword">cond</span>
292   ((projectile-file-exists-p <span class="org-string">"hack/update-codegen.sh"</span>) <span class="org-string">"./hack/update-codegen.sh"</span>)))
293</pre>
294</div></dd>
295<dt>compile</dt><dd><p>
296compile might be slightly different depending on the current major mode we
297are in, and maybe also depending on the folder.
298</p>
299
300<ul class="org-ul">
301<li>default to <code>go build -v ./...</code></li>
302<li><code>go</code> file (<code>go-mode</code>)
303<ul class="org-ul">
304<li>default to build the current package</li>
305<li>if it is a test file, tests the current package</li>
306</ul></li>
307</ul>
308
309<div class="org-src-container">
310<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-compile-command</span> ()
311  <span class="org-doc">"define a compile command for a ko project, depending on the openend file "</span>
312  (<span class="org-keyword">cond</span>
313   ((eq major-mode 'go-mode) (projectile-ko-compile-command-go))
314   ((eq major-mode 'yaml-mode) <span class="org-string">"yamllint ."</span>)
315   (t <span class="org-string">"go build -v ./..."</span>)
316   ))
317
318(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-compile-command-go</span> ()
319  <span class="org-doc">"compile command for a ko project if in a go file"</span>
320  (<span class="org-keyword">let*</span> ((current-file (buffer-file-name (current-buffer)))
321         (relative-current-file (file-relative-name current-file (projectile-project-root)))
322         (relative-current-folder (file-name-directory relative-current-file)))
323    (message relative-current-file)
324    (<span class="org-keyword">cond</span>
325     ((string-suffix-p <span class="org-string">"_test.go"</span> relative-current-file) (format <span class="org-string">"go test -c -v ./%s"</span> relative-current-folder))
326     (t (format <span class="org-string">"go build -v ./%s"</span> relative-current-folder)))))
327</pre>
328</div></dd>
329<dt>test</dt><dd><p>
330test might be slightly different depending on the current major mode we are in,
331and might depend on the folder.
332</p>
333
334<ul class="org-ul">
335<li>default to <code>go test -v ./...</code></li>
336<li><code>go</code> file (<code>go-mode</code>)
337<ul class="org-ul">
338<li>default to run tests on the current package</li>
339<li>if it is a test file, tests the current file (like <code>go-test-current-file</code> or
340<code>gotest-ui-current-file</code>)</li>
341</ul></li>
342</ul>
343
344<div class="org-src-container">
345<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-test-command</span> ()
346  <span class="org-doc">"define a test command for a ko project, depending on the openend file"</span>
347  (<span class="org-keyword">cond</span>
348   ((eq major-mode 'go-mode) (projectile-ko-test-command-go))
349   (t <span class="org-string">"go test -v ./..."</span>)))
350
351(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-test-command-go</span> ()
352  <span class="org-doc">"test command for a ko project if in a go file"</span>
353  (<span class="org-keyword">let*</span> ((current-file (buffer-file-name (current-buffer)))
354         (relative-current-file (file-relative-name current-file (projectile-project-root)))
355         (relative-current-folder (file-name-directory relative-current-file)))
356    (<span class="org-keyword">cond</span>
357     ((string-suffix-p <span class="org-string">"_test.go"</span> relative-current-file) (projectile-ko-command-go-test relative-current-file))
358     (t (format <span class="org-string">"go test -v ./%s"</span> relative-current-folder)))))
359
360(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-command-go-test</span> (current-file)
361  <span class="org-doc">"get the command for a go test"</span>
362  (<span class="org-keyword">cond</span>
363   ((gotest-module-available-p) (projectile-ko-command-go-test-gotest current-file))
364   (t (format <span class="org-string">"go test -v ./%s"</span> current-file))))
365
366(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-command-go-test-gotest</span> (current-file)
367  <span class="org-doc">"get the command for a go test with gotest module enabled"</span>
368  (message default-directory)
369  (<span class="org-keyword">let</span> ((data (go-test--get-current-file-testing-data)))
370    (format <span class="org-string">"go test -run='</span><span class="org-string"><span class="org-constant">%s</span></span><span class="org-string">' -v ./%s"</span> data (file-name-directory current-file))))
371
372(<span class="org-keyword">defun</span> <span class="org-function-name">gotest-module-available-p</span> ()
373  <span class="org-doc">"is go-test module available"</span>
374  (fboundp 'go-test--get-current-file-data))
375</pre>
376</div></dd>
377<dt>run</dt><dd><p>
378run is usually about running the project binary or something.
379</p>
380<div class="org-src-container">
381<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-run-command</span> ()
382  <span class="org-doc">"define a run command for a ko project, depending on the openend file "</span>
383  (<span class="org-keyword">cond</span>
384   ((eq major-mode 'go-mode) (projectile-ko-run-command-go))
385   <span class="org-comment-delimiter">;; </span><span class="org-comment">nothing by default ?</span>
386   ))
387
388(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-run-command-go</span> ()
389  <span class="org-doc">"test command for a ko project if in a go file"</span>
390  (<span class="org-keyword">let*</span> ((current-file (buffer-file-name (current-buffer)))
391         (relative-current-file (file-relative-name current-file (projectile-project-root)))
392         (relative-current-folder (file-name-directory relative-current-file)))
393    (<span class="org-keyword">cond</span>
394     ((string-prefix-p <span class="org-string">"cmd/"</span> relative-current-file) (format <span class="org-string">"go run ./%s"</span> relative-current-folder)))))
395</pre>
396</div></dd>
397<dt>package</dt><dd><p>
398package is usually about generating a package, for a maven project this would
399be <code>mvn package</code>, for a project with a <code>Dockerfile</code>, this could be build the image(s). For a
400<code>ko</code> project this is about building and pushing the images that are going to be
401deployed. This is achieved by doing a <code>ko resolve</code>.
402</p>
403<div class="org-src-container">
404<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-package-command</span> ()
405  <span class="org-doc">"define a package command for a ko project, depending on the openend file "</span>
406  (<span class="org-keyword">cond</span>
407   ((eq major-mode 'go-mode) (projectile-ko-package-command-go))
408   (t <span class="org-string">"ko resolve --push=false --oci-layout-path=/tmp/oci -f config"</span>)
409   ))
410
411(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-package-command-go</span> ()
412  <span class="org-doc">"package command for a ko project if in a go file"</span>
413  (<span class="org-keyword">let*</span> ((current-file (buffer-file-name (current-buffer)))
414         (relative-current-file (file-relative-name current-file (projectile-project-root)))
415         (relative-current-folder (file-name-directory relative-current-file)))
416    (<span class="org-keyword">cond</span>
417     ((string-prefix-p <span class="org-string">"cmd/"</span> relative-current-file) (format <span class="org-string">"ko publish --push=false ./%s"</span> relative-current-folder)))))
418</pre>
419</div></dd>
420<dt>install</dt><dd><p>
421install is about installing the project artifact somewhere (usually <code>make install</code>)
422</p>
423<div class="org-src-container">
424<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">projectile-ko-install-command</span> ()
425  <span class="org-doc">"define a install command for a ko project, depending on the openend file "</span>
426  <span class="org-string">"ko apply -f config/"</span>)
427</pre>
428</div></dd>
429</dl>
430</div>
431</div>
432
433<div id="outline-container-Others" class="outline-4">
434<h4 id="Others"><span class="todo TODO">TODO</span> Others</h4>
435<div class="outline-text-4" id="text-Others">
436<ul class="org-ul">
437<li>Detect project type
438<ul class="org-ul">
439<li><code>.ko.yaml</code> =&gt; run is <code>ko apply -f …</code></li>
440<li>is there a <code>Makefile</code> ?</li>
441<li><code>tkn</code> and <code>tekton</code> file</li>
442<li><code>home</code> detection: <code>systems</code>, <code>users</code>, <code>ci.nix</code>, <code>shell.nix</code>,
443<code>hosts.nix</code>, <code>systems.nix</code>
444<ul class="org-ul">
445<li>if in <code>pkgs</code>, run <code>nix-build pkgs -A …</code>, and try to detect the file derivations</li>
446<li>if in <code>tools/emacs</code> (elisp), tangle files from <code>~/desktop/org/notes</code></li>
447<li>detect <code>hostname</code> and act based on it:
448<ul class="org-ul">
449<li><code>naruhodo</code>: <code>make home-switch</code>, …</li>
450<li><code>wakasu</code>: <code>make switch</code>, …</li>
451<li>Could also detect using <code>nixos-version</code></li>
452</ul></li>
453</ul></li>
454</ul></li>
455<li>Hook projectile run/compile/test to multi-compile
456Group things together, so that I can either choose from a list of different compile
457options <b>or</b> run my command</li>
458</ul>
459
460<p>
461<a href="https://github.com/asok/projectile-rails">asok/projectile-rails: Emacs Rails mode based on projectile</a> is also quite interesting.
462</p>
463</div>
464</div>
465</div>
466</section>
467
468<section id="outline-container-Configuration%20layout" class="outline-2">
469<h2 id="Configuration%20layout">Configuration layout</h2>
470<div class="outline-text-2" id="text-Configuration%20layout">
471<p>
472Here we define the <code>config-projects.el</code> file that gets generated by the source blocks in our Org
473document. This is the file that actually gets loaded on startup. The placeholders in
474angled brackets correspond to the <code>NAME</code> directives above the <code>SRC</code> blocks throughout this
475document.
476</p>
477
478<div class="org-src-container">
479<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;;; </span><span class="org-comment">config-projects.el --- -*- lexical-binding: t; -*-</span>
480<span class="org-comment-delimiter">;;; </span><span class="org-comment">Commentary:</span>
481<span class="org-comment-delimiter">;;; </span><span class="org-comment">Project related configuration.</span>
482<span class="org-comment-delimiter">;;; </span><span class="org-comment">This is mainly using projectile now, but built-in projects module seems promising for long-term.</span>
483<span class="org-comment-delimiter">;;; </span><span class="org-comment">Note: this file is autogenerated from an org-mode file.</span>
484<span class="org-comment-delimiter">;;; </span><span class="org-comment">Code:</span>
485
486&lt;&lt;projectile&gt;&gt;
487
488(<span class="org-keyword">provide</span> '<span class="org-constant">config-projects</span>)
489<span class="org-comment-delimiter">;;; </span><span class="org-comment">config-projects.el ends here</span>
490</pre>
491</div>
492</div>
493</section>
494</main>
495<footer id="postamble" class="status">
496<footer>
497     <small><a href="/" rel="history">Index</a><a href="/sitemap.html">Sitemap</a><a href="https://dl.sbr.pm/">Files</a></small><br/>
498     <small class='questions'>Questions, comments ? Please use my <a href="https://lists.sr.ht/~vdemeester/public-inbox">public inbox</a> by sending a plain-text email to <a href="mailto:~vdemeester/public-inbox@lists.sr.ht">~vdemeester/public-inbox@lists.sr.ht</a>.</small><br/>
499     <small class='copyright'>
500      Content and design by Vincent Demeester
501      (<a rel='licence' href='http://creativecommons.org/licenses/by-nc-sa/3.0/'>Some rights reserved</a>)
502    </small><br />
503</footer>
504</footer>
505</body>
506</html>