This commit is contained in:
SqrtMinusOne 2022-03-30 12:00:29 +00:00
commit cc0139f007
65 changed files with 16993 additions and 0 deletions

0
.nojekyll Normal file
View file

View file

@ -0,0 +1,47 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBGDvIxUBDADI+XOmlMyqATKaIFZuJs4jXcrHVdSV2/h4tWQSgDluogC3U7nt
1BLeNeJ1iB751LIQ3iAhPNbEah1WRnBaNNRkwLi1kzVWtpNWYNa2tCsnzKd2pYHD
vC0FEgdgd+O3p/Q3lL4GJh0BNZrOE0MEmNYISXlhvDfBEzE30tOTJUpG87jRYOmh
X9Fm0nd0s7lelPLbxPy9RHolsmApESIcnjaXBQdeMMBVrQowCBY5r5LCqLXh0wfp
KOLg21D0Pe8Jq48Kxdq7C1MUdwT9RRkDdffxdH83m6HnOhMHsc9YjiI2Ecnsdwgi
0uhN0NVEoi7ulNOAO4ZKxdlWu2OyKvGAUqA3LeMHcGtglNOid8/aUtwiptoEV+qX
dI65uGH4oKOabRKoRqUp1nrACmJMK55ROvjel36tgWNh3YxCIiyxHwQQxpLwMLtd
C3F+ni7XWZlUy26KTvTXBRdQb6p40Q7XIht7KlJsLNptxYRsG5Zt+nRWmS1Srw2p
eKyU1r45Wxx4TnMAEQEAAbQjUGF2ZWwgS29yeXRvdiA8dGhleGNsb3VkQGdtYWls
LmNvbT6JAdQEEwEIAD4CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQSRRHKh
/Wd1wWb5br7tc5rfgceBYAUCYdGRcAUJAsOh2wAKCRDtc5rfgceBYAd4C/4nJifk
9JzW3bl5cZAEcMgDcWDsmVkSnnotJkOR6uj0Aia2JMdNIOO3AAXb8QDFdlfJ4VrT
w2gSil5DaN+3TL9QNd8QVX10hwJlt29LzB+Cb2MeJkpLXurwIF99FnYm28IAn2NM
yaj/0MP2U756MgoNXuyjoysln2QcG0qS3LFjLUbhkdinIHKU7hs5ZpNSfYwpQsQE
np2k0DVKiksfOh9soBRswr2pezuOHq6ArEiVqwre/4Hq00R+WDqEiCQC/oM3BgI6
8bzFSebexngA1EjNWUyPKoAg2CWEgLvEmAWU1X6lLZoF6L1P1TG1vzbaHnq0pbyg
yZ4ZLic7d87Ol/wLNcNJOozHx4ctuf5piGkTKTsdHh/BVmyMuOwPNWM2DR216DZV
q0pY3ZnL7y3lj2q8009xC4AJdjtrWDKkpZuBl4JVDTplc2rH7A7pWM1kNgC8jy+u
w40/y6SGD9Q4uYv3to/jZlGwuXiiUbygt9Dl3yzOyRw2cyaRdCdizvRIGAiJASIE
EAEIAAwFAmHRjhQFAwASdQAACgkQlxC4m8pXrXxaLQf/fGxORZiRhr4fjSglZIE3
i8yo6A27vdDg1+WCnQoKX2KfQzWxlO/8C3JUw9EaMpkF7lXIFlRU0oVDqXnleXlS
2pBff8s6kaqHSl3/0virum61aLpo/85pPltrcYdCZjzZP3J2UWhFWgBzaY9ADCyW
MvGS6VVpA6bfDmysaJwYGp+HGbi7gv8SGdPETjosCd3q/XR+LkyPskR+w75zONuS
XuzAhCMqusrD/6lsjiqhtwSuJjqIifP5Tq9gdNvdGiiF9kQYHGpOe+XIIT8bSAn0
Zl+7EKKggiB5K5MhfhfMy1TmBYj86QvS0EX6svZe0slQFqwOqToC98Oc/0/raIVU
PbkBjQRg7yMVAQwA1BgphbOMxpqqvwn077dyCk0xUHroxmMF8KmIsjxXRDtFnpPa
wA6ohXwidlmr/5LTXgBkiI3OsdMzDGq4HBgqPD5XVVtQjmvJSylm79E6Th1Yy8o1
it6IG06UDbMv5fKG41dt30MjT9WaB22KqF1qrGR9vCnRtE4HoB3Qej/JeYn++s/Y
3x027tixm0M96oCtHWzDPSgM6B2Gy2+40v8Tg+DRIEK4lJYMq8vjYiHskguAyTVs
a54villu+aLfhugy7OrFc6vp+Zf0uC25HdDdNVbS2pVMeRTJK8WKEvzh64TUFJK5
9GVze8to+hsvl54HrkVJteOxv5bh/BTIug+fWwmFEmDdritCfGggxLeNYCp9fXyE
GOaWxJPz+7FQKghj/4/wj0feE14Ipj3hIVXrMf9Z7mG/KUlajJ1svY5IcN4Rslvb
MdhoI1iSXXg7+QQXBwM9vRShbmlBoTIT6H50US//OOj7d9vq4NYU0KItwceDPyVG
9tH9Qs3EKrbYj1/NABEBAAGJAbwEGAEIACYCGwwWIQSRRHKh/Wd1wWb5br7tc5rf
gceBYAUCYdGRngUJAsOiCQAKCRDtc5rfgceBYB4GC/0Uy/J3e2GJ0CnQcui9gSV/
+Eds/m5kNcWFNe8BYcpZeXvoVD4F0ZgpKs5Gj663OMDLZ+Ea7WYgmz2IOH2cIPnr
6OtVhJWB3y4FFADNC7gCIKz+TxoZHF2Q5nzRHgcV+VhtH3CxfippIYatPATofOLS
OXeLuxQWySNMkvGkL1Xxzd4vVkWMBReE+jbCxzzUAd53lVjpd79oBcSK7K2q67RT
mGAFl8kNdKSz3nCnU55+qhKuvCAefRO24Znp5VM39yeEwHhMJ4mTfpHI5if+F9t2
HEl633Lx+G4pcWFksX2pNlinIdth4EMzKjTPKmn/F3r0Brs9eIBTlD2sBIFwyGV+
G3JLZReHgkYbfWlA8s1lqmWfBfmW6xkk2QXOd8KDZDATfiQc1Qy15WmWVIFK8x90
YqoPL6sAID/G1JZbwXXYmek9Vs2pazZUR1aLK1T5xfXsLz8ywEpfD3/wLHCtIFdu
/PLJ+IFezoD8GEfjDOxsfmQzPWkiPw0bAjd7xyUoWuM=
=Yn+B
-----END PGP PUBLIC KEY BLOCK-----

78
404.html Normal file
View file

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>404 Page not found</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>404 Error</h1>
<p>Page does not exist.</p>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

81
categories/index.html Normal file
View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Categories</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/categories/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>Categories</h1>
<ul>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

10
categories/index.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Categories on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/categories/</link>
<description>Recent content in Categories on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language><atom:link href="https://sqrtminusone.xyz/categories/index.xml" rel="self" type="application/rss+xml" />
</channel>
</rss>

10
config/index.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>https://sqrtminusone.xyz/configs/readme/</title>
<link rel="canonical" href="https://sqrtminusone.xyz/configs/readme/">
<meta name="robots" content="noindex">
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=https://sqrtminusone.xyz/configs/readme/">
</head>
</html>

1017
configs/console/index.html Normal file

File diff suppressed because it is too large Load diff

4098
configs/desktop/index.html Normal file

File diff suppressed because it is too large Load diff

6000
configs/emacs/index.html Normal file

File diff suppressed because it is too large Load diff

737
configs/guix/index.html Normal file
View file

@ -0,0 +1,737 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Guix</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<p><a href="https://guix.gnu.org/">GNU Guix</a> is (1) a transactional package manager and (2) a GNU/Linux distribution.</p>
<p>My personal selling points are declarative package configuration and transactional upgrades.</p>
<p>References:</p>
<ul>
<li><a href="https://guix.gnu.org/en/help/">Official help</a></li>
<li><a href="https://wiki.systemcrafters.cc/guix">System Crafters wiki</a></li>
<li><a href="https://gitlab.com/pjotrp/guix-notes">Pjotr Prins&rsquo; Guix notes</a></li>
<li><a href="https://www.youtube.com/watch?v=iBaqOK75cho&amp;list=PLEoMzSkcN8oNxnj7jm5V2ZcGc52002pQU">Davil Wilson&rsquo;s YouTube series</a></li>
</ul>
<div class="ox-hugo-toc toc">
<div class="heading">Table of Contents</div>
<ul>
<li><a href="#profiles">Profiles</a>
<ul>
<li><a href="#activate-profiles">Activate profiles</a></li>
<li><a href="#update-profiles">Update profiles</a></li>
<li><a href="#run-guix-package-in-profile">Run <code>guix package</code> in profile</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a></li>
<li><a href="#systems">Systems</a>
<ul>
<li><a href="#base-configuration">Base configuration</a></li>
<li><a href="#indigo">indigo</a></li>
<li><a href="#eminence">eminence</a></li>
<li><a href="#azure">azure</a></li>
</ul>
</li>
<li><a href="#system-installation">System installation</a>
<ul>
<li><a href="#preparation">Preparation</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#after-installation">After installation</a></li>
</ul>
</li>
<li><a href="#misc-software-and-notes">Misc software &amp; notes</a>
<ul>
<li><a href="#vpn">VPN</a>
<ul>
<li><a href="#vpn-start">vpn-start</a></li>
<li><a href="#vpn-stop">vpn-stop</a></li>
</ul>
</li>
<li><a href="#flatpak">flatpak</a></li>
<li><a href="#conda">conda</a></li>
<li><a href="#slack">Slack</a></li>
<li><a href="#virt-manager">virt-manager</a></li>
<li><a href="#wakatime-cli">wakatime-cli</a></li>
<li><a href="#manifest">Manifest</a></li>
</ul>
</li>
</ul>
</div>
<!--endtoc-->
<h2 id="profiles">Profiles</h2>
<p>A profile is a way to group Guix packages. Amongst its advantages, profiles can be defined by manifests, which in turn can be stored in VCS.</p>
<p>References:</p>
<ul>
<li><a href="https://guix.gnu.org/en/cookbook/en/html_node/Guix-Profiles-in-Practice.html">Guix Profiles in Practice</a></li>
</ul>
<h3 id="activate-profiles">Activate profiles</h3>
<p>A script to activate guix profiles. Usage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>activate-profiles [profile1] [profile2] ...
</span></span></code></pre></div><p>Source: <a href="https://github.com/daviwil/dotfiles/blob/master/Systems.org#activating-profiles">David Wilson&rsquo;s config</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GREEN<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;\033[1;32m&#39;</span>
</span></span><span style="display:flex;"><span>RED<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;\033[1;30m&#39;</span>
</span></span><span style="display:flex;"><span>NC<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;\033[0m&#39;</span>
</span></span><span style="display:flex;"><span>GUIX_EXTRA_PROFILES<span style="color:#f92672">=</span>$HOME/.guix-extra-profiles
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>profiles<span style="color:#f92672">=</span>$*
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> $# -eq <span style="color:#ae81ff">0</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> profiles<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$HOME<span style="color:#e6db74">/.config/guix/manifests/*.scm&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> profile in $profiles; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Remove the path and file extension, if any</span>
</span></span><span style="display:flex;"><span> profileName<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>basename $profile<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> profileName<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>profileName%.*<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> profilePath<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$GUIX_EXTRA_PROFILES<span style="color:#e6db74">/</span>$profileName<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> manifestPath<span style="color:#f92672">=</span>$HOME/.config/guix/manifests/$profileName.scm
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -f $manifestPath <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> echo
</span></span><span style="display:flex;"><span> echo -e <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>GREEN<span style="color:#e6db74">}</span><span style="color:#e6db74">Activating profile:&#34;</span> $manifestPath <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>NC<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> echo
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> mkdir -p $profilePath
</span></span><span style="display:flex;"><span> guix package --manifest<span style="color:#f92672">=</span>$manifestPath --profile<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$profilePath<span style="color:#e6db74">/</span>$profileName<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Source the new profile</span>
</span></span><span style="display:flex;"><span> GUIX_PROFILE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$profilePath<span style="color:#e6db74">/</span>$profileName<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -f $GUIX_PROFILE/etc/profile <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> . <span style="color:#e6db74">&#34;</span>$GUIX_PROFILE<span style="color:#e6db74">&#34;</span>/etc/profile
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> echo -e <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>RED<span style="color:#e6db74">}</span><span style="color:#e6db74">Couldn&#39;t find profile:&#34;</span> $GUIX_PROFILE/etc/profile <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>NC<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;No profile found at path&#34;</span> $profilePath
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><h3 id="update-profiles">Update profiles</h3>
<p>A script to update Guix profiles. Usage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>update-profiles [profile1] [profile2] ...
</span></span></code></pre></div><p>Source: once again, <a href="https://github.com/daviwil/dotfiles/blob/master/Systems.org#updating-profiles">David Wilson&rsquo;s config</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GREEN<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;\033[1;32m&#39;</span>
</span></span><span style="display:flex;"><span>NC<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;\033[0m&#39;</span>
</span></span><span style="display:flex;"><span>GUIX_EXTRA_PROFILES<span style="color:#f92672">=</span>$HOME/.guix-extra-profiles
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>profiles<span style="color:#f92672">=</span>$*
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> $# -eq <span style="color:#ae81ff">0</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> profiles<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$GUIX_EXTRA_PROFILES<span style="color:#e6db74">/*&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> profile in $profiles; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> profileName<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>basename $profile<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> profilePath<span style="color:#f92672">=</span>$GUIX_EXTRA_PROFILES/$profileName
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> echo
</span></span><span style="display:flex;"><span> echo -e <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>GREEN<span style="color:#e6db74">}</span><span style="color:#e6db74">Updating profile:&#34;</span> $profilePath <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>NC<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> echo
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> guix package --profile<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$profilePath<span style="color:#e6db74">/</span>$profileName<span style="color:#e6db74">&#34;</span> --manifest<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$HOME<span style="color:#e6db74">/.config/guix/manifests/</span>$profileName<span style="color:#e6db74">.scm&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><h3 id="run-guix-package-in-profile">Run <code>guix package</code> in profile</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GUIX_EXTRA_PROFILES<span style="color:#f92672">=</span>$HOME/.guix-extra-profiles
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>profileName<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>basename $1<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>profileName<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>profileName%.*<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>profilePath<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$GUIX_EXTRA_PROFILES<span style="color:#e6db74">/</span>$profileName<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -d $profilePath <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> guix package --profile<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$profilePath<span style="color:#e6db74">/</span>$profileName<span style="color:#e6db74">&#34;</span> <span style="color:#e6db74">${</span>@:2<span style="color:#e6db74">}</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> echo -e <span style="color:#e6db74">&#34;No profile found at path: &#34;</span> $profilePath
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span></code></pre></div><h2 id="channels">Channels</h2>
<p>Specifying additional channels.</p>
<p><a href="https://github.com/SqrtMinusOne/channel-q">channel-q</a> is my Guix channel. Don&rsquo;t use it at home.</p>
<p>References:</p>
<ul>
<li><a href="https://gitlab.com/nonguix/nonguix">nonguix channel repo</a></li>
<li><a href="https://guix.gnu.org/manual/en/html_node/Channels.html">Guix channels reference</a></li>
</ul>
<!--listend-->
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">cons*</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">channel</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#39;channel-q</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">url</span> <span style="color:#e6db74">&#34;file:///home/pavel/Code/channel-q&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">channel</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#39;flat</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">url</span> <span style="color:#e6db74">&#34;https://github.com/flatwhatson/guix-channel.git&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">introduction</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">make-channel-introduction</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;33f86a4b48205c0dc19d7c036c85393f0766f806&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">openpgp-fingerprint</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;736A C00E 1254 378B A982 7AF6 9DBE 8265 81B6 4490&#34;</span>))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">channel</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#39;nonguix</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">url</span> <span style="color:#e6db74">&#34;https://gitlab.com/nonguix/nonguix&#34;</span>)
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; (commit &#34;d54973e47b89fe5772a5b6e2d0c0b86acb089e27&#34;)</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">introduction</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">make-channel-introduction</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;897c1a470da759236cc11798f4e0a5f7d4d59fbc&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">openpgp-fingerprint</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5&#34;</span>))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">channel</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; What can possibly go wrong, huh</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#39;guix-gaming-games</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">url</span> <span style="color:#e6db74">&#34;https://gitlab.com/guix-gaming-channels/games.git&#34;</span>)
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Enable signature verification:</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">introduction</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">make-channel-introduction</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;c23d64f1b8cc086659f8781b27ab6c7314c5cca5&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">openpgp-fingerprint</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;50F3 3E2E 5B0C 3D90 0424 ABE8 9BDC F497 A4BB CC7F&#34;</span>))))
</span></span><span style="display:flex;"><span> %default-channels)
</span></span></code></pre></div><h2 id="systems">Systems</h2>
<p>Configuring systems with Guix.</p>
<p>Yes, all my machines are named after colors I like.</p>
<h3 id="base-configuration">Base configuration</h3>
<p>The base configuration is shared between all the machines.</p>
<p>While it&rsquo;s possible to make a single <code>.scm</code> file with base configuration and load it, I noticed that it produces more cryptic error messages whenever there is an error in the base file, so I opt-in for noweb.</p>
<p><code>guix system</code> invocation is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>sudo -E guix system reconfigure ~/.config/guix/systems/[system].scm
</span></span></code></pre></div><p>Common modules:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span>))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> system nss))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages bash))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> ((<span style="color:#a6e22e">gnu</span> packages base) <span style="color:#f92672">#</span>:select (<span style="color:#a6e22e">coreutils</span> glibc)))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages certs))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages version-control))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages vim))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages gnome))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages xorg))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages wm))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> packages openbox))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> services docker))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> services cups))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">gnu</span> services virtualization))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">srfi</span> srfi-1))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">guix</span> channels))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">guix</span> inferior))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">nongnu</span> packages linux))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-modules</span> (<span style="color:#a6e22e">nongnu</span> system linux-initrd))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-service-modules</span> desktop networking ssh xorg nix)
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">use-package-modules</span> ssh)
</span></span></code></pre></div><p>In principle, we could define a variable called <code>base-operating-system</code> and extend it in ancestors. However, then we would have to define mandatory fields like <code>host-name</code>, <code>bootloader</code> with dummy values. Since I&rsquo;m already using noweb, there is little point.</p>
<p>The following code will be inserted at the top of the <code>operating-system</code> definition.</p>
<p>Use the full Linux kernel. I hope I&rsquo;ll be able to use Libre kernel somewhere later.</p>
<p>Inferior in the kernel is used to avoid recompilation. It looks like I can pin these to different commits than in my <code>channels.scm</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">kernel</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">let*</span>
</span></span><span style="display:flex;"><span> ((<span style="color:#a6e22e">channels</span>
</span></span><span style="display:flex;"><span> (list (<span style="color:#a6e22e">channel</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#39;nonguix</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">url</span> <span style="color:#e6db74">&#34;https://gitlab.com/nonguix/nonguix&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">commit</span> <span style="color:#e6db74">&#34;393b8e0405f44835c498d7735a8ae9ff4682b07f&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">channel</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#39;guix</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">url</span> <span style="color:#e6db74">&#34;https://git.savannah.gnu.org/git/guix.git&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">commit</span> <span style="color:#e6db74">&#34;4c812db049d5c9f2c438748e180f9486ad221b0a&#34;</span>))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">inferior</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">inferior-for-channels</span> channels)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">first</span> (<span style="color:#a6e22e">lookup-inferior-packages</span> inferior <span style="color:#e6db74">&#34;linux&#34;</span> <span style="color:#e6db74">&#34;5.15.12&#34;</span>))))
</span></span><span style="display:flex;"><span><span style="color:#75715e">;; (kernel linux)</span>
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">initrd</span> microcode-initrd)
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">firmware</span> (list linux-firmware))
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">locale</span> <span style="color:#e6db74">&#34;en_US.utf8&#34;</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">timezone</span> <span style="color:#e6db74">&#34;Europe/Moscow&#34;</span>)
</span></span></code></pre></div><p>US/RU keyboard layout, switch with Alt+Shift.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">keyboard-layout</span> (<span style="color:#a6e22e">keyboard-layout</span> <span style="color:#e6db74">&#34;us,ru&#34;</span> <span style="color:#f92672">#</span>:options <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;grp:alt_shift_toggle&#34;</span>)))
</span></span></code></pre></div><p>User accounts.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">users</span> (<span style="color:#a6e22e">cons*</span> (<span style="color:#a6e22e">user-account</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#34;pavel&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">comment</span> <span style="color:#e6db74">&#34;Pavel&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">group</span> <span style="color:#e6db74">&#34;users&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">home-directory</span> <span style="color:#e6db74">&#34;/home/pavel&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">supplementary-groups</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;wheel&#34;</span> <span style="color:#75715e">;; sudo</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;netdev&#34;</span> <span style="color:#75715e">;; network devices</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;audio&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;video&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;input&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;tty&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;docker&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;scanner&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;libvirt&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;lp&#34;</span>)))
</span></span><span style="display:flex;"><span> %base-user-accounts))
</span></span></code></pre></div><p>Base packages, necessary right after the installation.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">packages</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">append</span>
</span></span><span style="display:flex;"><span> (list nss-certs
</span></span><span style="display:flex;"><span> git
</span></span><span style="display:flex;"><span> i3-gaps
</span></span><span style="display:flex;"><span> i3lock
</span></span><span style="display:flex;"><span> openbox
</span></span><span style="display:flex;"><span> xterm
</span></span><span style="display:flex;"><span> vim)
</span></span><span style="display:flex;"><span> %base-packages))
</span></span></code></pre></div><p>Default services for each machine:</p>
<ul>
<li>override the default <code>%desktop-services</code> to add OpenVPN support</li>
<li>add nix service</li>
<li>add docker service</li>
<li>add CUPS service</li>
<li>add libvirt service</li>
<li>add a symlink to ELF interpreter to where most Linux binaries expect it</li>
</ul>
<!--listend-->
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#66d9ef">define </span>%my-base-services
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">cons*</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">service</span> openssh-service-type)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">screen-locker-service</span> i3lock <span style="color:#e6db74">&#34;i3lock&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">extra-special-file</span> <span style="color:#e6db74">&#34;/lib64/ld-linux-x86-64.so.2&#34;</span> (<span style="color:#a6e22e">file-append</span> glibc <span style="color:#e6db74">&#34;/lib/ld-linux-x86-64.so.2&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">service</span> nix-service-type)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">service</span> cups-service-type
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">cups-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">web-interface?</span> <span style="color:#66d9ef">#t</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">service</span> docker-service-type)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">service</span> libvirt-service-type
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">libvirt-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">unix-sock-group</span> <span style="color:#e6db74">&#34;libvirt&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">tls-port</span> <span style="color:#e6db74">&#34;16555&#34;</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">service</span> virtlog-service-type)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">modify-services</span> %desktop-services
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">network-manager-service-type</span>
</span></span><span style="display:flex;"><span> config =&gt;
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">network-manager-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">inherit</span> config)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">vpn-plugins</span> (list network-manager-openvpn)))))))
</span></span></code></pre></div><h3 id="indigo">indigo</h3>
<p><code>indigo</code> is my desktop PC.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>&lt;&lt;system-common&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">operating-system</span>
</span></span><span style="display:flex;"><span> &lt;&lt;system-base&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">host-name</span> <span style="color:#e6db74">&#34;indigo&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">services</span> (<span style="color:#a6e22e">cons*</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">set-xorg-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">xorg-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">keyboard-layout</span> keyboard-layout)))
</span></span><span style="display:flex;"><span> %my-base-services))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader</span> grub-efi-bootloader)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">target</span> <span style="color:#e6db74">&#34;/boot/efi&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">keyboard-layout</span> keyboard-layout)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">swap-devices</span>
</span></span><span style="display:flex;"><span> (list (<span style="color:#a6e22e">uuid</span> <span style="color:#e6db74">&#34;3a77c542-7d24-46ff-8123-f7398d1c2677&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-systems</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">cons*</span> (<span style="color:#a6e22e">file-system</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mount-point</span> <span style="color:#e6db74">&#34;/&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">device</span> (<span style="color:#a6e22e">file-system-label</span> <span style="color:#e6db74">&#34;my-root&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;ext4&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-system</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mount-point</span> <span style="color:#e6db74">&#34;/boot/efi&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">device</span> <span style="color:#e6db74">&#34;/dev/sda1&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;vfat&#34;</span>))
</span></span><span style="display:flex;"><span> %base-file-systems)))
</span></span></code></pre></div><h3 id="eminence">eminence</h3>
<p><code>eminence</code> is a HP 15s laptop.</p>
<p><code>%backlight-udev-rule</code> should enable members of <code>video</code> group change the display backlight. See the relevant page at <a href="https://wiki.archlinux.org/title/Backlight">Arch Wiki</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>&lt;&lt;system-common&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#66d9ef">define </span>%backlight-udev-rule
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-rule</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;90-backlight.rules&#34;</span>
</span></span><span style="display:flex;"><span> (string-append <span style="color:#e6db74">&#34;ACTION==\&#34;add\&#34;, SUBSYSTEM==\&#34;backlight\&#34;, &#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;RUN+=\&#34;/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\&#34;&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;\n&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;ACTION==\&#34;add\&#34;, SUBSYSTEM==\&#34;backlight\&#34;, &#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;RUN+=\&#34;/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\&#34;&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">operating-system</span>
</span></span><span style="display:flex;"><span> &lt;&lt;system-base&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">host-name</span> <span style="color:#e6db74">&#34;eminence&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">services</span> (<span style="color:#a6e22e">cons*</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">set-xorg-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">xorg-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">keyboard-layout</span> keyboard-layout)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">modify-services</span> %my-base-services
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">elogind-service-type</span>
</span></span><span style="display:flex;"><span> config =&gt;
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">elogind-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">inherit</span> config)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">handle-lid-switch-external-power</span> <span style="color:#e6db74">&#39;suspend</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-service-type</span>
</span></span><span style="display:flex;"><span> config =&gt;
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">inherit</span> config)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">rules</span> (cons %backlight-udev-rule
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-configuration-rules</span> config))))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader</span> grub-efi-bootloader)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">target</span> <span style="color:#e6db74">&#34;/boot/efi&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">keyboard-layout</span> keyboard-layout)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">swap-devices</span>
</span></span><span style="display:flex;"><span> (list (<span style="color:#a6e22e">uuid</span> <span style="color:#e6db74">&#34;f93cf3f6-7ee7-42ec-8ee2-f3d896fdf9b5&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-systems</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">cons*</span> (<span style="color:#a6e22e">file-system</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mount-point</span> <span style="color:#e6db74">&#34;/&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">device</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">uuid</span> <span style="color:#e6db74">&#34;1d937704-bbeb-43b5-bc63-453886c426af&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;ext4</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;ext4&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-system</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mount-point</span> <span style="color:#e6db74">&#34;/boot/efi&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">device</span> (<span style="color:#a6e22e">uuid</span> <span style="color:#e6db74">&#34;0031-3784&#34;</span> <span style="color:#e6db74">&#39;fat32</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;vfat&#34;</span>))
</span></span><span style="display:flex;"><span> %base-file-systems)))
</span></span></code></pre></div><h3 id="azure">azure</h3>
<p><code>azure</code> is a Lenovo Ideapad 330 laptop.</p>
<p><code>%backlight-udev-rule</code> should enable members of <code>video</code> group change the display backlight. See the relevant page at <a href="https://wiki.archlinux.org/title/Backlight">Arch Wiki</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>&lt;&lt;system-common&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#66d9ef">define </span>%backlight-udev-rule
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-rule</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;90-backlight.rules&#34;</span>
</span></span><span style="display:flex;"><span> (string-append <span style="color:#e6db74">&#34;ACTION==\&#34;add\&#34;, SUBSYSTEM==\&#34;backlight\&#34;, &#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;RUN+=\&#34;/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\&#34;&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;\n&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;ACTION==\&#34;add\&#34;, SUBSYSTEM==\&#34;backlight\&#34;, &#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;RUN+=\&#34;/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\&#34;&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#a6e22e">operating-system</span>
</span></span><span style="display:flex;"><span> &lt;&lt;system-base&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">host-name</span> <span style="color:#e6db74">&#34;azure&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">services</span> (<span style="color:#a6e22e">cons*</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">set-xorg-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">xorg-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">keyboard-layout</span> keyboard-layout)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">modify-services</span> %my-base-services
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">elogind-service-type</span> config =&gt;
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">elogind-configuration</span> (<span style="color:#a6e22e">inherit</span> config)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">handle-lid-switch-external-power</span> <span style="color:#e6db74">&#39;suspend</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-service-type</span> config =&gt;
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-configuration</span> (<span style="color:#a6e22e">inherit</span> config)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">rules</span> (cons %backlight-udev-rule
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">udev-configuration-rules</span> config))))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader-configuration</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">bootloader</span> grub-efi-bootloader)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">target</span> <span style="color:#e6db74">&#34;/boot/efi&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">keyboard-layout</span> keyboard-layout)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">swap-devices</span>
</span></span><span style="display:flex;"><span> (list (<span style="color:#a6e22e">uuid</span> <span style="color:#e6db74">&#34;4b2dedb3-b111-4e69-8c05-6daa2b072c76&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-systems</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">cons*</span> (<span style="color:#a6e22e">file-system</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mount-point</span> <span style="color:#e6db74">&#34;/&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">device</span> (<span style="color:#a6e22e">file-system-label</span> <span style="color:#e6db74">&#34;my-root&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;ext4&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-system</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mount-point</span> <span style="color:#e6db74">&#34;/boot/efi&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">device</span> <span style="color:#e6db74">&#34;/dev/sda1&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;vfat&#34;</span>))
</span></span><span style="display:flex;"><span> %base-file-systems)))
</span></span></code></pre></div><h2 id="system-installation">System installation</h2>
<h3 id="preparation">Preparation</h3>
<p>In my case, the provided ISO doesn&rsquo;t work because of the Libre kernel.</p>
<p>Fortunately, David Wilson has made <a href="https://github.com/SystemCrafters/guix-installer">a repository</a> with a toolchain to make an ISO with the full kernel. In case it won&rsquo;t be an option, the <a href="https://gitlab.com/nonguix/nonguix">nonguix repo</a> also has instructions on how to do that.</p>
<p>When an ISO is there, we have to write it on a USB stick. Run <code>sudo fdisk -l</code> to get a list of disks.</p>
<p>The approach given in the official instruction is to create a bootable USB with <code>dd</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>sudo dd of=/dev/sdxX if=&lt;path-to-iso&gt; status=progress &amp;&amp; sync
</span></span></code></pre></div><p>However, I couldn&rsquo;t make it work for some strange reason. Fortunately, <code>gnome-disk-utility</code> was able to produce a bootable USB.</p>
<h3 id="installation">Installation</h3>
<p>Going further, the official instructions for installation &amp; SystemCrafters wiki entry are pretty good, so it&rsquo;s not necessary to repeat them here.</p>
<h3 id="after-installation">After installation</h3>
<p>After the installation, the strategy is as follows.</p>
<p>Set a password for the main user (pavel). Login with openbox to get a tolerable interface because i3&rsquo;s default config is horrible.</p>
<p><a href="https://guix.gnu.org/en/manual/en/html_node/Keyboard-Layout-and-Networking-and-Partitioning.html#Keyboard-Layout-and-Networking-and-Partitioning">Connect to the internet</a>.</p>
<p>Clone the dotfiles repo:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>mkdir Code
</span></span><span style="display:flex;"><span>cd Code
</span></span><span style="display:flex;"><span>git clone https://github.com/SqrtMinusOne/dotfiles.git
</span></span></code></pre></div><p>Copy the channels file and run guix pull:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>cp ~/Code/dotfiles/.config/guix/channels.scm ~/.config/guix
</span></span><span style="display:flex;"><span>guix pull
</span></span></code></pre></div><p>The first pull usually takes a while. After that install yadm and pull dotfiles:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>guix install yadm
</span></span><span style="display:flex;"><span>guix clone https://github.com/SqrtMinusOne/dotfiles.git
</span></span></code></pre></div><p>And activate the required profiles. Again, downloading &amp; building Emacs, Starship and stuff will take a while.</p>
<p>Don&rsquo;t forget to install <code>JetBrainsMono Nerd Font</code>.</p>
<h2 id="misc-software-and-notes">Misc software &amp; notes</h2>
<table>
<thead>
<tr>
<th>Category</th>
<th>Guix dependency</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>system</td>
<td>patchelf</td>
<td>A program to modify existsing ELF executables</td>
</tr>
<tr>
<td>system</td>
<td>glibc</td>
<td>A lot of stuff, including ELF interpeter and <code>ldd</code></td>
</tr>
</tbody>
</table>
<h3 id="vpn">VPN</h3>
<table>
<thead>
<tr>
<th>Category</th>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>system</td>
<td>openvpn</td>
</tr>
<tr>
<td>system</td>
<td>openvpn-update-resolve-conf</td>
</tr>
<tr>
<td>system</td>
<td>vpnc</td>
</tr>
</tbody>
</table>
<p>I&rsquo;m not sure how to properly spin up VPN on Guix, so here is what ended I&rsquo;m doing after some trial and error.</p>
<p>I&rsquo;m using CyberGhost VPN. <code>~/.vpn</code> folder stores its OpenVPN config (<code>openvpn.ovpn</code>), modified as follows:</p>
<ul>
<li>
<p>paths to <code>ca</code>, <code>cert</code> and <code>key</code> are made absolute</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#a6e22e">ca</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.vpn/</span><span style="color:#a6e22e">ca</span>.<span style="color:#a6e22e">crt</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">cert</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.vpn/</span><span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">crt</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">key</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.vpn/</span><span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">key</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div></li>
<li>
<p>added <code>auth-user-pass</code> with a link to login info</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#a6e22e">auth</span>-<span style="color:#a6e22e">user</span>-<span style="color:#a6e22e">pass</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.vpn/</span><span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">conf</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p><code>auth.conf</code> looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>login
</span></span><span style="display:flex;"><span>password
</span></span></code></pre></div></li>
<li>
<p>run <a href="https://github.com/alfredopalhares/openvpn-update-resolv-conf">openvpn-update-resolv-conf</a> script to fix DNS. <code>openvpn-update-resolve-conf</code> originates in my <a href="https://github.com/SqrtMinusOne/channel-q">channel-q</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#a6e22e">setenv</span> <span style="color:#a6e22e">PATH</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.guix-extra-profiles/</span><span style="color:#a6e22e">system</span><span style="color:#e6db74">/system/</span><span style="color:#a6e22e">bin</span>:<span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.guix-extra-profiles/</span><span style="color:#a6e22e">system</span><span style="color:#e6db74">/system/</span><span style="color:#a6e22e">sbin</span>:<span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.guix-extra-profiles/</span><span style="color:#a6e22e">console</span><span style="color:#e6db74">/console/</span><span style="color:#a6e22e">bin</span>:<span style="color:#e6db74">/run/</span><span style="color:#a6e22e">current</span>-<span style="color:#a6e22e">system</span><span style="color:#e6db74">/profile/</span><span style="color:#a6e22e">bin</span>:<span style="color:#e6db74">/run/</span><span style="color:#a6e22e">current</span>-<span style="color:#a6e22e">system</span><span style="color:#e6db74">/profile/</span><span style="color:#a6e22e">sbin</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">up</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.guix-extra-profiles/</span><span style="color:#a6e22e">system</span><span style="color:#e6db74">/system/</span><span style="color:#a6e22e">bin</span>/<span style="color:#a6e22e">update</span>-<span style="color:#a6e22e">resolv</span>-<span style="color:#a6e22e">conf</span>.<span style="color:#a6e22e">sh</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">down</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/.guix-extra-profiles/</span><span style="color:#a6e22e">system</span><span style="color:#e6db74">/system/</span><span style="color:#a6e22e">bin</span>/<span style="color:#a6e22e">update</span>-<span style="color:#a6e22e">resolv</span>-<span style="color:#a6e22e">conf</span>.<span style="color:#a6e22e">sh</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p><code>setenv PATH</code> is necessary because both <code>resolvconf</code> (openresolve) and <code>update-resolv-conf.sh</code> are shell scripts which need GNU coreutils and stuff, and OpenVPN clears PATH by default.</p>
</li>
<li>
<p>run a script to fix Docker routes</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#a6e22e">route</span>-<span style="color:#a6e22e">up</span> <span style="color:#e6db74">/home/</span><span style="color:#a6e22e">pavel</span><span style="color:#e6db74">/bin/</span><span style="color:#a6e22e">scripts</span>/<span style="color:#a6e22e">vpn</span>-<span style="color:#a6e22e">fix</span>-<span style="color:#a6e22e">routes</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>References:</p>
<ul>
<li><a href="https://github.com/moby/libnetwork/issues/779">Github issue</a></li>
</ul>
<p>The script itself:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Adding default route to </span>$route_vpn_gateway<span style="color:#e6db74"> with /0 mask...&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>IP<span style="color:#f92672">=</span>/run/current-system/profile/sbin/ip
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$IP route add default via $route_vpn_gateway
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Removing /1 routes...&#34;</span>
</span></span><span style="display:flex;"><span>$IP route del 0.0.0.0/1 via $route_vpn_gateway
</span></span><span style="display:flex;"><span>$IP route del 128.0.0.0/1 via $route_vpn_gateway
</span></span></code></pre></div></li>
</ul>
<h4 id="vpn-start">vpn-start</h4>
<p>As of now, CyberGhost doesn&rsquo;t provide ipv6, so we have to disable it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export DISPLAY<span style="color:#f92672">=</span>:0
</span></span><span style="display:flex;"><span>CONN<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>nmcli -f NAME con show --active | grep -Ev <span style="color:#e6db74">&#34;(.*docker.*|NAME|br-.*|veth.*|tun.*|vnet.*|virbr.*)&#34;</span> | sed <span style="color:#e6db74">&#39;s/ *$//g&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -z <span style="color:#e6db74">&#34;</span>$CONN<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;No connection!&#34;</span>
</span></span><span style="display:flex;"><span> notify-send <span style="color:#e6db74">&#34;VPN&#34;</span> <span style="color:#e6db74">&#34;No connection for VPN to run&#34;</span>
</span></span><span style="display:flex;"><span> exit
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Connection: </span>$CONN<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>notify-send <span style="color:#e6db74">&#34;VPN&#34;</span> <span style="color:#e6db74">&#34;Initializing for connection: </span>$CONN<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>pkexec nmcli con modify <span style="color:#e6db74">&#34;</span>$CONN<span style="color:#e6db74">&#34;</span> ipv6.method ignore
</span></span><span style="display:flex;"><span>nmcli connection up <span style="color:#e6db74">&#34;</span>$CONN<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>pkexec openvpn --config ~/.vpn/openvpn.ovpn
</span></span></code></pre></div><h4 id="vpn-stop">vpn-stop</h4>
<p>Also a script to reverse the changes.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>CONN<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>nmcli -f NAME con show --active | grep -Ev <span style="color:#e6db74">&#34;(.*docker.*|NAME|br-.*|veth.*|tun.*)&#34;</span> | sed <span style="color:#e6db74">&#39;s/ *$//g&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Connection: </span>$CONN<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>pkexec nmcli con modify <span style="color:#e6db74">&#34;</span>$CONN<span style="color:#e6db74">&#34;</span> ipv6.method auto
</span></span><span style="display:flex;"><span>nmcli connection up <span style="color:#e6db74">&#34;</span>$CONN<span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><h3 id="flatpak">flatpak</h3>
<p>As for now, the easiest way to install most of proprietary software is via flatpak. See the relevant section in <a href="/configs/desktop/">Desktop.org</a>.</p>
<h3 id="conda">conda</h3>
<p><a href="https://docs.conda.io/en/latest/">conda</a> is a package manager, which I use for managing various versions of Python &amp; Node.js.</p>
<p>It is packaged for GNU Guix, although the definition has its fair share of workarounds. It is almost surprising to see it work with all the C libraries and stuff. But there are still some problems.</p>
<p>First, it&rsquo;s impossible to perform <code>conda init</code> to patch files like <code>.bashrc</code>, because the command is hell-bent on modifying <code>/gnu/store/</code>. So I do this manually, look for the <code>init_conda</code> procedures in <a href="/configs/console/">Console.org</a>.</p>
<p>Second, the base environment has <code>/gnu/store/...</code> as a root, so don&rsquo;t install anything there (and don&rsquo;t run <code>conda</code> with superuser rights!).</p>
<p>Third, by default it tries to create envronments in <code>/gnu/store</code>. It&rsquo;s enough to create one environment like this to fix it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>mkdir -p ~/.conda/envs
</span></span><span style="display:flex;"><span>conda create -p ~/.conda/envs/test
</span></span></code></pre></div><p>Fourth, you may need to unset <code>$PYTHONPATH</code> if you have any global packages installed, otherwise Python from anaconda will try to import them instead of the conda versions.</p>
<p>Finally, I also want to have an ability to use global npm. Some settings for that are located in <a href="Console">Console.org</a>. Here we want to unset <code>NPM_CONFIG_USERCONFIG</code> if there is npm available in the environment.</p>
<p>So here is a script to set up conda hooks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Get writable conda envs with npm &amp; without it</span>
</span></span><span style="display:flex;"><span>readarray -t CONDA_ENVS_ALL <span style="color:#f92672">&lt;&lt;&lt;</span> <span style="color:#66d9ef">$(</span>conda env list --json | jq <span style="color:#e6db74">&#39;.envs[]&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>CONDA_ENVS_NPM<span style="color:#f92672">=()</span>
</span></span><span style="display:flex;"><span>CONDA_ENVS_NO_NPM<span style="color:#f92672">=()</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> env in <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>CONDA_ENVS_ALL[@]<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> env<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>env:1:<span style="color:#e6db74">${#</span>env<span style="color:#e6db74">}</span>-2<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -w <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -f <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/bin/npm&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> CONDA_ENVS_NPM<span style="color:#f92672">+=(</span>$env<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> CONDA_ENVS_NO_NPM<span style="color:#f92672">+=(</span>$env<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> env in <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>CONDA_ENVS_NPM[@]<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;Found npm in </span>$env<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> mkdir -p <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/activate.d&#34;</span>
</span></span><span style="display:flex;"><span> mkdir -p <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/deactivate.d&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;unset NPM_CONFIG_USERCONFIG&#34;</span> &gt; <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/activate.d/conda.sh&#34;</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;set -e NPM_CONFIG_USERCONFIG&#34;</span> &gt; <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/activate.d/conda.fish&#34;</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;export NPM_CONFIG_USERCONFIG=</span>$HOME<span style="color:#e6db74">/._npmrc&#34;</span> &gt; <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/deactivate.d/conda.sh&#34;</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;export NPM_CONFIG_USERCONFIG=</span>$HOME<span style="color:#e6db74">/._npmrc&#34;</span> &gt; <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/deactivate.d/conda.fish&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> env in <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>CONDA_ENVS_NO_NPM<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> echo <span style="color:#e6db74">&#34;Did not found npm in </span>$env<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span> rm -rf <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/activate.d/conda.sh&#34;</span> <span style="color:#f92672">||</span> true
</span></span><span style="display:flex;"><span> rm -rf <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/activate.d/conda.fish&#34;</span> <span style="color:#f92672">||</span> true
</span></span><span style="display:flex;"><span> rm -rf <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/deactivate.d/conda.sh&#34;</span> <span style="color:#f92672">||</span> true
</span></span><span style="display:flex;"><span> rm -rf <span style="color:#e6db74">&#34;</span>$env<span style="color:#e6db74">/etc/conda/deactivate.d/conda.fish&#34;</span> <span style="color:#f92672">||</span> true
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><h3 id="slack">Slack</h3>
<p>What a nonsense of a program.</p>
<p>I was able to launch the nix version with the following wrapper script:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export PATH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$HOME<span style="color:#e6db74">/bin/dummies:</span>$PATH<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>mkdir -p ~/.cache/slack
</span></span><span style="display:flex;"><span>slack -r ~/.cache/slack
</span></span></code></pre></div><p>Also, it requires a <code>lsb_release</code> in the PATH, so here is one:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;LSB Version: Hey. I spent an hour figuring out why Slack doesn&#39;t launch.&#34;</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Distributor ID: It seems like it requires an lsb_release.&#34;</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Description: But GNU Guix doesn&#39;t have one.&#34;</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Release: 42.2&#34;</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Codename: n/a&#34;</span>
</span></span></code></pre></div><h3 id="virt-manager">virt-manager</h3>
<p>Run the following to fix the network:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo virsh net-define /run/current-system/profile/etc/libvirt/qemu/networks/default.xml
</span></span><span style="display:flex;"><span>sudo virsh net-start default
</span></span><span style="display:flex;"><span>sudo herd restart libvirtd
</span></span></code></pre></div><h3 id="wakatime-cli">wakatime-cli</h3>
<table>
<thead>
<tr>
<th>Note</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>TODO</td>
<td>Package this for Guix</td>
</tr>
</tbody>
</table>
<p>Before I figure out how to package this for Guix:</p>
<ul>
<li>Clone <a href="https://github.com/wakatime/wakatime-cli">the repo</a></li>
<li>Run <code>go build</code></li>
<li>Copy the binary to the <code>~/bin</code> folder</li>
</ul>
<h3 id="manifest">Manifest</h3>
<p><a id="code-snippet--packages"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/format-guix-dependencies category)
</span></span></code></pre></div><p>System</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">specifications-&gt;manifest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(
</span></span><span style="display:flex;"><span> &lt;&lt;packages(<span style="color:#e6db74">&#34;system&#34;</span>)&gt;&gt;))
</span></span></code></pre></div>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

93
configs/index.html Normal file
View file

@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Configs</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/configs/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>Configs</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/configs/console/">0001-01-01 | Console</a></li>
<li><a href="https://sqrtminusone.xyz/configs/desktop/">0001-01-01 | Desktop</a></li>
<li><a href="https://sqrtminusone.xyz/configs/emacs/">0001-01-01 | Emacs config</a></li>
<li><a href="https://sqrtminusone.xyz/configs/guix/">0001-01-01 | Guix</a></li>
<li><a href="https://sqrtminusone.xyz/configs/mail/">0001-01-01 | Mail</a></li>
<li><a href="https://sqrtminusone.xyz/configs/readme/">0001-01-01 | My dotfiles</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

83
configs/index.xml Normal file
View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Configs on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/configs/</link>
<description>Recent content in Configs on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language><atom:link href="https://sqrtminusone.xyz/configs/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Console</title>
<link>https://sqrtminusone.xyz/configs/console/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/configs/console/</guid>
<description>#+TOC headlines 6
.profile Environment export QT_QPA_PLATFORMTHEME=&amp;#34;qt5ct&amp;#34; export QT_AUTO_SCREEN_SCALE_FACTOR=0 Set ripgrep config path
export RIPGREP_CONFIG_PATH=$HOME/.config/ripgrep/ripgreprc hledger path
export LEDGER_FILE=~/Documents/org-mode/ledger/ledger.journal Checking if running inside termux
if command -v termux-setup-storage &amp;gt; /dev/null; then export IS_ANDROID=true [[ -f ~/.android_profile ]] &amp;amp;&amp;amp; . ~/.android_profile fi My paths My script folders
if [ -d &amp;#34;$HOME/bin&amp;#34; ] ; then export PATH=&amp;#34;$HOME/bin:$PATH&amp;#34; export PATH=&amp;#34;$HOME/bin/scripts:$PATH&amp;#34; fi if [ -d &amp;#34;$HOME/.local/bin&amp;#34; ] ; then export PATH=&amp;#34;$HOME/.</description>
</item>
<item>
<title>Desktop</title>
<link>https://sqrtminusone.xyz/configs/desktop/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/configs/desktop/</guid>
<description>My general desktop environment configuration.
Parts prefixed with (OFF) are not used, but kept for historic purposes. For some reason GitHub&amp;rsquo;s org renderer ignores TODO status, hence such a prefix. Round brackets instead of square ones to prevent GitHub&amp;rsquo;s org renderer from screwing up.
References:
A few cases of literate configuration. My blog post that explains some of techniques from this file. Table of Contents Global customization Colors Xresources Colors in Xresources Fonts Themes MIME Device-specific settings EXWM Startup &amp;amp; UI Xsession Startup apps Pinentry Modeline Windows Moving windows Resizing windows Improving splitting windows Perspectives Workspaces and multi-monitor setup Tracking recently used workspaces The monitor list Switch to another monitor Move the workspace to another monitor Windmove between monitors Completions ivy-posframe Disable mouse movement Disable changing focus Linux app password-store-ivy emojis Keybindings EXWM keybindings App shortcuts Locking up Fixes Catch and report all errors raised when invoking command hooks Improve floating windows behavior EXWM config i3wm General settings Managing windows Workspaces Rules Scratchpad Launch script i3 config Gaps &amp;amp; borders Keybindings Move &amp;amp; resize windows OFF (OFF) Intergration with dmenu Integration with rofi Launching apps &amp;amp; misc keybindings Apps Media controls &amp;amp; brightness Screenshots Colors OFF (OFF) i3blocks Keyboard Layout Autostart Polybar General settings Colors Glyph settings Defining modules Generating glyphs Generating set of modules Global bar config Launch script Individual modules pulseaudio mpd cpu ram-memory swap-memory network ipstack-vpn openvpn xkeyboard battery weather sun aw-afk date pomm SEP TSEP i3 Rofi Theme Scripts Buku bookmarks Man pages Emojis pass Flameshot dunst keynav Config Using with picom Picom Shadows Fading Opacity General settings Zathura Various software Browsers Office &amp;amp; Multimedia LaTeX Dev Manifests Flatpak Nix Services Music GNU Mcron ActivityWatch PulseEffects xsettingsd nm-applet Discord rich presence Polkit Authentication agent Xmodmap VPN Davmail sqrt-data Shepherd config Sync Guix settings Global customization Colors My favorite color theme is Palenight (color codes), and I want to have one source of truth for these colors.</description>
</item>
<item>
<title>Emacs config</title>
<link>https://sqrtminusone.xyz/configs/emacs/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/configs/emacs/</guid>
<description>One day we won&amp;rsquo;t hate one another, no young boy will march to war and I will clean up my Emacs config. But that day isn&amp;rsquo;t today.
Introduction My configuration of GNU Emacs, an awesome text editor program that can do almost anything.
At the moment of this writing, this &amp;ldquo;almost anything&amp;rdquo; includes:
Writing code. With LSP &amp;amp; Co this functionality of Emacs may rival that of IDEs, and is at least on par with editors like VS Code.</description>
</item>
<item>
<title>Guix</title>
<link>https://sqrtminusone.xyz/configs/guix/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/configs/guix/</guid>
<description>GNU Guix is (1) a transactional package manager and (2) a GNU/Linux distribution.
My personal selling points are declarative package configuration and transactional upgrades.
References:
Official help System Crafters wiki Pjotr Prins&amp;rsquo; Guix notes Davil Wilson&amp;rsquo;s YouTube series Table of Contents Profiles Activate profiles Update profiles Run guix package in profile Channels Systems Base configuration indigo eminence azure System installation Preparation Installation After installation Misc software &amp;amp; notes VPN vpn-start vpn-stop flatpak conda Slack virt-manager wakatime-cli Manifest Profiles A profile is a way to group Guix packages.</description>
</item>
<item>
<title>Mail</title>
<link>https://sqrtminusone.xyz/configs/mail/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/configs/mail/</guid>
<description>:TOC: :include all :depth 3
My email configration. Currently I use lieer to fetch emails from Gmail, davmail &amp;amp; offlineimap to fetch emails from MS Exchange, notmuch to index, msmtp to send emails. Also using notmuch frontend from Emacs.
My problem with any particular mail setup was that I use Gmail labels quite extensively, and handling these over IMAP is rather awkward. Notmuch seems to be the only software that provides the same first-class support for labels.</description>
</item>
<item>
<title>My dotfiles</title>
<link>https://sqrtminusone.xyz/configs/readme/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/configs/readme/</guid>
<description>These are my GNU/Linux configuration files. View at GitHub.
Most of the software is configured with literate configuration strategy via Emacs&amp;rsquo; Org Mode. This way has its pros and cons, but overall it&amp;rsquo;s pretty nice to keep the configs interweaved with comments in a handful of files.
The files themselves are managed and deployed via yadm, but I use Org Mode for things like config templating.
My current GNU/Linux distribution is GNU Guix.</description>
</item>
</channel>
</rss>

633
configs/mail/index.html Normal file
View file

@ -0,0 +1,633 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Mail</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<p>:TOC: :include all :depth 3</p>
<p>My email configration. Currently I use <a href="https://github.com/gauteh/lieer">lieer</a> to fetch emails from Gmail, <a href="http://davmail.sourceforge.net/">davmail</a> &amp; <a href="http://www.offlineimap.org/">offlineimap</a> to fetch emails from MS Exchange, <a href="https://notmuchmail.org/">notmuch</a> to index, <a href="https://marlam.de/msmtp/">msmtp</a> to send emails. Also using notmuch frontend from Emacs.</p>
<p>My problem with any particular mail setup was that I use Gmail labels quite extensively, and handling these over IMAP is rather awkward. Notmuch seems to be the only software that provides the same first-class support for labels.</p>
<p>But I also have an Exchange account, with which I communicate via IMAP/SMTP adapter, and in this case, I synchronize notmuch tags and IMAP folders.</p>
<p>References:</p>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2021-02-27-gmail/">My post</a> about email configuration. I wrote it some time ago, but the general idea remains.</li>
</ul>
<div class="ox-hugo-toc toc">
<div class="heading">Table of Contents</div>
<ul>
<li><a href="#lieer">Lieer</a></li>
<li><a href="#davmail">DavMail</a></li>
<li><a href="#offlineimap">OfflineIMAP</a></li>
<li><a href="#notmuch">Notmuch</a>
<ul>
<li><a href="#config">Config</a></li>
<li><a href="#hooks">Hooks</a>
<ul>
<li><a href="#pre-new"><code>pre_new</code></a></li>
<li><a href="#post-new"><code>post_new</code></a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#sync-script">Sync script</a></li>
<li><a href="#msmtp">MSMTP</a></li>
<li><a href="#emacs">Emacs</a>
<ul>
<li><a href="#saved-filters-and-keybindings">Saved filters and keybindings</a></li>
<li><a href="#signing-messages">Signing messages</a></li>
</ul>
</li>
<li><a href="#mailcap">mailcap</a></li>
<li><a href="#guix-settings">Guix settings</a></li>
</ul>
</div>
<!--endtoc-->
<h2 id="lieer">Lieer</h2>
<table>
<thead>
<tr>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>python-lieer</td>
</tr>
</tbody>
</table>
<p>Lieer is a program to link up Gmail and notmuch. Basically, it downloads mail from Gmail via API, stores them in Maildir, and synchronizes labels with notmuch.</p>
<p>I have a separate directory in my <code>~/Mail</code> for each address. To init lieer, run the following command in the directory:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>gmi init &lt;address&gt;
</span></span></code></pre></div><p>After which the settings will be stored in <code>gmailieer.json</code> and the credentials in <code>.credentials.gmailieer.json</code>. The latter file is stored encrypted.</p>
<p>My preferred settings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>gmi set --replace-slash-with-dot
</span></span><span style="display:flex;"><span>gmi set --ignore-tags-local new
</span></span></code></pre></div><p>Running <code>gmi sync</code> in the required directory performs the synchronization. The first sync takes a while, the subsequent syncs are pretty fast.</p>
<h2 id="davmail">DavMail</h2>
<p>is a gateway between MS Exchange and the rest of the world, which uses IMAP/SMTP/LDAP/etc. As I have one corporate MS Exchange address, this is just the program I need. As of yet, it isn&rsquo;t packaged for Guix, but it&rsquo;s easy enough to download.</p>
<p>It has a GUI mode, but I prefer headless config.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#a6e22e">davmail.server</span><span style="color:#f92672">=</span><span style="color:#e6db74">true</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.mode</span><span style="color:#f92672">=</span><span style="color:#e6db74">Auto</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.url</span><span style="color:#f92672">=</span><span style="color:#e6db74">https://mail.etu.ru/owa/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.server.certificate.hash</span><span style="color:#f92672">=</span><span style="color:#e6db74">0C:9E:CF:D3:62:26:DB:FA:F1:EE:36:9D:60:E7:31:71:CF:1F:92:85</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.caldavPort</span><span style="color:#f92672">=</span><span style="color:#e6db74">1080</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.imapPort</span><span style="color:#f92672">=</span><span style="color:#e6db74">1143</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.ldapPort</span><span style="color:#f92672">=</span><span style="color:#e6db74">1389</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.popPort</span><span style="color:#f92672">=</span><span style="color:#e6db74">1110</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.smtpPort</span><span style="color:#f92672">=</span><span style="color:#e6db74">1025</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.imapAutoExpunge</span><span style="color:#f92672">=</span><span style="color:#e6db74">false</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">davmail.enableKeepalive</span><span style="color:#f92672">=</span><span style="color:#e6db74">false</span>
</span></span></code></pre></div><p>Also it&rsquo;s a bit of problem to get it launched as it looks for its jars in the pwd, so here is a script.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd $HOME/bin/davmail-6.0.0-3375
</span></span><span style="display:flex;"><span>./davmail davmail.properties
</span></span></code></pre></div><p>Shepherd service is defined in <a href="/configs/desktop/#davmail">Desktop.org</a>.</p>
<h2 id="offlineimap">OfflineIMAP</h2>
<table>
<thead>
<tr>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>offlineimap</td>
</tr>
</tbody>
</table>
<p><a href="https://github.com/OfflineIMAP/offlineimap">OfflineIMAP</a> is a program that can synchronize IMAP mailbox and Maildir. Lieer does everything by itself, but my pirate Exchange IMAP needs this program. There is also <a href="https://isync.sourceforge.io/">isync</a>, but there I had some weird issues with duplicate UIDs, which don&rsquo;t occur for OfflineIMAP.</p>
<p>I have a few options for setting a username and password. First, I can run <code>pass</code> in <code>remotepasswordeval</code>, and while this will work, it will keep my keyring unlocked because I want to run <code>offlineimap</code> every couple of minutes.</p>
<p>Another option is to use noweb and not push the file below to the version control. Then I have a plaintext password of email on my computer, but I think it&rsquo;s a lesser evil than the entire keyring.</p>
<p>I would use <code>password-store-get</code> from password-store.el, but I want this to be able to run without any 3rd party packages, so it&rsquo;s just bash.</p>
<p><a id="code-snippet--mail-username"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pass show Job/Digital/Email/pvkorytov@etu.ru | sed -n <span style="color:#e6db74">&#39;s/username: //;2p&#39;</span>
</span></span></code></pre></div><p><a id="code-snippet--mail-password"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pass show Job/Digital/Email/pvkorytov@etu.ru | head -n <span style="color:#ae81ff">1</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[general]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">accounts</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">pvkorytov</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Account pvkorytov]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">localrepository</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">pvkorytov-local</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">remoterepository</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">pvkorytov-remote</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Repository pvkorytov-local]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">type</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">Maildir</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">localfolders</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">~/Mail/pvkorytov_etu/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Repository pvkorytov-remote]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">type</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">IMAP</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">remotehost</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">localhost</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">remoteuser</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;&lt;mail-username()&gt;&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">remotepass</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;&lt;mail-password()&gt;&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">remoteport</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">1143</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">starttls</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">no</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ssl</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">no</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">sslcacertfile</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/etc/ssl/certs/ca-certificates.crt</span>
</span></span></code></pre></div><h2 id="notmuch">Notmuch</h2>
<table>
<thead>
<tr>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>notmuch</td>
</tr>
<tr>
<td>parallel</td>
</tr>
</tbody>
</table>
<p>Notmuch is an email indexer program, which handles labels in a way somewhat similar to Gmail. It also provides a frontend for Emacs, but it&rsquo;s not the only one available.</p>
<h3 id="config">Config</h3>
<p>Not much is going on here.</p>
<p>First, the database path.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[database]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">path</span><span style="color:#f92672">=</span><span style="color:#e6db74">/home/pavel/Mail</span>
</span></span></code></pre></div><p>My name and list of emails. It&rsquo;s not like it&rsquo;s a secret anyhow.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[user]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">Pavel Korytov</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">primary_email</span><span style="color:#f92672">=</span><span style="color:#e6db74">thexcloud@gmail.com</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">other_email</span><span style="color:#f92672">=</span><span style="color:#e6db74">progin6304@gmail.com;pvkorytov@etu.ru</span>
</span></span></code></pre></div><p>A list of tags which will be added by <code>notmuch new</code> and directory names which will be ignored by <code>notmuch new</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[new]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">tags</span><span style="color:#f92672">=</span><span style="color:#e6db74">new;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ignore</span><span style="color:#f92672">=</span><span style="color:#e6db74">.osync_workdir;.mbsyncstate;.uidvalidity;.lock;/.*gmailieer\.json.*/</span>
</span></span></code></pre></div><p>Exclude these tags from search by default.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[search]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">exclude_tags</span><span style="color:#f92672">=</span><span style="color:#e6db74">trash;spam;</span>
</span></span></code></pre></div><p>Maildir compatibility.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[maildir]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">synchronize_flags</span><span style="color:#f92672">=</span><span style="color:#e6db74">true</span>
</span></span></code></pre></div><h3 id="hooks">Hooks</h3>
<p>Now we have to link up lieer &amp; davmail&rsquo;s maildir and with notmuch. This is done via the notmuch hook system, which allows running custom scripts before and after any command.</p>
<p>With lieer and Gmail, it is enough to simply run the program, because Gmail has first-class support for tags. Maildir does not, so I decide to synchronize notmuch tags and IMAP folders. In essence, the idea is to:</p>
<ul>
<li>move emails to their folders by tags <em>before</em> the synchronization</li>
<li>tag mails by their folders <em>after</em> the synchronization</li>
</ul>
<p>The problem is that with that approach one email can have only one tag, but it&rsquo;s better than nothing.</p>
<p>So, here are the rules which match tags &amp; folders:</p>
<p><a id="table--pvkorytov-tags"></a></p>
<table>
<thead>
<tr>
<th>tag</th>
<th>folder</th>
</tr>
</thead>
<tbody>
<tr>
<td>inbox</td>
<td>INBOX</td>
</tr>
<tr>
<td>sent</td>
<td>Sent</td>
</tr>
<tr>
<td>spam</td>
<td>Junk</td>
</tr>
<tr>
<td>trash</td>
<td>Trash</td>
</tr>
<tr>
<td>job.digital</td>
<td>Job_Digital</td>
</tr>
<tr>
<td>job.digital.docs</td>
<td>Job_Digital.Docs</td>
</tr>
<tr>
<td>job.digital.support</td>
<td>Job_Digital.Support</td>
</tr>
<tr>
<td>job.digital.superservice</td>
<td>Job_Digital.Superservice</td>
</tr>
<tr>
<td>job.digital.applicants</td>
<td>Job_Digital.Applicants</td>
</tr>
</tbody>
</table>
<p>And below is a noweb function, which generates the following commands for notmuch to execute:</p>
<ul>
<li><em>before</em> sync:
<ul>
<li><code>notmuch search --output files &quot;NOT path:[PATH] AND tag:[TAG] AND tag:[ROOT_TAG]&quot; | xargs -I ! mv ! [PATH]</code>
Move emails with <code>TAG</code> but outside the matching <code>PATH</code> to the latter</li>
<li><code>notmuch search --output=files &quot;NOT path:[ARCHIVE_PATH] AND tag:[ROOT_TAG] AND NOT tag:[TAG1] ... AND NOT tag:[TAGN]&quot; | xargs -I ! mv ! [ARCHIVE_PATH]</code>
Move untagged emails to the <code>ARCHIVE_PATH</code></li>
</ul>
</li>
<li><em>after</em> sync:
<ul>
<li><code>notmuch tag +[TAG] &quot;path:[PATH] AND NOT tag:[TAG]&quot;</code>
Tag emails in <code>PATH</code> which do not yet have the matching <code>TAG</code></li>
<li><code>notmuch tag -[TAG] &quot;NOT path:[PATH] AND tag:[TAG] AND tag:[ROOT_TAG]&quot;</code>
Remove <code>TAG</code> from emails which are outside the matching <code>PATH</code></li>
</ul>
</li>
</ul>
<p>These rules are getting included in the respective hooks.</p>
<p><a id="code-snippet--mail-tags"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/maildir-root <span style="color:#e6db74">&#34;~/Mail&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(let ((rules <span style="color:#f92672">&#39;</span>()))
</span></span><span style="display:flex;"><span> (dolist (row tags)
</span></span><span style="display:flex;"><span> (let ((tag (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> row))
</span></span><span style="display:flex;"><span> (folder (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> row)))
</span></span><span style="display:flex;"><span> (unless (string-empty-p make_tag)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;rules</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;notmuch tag +%s \&#34;path:%s/%s/cur/** AND NOT tag:%s\&#34;&#34;</span>
</span></span><span style="display:flex;"><span> tag root folder tag)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span> (unless (string-empty-p remove)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;rules</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;notmuch tag -%s \&#34;NOT path:%s/%s/cur/** AND tag:%s AND tag:%s\&#34;&#34;</span>
</span></span><span style="display:flex;"><span> tag root folder tag root_tag)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span> (unless (string-empty-p move)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;rules</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;notmuch search --output=files \&#34;NOT path:%s/%s/cur/** AND tag:%s AND tag:%s\&#34;&#34;</span>
</span></span><span style="display:flex;"><span> root folder tag root_tag)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34; | xargs -I ! mv ! %s/%s/%s/cur/&#34;</span> my/maildir-root root folder))
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>))))
</span></span><span style="display:flex;"><span> (unless (string-empty-p archive_root)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;rules</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;notmuch search --output=files \&#34;NOT path:%s/%s/cur/** AND %s AND tag:%s\&#34;&#34;</span>
</span></span><span style="display:flex;"><span> root archive_root
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapconcat</span>
</span></span><span style="display:flex;"><span> (lambda (row)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;NOT tag:%s&#34;</span> (<span style="color:#a6e22e">car</span> row)))
</span></span><span style="display:flex;"><span> tags
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34; AND &#34;</span>)
</span></span><span style="display:flex;"><span> root_tag)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34; | xargs -I ! mv ! %s/%s/%s/cur/&#34;</span> my/maildir-root root archive_root))
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span> (string-join rules <span style="color:#e6db74">&#34;\n&#34;</span>))
</span></span></code></pre></div><h4 id="pre-new"><code>pre_new</code></h4>
<p>This hook runs fetch from Gmail &amp; offlineimap in parallel before the <code>notmuch new</code> command. The <code>parallel</code> command is provided by <a href="https://www.gnu.org/software/parallel/">GNU Parallel</a>.</p>
<p>It isn&rsquo;t necessary to run <code>cd</code> for offlineimap, but it&rsquo;s easier to write that way.</p>
<p><a id="code-snippet--pre-new-pvkorytov-tags"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/mail-format-tags-rules tags <span style="color:#e6db74">&#34;pvkorytov_etu&#34;</span> <span style="color:#e6db74">&#34;pvkorytov&#34;</span> <span style="color:#66d9ef">nil</span> <span style="color:#66d9ef">nil</span> <span style="color:#66d9ef">t</span> <span style="color:#e6db74">&#34;Archive&#34;</span>)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># GMI=&#34;/home/pavel/Programs/miniconda3/envs/mail/bin/gmi&#34;</span>
</span></span><span style="display:flex;"><span>GMI<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gmi&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Running pre-new filters&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;mail-tags(move=&#34;t&#34;,archive_root=&#34;Archive&#34;)&gt;&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;Pre-new filters done&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">parallel --link -j0 &#34;(cd /home/pavel/Mail/{1}/ &amp;&amp; {2} {3})&#34; ::: thexcloud progin6304 pvkorytov_etu ::: &#34;$GMI&#34; &#34;$GMI&#34; &#34;offlineima</span>p<span style="color:#e6db74">&#34; ::: sync sync &#34;&#34;
</span></span></span></code></pre></div><h4 id="post-new"><code>post_new</code></h4>
<p>And this hook tags different mailboxes with different tags.</p>
<p><a id="code-snippet--post-new-pvkorytov-tags"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/mail-format-tags-rules tags <span style="color:#e6db74">&#34;pvkorytov_etu&#34;</span> <span style="color:#e6db74">&#34;pvkorytov&#34;</span> <span style="color:#66d9ef">t</span> <span style="color:#66d9ef">t</span>)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>notmuch tag +main <span style="color:#e6db74">&#34;path:thexcloud/** AND tag:new&#34;</span>
</span></span><span style="display:flex;"><span>notmuch tag +progin <span style="color:#e6db74">&#34;path:progin6304/** AND tag:new&#34;</span>
</span></span><span style="display:flex;"><span>notmuch tag +pvkorytov <span style="color:#e6db74">&#34;path:pvkorytov_etu/** AND tag:new&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Running post-new filters&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;mail-tags(ma</span>ke_tag<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;t&#34;</span>,remove<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;t&#34;</span><span style="color:#f92672">)</span>&gt;&gt;
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Post-new filters done&#34;</span>
</span></span><span style="display:flex;"><span>notmuch tag -new <span style="color:#e6db74">&#34;tag:new&#34;</span>
</span></span></code></pre></div><h2 id="sync-script">Sync script</h2>
<p>A script to run <code>notmuch new</code> and push a notification if there is new mail.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export DISPLAY<span style="color:#f92672">=</span>:0
</span></span><span style="display:flex;"><span>CHECK_FILE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/home/pavel/Mail/.last_check&#34;</span>
</span></span><span style="display:flex;"><span>QUERY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;tag:unread&#34;</span>
</span></span><span style="display:flex;"><span>ALL_QUERY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;tag:unread&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -f <span style="color:#e6db74">&#34;</span>$CHECK_FILE<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> DATE<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>cat <span style="color:#e6db74">&#34;</span>$CHECK_FILE<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> QUERY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$QUERY<span style="color:#e6db74"> and date:@</span>$DATE<span style="color:#e6db74">..&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>notmuch new
</span></span><span style="display:flex;"><span>NEW_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;</span>$QUERY<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>ALL_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;</span>$ALL_QUERY<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> $NEW_UNREAD -gt <span style="color:#ae81ff">0</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> MAIN_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;tag:unread AND tag:main&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> PROGIN_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;tag:unread AND tag:progin&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> ETU_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;tag:unread AND tag:pvkorytov&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> read -r -d <span style="color:#e6db74">&#39;&#39;</span> NOTIFICATION <span style="color:#e6db74">&lt;&lt;EOM
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$NEW_UNREAD new messages
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$MAIN_UNREAD thexcloud@gmail.com
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$PROGIN_UNREAD progin6304@gmail.com
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$ETU_UNREAD pvkorytov@etu.ru
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$ALL_UNREAD total
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOM</span>
</span></span><span style="display:flex;"><span> notify-send <span style="color:#e6db74">&#34;New Mail&#34;</span> <span style="color:#e6db74">&#34;</span>$NOTIFICATION<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">$(</span>date +%s<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span> &gt; $CHECK_FILE
</span></span></code></pre></div><p>The script is ran via GNU Mcron every 5 minutes.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">job</span> <span style="color:#e6db74">&#34;*/5 * * * * &#34;</span> <span style="color:#e6db74">&#34;~/bin/scripts/check-email&#34;</span>)
</span></span></code></pre></div><h2 id="msmtp">MSMTP</h2>
<table>
<thead>
<tr>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>msmtp</td>
</tr>
</tbody>
</table>
<p>Sending emails can be done with MSMTP. It automatially chooses the email address and server based on the contents of the message, which is handy if there are multiple mailboxes to be managed.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#a6e22e">defaults</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">auth</span> <span style="color:#a6e22e">on</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">tls</span> <span style="color:#a6e22e">on</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">tls_trust_file</span> <span style="color:#e6db74">/etc/</span><span style="color:#a6e22e">ssl</span><span style="color:#e6db74">/certs/</span><span style="color:#a6e22e">ca</span>-<span style="color:#a6e22e">certificates</span>.<span style="color:#a6e22e">crt</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">logfile</span> ~/.<span style="color:#a6e22e">msmtp</span>.<span style="color:#a6e22e">log</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">account</span> <span style="color:#a6e22e">main</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">host</span> <span style="color:#a6e22e">smtp</span>.<span style="color:#a6e22e">gmail</span>.<span style="color:#a6e22e">com</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">port</span> <span style="color:#ae81ff">587</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">from</span> <span style="color:#a6e22e">thexcloud</span>@<span style="color:#a6e22e">gmail</span>.<span style="color:#a6e22e">com</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">user</span> <span style="color:#a6e22e">thexcloud</span>@<span style="color:#a6e22e">gmail</span>.<span style="color:#a6e22e">com</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">passwordeval</span> <span style="color:#e6db74">&#34;pass show My_Online/APIs/google-main-app-password | head -n 1&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">account</span> <span style="color:#a6e22e">progin</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">host</span> <span style="color:#a6e22e">smtp</span>.<span style="color:#a6e22e">gmail</span>.<span style="color:#a6e22e">com</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">port</span> <span style="color:#ae81ff">587</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">from</span> <span style="color:#a6e22e">progin6304</span>@<span style="color:#a6e22e">gmail</span>.<span style="color:#a6e22e">com</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">user</span> <span style="color:#a6e22e">progin6304</span>@<span style="color:#a6e22e">gmail</span>.<span style="color:#a6e22e">com</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">passwordeval</span> <span style="color:#e6db74">&#34;pass show My_Online/ETU/progin6304@gmail.com | head -n 1&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">account</span> <span style="color:#a6e22e">pvkorytov</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">tls</span> <span style="color:#a6e22e">off</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">auth</span> <span style="color:#a6e22e">plain</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">host</span> <span style="color:#a6e22e">localhost</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">port</span> <span style="color:#ae81ff">1025</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">from</span> <span style="color:#a6e22e">pvkorytov</span>@<span style="color:#a6e22e">etu</span>.<span style="color:#a6e22e">ru</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">user</span> <span style="color:#a6e22e">pvkorytov</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">passwordeval</span> <span style="color:#e6db74">&#34;pass show Job/Digital/Email/pvkorytov@etu.ru | head -n 1&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><h2 id="emacs">Emacs</h2>
<table>
<thead>
<tr>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>emacs-notmuch</td>
</tr>
</tbody>
</table>
<p>Finally, Emacs configuration. Let&rsquo;s start with some variables:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq user-mail-address <span style="color:#e6db74">&#34;thexcloud@gmail.com&#34;</span>)
</span></span><span style="display:flex;"><span>(setq <span style="color:#a6e22e">user-full-name</span> <span style="color:#e6db74">&#34;Pavel Korytov&#34;</span>)
</span></span></code></pre></div><p>Then, the problem with my Guix setup is that Emacs by default doesn&rsquo;t see the elisp files of notmuch, so here is a small workaround:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(let ((default-directory <span style="color:#e6db74">&#34;/home/pavel/.guix-extra-profiles/mail/mail/share/emacs/site-lisp&#34;</span>))
</span></span><span style="display:flex;"><span> (normal-top-level-add-subdirs-to-load-path))
</span></span></code></pre></div><p>Finally the proper notmuch settings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package notmuch
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; :ensure nil</span>
</span></span><span style="display:flex;"><span> :commands (notmuch notmuch-search)
</span></span><span style="display:flex;"><span> :init
</span></span><span style="display:flex;"><span> (my/use-doom-colors
</span></span><span style="display:flex;"><span> (notmuch-wash-cited-text :foreground (doom-color <span style="color:#e6db74">&#39;yellow</span>)))
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq mail-specify-envelope-from <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span> (setq message-sendmail-envelope-from <span style="color:#e6db74">&#39;header</span>)
</span></span><span style="display:flex;"><span> (setq mail-envelope-from <span style="color:#e6db74">&#39;header</span>)
</span></span><span style="display:flex;"><span> (setq notmuch-always-prompt-for-sender <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span> (setq message-send-mail-function <span style="color:#a6e22e">#&#39;</span>message-send-mail-with-sendmail)
</span></span><span style="display:flex;"><span> (setq sendmail-program (executable-find <span style="color:#e6db74">&#34;msmtp&#34;</span>))
</span></span><span style="display:flex;"><span> (setq send-mail-function <span style="color:#a6e22e">#&#39;</span>sendmail-send-it)
</span></span><span style="display:flex;"><span> (setq mml-secure-openpgp-sign-with-sender <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span> (setq notmuch-mua-user-agent-function <span style="color:#e6db74">&#39;notmuch-mua-user-agent-full</span>)
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Use org-contacts for completion</span>
</span></span><span style="display:flex;"><span> (require <span style="color:#e6db74">&#39;org-contacts</span>)
</span></span><span style="display:flex;"><span> (setq notmuch-address-command <span style="color:#e6db74">&#39;as-is</span>)
</span></span><span style="display:flex;"><span> (add-hook <span style="color:#e6db74">&#39;notmuch-hello-mode-hook</span>
</span></span><span style="display:flex;"><span> (lambda () (display-line-numbers-mode <span style="color:#ae81ff">0</span>))))
</span></span></code></pre></div><p>The file to which this is tangled is read in the init.el.</p>
<h3 id="saved-filters-and-keybindings">Saved filters and keybindings</h3>
<p>I want to have the saved filters available in both notmuch interface as as keybindings. So a bit more of abusing org tables.</p>
<p>Root keybindings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my-leader-def
</span></span><span style="display:flex;"><span> :infix <span style="color:#e6db74">&#34;am&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&#34;</span> <span style="color:#f92672">&#39;</span>(:which-key <span style="color:#e6db74">&#34;notmuch&#34;</span>)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;m&#34;</span> (my/command-in-persp <span style="color:#e6db74">&#34;notmuch&#34;</span> <span style="color:#e6db74">&#34;mail&#34;</span> <span style="color:#ae81ff">0</span> (notmuch)))
</span></span></code></pre></div><p><a id="table--root-tags"></a></p>
<table>
<thead>
<tr>
<th>Root tag</th>
<th>Prefix</th>
<th>Keybinding description</th>
</tr>
</thead>
<tbody>
<tr>
<td>main</td>
<td>t</td>
<td><a href="mailto:thexcloud@gmail.com">thexcloud@gmail.com</a></td>
</tr>
<tr>
<td>progin</td>
<td>p</td>
<td><a href="mailto:progin6304@gmail.com">progin6304@gmail.com</a></td>
</tr>
<tr>
<td>pvkorytov</td>
<td>e</td>
<td><a href="mailto:pvkorytov@etu.ru">pvkorytov@etu.ru</a></td>
</tr>
</tbody>
</table>
<p><a id="table--filter-tags"></a></p>
<table>
<thead>
<tr>
<th>Tag</th>
<th>Prefix</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>inbox</td>
<td>i</td>
<td>inbox</td>
</tr>
<tr>
<td>unread</td>
<td>u</td>
<td>unread</td>
</tr>
<tr>
<td>sent</td>
<td>s</td>
<td>sent</td>
</tr>
<tr>
<td></td>
<td>a</td>
<td>all mail</td>
</tr>
</tbody>
</table>
<p>The following formats the tables above to a proper syntax for <code>setq notmuch-saved-searches</code>:</p>
<p><a id="code-snippet--format-notmuch-saved-searches"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(let ((searches <span style="color:#f92672">&#39;</span>()))
</span></span><span style="display:flex;"><span> (dolist (root_tag root_tags)
</span></span><span style="display:flex;"><span> (dolist (tag filter_tags)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;searches</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;(:name \&#34;%s\&#34; :query \&#34;%s\&#34;)&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;%s (%s)&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> root_tag)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> tag))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">&#34;tag:&#34;</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> root_tag)
</span></span><span style="display:flex;"><span> (unless (string-empty-p (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> tag))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">&#34; AND tag:&#34;</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> tag)))))
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>)))
</span></span><span style="display:flex;"><span> (string-join searches <span style="color:#e6db74">&#34;\n&#34;</span>))
</span></span></code></pre></div><p>And the following does the same for my general.el definer:</p>
<p><a id="code-snippet--format-notmuch-keybindings"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(let ((bindings <span style="color:#f92672">&#39;</span>()))
</span></span><span style="display:flex;"><span> (dolist (root_tag root_tags)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;bindings</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;\&#34;%s\&#34; &#39;(:which-key \&#34;%s\&#34;)&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> root_tag)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> root_tag))
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span> (dolist (tag filter_tags)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;bindings</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;\&#34;%s\&#34; (my/command-in-persp \&#34;%s\&#34; \&#34;mail\&#34; 0 (notmuch-search \&#34;%s\&#34;))&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> root_tag) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> tag))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">&#34;tag:&#34;</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> root_tag)
</span></span><span style="display:flex;"><span> (unless (string-empty-p (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> tag))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">&#34; AND tag:&#34;</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> tag))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> tag))
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">t</span>)))
</span></span><span style="display:flex;"><span> (string-join bindings <span style="color:#e6db74">&#34;\n&#34;</span>))
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq notmuch-saved-searches
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>((:name <span style="color:#e6db74">&#34;drafts&#34;</span> :query <span style="color:#e6db74">&#34;tag:draft&#34;</span>)
</span></span><span style="display:flex;"><span> &lt;&lt;format-notmuch-saved-searches()&gt;&gt;))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(my-leader-def
</span></span><span style="display:flex;"><span> :infix <span style="color:#e6db74">&#34;am&#34;</span>
</span></span><span style="display:flex;"><span> &lt;&lt;format-notmuch-keybindings()&gt;&gt;)
</span></span></code></pre></div><h3 id="signing-messages">Signing messages</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(with-eval-after-load <span style="color:#e6db74">&#39;notmuch</span>
</span></span><span style="display:flex;"><span> (add-hook <span style="color:#e6db74">&#39;message-setup-hook</span> <span style="color:#e6db74">&#39;mml-secure-sign-pgpmime</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(setq mml-secure-key-preferences
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>((OpenPGP
</span></span><span style="display:flex;"><span> (sign
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;thexcloud@gmail.com&#34;</span> <span style="color:#e6db74">&#34;914472A1FD6775C166F96EBEED739ADF81C78160&#34;</span>))
</span></span><span style="display:flex;"><span> (encrypt))
</span></span><span style="display:flex;"><span> (CMS
</span></span><span style="display:flex;"><span> (sign)
</span></span><span style="display:flex;"><span> (encrypt))))
</span></span></code></pre></div><h2 id="mailcap">mailcap</h2>
<p>mailcap file is a file which defines how to read to different MIME types. Notmuch also uses it, so why not keep it here.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>audio/*; mpc add %s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>image/*; feh %s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>application/msword; /usr/bin/xdg-open %s
</span></span><span style="display:flex;"><span>application/pdf; zathura-wrapper %s
</span></span><span style="display:flex;"><span>application/postscript ; zathura-wrapper %s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>text/html; firefox %s
</span></span></code></pre></div><h2 id="guix-settings">Guix settings</h2>
<p><a id="code-snippet--packages"></a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/format-guix-dependencies)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">specifications-&gt;manifest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(
</span></span><span style="display:flex;"><span> &lt;&lt;packages()&gt;&gt;))
</span></span></code></pre></div>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

160
configs/readme/index.html Normal file
View file

@ -0,0 +1,160 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>My dotfiles</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<figure><img src="https://forthebadge.com/images/badges/works-on-my-machine.svg"/>
</figure>
<p>These are my GNU/Linux configuration files. <a href="https://github.com/SqrtMinusOne/dotfiles">View at GitHub</a>.</p>
<p>Most of the software is configured with <a href="https://leanpub.com/lit-config/read">literate configuration</a> strategy via Emacs&rsquo; <a href="https://orgmode.org/">Org Mode</a>. This way has its pros and cons, but overall it&rsquo;s pretty nice to keep the configs interweaved with comments in a handful of files.</p>
<p>The files themselves are managed and deployed via <a href="https://yadm.io/">yadm</a>, but I use Org Mode for things like config templating.</p>
<p>My current GNU/Linux distribution is <a href="https://guix.gnu.org/">GNU Guix</a>. I like Guix because, among other things, it allows <a href="https://guix.gnu.org/cookbook/en/html_node/Advanced-package-management.html#Advanced-package-management">declaring the required software</a> in configuration files, so I can have the same set of programs across multiple machines (look for tables with &ldquo;Guix dependency&rdquo; in the header).</p>
<p>The central program to all of that is, of course <a href="https://www.gnu.org/software/emacs/">GNU Emacs</a>. As of the moment of this writing, it takes ~50% of my screen time and has the largest share of configuration here.</p>
<p>Table of contents and software:</p>
<ul>
<li><a href="/configs/emacs/">Emacs.org</a>
<ul>
<li><a href="/configs/emacs/">GNU Emacs</a></li>
</ul>
</li>
<li><a href="/configs/desktop/">Desktop.org</a>
<ul>
<li><em>Active</em>: <a href="/configs/desktop/#exwm">EXWM</a>, <a href="/configs/desktop/#polybar">Polybar</a>, <a href="/configs/desktop/#rofi">Rofi</a>, <a href="/configs/desktop/#flameshot">Flameshot</a>, <a href="/configs/desktop/#dunst">dunst</a>, <a href="/configs/desktop/#picom">Picom</a>, <a href="/configs/desktop/#zathura">Zathura</a></li>
<li><em>In Limbo</em>: <a href="/configs/desktop/#i3wm">i3wm</a>, <a href="/configs/desktop/#keynav">keynav</a></li>
</ul>
</li>
<li><a href="/configs/console/">Console.org</a>
<ul>
<li><em>Active</em>: <a href="/configs/console/#dot-profile">.profile</a>, <a href="/configs/console/#bash">Bash</a>, <a href="/configs/console/#fish">Fish</a>, <a href="/configs/console/#starship-prompt">Starship</a>, <a href="/configs/console/#tmux">Tmux</a>, <a href="/configs/console/#alacritty">Alacritty</a></li>
<li><em>In Limbo</em>: <a href="/configs/console/#nushell">Nushell</a></li>
</ul>
</li>
<li><a href="/configs/guix/">Guix.org</a></li>
<li><a href="/configs/mail/">Mail.org</a>
<ul>
<li><em>Active</em>: <a href="/configs/mail/#lieer">Lieer</a>, <a href="/configs/mail/#davmail">DavMail</a>, <a href="/configs/mail/#offlineimap">OfflineIMAP</a>, <a href="/configs/mail/#notmuch">Notmuch</a></li>
</ul>
</li>
</ul>
<p>(<em>Apparently, links on the second level work only in Emacs 🙁</em>)</p>
<p>A few other repositories I may consider a part of my config:</p>
<ul>
<li><a href="https://github.com/SqrtMinusOne/channel-q">channel-q</a> is my Guix channel</li>
<li><a href="https://github.com/SqrtMinusOne/sqrt-data">sqrt-data</a> is a home for my statistics gathering effort</li>
<li>Emacs packages that I wrote (some of them originated in my Emacs config):
<ul>
<li><a href="https://github.com/SqrtMinusOne/lyrics-fetcher.el">lyrics-fetcher.el</a></li>
<li><a href="https://github.com/SqrtMinusOne/pomm.el">pomm.el</a></li>
<li><a href="https://github.com/SqrtMinusOne/perspective-exwm.el">perspective-exwm.el</a></li>
<li><a href="https://github.com/SqrtMinusOne/exwm-modeline">exwm-modeline.el</a></li>
<li><a href="https://github.com/SqrtMinusOne/org-journal-tags">org-journal-tags</a></li>
<li><a href="https://github.com/SqrtMinusOne/elfeed-summary">elfeed-summary</a></li>
<li><a href="https://github.com/SqrtMinusOne/password-store-ivy">password-store-ivy</a></li>
</ul>
</li>
</ul>
<p>Posts about my configuration:</p>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2022-01-03-exwm/">Using EXWM and perspective.el on a multi-monitor setup</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/">Getting a consistent set of keybindings between i3 and Emacs</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-09-07-emms/">My EMMS and elfeed setup</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-05-01-org-python/">Replacing Jupyter Notebook with Org Mode</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-02-27-gmail/">Multiple Gmail accounts &amp; labels with Emacs</a></li>
</ul>
<h2 id="some-statistics">Some statistics</h2>
<figure><img src="https://sqrtminusone.xyz/stats/all.png"/>
</figure>
<figure><img src="https://sqrtminusone.xyz/stats/emacs-vim.png"/>
</figure>
<figure><img src="https://sqrtminusone.xyz/stats/literate-config.png"/>
</figure>
<h2 id="misc">Misc</h2>
<h3 id="notes">Notes</h3>
<ul>
<li><code>M-u C-c C-v t</code> to tangle a particular block</li>
<li><code>M-u M-u C-c C-v t</code> to tangle a particular file</li>
<li><code>C-c C-v d</code> to demarcate a block</li>
</ul>
<p>Uses yadm&rsquo;s <code>post_alt</code> hook to create symlinks</p>
<h3 id="encrypted-files">Encrypted files</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Mail/thexcloud/.credentials.gmailieer.json
</span></span><span style="display:flex;"><span>Mail/progin6304/.credentials.gmailieer.json
</span></span><span style="display:flex;"><span>.emacs.d/private.org
</span></span><span style="display:flex;"><span>.emacs.d/private.el
</span></span><span style="display:flex;"><span>.emacs.d/.trello/sqrtminusone.el
</span></span></code></pre></div>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

98
index.html Normal file
View file

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang=""><head>
<meta name="generator" content="Hugo 0.96.0" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Index</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h2 id="about">About</h2>
<figure class="avatar"><img src="/logo_sq.png"/>
</figure>
<p><strong>Pavel Korytov</strong></p>
<p>Master&rsquo;s student of Saint-Petersurg State Electrotechnical University, Russia at Software Engineering.</p>
<h3 id="links">Links</h3>
<ul>
<li><a href="mailto:thexcloud@gmail.com">e-mail</a></li>
<li><a href="https://github.com/SqrtMinusOne">GitHub</a></li>
<li><a href="https://vk.com/sqrtminusone">VK</a></li>
<li><a href="https://twitter.com/SqrtMinusTwo">Twitter</a></li>
<li><a href="https://www.reddit.com/user/XCapitan_1">Reddit</a></li>
</ul>
<h3 id="also">Also</h3>
<ul>
<li><a href="https://sqrtminusone.xyz/git/">My instance of Gitea</a></li>
<li><a href="/0x914472A1FD6775C166F96EBEED739ADF81C78160.asc">My PGP key</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

84
index.xml Normal file
View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Index on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/</link>
<description>Recent content in Index on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 12 Feb 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>A few cases of literate configuration</title>
<link>https://sqrtminusone.xyz/posts/2022-02-12-literate/</link>
<pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-02-12-literate/</guid>
<description>A post that arose from the discussion of literate configuration on the System Crafters Discord.
I am using the literate configuration strategy (based on Emacs&amp;rsquo; Org Mode) to manage most of my configuration files. A piece of such a configuration can be as simple as an Org file, which is tangled to one or many plain-text configuration files, but it can be more.
In my opinion, a literate configuration can be more straightforward and concise than a &amp;ldquo;normal&amp;rdquo; one, thanks to Org Mode&amp;rsquo;s capabilities of working with source code.</description>
</item>
<item>
<title>Using EXWM and perspective.el on multi-monitor setup</title>
<link>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</link>
<pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</guid>
<description>I wrote about Emacs and i3 integration around two months ago. Shortly after however, I decided to give EXWM another try, mainly because my largest reservation - lack of performance - seems to have been resolved by updates to the native compilation since my first attempt. Or I may have lost some sensitivity to that issue. Regardless, the second dive into EXWM thus far feels successful, and I think it&amp;rsquo;s the right time to share some of my thoughts on the subject.</description>
</item>
<item>
<title>Getting a consistent set of keybindings between i3 and Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</link>
<pubDate>Wed, 06 Oct 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</guid>
<description>Intro One advantage of EXWM for an Emacs user is that EXWM gives one set of keybindings to manage both Emacs windows and X windows. In every other WM, like my preferred i3wm, two orthogonal keymaps seem to be necessary. But, as both programs are quite customizable, I want to see whether I can replicate at least some part of the EXWM goodness in i3.
But why not just use EXWM?</description>
</item>
<item>
<title>My EMMS and elfeed setup</title>
<link>https://sqrtminusone.xyz/posts/2021-09-07-emms/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-09-07-emms/</guid>
<description>Intro This is the current state of my quest to live in Emacs, at least in part of reading RSS and music.
Even before I lost my mind about customizing obscure keyboard-driven software, I tried Inoreader, self-hosted FreshRSS, and then newsboat from the RSS side and ncmpcpp+MPD from the audio player side. At some point, I got curious about whether I can do the same in Emacs.
The respective emacs packages, elfeed and EMMS, proved somewhat tricky to set up, i.</description>
</item>
<item>
<title>Replacing Jupyter Notebook with Org Mode</title>
<link>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</link>
<pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</guid>
<description>Why? Jupyter Notebook and its successor Jupyter Lab providing an interactive development environment for many programming languages are in lots of ways great pieces of software.
But while I was using the former, and then the latter, I was also an as-full-time-as-one-can-get NeoVim user. &amp;ldquo;As one can get&amp;rdquo; is because, of course, there is no sensible way to extend the NeoVim editing experience to the Jupyter ecosystem.
A possibility for change appeared with my discovery of Emacs not so long ago.</description>
</item>
<item>
<title>Multiple Gmail accounts &amp; labels with Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</link>
<pubDate>Sat, 27 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</guid>
<description>Intro For quite some time, e-mail seemed like an anomaly in my workflow. I am a long time Gmail user, and my decade-old account has a somewhat formidable quantity of labels and filters. My messages are often assigned multiple labels, and I also like to keep only a bunch of messages in the inbox.
Although, in my opinion, Gmail web UI was and still is leagues ahead of many of its competitors and even allows keyboard-centric workflow, it&amp;rsquo;s awkward to use with a keyboard-driven browser, and for no money on Earth I would enable browser notifications.</description>
</item>
<item>
<title>Hello, world!</title>
<link>https://sqrtminusone.xyz/posts/hello-world/</link>
<pubDate>Mon, 01 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/hello-world/</guid>
<description>Hello, world! Eventually, there will be something interesting here. Or not.
Regradless, I&amp;rsquo;ll check if I can write some Python here
print(&amp;#34;Hello, world&amp;#34;) Hello, world </description>
</item>
</channel>
</rss>

6
logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

BIN
logo_sq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
ox-hugo/all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
ox-hugo/emacs-vim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
ox-hugo/emms-screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ox-hugo/i3-emacs-demo.mp4 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
ox-hugo/literate-config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
ox-hugo/mail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

BIN
ox-hugo/main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

BIN
ox-hugo/notification.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 KiB

BIN
ox-hugo/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 KiB

View file

@ -0,0 +1,312 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Multiple Gmail accounts &amp; labels with Emacs</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h2 id="intro">Intro</h2>
<p>For quite some time, e-mail seemed like an anomaly in my workflow. I am a long time Gmail user, and my decade-old account has a somewhat formidable quantity of labels and filters. My messages are often assigned multiple labels, and I also like to keep only a bunch of messages in the inbox.</p>
<p>Although, in my opinion, Gmail web UI was and still is leagues ahead of many of its competitors and even allows keyboard-centric workflow, it&rsquo;s awkward to use with a keyboard-driven browser, and for no money on Earth I would enable browser notifications.</p>
<p>Any classical IMAP/SMTP client is hard to use in my case, because a message with multiple labels is copied to IMAP folders for each of the label plus the inbox folder, and the copies look like different messages from the client-side. For example, a message can be read in one label and unread in another.</p>
<p>For a few years, my solution was <a href="https://getmailspring.com/">Mailspring</a>, which provides first-class support for labels. However, it has a feature to deploy <a href="https://www.bbc.com/news/technology-56071437">spy pixels</a> on emails (and offers no protection from them, obviously), the client is Electron-based with a mouse-driven interface, and the sync engine was closed-source at the time.</p>
<p>So, I found an alternative in Emacs+notmuch+lieer and ditched one more proprietary app (the last big one I can&rsquo;t let go of is DataGrip).</p>
<figure><img src="/ox-hugo/main.png"/>
</figure>
<figure><img src="/ox-hugo/mail.png"/>
</figure>
<p>Notmuch&rsquo;s tags are just as advanced as Gmail&rsquo;s labels, so I have basically the same mail structure accessible from Emacs, Gmail Android client and even the web UI when I don&rsquo;t have access to the first two.</p>
<p>Also, I think the setup I describe here is pretty straightforward and less complex than many I encountered, but my impression is not the most reliable source of such knowledge.</p>
<p>In any case, what follows is a description of my current workflow with instructions of varying levels of precision of how to get there.</p>
<h2 id="setting-up">Setting up</h2>
<h3 id="gmail">Gmail</h3>
<p>Before we start, some setup is required for the Gmail account.</p>
<p>First, as there is no way to enable SMTP without IMAP on Gmail, you have to set &ldquo;Enable IMAP&rdquo; in the &ldquo;Forwarding and POP/IMAP&rdquo; tab in the settings. If you use two-factor auth, generate an <a href="https://support.google.com/accounts/answer/185833?hl=en">app password</a>.</p>
<p>Also, make sure your labels do not contain whitespaces because if they do, you will have to type them in quotes all the time.</p>
<h3 id="lieer">lieer</h3>
<p><a href="https://github.com/gauteh/lieer">lieer</a> (formerly gmailieer) is a program that uses Gmail API to download email and synchronize Gmail labels with notmuch tags. Because of its usage of Gmail API instead of IMAP, there are no problems with duplicating emails in different labels, etc.</p>
<p>As I need to use multiple versions of Python &amp; Node.js for other reasons, I manage my installations of them with <a href="https://anaconda.org">Anaconda</a> (Miniconda, to be precise). You may instead use <a href="https://docs.python.org/3/library/venv.html">venv</a> or even the system-wide installation of Python and omit the <code>conda</code> clauses, but in my experience Anaconda makes life easier in that regard.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Create an environment with the name &#34;mail&#34;</span>
</span></span><span style="display:flex;"><span>conda create --name mail
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Activate the environment</span>
</span></span><span style="display:flex;"><span>conda activate mail
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Install Python</span>
</span></span><span style="display:flex;"><span>conda install python
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Download and install lieer</span>
</span></span><span style="display:flex;"><span>git clone https://github.com/gauteh/lieer.git
</span></span><span style="display:flex;"><span>cd lieer
</span></span><span style="display:flex;"><span>pip install .
</span></span></code></pre></div><p>After which we may check if the <code>gmi</code> executable is available:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>which gmi
</span></span></code></pre></div><h3 id="notmuch">Notmuch</h3>
<p><a href="https://notmuchmail.org/">Notmuch</a> is present in most of the package repositories, so you can install it with your package manager, which is <code>pacman</code> in my case.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo pacman -S notmuch
</span></span></code></pre></div><p>After the installation, run <code>notmuch setup</code>. That will inquire the parameters and create the <code>.notmuch-config</code> file with the answers.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Your full name <span style="color:#f92672">[</span>Pavel<span style="color:#f92672">]</span>: Pavel Korytov
</span></span><span style="display:flex;"><span>Your primary email address <span style="color:#f92672">[</span>pavel@pdsk.<span style="color:#f92672">(</span>none<span style="color:#f92672">)]</span>: thexcloud@gmail.com
</span></span><span style="display:flex;"><span>Additional email address <span style="color:#f92672">[</span>Press <span style="color:#e6db74">&#39;Enter&#39;</span> <span style="color:#66d9ef">if</span> none<span style="color:#f92672">]</span>:
</span></span><span style="display:flex;"><span>Top-level directory of your email archive <span style="color:#f92672">[</span>/home/pavel/mail<span style="color:#f92672">]</span>: /home/pavel/Mail
</span></span><span style="display:flex;"><span>Tags to apply to all new messages <span style="color:#f92672">(</span>separated by spaces<span style="color:#f92672">)</span> <span style="color:#f92672">[</span>unread inbox<span style="color:#f92672">]</span>: new
</span></span><span style="display:flex;"><span>Tags to exclude when searching messages <span style="color:#f92672">(</span>separated by spaces<span style="color:#f92672">)</span> <span style="color:#f92672">[</span>deleted spam<span style="color:#f92672">]</span>:
</span></span></code></pre></div><p>It is important to set the <code>new</code> tag for the new messages instead of the default <code>unread</code> and <code>inbox</code>.</p>
<p>Next, add the rule to ignore JSON files to the <code>[new]</code> section of the <code>.notmuch-config</code> file, so it would look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>new<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>tags<span style="color:#f92672">=</span>new
</span></span><span style="display:flex;"><span>ignore<span style="color:#f92672">=</span>/.*<span style="color:#f92672">[</span>.<span style="color:#f92672">](</span>json|lock|bak<span style="color:#f92672">)</span>$/
</span></span></code></pre></div><p>That is needed to ignore the lieer config files. Although, as I have noticed, notmuch is generally pretty good at detecting wrong files in its directories, an explicit ignore rule won&rsquo;t hurt.</p>
<p>Now, create the mail directory and run the <a href="https://notmuchmail.org/manpages/notmuch-new-1/">notmuch new</a> command. As notmuch has probably already noticed you, it uses the <a href="https://en.wikipedia.org/wiki/Maildir">maildir</a> format, which basically means that one message is stored in one file.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># The same directory mentioned in the 4th question</span>
</span></span><span style="display:flex;"><span>mkdir ~/Mail
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Initialize notmuch</span>
</span></span><span style="display:flex;"><span>notmuch new
</span></span></code></pre></div><h3 id="add-an-account">Add an account</h3>
<p>After that, we can create a directory for a mail account and initialize lieer.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd ~/Mail
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Use whatever name you want</span>
</span></span><span style="display:flex;"><span>mkdir thexcloud
</span></span><span style="display:flex;"><span>cd thexcloud
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Intialize lieer</span>
</span></span><span style="display:flex;"><span>gmi init thexcloud@gmail.com
</span></span></code></pre></div><p>Running <code>gmi init</code> will run an OAuth authentication to your Gmail account. The credentials will be stored in <code>.credentials.gmailieer.json</code> file, so make sure not to expose it somewhere.</p>
<p>We also can add a few settings for lieer, which will make life easier. First, dots seem to be less awkward to type than slashes for the nested tags:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gmi set --replace-slash-with-dot
</span></span></code></pre></div><p>Then, we don&rsquo;t want the <code>new</code> tag to be pushed back to Gmail</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gmi set --ignore-tags-local new
</span></span></code></pre></div><p>Now we can finally download the mail directory. To initiate the download, run</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gmi sync
</span></span></code></pre></div><p>The first download can easily take several hours, depending on the size of your email and the speed of your internet connection, but subsequent runs will be much faster.</p>
<p>The last thing to do here is to add the <code>gmi sync</code> command to notmuch&rsquo;s <a href="https://notmuchmail.org/manpages/notmuch-hooks-5/">pre-new hook</a>, so that the email will be synchronized on the <code>notmuch new</code> command.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Create the hooks folder</span>
</span></span><span style="display:flex;"><span>mkdir -p ~/Mail/.notmuch/hooks
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Create the file</span>
</span></span><span style="display:flex;"><span>cd ~/Mail/.notmuch/hooks
</span></span><span style="display:flex;"><span>cat &gt; pre-new <span style="color:#e6db74">&lt;&lt;EOF
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">eval &#34;$(conda shell.bash hook)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">conda activate mail
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">(cd /home/pavel/Mail/thexcloud/ &amp;&amp; gmi sync)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>chmod +x pre-new
</span></span></code></pre></div><p>Side note: as a hook for <code>conda</code> tends to be rather slow, I run the <code>gmi</code> command with system-wide Python as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>GMI<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/home/pavel/Programs/miniconda3/envs/mail/bin/gmi&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">(</span>cd /home/pavel/Mail/thexcloud/ <span style="color:#f92672">&amp;&amp;</span> $GMI sync<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>Which doesn&rsquo;t seem to cause any particular trouble in that case.</p>
<h3 id="emacs">Emacs</h3>
<p>There are plenty of different <a href="https://notmuchmail.org/frontends/">frontends</a> for notmuch (even GUI apps), but the one I&rsquo;m sticking with the Emacs.</p>
<p>Configuration for Emacs is pretty straightforward, but you probably want to use the notmuch package which came with the system package, because otherwise, you may end up with different versions of frontend and backend.</p>
<p>That&rsquo;s how it can be done with <code>use-package</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package notmuch
</span></span><span style="display:flex;"><span> :ensure <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span> :commands (notmuch)
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (add-hook <span style="color:#e6db74">&#39;notmuch-hello-mode-hook</span>
</span></span><span style="display:flex;"><span> (lambda () (display-line-numbers-mode <span style="color:#ae81ff">0</span>))))
</span></span></code></pre></div><p>The only notable observation here is that <code>display-line-numbers-mode</code> seems to break formatting of the <code>notmuch-hello</code> page.</p>
<p>If you use evil-mode, you also should enable the <a href="https://github.com/emacs-evil/evil-collection/blob/master/modes/notmuch/evil-collection-notmuch.el">evil-collection mode for notmuch</a>.</p>
<p>Now run <code>M-x notmuch</code> and the <code>notmuch-hello</code> page should appear. Running <code>notmuch-poll-and-refresh-this-buffer</code> (<code>gR</code> with evil bindings) will run the <code>notmuch new</code> command and refresh the buffer. All the syncronized messages should be present.</p>
<p>I should note that <a href="https://notmuchmail.org/notmuch-emacs/">notmuch frontend for Emacs</a> is the most user-friendly Emacs app I have seen so far. UI, commands and keybindings are self-descriptive, all the options can be configured with the build-in <code>customize</code> interface. It may be useful to look through <a href="https://notmuchmail.org/emacstips/">emacs tips</a> at the official site and <a href="https://notmuchmail.org/manpages/">notmuch man pages</a>, in particular <a href="https://notmuchmail.org/manpages/notmuch-search-terms-7/">syntax for notmuch queries</a>.</p>
<h3 id="reading-mail">Reading mail</h3>
<p><code>notmuch-search-show-thread</code> (<code>RET</code>) opens the thread under the cursor.</p>
<p><code>notmuch-show-view-part</code> (<code>. v</code> with evil) opens an attachment with associations defined in <a href="https://linux.die.net/man/4/mailcap">.mailcap</a> file. Mine looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>audio/*; mpc add %s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>image/*; feh %s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>application/msword; /usr/bin/xdg-open %s
</span></span><span style="display:flex;"><span>application/pdf; zathura %s
</span></span><span style="display:flex;"><span>application/postscript ; zathura %s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>text/html; /usr/bin/xdg-open %s
</span></span></code></pre></div><p>Here watch out for the last line, default version of which may be set as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>text/html; /usr/bin/xdg-open %s ; copiousoutput
</span></span></code></pre></div><p>Which causes a temporary file to be deleted before it could be opened because recent versions of <code>xdg-open</code> do not block the input.</p>
<p>As expected, Emacs mail reader does not trigger any <a href="https://www.emailprivacytester.com/">spy pixels or other tracking contents of email</a> (not any I know of, at least). However, opening an HTML email in a browser will even run embedded JavaScript. Therefore, <strong>in no case open emails you do not trust with <code>xdg-open</code></strong>. Even if you use NoScript, the browser will still load all the CSS, videos and even iframes, which can be used to track you.</p>
<p>Even Gmail web UI is preferable to view the message in a browser, because the former blocks most of the malicious stuff and does not seem to leak your IP to the sender, for what it&rsquo;s worth.</p>
<h3 id="sending-mail">Sending mail</h3>
<p>To start composing a message, run <code>notmuch-mua-new-mail</code> (<code>C</code> with evil bindings).</p>
<p>After doing so, <code>C-c C-c</code> will run <code>notmuch-mua-send-and-exit</code>, which will invoke the function stated in the <code>message-send-mail-function</code> variable. The default value of the variable is <code>sendmail-query-once</code>, which will inquire the parameters and save them as custom variables.</p>
<p>If SMTP is used, <code>send-mail-function</code> will be set to the one from the built-it <a href="https://www.emacswiki.org/emacs/SendingMail">smtpmail</a> package. SMTP parameters for Gmail are listed <a href="https://support.google.com/mail/answer/7126229?hl=en">here</a>.</p>
<p>Authorization parameters will be saved to your <a href="https://www.emacswiki.org/emacs/GnusAuthinfo">authinfo</a> file. If you didn&rsquo;t have one, the plaintext <code>.authinfo</code> will be created, so it&rsquo;s reasonable to encrypt it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd ~
</span></span><span style="display:flex;"><span>gpg -o .authinfo.gpg -c --cipher-algo AES256 .authinfo
</span></span></code></pre></div><p>However, if you plan to use multiple accounts with different SMTP servers, it makes more sense to use something like <a href="https://marlam.de/msmtp/msmtp.html">MSMTP</a> to manage multiple accounts. Here are a couple of examples (<a href="https://www.reddit.com/r/emacs/comments/9piml5/a%5Ffew%5Fquick%5Femacsnotmuch%5Fquestions/e83zcck?utm%5Fsource=share&amp;utm%5Fmedium=web2x&amp;context=3">1</a>, <a href="https://www.reddit.com/r/emacs/comments/9piml5/a%5Ffew%5Fquick%5Femacsnotmuch%5Fquestions/e84otah?utm%5Fsource=share&amp;utm%5Fmedium=web2x&amp;context=3">2</a>) how to do that.</p>
<p>Another alternative for Gmail is to use <a href="https://github.com/gauteh/lieer/wiki/GNU-Emacs-and-Lieer">lieer as sendmail program</a>. That may make sense if you don&rsquo;t want to enable IMAP and SMTP on your account.</p>
<p>There are also <a href="https://notmuchmail.org/emacstips/#index13h2">a bunch of ways</a> to set up address completion if the built-in completion based on notmuch database does not suffice.</p>
<p>I also use <a href="https://github.com/mhayashi1120/Emacs-langtool">LanguageTool for Emacs</a> to do a spell checking of important emails (integrations like that really make Emacs shine). For some reason, developers don&rsquo;t give a link to download the server on the frontpage, so <a href="https://dev.languagetool.org/http-server">here it is</a>. And here is the relevant part of my Emacs config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package langtool
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :commands (langtool-check)
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq langtool-language-tool-server-jar <span style="color:#e6db74">&#34;/home/pavel/Programs/LanguageTool-5.1/languagetool-server.jar&#34;</span>)
</span></span><span style="display:flex;"><span> (setq langtool-mother-tongue <span style="color:#e6db74">&#34;ru&#34;</span>))
</span></span></code></pre></div><p>As a last note here, to set up a signature create the <code>.signature</code> file in the <code>$HOME</code> directory. If you need more complex logic here, for instance, different signatures for different accounts, you can put an arbitrary expression to the <code>mail-signature</code> variable or apply <a href="https://notmuchmail.org/emacstips/#index16h2">this gnus-alias tip</a>.</p>
<h2 id="another-account">Another account</h2>
<h3 id="adding-an-account">Adding an account</h3>
<p>Now we can send and receive mail from one account. Adding another account is also pretty easy.</p>
<p>If another account is Gmail, the process starts the same as before:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Create a directory</span>
</span></span><span style="display:flex;"><span>mkdir -p ~/Mail/progin6304
</span></span><span style="display:flex;"><span>cd ~/Mail/progin6304
</span></span><span style="display:flex;"><span><span style="color:#75715e"># OAuth</span>
</span></span><span style="display:flex;"><span>gmi init progin6304@gmail.com
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Settings</span>
</span></span><span style="display:flex;"><span>gmi set --replace-slash-with-dot
</span></span></code></pre></div><p>However, before running <code>gmi sync</code> for the second account, we want to make sure that we can distinguish the message from different accounts. To do that, I add the <code>main</code> for the main account and <code>progin</code> for the second account. We also don&rsquo;t want these labels to be pushed:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd ~/Mail/thexcloud
</span></span><span style="display:flex;"><span>gmi set --ignore-tags-local new,mail,progin
</span></span><span style="display:flex;"><span>cd ~/Mail/progin6304
</span></span><span style="display:flex;"><span>gmi set --ignore-tags-local new,mail,progin
</span></span></code></pre></div><p>Now we can use notmuch&rsquo;s <code>post-new</code> hook to tag the messages based on their folder as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd ~/Mail/.notmuch/hooks
</span></span><span style="display:flex;"><span>cat &gt; post-new <span style="color:#e6db74">&lt;&lt;EOF
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">notmuch tag +main &#34;path:thexcloud/** AND tag:new&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">notmuch tag +progin &#34;path:progin6304/** AND tag:new&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">notmuch tag -new &#34;tag:new&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>chmod +x post-new
</span></span></code></pre></div><p>Now it finally makes sense why we wanted to use the <code>new</code> tag in the first place. In principle, any kind of tagging logic can be applied here, but for the reasons I stated earlier, I prefer to set up filters in the Gmail web interface.</p>
<p>The last thing to do is to modify the <code>pre-new</code> hook:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>GMI<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/home/pavel/Programs/miniconda3/envs/mail/bin/gmi&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">(</span>cd /home/pavel/Mail/thexcloud/ <span style="color:#f92672">&amp;&amp;</span> $GMI sync<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">(</span>cd /home/pavel/Mail/progin6304/ <span style="color:#f92672">&amp;&amp;</span> $GMI sync<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>After which we can finally tag the existing messages and download ones from the new account</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>notmuch tag +main <span style="color:#e6db74">&#34;path:thexcloud/**&#34;</span>
</span></span><span style="display:flex;"><span>notmuch new
</span></span></code></pre></div><p>The obvious problem, however, is that the messages are fetched sequentially, which is rather slow. A solution is to use something like <a href="http://www.gnu.org/software/parallel/">GNU Parallel</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>GMI<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/home/pavel/Programs/miniconda3/envs/mail/bin/gmi&#34;</span>
</span></span><span style="display:flex;"><span>parallel -j0 <span style="color:#e6db74">&#34;(cd /home/pavel/Mail/{}/ &amp;&amp; </span>$GMI<span style="color:#e6db74"> sync)&#34;</span> ::: thexcloud progin6304
</span></span></code></pre></div><p>I haven&rsquo;t encountered any trouble with that solution so far (and I don&rsquo;t see anything thread-unsafe in the lieer code), but I&rsquo;ll keep an eye on that.</p>
<p>In principle, it shouldn&rsquo;t be too hard to add a normal IMAP account as well with <a href="https://isync.sourceforge.io/mbsync.html">mbsync</a>, but I expect it would require something like iterating through the directory structure and assigning notmuch labels based on that. I&rsquo;ll probably try that some time in the future.</p>
<h3 id="emacs">Emacs</h3>
<p>With that done, I also want separate entries on the start page for each of the accounts. Doing that is easy enough, just modify the <code>notmuch-saved-searches</code> variable with <code>customize-group</code> or like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq notmuch-saved-searches
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>((:name <span style="color:#e6db74">&#34;inbox (main)&#34;</span> :query <span style="color:#e6db74">&#34;tag:inbox AND tag:main&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;unread (main)&#34;</span> :query <span style="color:#e6db74">&#34;tag:unread AND tag:main&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;sent (main)&#34;</span> :query <span style="color:#e6db74">&#34;tag:sent AND tag:main&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;all mail (main)&#34;</span> :query <span style="color:#e6db74">&#34;tag:main&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;inbox (progin)&#34;</span> :query <span style="color:#e6db74">&#34;tag:inbox AND tag:progin&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;unread (progin)&#34;</span> :query <span style="color:#e6db74">&#34;tag:unread AND tag:progin&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;sent (progin)&#34;</span> :query <span style="color:#e6db74">&#34;tag:sent AND tag:progin&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;all main (progin)&#34;</span> :query <span style="color:#e6db74">&#34;tag:progin&#34;</span>)
</span></span><span style="display:flex;"><span> (:name <span style="color:#e6db74">&#34;drafts&#34;</span> :query <span style="color:#e6db74">&#34;tag:draft&#34;</span>)))
</span></span></code></pre></div><h2 id="notification-for-new-messages">Notification for new messages</h2>
<p>Now, we can send and receive mail, but we also probably want notifications for new emails. To do that, I wrote a simple script:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e"># To run notify-send from cron</span>
</span></span><span style="display:flex;"><span>export DISPLAY<span style="color:#f92672">=</span>:0
</span></span><span style="display:flex;"><span><span style="color:#75715e"># A file with last time of sync</span>
</span></span><span style="display:flex;"><span>CHECK_FILE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/home/pavel/Mail/.last_check&#34;</span>
</span></span><span style="display:flex;"><span>QUERY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;tag:unread&#34;</span>
</span></span><span style="display:flex;"><span>ALL_QUERY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;tag:unread&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># If the file exists, check also the new messages from the last sync</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -f <span style="color:#e6db74">&#34;</span>$CHECK_FILE<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> DATE<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>cat <span style="color:#e6db74">&#34;</span>$CHECK_FILE<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> QUERY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$QUERY<span style="color:#e6db74"> and date:@</span>$DATE<span style="color:#e6db74">..&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>notmuch new
</span></span><span style="display:flex;"><span>NEW_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;</span>$QUERY<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>ALL_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;</span>$ALL_QUERY<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># I don&#39;t really care if there are unread messages for which I&#39;ve already seen a notification</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> $NEW_UNREAD -gt <span style="color:#ae81ff">0</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> MAIN_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;tag:unread AND tag:main&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> PROGIN_UNREAD<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>notmuch count <span style="color:#e6db74">&#34;tag:unread AND tag:progin&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span> read -r -d <span style="color:#e6db74">&#39;&#39;</span> NOTIFICATION <span style="color:#e6db74">&lt;&lt;EOM
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$NEW_UNREAD new messages
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$MAIN_UNREAD thexcloud@gmail.com
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$PROGIN_UNREAD progin6304@gmail.com
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$ALL_UNREAD total
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOM</span>
</span></span><span style="display:flex;"><span> notify-send <span style="color:#e6db74">&#34;New Mail&#34;</span> <span style="color:#e6db74">&#34;</span>$NOTIFICATION<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Save sync timestamp</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">$(</span>date +%s<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span> &gt; $CHECK_FILE
</span></span></code></pre></div><p>The script is launched with cron every 5 minutes:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>*/5 * * * * bash /home/pavel/bin/scripts/check-email
</span></span></code></pre></div><p>Here&rsquo;s how the notification looks like:
<img src="/ox-hugo/notification.png" alt=""></p>
<h2 id="caveats">Caveats</h2>
<ul>
<li><a href="https://github.com/gauteh/lieer#caveats">lieer</a> has an extensive list of caveats concerning Gmail API</li>
<li>Make sure that you understand the <a href="https://github.com/gauteh/lieer#changing-ignored-tags-and-translation-after-initial-sync">implications</a> of lieer&rsquo;s <code>--ignore-tags-locally</code> and <code>--ignore-tags-remote</code></li>
<li>If two of your accounts receive the same email, it will be stored as one email in notmuch, so tags from these accounts will be merged and pushed back on the next sync. To solve that, you can set tags from one account to be ignored on the rest of the accounts</li>
<li>A sent email is being downloaded again on the next sync. Not a great deal, but it is somewhat annoying to download recently sent attachments.</li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,429 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Replacing Jupyter Notebook with Org Mode</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<figure><img src="/ox-hugo/org-python-screenshot.png"/>
</figure>
<h2 id="why">Why?</h2>
<p><a href="https://jupyter-notebook.readthedocs.io/en/stable/">Jupyter Notebook</a> and its successor <a href="https://jupyterlab.readthedocs.io/en/stable/">Jupyter Lab</a> providing an interactive development environment for many programming languages are in lots of ways great pieces of software.</p>
<p>But while I was using the former, and then the latter, I was also an as-full-time-as-one-can-get NeoVim user. &ldquo;As one can get&rdquo; is because, of course, there is no sensible way to extend the NeoVim editing experience to the Jupyter ecosystem.</p>
<p>A possibility for change appeared with my discovery of Emacs not so long ago. Emacs, a substantially more extensible piece of software, potentially can be used for the mentioned kind of programming. So I decided to try.</p>
<p>Sometime past that decision, it&rsquo;s time to wrap up the results. To start with, I&rsquo;ll briefly discuss the pros &amp; cons of using Org mode rather than Jupyter Notebook/Lab. Here is my list of advantages:</p>
<ul>
<li>Emacs, at least for me, is way more comfortable to use than a browser</li>
<li>Org mode allows using multiple programming languages in one file or multiple sessions with one programming language</li>
<li>Richer &amp; way more flexible export &amp; tangle capacities</li>
<li>More reasonable version control because org mode is just plain text, contrary to Jupyter&rsquo;s JSONs</li>
</ul>
<p>The first point deserves to be spelled out with more detail. To start with, Emacs is an objectively better text editor than Jupyter, as Emacs offers a considerable superset of Jupyter&rsquo;s features concerning just writing and editing text. The farthest one can go with Jupyter Lab is to install a vim emulation plugin, which still isn&rsquo;t as good as Evil mode.</p>
<p>The fact that Emacs can be used for different purposes also helps. For instance, I often write LaTeX documents, which are loosely based on the nearby code, e.g. using some generated charts or tables. Switching an Emacs buffer is easier than switching between Emacs and browser, not to mention that Emacs buffers usually have the same set of keybindings.</p>
<p>Emacs&rsquo; buffer management system, which is good enough for a window manager, is barely comparable to the tabs of Jupyter Lab. And so on.</p>
<p>As for why one may want to use Jupyter instead, here is my take on cons:</p>
<ul>
<li>Potential performance issues</li>
<li>The output is not as rich as in the browser</li>
<li>Collaboration with non-Emacs users is somewhat complicated</li>
</ul>
<p>Separation of kernels, server, and client together with non-blocking JavaScript-based UI is a good argument for using Jupyter. And it certainly won&rsquo;t be a problem for a browser to suddenly print a line a million characters long.</p>
<p>As for the richness of the output, while there are ways to work around the limitations of Emacs there, in some cases the best thing one can do is open the output in question with a browser. I&rsquo;ll discuss doing that further below.</p>
<p>The collaboration issue can be alleviated with rich export capabilities, but if someone wants to change something in your Org file, execute it and send it back to you, they have to use Emacs.</p>
<h2 id="basic-setup">Basic setup</h2>
<p>The core package to this whole venture is <a href="https://github.com/nnicandro/emacs-jupyter">emacs-jupyter</a> (another notable alternative <a href="https://github.com/millejoh/emacs-ipython-notebook">ein</a>, using which can help with the collaboration problem).</p>
<p>Install it however you install packages in Emacs, here is my preferred way with <code>use-package</code> and <code>straight.el</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package jupyter
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>)
</span></span></code></pre></div><p>Then, we have to enable languages for <code>org-babel</code>. Put the following in your org mode config section:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(org-babel-do-load-languages
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;org-babel-load-languages</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>((emacs-lisp <span style="color:#f92672">.</span> <span style="color:#66d9ef">t</span>) <span style="color:#75715e">;; Other languages</span>
</span></span><span style="display:flex;"><span> (shell <span style="color:#f92672">.</span> <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Python &amp; Jupyter</span>
</span></span><span style="display:flex;"><span> (python <span style="color:#f92672">.</span> <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span> (jupyter <span style="color:#f92672">.</span> <span style="color:#66d9ef">t</span>)))
</span></span></code></pre></div><p>Now, you should be able to use source blocks with names like <code>jupyter-LANG</code>, e.g. <code>jupyter-python</code>. To use just <code>LANG</code> src blocks, call the following function after <code>org-babel-do-load-languages</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(org-babel-jupyter-override-src-block <span style="color:#e6db74">&#34;python&#34;</span>)
</span></span></code></pre></div><p>That overrides the built-in <code>python</code> block with <code>jupyter-python</code>.</p>
<p>If you use <a href="https://github.com/astahlman/ob-async">ob-async</a>, you have to set <code>jupyter-LANG</code> blocks as ignored by this package, because emacs-jupyter has async execution of its own.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq ob-async-no-async-languages-alist <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;python&#34;</span> <span style="color:#e6db74">&#34;jupyter-python&#34;</span>))
</span></span></code></pre></div><h2 id="environments">Environments</h2>
<p>So, we&rsquo;ve set up a basic emacs-jupyter configuration.</p>
<p>The catch here is that Jupyter should be available on Emacs startup (at the time of evaluation of the <code>emacs-jupyter</code> package, to be precise). That means, if you are launching Emacs with something like an application launcher, global Python &amp; Jupyter will be used.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span>sys<span style="color:#f92672">.</span>executable
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/usr/bin/python3
</span></span></code></pre></div><p>Which is probably not what we want. To resolve that, we have to make the right Python available at the required time.</p>
<h3 id="anaconda">Anaconda</h3>
<p>If you were using Jupyter Lab or Notebook before, there is a good chance you install it via <a href="https://anaconda.org/">Anaconda</a>. If not, in a nutshell, it is a package &amp; environment manager, which specializes in Python &amp; R, but also supports a whole lot of stuff like Node.js. In my opinion, it is the easiest way to manage multiple Python installations if you don&rsquo;t use some advanced package manager like Guix.</p>
<p>As one may expect, there is an Emacs package called <a href="https://github.com/necaris/conda.el">conda.el</a> to help working with conda environments in Emacs. We have to put it somewhere before the <code>emacs-jupyter</code> package and call <code>conda-env-activate</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package conda
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq conda-anaconda-home (<span style="color:#a6e22e">expand-file-name</span> <span style="color:#e6db74">&#34;~/Programs/miniconda3/&#34;</span>))
</span></span><span style="display:flex;"><span> (setq conda-env-home-directory (<span style="color:#a6e22e">expand-file-name</span> <span style="color:#e6db74">&#34;~/Programs/miniconda3/&#34;</span>))
</span></span><span style="display:flex;"><span> (setq conda-env-subdirectory <span style="color:#e6db74">&#34;envs&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(unless (getenv <span style="color:#e6db74">&#34;CONDA_DEFAULT_ENV&#34;</span>)
</span></span><span style="display:flex;"><span> (conda-env-activate <span style="color:#e6db74">&#34;base&#34;</span>))
</span></span></code></pre></div><p>If you have Anaconda installed on a custom path, as I do, you&rsquo;d have to add these <code>setq</code> lines in the <code>:config</code> section. Also, there is no point in activating the environment if Emacs is somehow already launched in an environment.</p>
<p>That&rsquo;ll give us Jupyter from a base conda environment.</p>
<p>If you use a plain virtual environment, you can use <a href="https://github.com/porterjamesj/virtualenvwrapper.el">virtualenvwrapper.el</a>, which is similar in its design to conda.el (or, rather, the other way round).</p>
<h3 id="switching-an-environment">Switching an environment</h3>
<p>However, as you will notice rather soon, <code>emacs-jupyter</code> will always use the Python kernel found on startup. So if you switch to a new environment, the code will still be running in the old one, which is not too convenient.</p>
<p>Fortunately, to fix that we have only to force the refresh of Jupyter kernelspecs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/jupyter-refresh-kernelspecs ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Refresh Jupyter kernelspecs&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (jupyter-available-kernelspecs <span style="color:#66d9ef">t</span>))
</span></span></code></pre></div><p>Calling <code>M-x my/jupyter-refresh-kernelspecs</code> after an environment switch will give you a new kernel. Just keep in mind that a kernelspec seems to be attached to a session, so you&rsquo;d also have to change the session name to get a new kernel.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span>sys<span style="color:#f92672">.</span>executable
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/home/pavel/Programs/miniconda3/bin/python
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(conda-env-activate <span style="color:#e6db74">&#34;ann&#34;</span>)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span>sys<span style="color:#f92672">.</span>executable
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/home/pavel/Programs/miniconda3/bin/python
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/jupyter-refresh-kernelspecs)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span>sys<span style="color:#f92672">.</span>executable
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/home/pavel/Programs/miniconda3/envs/ann/bin/python
</span></span></code></pre></div><h2 id="programming">Programming</h2>
<p>To test if everything is working correctly, run <code>M-x jupyter-run-repl</code>, which should give you a REPL with a chosen kernel. If so, we can finally start using Python in org mode.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python :session hello :async yes
</span></span><span style="display:flex;"><span>print(&#39;Hello, world!&#39;)
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>: Hello, world!
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>To avoid repeating similar arguments for the src block, we can set the <code>header-args</code> property at the start of the file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+PROPERTY: header-args:python :session hello
</span></span><span style="display:flex;"><span>#+PROPERTY: header-args:python+ :async yes
</span></span></code></pre></div><p>When a kernel is initialized, an associated REPL buffer is also created with a name like <code>*jupyter-repl[python 3.9.2]-hello*</code>.</p>
<p>One advantage of emacs-jupyter over the standard Org source execution is that kernel requests for input are queried through the minibuffer. So, you can run a code like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python
</span></span><span style="display:flex;"><span>name = input(&#39;Name: &#39;)
</span></span><span style="display:flex;"><span>print(f&#39;Hello, {name}!&#39;)
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>: Hello, Pavel!
</span></span></code></pre></div><p>without any additional setup.</p>
<h2 id="code-output">Code output</h2>
<h3 id="images">Images</h3>
<p>Image output should work out of the box. Run <code>M-x org-toggle-inline-images</code> (<code>C-c C-x C-v</code>) after the execution to see the image inline.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python
</span></span><span style="display:flex;"><span>import matplotlib.pyplot as plt
</span></span><span style="display:flex;"><span>fig, ax = plt.subplots()
</span></span><span style="display:flex;"><span>ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
</span></span><span style="display:flex;"><span>pass
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>[[file:./.ob-jupyter/86b3c5e1bbaee95d62610e1fb9c7e755bf165190.png]]
</span></span></code></pre></div><p>There is some room for improvement though. First, you can add the following hook if you don&rsquo;t want to press this awkward keybinding every time:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;org-babel-after-execute-hook</span> <span style="color:#e6db74">&#39;org-redisplay-inline-images</span>)
</span></span></code></pre></div><p>Second, we may override the image save path like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python :file img/hello.png
</span></span><span style="display:flex;"><span>import matplotlib.pyplot as plt
</span></span><span style="display:flex;"><span>fig, ax = plt.subplots()
</span></span><span style="display:flex;"><span>ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
</span></span><span style="display:flex;"><span>pass
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>[[file:img/hello.png]]
</span></span></code></pre></div><p>That can save you a <code>savefig</code> call if the image has to be used somewhere further.</p>
<p>Finally, by default, the image has a transparent background and a ridiculously small size. That can be fixed with some matplotlib settings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> matplotlib <span style="color:#66d9ef">as</span> mpl
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mpl<span style="color:#f92672">.</span>rcParams[<span style="color:#e6db74">&#39;figure.dpi&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">200</span>
</span></span><span style="display:flex;"><span>mpl<span style="color:#f92672">.</span>rcParams[<span style="color:#e6db74">&#39;figure.facecolor&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1&#39;</span>
</span></span></code></pre></div><p>At the same time, we can set the image width to prevent images from becoming too large. I prefer to do it inside a <code>emacs-lisp</code> code block in the same org file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq-local org-image-actual-width <span style="color:#f92672">&#39;</span>(<span style="color:#ae81ff">1024</span>))
</span></span></code></pre></div><h3 id="basic-tables">Basic tables</h3>
<p>If you are evaluating something like pandas DataFrame, it will be outputted in the HTML format, wrapped in the <code>begin_export</code> block. To view the data in text format, you can set <code>:display plain</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python :display plain
</span></span><span style="display:flex;"><span>import pandas as pd
</span></span><span style="display:flex;"><span>pd.DataFrame({&#34;a&#34;: [1, 2], &#34;b&#34;: [3, 4]})
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>: a b
</span></span><span style="display:flex;"><span>: 0 1 3
</span></span><span style="display:flex;"><span>: 1 2 4
</span></span></code></pre></div><p>Another solution is to use something like the <a href="https://pypi.org/project/tabulate/">tabulate</a> package:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python
</span></span><span style="display:flex;"><span>import pandas as pd
</span></span><span style="display:flex;"><span>import tabulate
</span></span><span style="display:flex;"><span>df = pd.DataFrame({&#34;a&#34;: [1, 2], &#34;b&#34;: [3, 4]})
</span></span><span style="display:flex;"><span>print(tabulate.tabulate(df, headers=df.columns, tablefmt=&#34;orgtbl&#34;))
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>: | | a | b |
</span></span><span style="display:flex;"><span>: |----+-----+-----|
</span></span><span style="display:flex;"><span>: | 0 | 1 | 3 |
</span></span><span style="display:flex;"><span>: | 1 | 2 | 4 |
</span></span></code></pre></div><h3 id="html-and-other-rich-output">HTML &amp; other rich output</h3>
<p>Yet another solution is to use emacs-jupyter&rsquo;s option <code>:pandoc t</code>, which invokes pandoc to convert HTML, LaTeX, and Markdown to Org. Predictably, this is slower than the options above.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src python :pandoc t
</span></span><span style="display:flex;"><span>import pandas as pd
</span></span><span style="display:flex;"><span>df = pd.DataFrame({&#34;a&#34;: [1, 2], &#34;b&#34;: [3, 4]})
</span></span><span style="display:flex;"><span>df
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+RESULTS:
</span></span><span style="display:flex;"><span>:RESULTS:
</span></span><span style="display:flex;"><span>| | a | b |
</span></span><span style="display:flex;"><span>|---+---+---|
</span></span><span style="display:flex;"><span>| 0 | 1 | 3 |
</span></span><span style="display:flex;"><span>| 1 | 2 | 4 |
</span></span><span style="display:flex;"><span>:END:
</span></span></code></pre></div><p>Also, every once in a while I have to view an actual, unconverted HTML in a browser, e.g. when using <a href="https://python-visualization.github.io/folium/">folium</a> or <a href="https://spacy.io/usage/visualizers">displaCy</a>.</p>
<p>To do that, I&rsquo;ve written a small function, which performs <code>xdg-open</code> on the HTML export block under the cursor:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/org-view-html-tmp-dir <span style="color:#e6db74">&#34;/tmp/org-html-preview/&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(use-package f
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/org-view-html ()
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (let ((elem (org-element-at-point))
</span></span><span style="display:flex;"><span> (temp-file-path (<span style="color:#a6e22e">concat</span> my/org-view-html-tmp-dir (<span style="color:#a6e22e">number-to-string</span> (<span style="color:#a6e22e">random</span> (<span style="color:#a6e22e">expt</span> <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">32</span>))) <span style="color:#e6db74">&#34;.html&#34;</span>)))
</span></span><span style="display:flex;"><span> (cond
</span></span><span style="display:flex;"><span> ((not (<span style="color:#a6e22e">eq</span> <span style="color:#e6db74">&#39;export-block</span> (<span style="color:#a6e22e">car</span> elem)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">message</span> <span style="color:#e6db74">&#34;Not in an export block!&#34;</span>))
</span></span><span style="display:flex;"><span> ((not (<span style="color:#a6e22e">string-equal</span> (<span style="color:#a6e22e">plist-get</span> (<span style="color:#a6e22e">car</span> (<span style="color:#a6e22e">cdr</span> elem)) :type) <span style="color:#e6db74">&#34;HTML&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">message</span> <span style="color:#e6db74">&#34;Export block is not HTML!&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#66d9ef">t</span> (progn
</span></span><span style="display:flex;"><span> (f-mkdir my/org-view-html-tmp-dir)
</span></span><span style="display:flex;"><span> (f-write (<span style="color:#a6e22e">plist-get</span> (<span style="color:#a6e22e">car</span> (<span style="color:#a6e22e">cdr</span> elem)) :value) <span style="color:#e6db74">&#39;utf-8</span> temp-file-path)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">start-process</span> <span style="color:#e6db74">&#34;org-html-preview&#34;</span> <span style="color:#66d9ef">nil</span> <span style="color:#e6db74">&#34;xdg-open&#34;</span> temp-file-path))))))
</span></span></code></pre></div><p><code>f.el</code> is used by a lot of packages, including the above-mentioned <code>conda.el</code>, so you probably already have it installed.</p>
<p>Put a cursor on the <code>begin_export html</code> block and run <code>M-x my/org-view-html</code>.</p>
<p>There also <a href="https://github.com/nnicandro/emacs-jupyter#building-the-widget-support-experimental">seems to be widgets support</a> in emacs-jupyter, but I wasn&rsquo;t able to make it work.</p>
<h3 id="dataframes">DataFrames</h3>
<p>Last but not least option I want to mention here is specifically about pandas&rsquo; DataFrames. There aren&rsquo;t many good options to view the full dataframe inside Emacs. One way I can think of is to save the dataframe in CSV and view it with <code>csv-mode</code>.</p>
<p>However, there are standalone packages to view dataframes. One I can point out is <a href="https://github.com/man-group/dtale">dtale</a>, which is a Flask + React app designed just for that purpose. It has a rather extensive list of features, including charting, basic statistical instruments, filters, etc. <a href="http://alphatechadmin.pythonanywhere.com/dtale/main/1">Here</a> is an online demo.</p>
<p>The problem here is that it&rsquo;s a browser app, which means it defies one of the purposes of using Org Mode in the first place. What&rsquo;s more, this application is a rather huge one with lots of dependencies, and they have to be installed in the same environment as your project.</p>
<p>So this approach has its pros and cons as well. Example usage is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> dtale
</span></span><span style="display:flex;"><span>d <span style="color:#f92672">=</span> dtale<span style="color:#f92672">.</span>show(df)
</span></span><span style="display:flex;"><span>d<span style="color:#f92672">.</span>open_browser() <span style="color:#75715e"># Or get an URL from d._url</span>
</span></span></code></pre></div><p>Another notable alternative is <a href="https://github.com/adamerose/pandasgui">PandasGUI</a>, which, as one can guess, is a GUI (PyQt5) application, although it uses QtWebEngine inside.</p>
<h2 id="remote-kernels">Remote kernels</h2>
<p>There are yet some problems in the current configuration.</p>
<ul>
<li>Input/output handling is far from perfect. For instance, (at least in my configuration) Emacs tends to get slow for log-like outputs, e.g. Keras with <code>verbose=2</code>. It may even hang if the output is one long line.</li>
<li><code>ipdb</code> behaves awkwardly if called from an <code>src</code> block, although it at least will let you type <code>quit</code>.</li>
<li>Whenever you close Emacs, kernels are stopped, so you&rsquo;d have to execute the code again on the next start.</li>
</ul>
<h3 id="using-a-remote-kernel">Using a &ldquo;remote&rdquo; kernel</h3>
<p>For the reasons above I sometimes prefer to use a standalone kernel. To start a Jupyter kernel, run the following command in the environment and path you need:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>jupyter kernel --kernel<span style="color:#f92672">=</span>python
</span></span></code></pre></div><p>After the kernel is launched, write the path to the connection file into the <code>:session</code> header and press <code>C-c C-c</code> to refresh the setup:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+PROPERTY: header-args:python :session /home/pavel/.local/share/jupyter/runtime/kernel-e770599c-2c98-429b-b9ec-4d1ddf5fc16c.json
</span></span></code></pre></div><p>Now python source blocks should be executed in the kernel.</p>
<p>To open a REPL, run <code>M-x jupyter-connect-repl</code> and select the given JSON. Or launch a standalone REPL like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>jupyter qtconsole --existing kernel-e770599c-2c98-429b-b9ec-4d1ddf5fc16c.json
</span></span></code></pre></div><p>Executing a piece of code in the REPL allows proper debugging, for instance with <code>%pdb</code> magic. Also, Jupyter QtConsole generally handles large outputs better and even allows certain kinds of rich output in the REPL.</p>
<h3 id="some-automation">Some automation</h3>
<p>Now, I wouldn&rsquo;t use Emacs if it wasn&rsquo;t possible to automate at least some of the listed steps. So here are the functions I&rsquo;ve written for that.</p>
<p>First, we need to get open ports on the system:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/get-open-ports ()
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapcar</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">#&#39;string-to-number</span>
</span></span><span style="display:flex;"><span> (split-string (shell-command-to-string <span style="color:#e6db74">&#34;ss -tulpnH | awk &#39;{print $5}&#39; | sed -e &#39;s/.*://&#39;&#34;</span>) <span style="color:#e6db74">&#34;\n&#34;</span>)))
</span></span></code></pre></div><p>Then, list the available kernel JSONs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/jupyter-runtime-folder (<span style="color:#a6e22e">expand-file-name</span> <span style="color:#e6db74">&#34;~/.local/share/jupyter/runtime&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/list-jupyter-kernel-files ()
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapcar</span>
</span></span><span style="display:flex;"><span> (lambda (file) (<span style="color:#a6e22e">cons</span> (<span style="color:#a6e22e">car</span> file) (<span style="color:#a6e22e">cdr</span> (<span style="color:#a6e22e">assq</span> <span style="color:#e6db74">&#39;shell_port</span> (json-read-file (<span style="color:#a6e22e">car</span> file))))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">sort</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">directory-files-and-attributes</span> my/jupyter-runtime-folder <span style="color:#66d9ef">t</span> <span style="color:#e6db74">&#34;.*kernel.*json$&#34;</span>)
</span></span><span style="display:flex;"><span> (lambda (x y) (not (<span style="color:#a6e22e">time-less-p</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">6</span> x) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">6</span> y)))))))
</span></span></code></pre></div><p>And query the user for a running kernel:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/select-jupyter-kernel ()
</span></span><span style="display:flex;"><span> (let ((ports (my/get-open-ports))
</span></span><span style="display:flex;"><span> (files (my/list-jupyter-kernel-files)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">completing-read</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Jupyter kernels: &#34;</span>
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (file)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">member</span> (<span style="color:#a6e22e">cdr</span> file) ports))
</span></span><span style="display:flex;"><span> files))))
</span></span></code></pre></div><p>After which we can use the <code>my/select-jupyter-kernel</code> function however we want:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/insert-jupyter-kernel ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Insert a path to an active Jupyter kernel into the buffer&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">insert</span> (my/select-jupyter-kernel)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/jupyter-connect-repl ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Open emacs-jupyter REPL, connected to a Jupyter kernel&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (jupyter-connect-repl (my/select-jupyter-kernel) <span style="color:#66d9ef">nil</span> <span style="color:#66d9ef">nil</span> <span style="color:#66d9ef">nil</span> <span style="color:#66d9ef">t</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/jupyter-qtconsole ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Open Jupyter QtConsole, connected to a Jupyter kernel&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">start-process</span> <span style="color:#e6db74">&#34;jupyter-qtconsole&#34;</span> <span style="color:#66d9ef">nil</span> <span style="color:#e6db74">&#34;setsid&#34;</span> <span style="color:#e6db74">&#34;jupyter&#34;</span> <span style="color:#e6db74">&#34;qtconsole&#34;</span> <span style="color:#e6db74">&#34;--existing&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">file-name-nondirectory</span> (my/select-jupyter-kernel))))
</span></span></code></pre></div><p>The first function, which simply inserts the path to the kernel, is meant to be used on the <code>:session</code> header. One can go even further and locate the header automatically, but that&rsquo;s an idea for next time.</p>
<p>The second one opens a REPL provided by emacs-jupyter. The <code>t</code> argument is necessary to pop up the REPL immediately.</p>
<p>The last one launches Jupyter QtConsole. <code>setsid</code> is required to run the program in a new session, so it won&rsquo;t close together with Emacs.</p>
<h3 id="cleaning-up">Cleaning up</h3>
<p>I&rsquo;ve also noticed that there are JSON files left in the runtime folder whenever the kernel isn&rsquo;t stopped correctly. So here is a cleanup function.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/jupyter-cleanup-kernels ()
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (let* ((ports (my/get-open-ports))
</span></span><span style="display:flex;"><span> (files (my/list-jupyter-kernel-files))
</span></span><span style="display:flex;"><span> (to-delete (seq-filter
</span></span><span style="display:flex;"><span> (lambda (file)
</span></span><span style="display:flex;"><span> (not (<span style="color:#a6e22e">member</span> (<span style="color:#a6e22e">cdr</span> file) ports)))
</span></span><span style="display:flex;"><span> files)))
</span></span><span style="display:flex;"><span> (when (and (length&gt; to-delete <span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span> (y-or-n-p (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;Delete %d files?&#34;</span> (<span style="color:#a6e22e">length</span> to-delete))))
</span></span><span style="display:flex;"><span> (dolist (file to-delete)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">delete-file</span> (<span style="color:#a6e22e">car</span> file)))))
</span></span></code></pre></div><h2 id="export">Export</h2>
<p>An uncountable number of articles have been written already on the subject of Org Mode export, so I will just cover my particular setup.</p>
<h3 id="html">HTML</h3>
<p>Export to a standalone HTML is an easy way to share the code with someone who doesn&rsquo;t use Emacs, just remember that HTML may not be the only file you&rsquo;d have to share if you have images in the document. Although you may use something like <a href="https://github.com/BitLooter/htmlark">htmlark</a> later to get a proper self-contained HTML.</p>
<p>To do the export, run <code>M-x org-html-export-to-html</code>. It should work out of the box, however, we can improve the output a bit.</p>
<p>First, we can add a custom CSS to the file. I like this one:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+HTML_HEAD: &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;https://gongzhitaao.org/orgcss/org.css&#34;/&gt;
</span></span></code></pre></div><p>To get a syntax highlighting, we need the <code>htmlize</code> package:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package htmlize
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :after ox
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq org-html-htmlize-output-type <span style="color:#e6db74">&#39;css</span>))
</span></span></code></pre></div><p>If you use the <a href="https://github.com/Fanael/rainbow-delimiters">rainbow-delimeters</a> package, as I do, default colors for delimiters may not look good with the light theme. To fix such issues, put an HTML snippet like this in a <code>begin_export html</code> block or a CSS file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">style</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text/css&#34;</span>&gt;
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">org-rainbow-delimiters-depth-1</span><span style="color:#f92672">,</span> .<span style="color:#a6e22e">org-rainbow-delimiters-depth-2</span><span style="color:#f92672">,</span> .<span style="color:#a6e22e">org-rainbow-delimiters-depth-3</span><span style="color:#f92672">,</span> .<span style="color:#a6e22e">org-rainbow-delimiters-depth-4</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">color</span>: <span style="color:#66d9ef">black</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">style</span>&gt;
</span></span></code></pre></div><h3 id="latex-pdf">LaTeX -&gt; pdf</h3>
<p>Even though I use LaTeX quite extensively, I don&rsquo;t like to add another layer of complexity here and 98% of the time write plain <code>.tex</code> files. LaTeX by itself provides many good options whenever you need to write a document together with some data or source code, contrary to &ldquo;traditional&rdquo; text processors.</p>
<p>Nevertheless, I want to get at least a tolerable pdf from Org, so here is a piece of my config with some inline comments.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/setup-org-latex ()
</span></span><span style="display:flex;"><span> (setq org-latex-compiler <span style="color:#e6db74">&#34;xelatex&#34;</span>) <span style="color:#75715e">;; Probably not necessary</span>
</span></span><span style="display:flex;"><span> (setq org-latex-pdf-process <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;latexmk -outdir=%o %f&#34;</span>)) <span style="color:#75715e">;; Use latexmk</span>
</span></span><span style="display:flex;"><span> (setq org-latex-listings <span style="color:#e6db74">&#39;minted</span>) <span style="color:#75715e">;; Use minted to highlight source code</span>
</span></span><span style="display:flex;"><span> (setq org-latex-minted-options <span style="color:#75715e">;; Some minted options I like</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>((<span style="color:#e6db74">&#34;breaklines&#34;</span> <span style="color:#e6db74">&#34;true&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;tabsize&#34;</span> <span style="color:#e6db74">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;autogobble&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;linenos&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;numbersep&#34;</span> <span style="color:#e6db74">&#34;0.5cm&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;xleftmargin&#34;</span> <span style="color:#e6db74">&#34;1cm&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;frame&#34;</span> <span style="color:#e6db74">&#34;single&#34;</span>)))
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Use extarticle without the default packages</span>
</span></span><span style="display:flex;"><span> (add-to-list <span style="color:#e6db74">&#39;org-latex-classes</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;org-plain-extarticle&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;\\documentclass{extarticle}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">[NO-DEFAULT-PACKAGES]
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">[PACKAGES]
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">[EXTRA]&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;\\section{%s}&#34;</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;\\section*{%s}&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;\\subsection{%s}&#34;</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;\\subsection*{%s}&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;\\subsubsection{%s}&#34;</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;\\subsubsection*{%s}&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;\\paragraph{%s}&#34;</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;\\paragraph*{%s}&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;\\subparagraph{%s}&#34;</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;\\subparagraph*{%s}&#34;</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;; Make sure to eval the function when org-latex-classes list already exists</span>
</span></span><span style="display:flex;"><span>(with-eval-after-load <span style="color:#e6db74">&#39;ox-latex</span>
</span></span><span style="display:flex;"><span> (my/setup-org-latex))
</span></span></code></pre></div><p>In the document itself, add the following headers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+LATEX_CLASS: org-plain-extarticle
</span></span><span style="display:flex;"><span>#+LATEX_CLASS_OPTIONS: [a4paper, 14pt]
</span></span></code></pre></div><p>14pt size is required by certain state standards of ours for some reason.</p>
<p>After which you can put whatever you want in the preamble with <code>LATEX_HEADER</code>. My workflow with LaTeX is to write a bunch of <code>.sty</code> files beforehand and import the necessary ones in the preamble. <a href="https://github.com/SqrtMinusOne/LaTeX%5Ftemplates">Here</a> is the repo with these files, although quite predictably, it&rsquo;s a mess. At any rate, I have to write something like the following in the target Org file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+LATEX_HEADER: \usepackage{styles/generalPreamble}
</span></span><span style="display:flex;"><span>#+LATEX_HEADER: \usepackage{styles/reportFormat}
</span></span><span style="display:flex;"><span>#+LATEX_HEADER: \usepackage{styles/mintedSourceCode}
</span></span><span style="display:flex;"><span>#+LATEX_HEADER: \usepackage{styles/russianLocale}
</span></span></code></pre></div><p><code>M-x org-latex-export-to-latex</code> should export the document to the .tex file. As an alternative, run <code>M-x org-export-dispatch</code> (by default should be on <code>C-c C-e</code>) an pick the required option there.</p>
<h3 id="ipynb">ipynb</h3>
<p>One last export backend I want to mention is <a href="https://github.com/jkitchin/ox-ipynb">ox-ipynb</a>, which allows exporting Org documents to Jupyter notebooks. Sometimes it works, sometimes it doesn&rsquo;t.</p>
<p>Also, the package isn&rsquo;t on MELPA, so you have to install it from the repo directly.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package ox-ipynb
</span></span><span style="display:flex;"><span> :straight (:host github :repo <span style="color:#e6db74">&#34;jkitchin/ox-ipynb&#34;</span>)
</span></span><span style="display:flex;"><span> :after ox)
</span></span></code></pre></div><p>To (try to) do export, run <code>M-x ox-ipynb-export-org-file-ipynb-file</code>.</p>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,306 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>My EMMS and elfeed setup</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h2 id="intro">Intro</h2>
<figure><img src="/ox-hugo/emms-screenshot.png"/>
</figure>
<p>This is the current state of my quest to live in Emacs, at least in part of reading RSS and music.</p>
<p>Even before I lost my mind about customizing obscure keyboard-driven software, I tried Inoreader, self-hosted FreshRSS, and then newsboat from the RSS side and ncmpcpp+MPD from the audio player side. At some point, I got curious about whether I can do the same in Emacs.</p>
<p>The respective emacs packages, elfeed and EMMS, proved somewhat tricky to set up, i.e. I had to figure out the source code in both cases. I even submitted a small patch to EMMS to make it parse my MPD library correctly.</p>
<p>But in the end, only extensive customization capacities of Emacs enabled me to make a setup where these parts nicely come together and do more or less exactly what I want. However, this means there are a lot of degrees of freedom involved, so Ill try to cover the important parts and link to the original sources wherever possible.</p>
<p>Id call it “workflow”, but the “work” part does not quite catch the point here.</p>
<h2 id="mpd">MPD</h2>
<p>So, we have to start somewhere.</p>
<p><a href="https://www.musicpd.org/">MPD</a> is a server for playing music, although it is usually hosted on the local machine, i.e. the one on which you intend to listen to music. There is <a href="https://www.musicpd.org/clients/">bunch of clients</a> available (take a look at <a href="https://github.com/ncmpcpp/ncmpcpp">ncmpcpp</a> is you like terminal-based apps), but here our point of interest is its integration with EMMS.</p>
<p>While EMMS is capable of playing music without it, MPD has the advantage of being independent of Emacs. That means it won&rsquo;t close if Emacs crashes and it can be controlled more easily with other means.</p>
<p>MPD configuration is a pretty easy process. First, install MPD and <a href="https://www.musicpd.org/clients/mpc/">mpc</a> (a minimal MPD CLI client) from your distribution&rsquo;s package repository. After doing that, you&rsquo;d have to create a config file at the location <code>~/.config/mpd/mpd.conf</code>. Mine looks something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#a6e22e">music_directory</span> <span style="color:#e6db74">&#34;~/Music&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">playlist_directory</span> <span style="color:#e6db74">&#34;~/.mpd/playlists&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">db_file</span> <span style="color:#e6db74">&#34;~/.mpd/database&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">log_file</span> <span style="color:#e6db74">&#34;~/.mpd/log&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">pid_file</span> <span style="color:#e6db74">&#34;~/.mpd/pid&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">state_file</span> <span style="color:#e6db74">&#34;~/.mpd/state&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">sticker_file</span> <span style="color:#e6db74">&#34;~/.mpd/sticker.sql&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#a6e22e">audio_output</span> {<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#a6e22e">type</span> <span style="color:#e6db74">&#34;pulse&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#a6e22e">name</span> <span style="color:#e6db74">&#34;My Pulse Output&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>}<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>Here <code>music_directory</code> is, well, a directory in which MPD will look for music files. Take a look at <a href="https://linux.die.net/man/5/mpd.conf">man mpd.conf</a> and <a href="https://github.com/MusicPlayerDaemon/MPD/blob/master/doc/mpdconf.example">the default config example</a> for more information.</p>
<p>Because MPD is a daemon, it has to be started in order to work. The easiest way is to add <code>mpd</code> to your init system, e.g. with GNU Shepherd:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#66d9ef">define </span>mpd
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">make</span> &lt;service&gt;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">#</span>:provides <span style="color:#f92672">&#39;</span>(mpd)
</span></span><span style="display:flex;"><span> <span style="color:#f92672">#</span>:respawn? <span style="color:#66d9ef">#t</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">#</span>:start (<span style="color:#a6e22e">make-forkexec-constructor</span> <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;mpd&#34;</span> <span style="color:#e6db74">&#34;--no-daemon&#34;</span>))
</span></span><span style="display:flex;"><span> <span style="color:#f92672">#</span>:stop (<span style="color:#a6e22e">make-kill-destructor</span>)))
</span></span></code></pre></div><p>You can also launch <code>mpd</code> manually, as it will daemonize itself by default. To check if MPD is working, run <code>mpc status</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mpc status
</span></span></code></pre></div><p>Take a look at <a href="https://mpd.readthedocs.io/en/stable/user.html#configuration">the official documentation</a> for more information on this subject.</p>
<h3 id="music-directory">Music directory</h3>
<p>The next question after we&rsquo;ve set up MPD is how to organize the music directory.</p>
<p>MPD itself is not too concerned about the structure of your <code>music_directory</code>, because it uses audio tags to classify the files. However, if you want to have album covers in EMMS, you need to have one folder per one album. Otherwise and in other respects, the structure can be arbitrary.</p>
<p>So we need to tag the audio files. My favorite audio-tagging software is <a href="https://picard.musicbrainz.org/">MusicBrainz Picard</a>, which can set tags automatically even if the file has no metadata at all, and move the file automatically according to the configuration. The aforementioned ncmpcpp also has a decent tag editor; finally, there is a simple <a href="https://mutagen.readthedocs.io/en/latest/man/mid3v2.html">mutagen-based CLI tool</a>.</p>
<h2 id="emms">EMMS</h2>
<p><a href="https://www.gnu.org/software/emms/">EMMS</a> is the Emacs Multimedia System, a package that can get play stuff from various sources using various players. It is a part of Emacs, which means you can use the built-in version, but the git version has a few useful patches, so I advise using the latter.</p>
<p>Install it however you usually install packages in Emacs; I use use-package + straight:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package emms
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>)
</span></span></code></pre></div><h3 id="setup-and-mpd-integration">Setup &amp; MPD integration</h3>
<p>Now we have to configure EMMS. The following expressions have to be executed after EMMS is loaded, which means we can add them to the <code>:config</code> section of the <code>use-package</code> expression above.</p>
<p>First, EMMS exposes a handy function that loads all the stable EMMS features. You can take a look at its source and pick the features you need or load everything like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(require <span style="color:#e6db74">&#39;emms-setup</span>)
</span></span><span style="display:flex;"><span>(emms-all)
</span></span></code></pre></div><p>Then we need to set up a directory for EMMS files and the required parameters for <code>emms-player-mpd</code>. Note that <code>emms-player-mpd-music-directory</code> should be set to the same value as <code>music_directory</code> in <code>mpd.conf</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq emms-source-file-default-directory (<span style="color:#a6e22e">expand-file-name</span> <span style="color:#e6db74">&#34;~/Music/&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(setq emms-player-mpd-server-name <span style="color:#e6db74">&#34;localhost&#34;</span>)
</span></span><span style="display:flex;"><span>(setq emms-player-mpd-server-port <span style="color:#e6db74">&#34;6600&#34;</span>)
</span></span><span style="display:flex;"><span>(setq emms-player-mpd-music-directory <span style="color:#e6db74">&#34;~/Music&#34;</span>)
</span></span></code></pre></div><p>Add the required functions to EMMS lists:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(add-to-list <span style="color:#e6db74">&#39;emms-info-functions</span> <span style="color:#e6db74">&#39;emms-info-mpd</span>)
</span></span><span style="display:flex;"><span>(add-to-list <span style="color:#e6db74">&#39;emms-player-list</span> <span style="color:#e6db74">&#39;emms-player-mpd</span>)
</span></span></code></pre></div><p>Now we can connect EMMS to MPD. For some reason, executing this function stops the MPD playback, but it is not a big issue because it has to be executed only once.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(emms-player-mpd-connect)
</span></span></code></pre></div><p>The last thing we may want is to link EMMS playlist clearing to MPD playlist clearing. I&rsquo;m not sure how this interacts with MPD&rsquo;s own playlists because I don&rsquo;t use them, so you may need to watch out here if you do.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;emms-playlist-cleared-hook</span> <span style="color:#e6db74">&#39;emms-player-mpd-clear</span>)
</span></span></code></pre></div><h3 id="usage">Usage</h3>
<p>One rough edge of EMMS &amp; MPD integration is that EMMS and MPD have separate libraries and playlists.</p>
<p>So, first we have to populate the MPD library with <code>M-x emms-player-mpd-update-all</code>. This operation is executed asynchronously by MPD and may take a few minutes for the first run. The subsequent runs are much faster. You can do the same by invoking <code>mpc update</code> from the command line.</p>
<p>Second, we have to populate the EMMS library (cache) from the MPD library. To do that, run <code>M-x emms-cache-set-all-from-mpd</code>. If something went wrong with the EMMS cache, you always can clean it with <code>M-x emms-cache-reset</code>.</p>
<p>After this is done, we can finally play music! To do that, run <code>M-x emms-browser</code>. The left window should have the EMMS browser buffer with the loaded library, the right one should contain (as for now empty) playlist.</p>
<p>In the browser we can use the following commands to add elements to the playlist:</p>
<ul>
<li><code>M-x emms-browser-toggle-subitems</code> (<code>&lt;tab&gt;</code> in evil, <code>SPC</code> in vanilla) to open/close the element under cursor</li>
<li><code>M-x emms-browser-add-tracks</code> (<code>RET</code> in both styles) to add the element under the cursor to the playlist</li>
</ul>
<p>Now, we have a few tracks in the EMMS playlist, but they are not in the MPD playlist yet.</p>
<p>In the EMMS playlist buffer, <code>M-x emms-playlist-mode-play-smart</code> (<code>RET</code>) will sync the playlists and start playing the song under the cursor. Also, use</p>
<ul>
<li><code>M-x emms-playlist-mode-kill-track</code> (<code>D</code>) to remove the element under cursor</li>
<li><code>M-x emms-playlist-clear</code> (<code>C</code>) to clear the playlist. With the hook from the previous section this should also clear the MPD playlist.</li>
</ul>
<p>Take a look at the <a href="https://www.gnu.org/software/emms/manual/">EMMS manual</a> for more information, including sections about <a href="https://www.gnu.org/software/emms/manual/#Interactive-Playlists">playlist</a> and <a href="https://www.gnu.org/software/emms/manual/#The-Browser">browser.</a></p>
<h3 id="fetching-lyrics">Fetching lyrics</h3>
<p>One feature of ncmpcpp I was missing here is fetching lyrics, so I&rsquo;ve written a small package to do just that.</p>
<p>Debugging the package turned out to be quite funny because apparently, there is no way around parsing HTML with this task. So I&rsquo;ve chosen genius.com as the source, but the site turned out to provide different versions of itself (with different DOMs!) to different users.</p>
<p>At any rate, I&rsquo;ve processed the cases I found, and it seems to be working, at least for me. To use the package, <a href="https://genius.com/api-clients/new">get the API key</a> from Genius and install it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package lyrics-fetcher
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :after (emms)
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq lyrics-fetcher-genius-access-token
</span></span><span style="display:flex;"><span> (password-store-get <span style="color:#e6db74">&#34;My_Online/APIs/genius.com&#34;</span>)))
</span></span></code></pre></div><p>To fetch lyrics for the current playing EMMS song, run <code>M-x lyrics-fetcher-show-lyrics</code>. Or run <code>M-x lyrics-fetcher-emms-browser-show-at-point</code> to fetch data for the current point in the EMMS browser. See <a href="https://github.com/SqrtMinusOne/lyrics-fetcher.el">the package homepage</a> for more information.</p>
<h3 id="album-covers">Album covers</h3>
<p>I&rsquo;ve mentioned above that EMMS supports displaying album covers.</p>
<p>For this to work, it is necessary to have one album per one folder. By default the cover image should be saved to images named <code>cover_small</code> (100x100 recommended), <code>cover_medium</code> (200x200 recommended) and <code>cover_large</code>. The small version is to be displayed in the EMMS browser, the medium one in the playlist.</p>
<p>It&rsquo;s not required for images to be exactly of these sizes, but they definitely should be of one size across different albums to look nice in the interface.</p>
<p>You can resize images with ImageMagick with commands like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>convert cover.jpg -resize 100x100^ -gravity Center -extent 100x100 cover_small.jpg
</span></span><span style="display:flex;"><span>convert cover.jpg -resize 200x200^ -gravity Center -extent 200x200 cover_medium.jpg
</span></span></code></pre></div><p><code>lyrics-fetcher</code> can (try to) do this automatically by downloading the cover from genius.com with <code>M-x lyrics-fetcher-emms-browser-fetch-covers-at-point</code> in EMMS browser.</p>
<h2 id="mpv-and-youtube">MPV and YouTube</h2>
<p><a href="https://mpv.io/">MPV</a> is an extensible media player, which integrates with <a href="https://github.com/ytdl-org/youtube-dl">youtube-dl</a> and is controllable by EMMS, thus quite fitting for this setup.</p>
<h3 id="mpv-and-youtube-dl">MPV and youtube-dl</h3>
<p>First, install both <code>mpv</code> and <code>youtube-dl</code> from your distribution&rsquo;s package repository.</p>
<p>Then we can add another player to the list:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(add-to-list <span style="color:#e6db74">&#39;emms-player-list</span> <span style="color:#e6db74">&#39;emms-player-mpv</span> <span style="color:#66d9ef">t</span>)
</span></span></code></pre></div><p>EMMS determines which player to use by a regexp. <code>emms-player-mpd</code> sets the default regexp from MPD&rsquo;s diagnostic output so that regex opens basically everything, including videos, HTTPS links, etc. That is fine if MPD is the only player in EMMS, but as we want to use MPV as well, we need to override the regexes.</p>
<p>MPD regexp can look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(emms-player-set emms-player-mpd
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;regex</span>
</span></span><span style="display:flex;"><span> (emms-player-simple-regexp
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;m3u&#34;</span> <span style="color:#e6db74">&#34;ogg&#34;</span> <span style="color:#e6db74">&#34;flac&#34;</span> <span style="color:#e6db74">&#34;mp3&#34;</span> <span style="color:#e6db74">&#34;wav&#34;</span> <span style="color:#e6db74">&#34;mod&#34;</span> <span style="color:#e6db74">&#34;au&#34;</span> <span style="color:#e6db74">&#34;aiff&#34;</span>))
</span></span></code></pre></div><p>And a regexp for MPV to open videos and youtube URLs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(emms-player-set emms-player-mpv
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;regex</span>
</span></span><span style="display:flex;"><span> (rx (or (<span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;https://&#34;</span> (<span style="color:#a6e22e">*</span> nonl) <span style="color:#e6db74">&#34;youtube.com&#34;</span> (<span style="color:#a6e22e">*</span> nonl))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">+</span> (<span style="color:#e6db74">? </span>(or <span style="color:#e6db74">&#34;https://&#34;</span> <span style="color:#e6db74">&#34;http://&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">*</span> nonl)
</span></span><span style="display:flex;"><span> (regexp (<span style="color:#a6e22e">eval</span> (emms-player-simple-regexp
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;mp4&#34;</span> <span style="color:#e6db74">&#34;mov&#34;</span> <span style="color:#e6db74">&#34;wmv&#34;</span> <span style="color:#e6db74">&#34;webm&#34;</span> <span style="color:#e6db74">&#34;flv&#34;</span> <span style="color:#e6db74">&#34;avi&#34;</span> <span style="color:#e6db74">&#34;mkv&#34;</span>)))))))
</span></span></code></pre></div><p>Then, by default youtube-dl plays the video in the best possible quality, which may be pretty high. To have some control over it, we can modify the <code>--ytdl-format</code> key in the <code>emms-player-mpv-parameters</code> variable. I&rsquo;ve come up with the following solution:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/youtube-dl-quality-list
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;bestvideo[height&lt;=720]+bestaudio/best[height&lt;=720]&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;bestvideo[height&lt;=480]+bestaudio/best[height&lt;=480]&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;bestvideo[height&lt;=1080]+bestaudio/best[height&lt;=1080]&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(setq my/default-emms-player-mpv-parameters
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;--quiet&#34;</span> <span style="color:#e6db74">&#34;--really-quiet&#34;</span> <span style="color:#e6db74">&#34;--no-audio-display&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/set-emms-mpd-youtube-quality (quality)
</span></span><span style="display:flex;"><span> (interactive <span style="color:#e6db74">&#34;P&#34;</span>)
</span></span><span style="display:flex;"><span> (unless quality
</span></span><span style="display:flex;"><span> (setq quality (<span style="color:#a6e22e">completing-read</span> <span style="color:#e6db74">&#34;Quality: &#34;</span> my/youtube-dl-quality-list <span style="color:#66d9ef">nil</span> <span style="color:#66d9ef">t</span>)))
</span></span><span style="display:flex;"><span> (setq emms-player-mpv-parameters
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`</span>(<span style="color:#f92672">,@</span>my/default-emms-player-mpv-parameters <span style="color:#f92672">,</span>(<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;--ytdl-format=%s&#34;</span> quality))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(my/set-emms-mpd-youtube-quality (<span style="color:#a6e22e">car</span> my/youtube-dl-quality-list))
</span></span></code></pre></div><p>Run <code>M-x my/set-emms-mpd-youtube-quality</code> to pick the required quality. Take a look at <a href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#format-selection">youtube-dl docs</a> for more information about the format selection.</p>
<p>Now <code>M-x emms-add-url</code> should work on YouTube URLs just fine. Just keep in mind that it will only add the URL to the playlist, not play it right away.</p>
<h3 id="cleanup-emms-cache">Cleanup EMMS cache</h3>
<p>All the added URLs stay in the EMMS cache after being played. We probably don&rsquo;t want them to remain there, so here is a function to remove URLs from the EMMS cache.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/emms-cleanup-urls ()
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (let ((keys-to-delete <span style="color:#f92672">&#39;</span>()))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">maphash</span> (lambda (key value)
</span></span><span style="display:flex;"><span> (when (<span style="color:#a6e22e">eq</span> (<span style="color:#a6e22e">cdr</span> (<span style="color:#a6e22e">assoc</span> <span style="color:#e6db74">&#39;type</span> value)) <span style="color:#e6db74">&#39;url</span>)
</span></span><span style="display:flex;"><span> (add-to-list <span style="color:#e6db74">&#39;keys-to-delete</span> key)))
</span></span><span style="display:flex;"><span> emms-cache-db)
</span></span><span style="display:flex;"><span> (dolist (key keys-to-delete)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">remhash</span> key emms-cache-db)))
</span></span><span style="display:flex;"><span> (setq emms-cache-dirty <span style="color:#66d9ef">t</span>))
</span></span></code></pre></div><h2 id="youtube-rss">YouTube RSS</h2>
<h3 id="where-to-get-urls">Where to get URLs?</h3>
<p>So, we are able to watch YouTube videos by URLs, but where to get URLs from? A natural solution is to use <a href="https://github.com/skeeto/elfeed">elfeed</a> and RSS feeds.</p>
<p>I&rsquo;ve tried a bunch of options to get feeds for YouTube channels. The first one is <a href="https://api.invidious.io/">Invidious</a>, a FOSS YouTube frontend. The problem here is that various instances I tried weren&rsquo;t particularly stable (at least when I was using them) and hosting the thing by myself would be overkill. And switching instances is causing duplicate entries in the Elfeed DB.</p>
<p>The second option is to use YouTube&rsquo;s own RSS. The feed URL looks like <code>https://www.youtube.com/feeds/videos.xml?channel_id=&lt;CHANNEL_ID&gt;=</code>. <a href="https://stackoverflow.com/questions/14366648/how-can-i-get-a-channel-id-from-youtube">Here are</a> a couple of options of figuring out <code>CHANNEL_ID</code> in case it&rsquo;s not easily available. The problem with YouTube RSS is that it uses fields that are not supported by elfeed, so the feed entry lacks a preview and description.</p>
<p>As my workaround, I&rsquo;ve written a small <a href="https://github.com/SqrtMinusOne/yt-rss">web-server</a> which converts an RSS feed from YouTube to an elfeed-compatible Atom feed. It doesn&rsquo;t do much, so you can just download the thing and launch it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone https://github.com/SqrtMinusOne/yt-rss.git
</span></span><span style="display:flex;"><span>cd ./yt-rss
</span></span><span style="display:flex;"><span>pip install -r requirements.txt
</span></span><span style="display:flex;"><span>gunicorn main:app
</span></span></code></pre></div><p>A feed for a particular channel will be available at</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>http://localhost:8000/&lt;channel_id&gt;?token=&lt;token&gt;
</span></span></code></pre></div><p>where <code>&lt;token&gt;</code> is set in <code>.env</code> file to the default value of <code>12345</code>.</p>
<h3 id="elfeed">Elfeed</h3>
<p><a href="https://github.com/skeeto/elfeed">Elfeed</a> is an Emacs Atom &amp; RSS reader. It&rsquo;s a pretty popular package with lots of information written over the years, so I&rsquo;ll cover just my particular setup.</p>
<p>My elfeed config, sans keybindings, looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package elfeed
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :commands (elfeed)
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq elfeed-db-directory <span style="color:#e6db74">&#34;~/.elfeed&#34;</span>)
</span></span><span style="display:flex;"><span> (setq elfeed-enclosure-default-dir (<span style="color:#a6e22e">expand-file-name</span> <span style="color:#e6db74">&#34;~/Downloads&#34;</span>))
</span></span><span style="display:flex;"><span> (advice-add <span style="color:#a6e22e">#&#39;</span>elfeed-insert-html
</span></span><span style="display:flex;"><span> :around
</span></span><span style="display:flex;"><span> (lambda (fun <span style="color:#66d9ef">&amp;rest</span> r)
</span></span><span style="display:flex;"><span> (let ((shr-use-fonts <span style="color:#66d9ef">nil</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">apply</span> fun r)))))
</span></span></code></pre></div><p>The advice there forces elfeed to use monospace fonts in the show buffer.</p>
<p>I also use <a href="https://github.com/remyhonig/elfeed-org">elfeed-org</a>, which gives an option to store the feed config in an <code>.org</code> file instead of a variable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package elfeed-org
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :after (elfeed)
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> (setq rmh-elfeed-org-files <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;~/.emacs.d/elfeed.org&#34;</span>))
</span></span><span style="display:flex;"><span> (elfeed-org))
</span></span></code></pre></div><p>So, however you&rsquo;ve got URLs for YouTube channels, put them into elfeed.</p>
<p>To fetch the feeds, open elfeed with <code>M-x elfeed</code> and run <code>M-x elfeed-search-fetch</code> in the search buffer. And as usual, take a look at <a href="https://github.com/skeeto/elfeed">the package documentation</a> for more information.</p>
<p>To help with navigating through the long list of entries, I&rsquo;ve made the following function to narrow the search buffer to the feed of the entry under cursor:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/elfeed-search-filter-source (entry)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Filter elfeed search buffer by the feed under cursor.&#34;</span>
</span></span><span style="display:flex;"><span> (interactive (<span style="color:#a6e22e">list</span> (elfeed-search-selected :ignore-region)))
</span></span><span style="display:flex;"><span> (when (elfeed-entry-p entry)
</span></span><span style="display:flex;"><span> (elfeed-search-set-filter
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;@6-months-ago &#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;+unread &#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;=&#34;</span>
</span></span><span style="display:flex;"><span> (replace-regexp-in-string
</span></span><span style="display:flex;"><span> (rx <span style="color:#e6db74">&#34;?&#34;</span> (<span style="color:#a6e22e">*</span> not-newline) eos)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span> (elfeed-feed-url (elfeed-entry-feed entry)))))))
</span></span></code></pre></div><p>So I mostly alternate between <code>M-x my/elfeed-search-filter-source</code> and <code>M-x elfeed-search-clear-filter</code>. I tag the entries which I want to watch later with <code>+later</code>, and add the ones I want to watch right now to the playlist.</p>
<h3 id="integrating-with-emms">Integrating with EMMS</h3>
<p>Finally, here&rsquo;s the solution I came up with to add an entry from elfeed to the EMMS playlist. First, we&rsquo;ve got to get a URL:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/get-youtube-url (link)
</span></span><span style="display:flex;"><span> (let ((watch-id (cadr
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">assoc</span> <span style="color:#e6db74">&#34;watch?v&#34;</span>
</span></span><span style="display:flex;"><span> (url-parse-query-string
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">substring</span>
</span></span><span style="display:flex;"><span> (url-filename
</span></span><span style="display:flex;"><span> (url-generic-parse-url link))
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">1</span>))))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">&#34;https://www.youtube.com/watch?v=&#34;</span> watch-id)))
</span></span></code></pre></div><p>This function is intended to work with both Invidious and YouTube RSS feeds. Of course, it will require some adaptation if you want to watch channels from something like PeerTube or Odysee.</p>
<p>The easiest way to put the URL to the playlist is to define a new source for EMMS:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(define-emms-source elfeed (entry)
</span></span><span style="display:flex;"><span> (let ((track (emms-track
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;url</span> (my/get-youtube-url (elfeed-entry-link entry)))))
</span></span><span style="display:flex;"><span> (emms-track-set track <span style="color:#e6db74">&#39;info-title</span> (elfeed-entry-title entry))
</span></span><span style="display:flex;"><span> (emms-playlist-insert-track track)))
</span></span></code></pre></div><p>Because <code>define-emms-source</code> is an EMMS macro, the code block above has to be evaluated with EMMS loaded. E.g. you can wrap it into <code>(with-eval-after-load 'emms ...)</code> or put in the <code>:config</code> section.</p>
<p>The macro defines a bunch of functions to work with the source, which we can use in another function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/elfeed-add-emms-youtube ()
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (emms-add-elfeed elfeed-show-entry)
</span></span><span style="display:flex;"><span> (elfeed-tag elfeed-show-entry <span style="color:#e6db74">&#39;watched</span>)
</span></span><span style="display:flex;"><span> (elfeed-show-refresh))
</span></span></code></pre></div><p>Now, calling <code>M-x my/elfeed-add-emms-youtube</code> in the <code>*elfeed-show*</code> buffer will add the correct URL to the playlist and tag the entry with <code>+watched</code>. I&rsquo;ve bound the function to <code>gm</code>.</p>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,267 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Getting a consistent set of keybindings between i3 and Emacs</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h2 id="intro">Intro</h2>
<p>One advantage of EXWM for an Emacs user is that EXWM gives one set of keybindings to manage both Emacs windows and X windows. In every other WM, like my preferred <a href="https://i3wm.org">i3wm</a>, two orthogonal keymaps seem to be necessary. But, as both programs are quite customizable, I want to see whether I can replicate at least some part of the EXWM goodness in i3.</p>
<p>But why not just use EXWM? One key reason is that to my taste (and perhaps on my hardware) EXWM didn&rsquo;t feel snappy enough. Also, I really like i3&rsquo;s tree-based layout structure; I feel like it fits my workflow much better than anything else I tried, including the master/stack paradigm of <a href="https://xmonad.org/">XMonad</a>, for instance.</p>
<p>One common point of criticism of i3 is that it is not extensible enough, especially compared to WMs that are configured in an actual programing language, like the mentioned XMonad, <a href="http://www.qtile.org/">Qtile</a>, <a href="https://awesomewm.org/">Awesome</a>, etc. But I think i3&rsquo;s extensibility is underappreciated, although the contents of this article may lie closer to the limits of how far one can go there.</p>
<p>Here is a small demo of how it currently works:</p>
<video controls width="100%">
<source src="/ox-hugo/i3-emacs-demo.mp4" type="video/mp4">
</video>
<h2 id="emacs-integration">Emacs integration</h2>
<p>What I&rsquo;m trying to do is actually quite simple, so I&rsquo;m somewhat surprised I didn&rsquo;t find anything similar on the Internet. But I didn&rsquo;t look too hard.</p>
<p>The basic idea is to launch a normal i3 command with <code>i3-msg</code> in case the current window is not Emacs, otherwise pass that command to Emacs with <code>emacsclient</code>. In Emacs, execute the command if possible, otherwise pass the command back to i3.</p>
<p>This may seem like a lot of overhead, but I didn&rsquo;t feel it even in the worst case (i3 -&gt; Emacs -&gt; i3), so at least in that regard, the interaction feels seamless. The only concern is that this command flow is vulnerable to Emacs getting stuck, but it is still much less of a problem than with EXWM.</p>
<p>One interesting observation here is that Emacs windows and X windows are sort of one-level entities, so I can talk just about &ldquo;windows&rdquo;.</p>
<p>At any rate, we need a script to do the i3 -&gt; Emacs part:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> <span style="color:#66d9ef">$(</span>xdotool getactivewindow getwindowname<span style="color:#66d9ef">)</span> <span style="color:#f92672">=</span>~ ^emacs<span style="color:#f92672">(</span>:.*<span style="color:#f92672">)</span>?@.* <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> command<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;(my/emacs-i3-integration \&#34;</span>$@<span style="color:#e6db74">\&#34;)&#34;</span>
</span></span><span style="display:flex;"><span> emacsclient -e <span style="color:#e6db74">&#34;</span>$command<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> i3-msg $@
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span></code></pre></div><p>My <a href="https://sqrtminusone.xyz/configs/emacs/#custom-frame-title">Emacs frame title is set</a> to <code>emacs[:&lt;projectile-project-name&gt;]@&lt;hostname&gt;</code>, hence the regex. The script is saved to an executable called <code>emacs-i3-integration</code>.</p>
<p>For this to work, we need to make sure that Emacs starts a server, so here is an expression to do just that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;after-init-hook</span> <span style="color:#a6e22e">#&#39;</span>server-start)
</span></span></code></pre></div><p>The function <code>my/emacs-i3-integration</code>, which is an entrypoint for the i3 integration, will be defined a bit later.</p>
<p>And here is a simple macro to do the Emacs -&gt; i3 part:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defmacro i3-msg (<span style="color:#66d9ef">&amp;rest</span> args)
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`</span>(<span style="color:#a6e22e">start-process</span> <span style="color:#e6db74">&#34;emacs-i3-windmove&#34;</span> <span style="color:#66d9ef">nil</span> <span style="color:#e6db74">&#34;i3-msg&#34;</span> <span style="color:#f92672">,@</span>args))
</span></span></code></pre></div><h2 id="handling-i3-commands">Handling i3 commands</h2>
<p>Now we have to handle the required set of i3 commands. It is worth noting here that I&rsquo;m not trying to implement a general mechanism to apply i3 commands to Emacs, rather I&rsquo;m implementing a small subset that I use in my i3 configuration and that maps reasonably to the Emacs concepts.</p>
<p>Also, I use <a href="https://github.com/emacs-evil/evil">evil-mode</a> and generally configure the software to have vim-style bindings where possible. So if you don&rsquo;t use evil-mode you&rsquo;d have to detangle the given functions from evil, but then, I guess, you do not use super+hjkl to manage windows either.</p>
<h3 id="focus"><code>focus</code></h3>
<p>First, for the <code>focus</code> command I want to move to an Emacs window in the given direction if there is one, otherwise move to an X window in the same direction. Fortunately, i3 and <code>windmove</code> have the same names for directions, so the function is rather straightforward.</p>
<p>One caveat here is that the minibuffer is always the bottom-most Emacs window, so it is necessary to check for that as well.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/emacs-i3-windmove (dir)
</span></span><span style="display:flex;"><span> (let ((other-window (windmove-find-other-window dir)))
</span></span><span style="display:flex;"><span> (if (or (<span style="color:#a6e22e">null</span> other-window) (<span style="color:#a6e22e">window-minibuffer-p</span> other-window))
</span></span><span style="display:flex;"><span> (i3-msg <span style="color:#e6db74">&#34;focus&#34;</span> (<span style="color:#a6e22e">symbol-name</span> dir))
</span></span><span style="display:flex;"><span> (windmove-do-window-select dir))))
</span></span></code></pre></div><p>The relevant section of the i3 config looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindsym $mod+h exec emacs-i3-integration focus left
</span></span><span style="display:flex;"><span>bindsym $mod+j exec emacs-i3-integration focus down
</span></span><span style="display:flex;"><span>bindsym $mod+k exec emacs-i3-integration focus up
</span></span><span style="display:flex;"><span>bindsym $mod+l exec emacs-i3-integration focus right
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>bindsym $mod+Left exec emacs-i3-integration focus left
</span></span><span style="display:flex;"><span>bindsym $mod+Down exec emacs-i3-integration focus down
</span></span><span style="display:flex;"><span>bindsym $mod+Up exec emacs-i3-integration focus up
</span></span><span style="display:flex;"><span>bindsym $mod+Right exec emacs-i3-integration focus right
</span></span></code></pre></div><p>The Emacs function has to be called like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/emacs-i3-windmove <span style="color:#e6db74">&#39;right</span>)
</span></span></code></pre></div><h3 id="move"><code>move</code></h3>
<p>For the <code>move</code> command I want the following behavior:</p>
<ul>
<li>if there is space in the required direction, move the Emacs window there;</li>
<li>if there is no space in the required direction, but space in the orthogonal directions, move the Emacs window so that there is no more space in the orthogonal directions;</li>
<li>otherwise, move an X window (which has to be an Emacs frame).</li>
</ul>
<p>For the first part, <code>window-swap-states</code> with <code>windmove-find-other-window</code> do well enough.</p>
<p><code>evil-move-window</code> works well for the second part. By itself it doesn&rsquo;t behave quite like i3, for instance, <code>(evil-move-window 'right)</code> in a three-column split would move the window from the far left side to the far right side (bypassing center). Hence the combination as described here.</p>
<p>So here is a simple predicate which checks whether there is space in the given direction.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/emacs-i3-direction-exists-p (dir)
</span></span><span style="display:flex;"><span> (some (lambda (dir)
</span></span><span style="display:flex;"><span> (let ((win (windmove-find-other-window dir)))
</span></span><span style="display:flex;"><span> (and win (not (<span style="color:#a6e22e">window-minibuffer-p</span> win)))))
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;width</span> <span style="color:#f92672">&#39;</span>(left right))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;height</span> <span style="color:#f92672">&#39;</span>(up down)))))
</span></span></code></pre></div><p>And the implementation of the move command.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/emacs-i3-move-window (dir)
</span></span><span style="display:flex;"><span> (let ((other-window (windmove-find-other-window dir))
</span></span><span style="display:flex;"><span> (other-direction (my/emacs-i3-direction-exists-p
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;up</span> <span style="color:#e6db74">&#39;width</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;down</span> <span style="color:#e6db74">&#39;width</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;left</span> <span style="color:#e6db74">&#39;height</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;right</span> <span style="color:#e6db74">&#39;height</span>)))))
</span></span><span style="display:flex;"><span> (cond
</span></span><span style="display:flex;"><span> ((and other-window (not (<span style="color:#a6e22e">window-minibuffer-p</span> other-window)))
</span></span><span style="display:flex;"><span> (window-swap-states (<span style="color:#a6e22e">selected-window</span>) other-window))
</span></span><span style="display:flex;"><span> (other-direction
</span></span><span style="display:flex;"><span> (evil-move-window dir))
</span></span><span style="display:flex;"><span> (<span style="color:#66d9ef">t</span> (i3-msg <span style="color:#e6db74">&#34;move&#34;</span> (<span style="color:#a6e22e">symbol-name</span> dir))))))
</span></span></code></pre></div><p>The relevant section of the i3 config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindsym $mod+Shift+h exec emacs-i3-integration move left
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+j exec emacs-i3-integration move down
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+k exec emacs-i3-integration move up
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+l exec emacs-i3-integration move right
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+Left exec emacs-i3-integration move left
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+Down exec emacs-i3-integration move down
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+Up exec emacs-i3-integration move up
</span></span><span style="display:flex;"><span>bindsym $mod+Shift+Right exec emacs-i3-integration move right
</span></span></code></pre></div><h3 id="resize-and-balance-windows"><code>resize</code> and balance windows</h3>
<p>Next on the line are <code>resize grow</code> and <code>resize shrink</code>. <code>evil-window-</code> functions do nicely for this task.</p>
<p>This function also checks whether there is space to resize in the given direction with the help of the predicate defined above. The command is forwarded back to i3 if there is not.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/emacs-i3-resize-window (dir kind value)
</span></span><span style="display:flex;"><span> (if (or (one-window-p)
</span></span><span style="display:flex;"><span> (not (my/emacs-i3-direction-exists-p dir)))
</span></span><span style="display:flex;"><span> (i3-msg <span style="color:#e6db74">&#34;resize&#34;</span> (<span style="color:#a6e22e">symbol-name</span> kind) (<span style="color:#a6e22e">symbol-name</span> dir)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;%s px or %s ppt&#34;</span> value value))
</span></span><span style="display:flex;"><span> (setq value (<span style="color:#a6e22e">/</span> value <span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span> (pcase kind
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;shrink</span>
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;width</span>
</span></span><span style="display:flex;"><span> (evil-window-decrease-width value))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;height</span>
</span></span><span style="display:flex;"><span> (evil-window-decrease-height value))))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;grow</span>
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;width</span>
</span></span><span style="display:flex;"><span> (evil-window-increase-width value))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;height</span>
</span></span><span style="display:flex;"><span> (evil-window-increase-height value)))))))
</span></span></code></pre></div><p>Here I&rsquo;m following the default configuration of i3, which creates a &ldquo;submode&rdquo; to resize windows.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mode <span style="color:#e6db74">&#34;resize&#34;</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> bindsym h exec emacs-i3-integration resize shrink width <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span> bindsym j exec emacs-i3-integration resize grow height <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span> bindsym k exec emacs-i3-integration resize shrink height <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span> bindsym l exec emacs-i3-integration resize grow width <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> bindsym Shift+h exec emacs-i3-integration resize shrink width <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span> bindsym Shift+j exec emacs-i3-integration resize grow height <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span> bindsym Shift+k exec emacs-i3-integration resize shrink height <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span> bindsym Shift+l exec emacs-i3-integration resize grow width <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># same bindings, but for the arrow keys</span>
</span></span><span style="display:flex;"><span> bindsym Left exec emacs-i3-integration resize shrink width <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span> bindsym Down exec emacs-i3-integration resize grow height <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span> bindsym Up exec emacs-i3-integration resize shrink height <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span> bindsym Right exec emacs-i3-integration resize grow width <span style="color:#ae81ff">10</span> px or <span style="color:#ae81ff">10</span> ppt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> bindsym Shift+Left exec emacs-i3-integration resize shrink width <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span> bindsym Shift+Down exec emacs-i3-integration resize grow height <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span> bindsym Shift+Up exec emacs-i3-integration resize shrink height <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span> bindsym Shift+Right exec emacs-i3-integration resize grow width <span style="color:#ae81ff">100</span> px or <span style="color:#ae81ff">100</span> ppt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> bindsym equal exec i3-emacs-balance-windows
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># back to normal: Enter or Escape</span>
</span></span><span style="display:flex;"><span> bindsym Return mode <span style="color:#e6db74">&#34;default&#34;</span>
</span></span><span style="display:flex;"><span> bindsym Escape mode <span style="color:#e6db74">&#34;default&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>Next, Emacs has a built-in function called <code>balance-windows</code>, but i3 doesn&rsquo;t. Fortunately, there is a Python package called <a href="https://github.com/atreyasha/i3-balance-workspace">i3-balance-workspace</a>, which performs a similar operation with i3&rsquo;s IPC. If you use Guix as I do, I&rsquo;ve written a <a href="https://github.com/SqrtMinusOne/channel-q/blob/master/i3-balance-workspace.scm">package definition</a>.</p>
<p>So here is a small wrapper which calls <code>i3_balance_workspace</code> and <code>M-x balance-windows</code> if the current window is Emacs.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> <span style="color:#66d9ef">$(</span>xdotool getactivewindow getwindowname<span style="color:#66d9ef">)</span> <span style="color:#f92672">=</span>~ ^emacs<span style="color:#f92672">(</span>:.*<span style="color:#f92672">)</span>?@.* <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> emacsclient -e <span style="color:#e6db74">&#34;(balance-windows)&#34;</span> &amp;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>i3_balance_workspace
</span></span></code></pre></div><h3 id="layout-toggle-split"><code>layout toggle split</code></h3>
<p><a href="https://github.com/emacsorphanage/transpose-frame">transpose-frame</a> is a package to &ldquo;transpose&rdquo; the current Emacs windows layout, which behaves somewhat similar to the <code>layout toggle split</code> command in i3, so I&rsquo;ll use it as well.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package transpose-frame
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :commands (transpose-frame))
</span></span></code></pre></div><p>The i3 config for this command:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindsym $mod+e exec emacs-i3-integration layout toggle split
</span></span></code></pre></div><h3 id="the-entrypoint">The entrypoint</h3>
<p>Finally, the entrypoint for the Emacs integration. In addition to the commands defined above, it processes <code>split</code> and <code>kill</code> commands and passes every other command back to i3.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/emacs-i3-integration (command)
</span></span><span style="display:flex;"><span> (pcase command
</span></span><span style="display:flex;"><span> ((rx bos <span style="color:#e6db74">&#34;focus&#34;</span>)
</span></span><span style="display:flex;"><span> (my/emacs-i3-windmove
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">intern</span> (<span style="color:#a6e22e">elt</span> (split-string command) <span style="color:#ae81ff">1</span>))))
</span></span><span style="display:flex;"><span> ((rx bos <span style="color:#e6db74">&#34;move&#34;</span>)
</span></span><span style="display:flex;"><span> (my/emacs-i3-move-window
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">intern</span> (<span style="color:#a6e22e">elt</span> (split-string command) <span style="color:#ae81ff">1</span>))))
</span></span><span style="display:flex;"><span> ((rx bos <span style="color:#e6db74">&#34;resize&#34;</span>)
</span></span><span style="display:flex;"><span> (my/emacs-i3-resize-window
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">intern</span> (<span style="color:#a6e22e">elt</span> (split-string command) <span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">intern</span> (<span style="color:#a6e22e">elt</span> (split-string command) <span style="color:#ae81ff">1</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">string-to-number</span> (<span style="color:#a6e22e">elt</span> (split-string command) <span style="color:#ae81ff">3</span>))))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;layout toggle split&#34;</span> (transpose-frame))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;split h&#34;</span> (evil-window-split))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;split v&#34;</span> (evil-window-vsplit))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;kill&#34;</span> (evil-quit))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">-</span> (i3-msg command))))
</span></span></code></pre></div><p>The rest of the relevant i3 config to do the splits:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindsym $mod+s exec emacs-i3-integration split h
</span></span><span style="display:flex;"><span>bindsym $mod+v exec emacs-i3-integration split v
</span></span></code></pre></div><p>And to kill the window:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindsym $mod+Shift+q exec emacs-i3-integration kill
</span></span></code></pre></div><h3 id="switching-i3-tabs">Switching i3 tabs</h3>
<p>As I use i3&rsquo;s tabbed layout quite extensively, occasionally I want to switch out of the Emacs tab with one button, and that&rsquo;s where my integration may interfere.</p>
<p>As a workaround, I found a small Rust program called <a href="https://github.com/nikola-kocic/i3-switch-tabs">i3-switch-tabs</a>, which also communicates with i3 via its IPC to switch the top-level tab. I&rsquo;ve written a <a href="https://github.com/SqrtMinusOne/channel-q/blob/master/i3-switch-tabs.scm">Guix package definition</a> for that as well.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindsym $mod+period exec i3-switch-tabs right
</span></span><span style="display:flex;"><span>bindsym $mod+comma exec i3-switch-tabs left
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>So, how does all of that feel? Actually, I got used to that setup pretty quickly. Using <code>&lt;s-Q&gt;</code> to quit windows and the <code>&lt;s-r&gt;</code> submode to resize them is particularly nice. I&rsquo;ve seen people making hydras in Emacs to do the latter.</p>
<p>All of that would probably be easier to do in a WM which is configured in a programming language rather than in a self-cooked DSL, so I may try to replicate that somewhere else in an unknown time in the future. Meanwhile, it&rsquo;s pretty good.</p>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,453 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Using EXWM and perspective.el on multi-monitor setup</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<p>I wrote about <a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/">Emacs and i3</a> integration around two months ago. Shortly after however, I decided to give EXWM another try, mainly because my largest reservation - lack of performance - seems to have been resolved by updates to the native compilation since my first attempt. Or I may have lost some sensitivity to that issue. Regardless, the second dive into EXWM thus far feels successful, and I think it&rsquo;s the right time to share some of my thoughts on the subject.</p>
<p>Before we start though, I&rsquo;ll point out that I won&rsquo;t go into detail about the initial setup. I think David Wilson&rsquo;s &ldquo;<a href="https://systemcrafters.net/emacs-desktop-environment/">Emacs Desktop Environment</a>&rdquo; series describes this part pretty well, so I don&rsquo;t feel the need to repeat much of that.</p>
<p>This post is a sort of a snapshot of the path from the baseline of <a href="https://github.com/daviwil/emacs-from-scratch/blob/master/Desktop.org">Emacs From Scratch</a> to my image of a perfect window manager, and it may or may not be coincidental that the latter resembles i3 in many aspects.</p>
<p>After all, I was using i3 for more than two years, so it&rsquo;s not something I can easily let go of. But I think (or would like to think) that&rsquo;s because the ideas are good, not because I&rsquo;m overly conservative in my workflow choices.</p>
<h2 id="perspective-dot-el">perspective.el</h2>
<p><a href="https://github.com/nex3/perspective-el">perspective.el</a> is one package I like that provides workspaces for Emacs, called &ldquo;perspectives&rdquo;. Each perspective has a separate buffer list, window layout, and a few other things that make it easier to separate things within Emacs.</p>
<p>One feature I&rsquo;d like to highlight is integration between perspective.el and <a href="https://github.com/Alexander-Miller/treemacs">treemacs</a>, where one perspective can have a separate treemacs tree. Although now tab-bar.el seems to be getting into shape to compete with perspective.el, as of the time of this writing, there&rsquo;s no such integration, at least not out of the box.</p>
<p>perspective.el works with EXWM more or less as one would expect - each EXWM workspace has its own set of perspectives. That way it feels somewhat like having multiple Emacs frames in a tiling window manager, although, of course, much more integrated with Emacs.</p>
<p>However, there are still some issues. For instance, I was having strange behaviors with floating windows, EXWM buffers in perspectives, etc. So I&rsquo;ve made a package called <a href="https://github.com/SqrtMinusOne/perspective-exwm.el">perspective-exwm.el</a> that does two things:</p>
<ul>
<li>Fixes issues I found with some advises and hooks. Take a look at the package homepage for more detail on that.</li>
<li>Provides some additional functionality that makes use of both perspective.el and EXWM.</li>
</ul>
<p>So, you can install the package however you normally do so. E.g. I do that with straight.el &amp; use-package:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package perspective-exwm
</span></span><span style="display:flex;"><span> :straight <span style="color:#66d9ef">t</span>
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>)
</span></span></code></pre></div><p>Then load the provided minor mode before <code>exwm-init</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(use-package exwm
</span></span><span style="display:flex;"><span> :config
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span> (perspective-exwm-mode)
</span></span><span style="display:flex;"><span> (exwm-init))
</span></span></code></pre></div><h3 id="initial-perspective-names">Initial perspective names</h3>
<p>One nice thing this package can do is set up the initial perspective names for different workspaces. By default, enabling <code>perspective-exwm-mode</code> sets names like <code>main-1</code> for workspace with index 1 and so on, because otherwise different perspectives will share the same <code>*scratch*</code> buffer.</p>
<p>But names can be overridden like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq perspective-exwm-override-initial-name
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>((<span style="color:#ae81ff">0</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;misc&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;core&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;browser&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#ae81ff">3</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;comms&#34;</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#ae81ff">4</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34;dev&#34;</span>)))
</span></span></code></pre></div><h3 id="assigning-apps-to-workspaces-and-perspectives">Assigning apps to workspaces and perspectives</h3>
<p>By default, a new Emacs buffer opens in the current perspective in the current workspace, but sure enough, it&rsquo;s possible to change that.</p>
<p>For EXWM windows, the <code>perspective-exwm</code> package provides a function called <code>perspective-exwm-assign-window</code>, which is intended to be used in <code>exwm-manage-finish-hook</code>, for instance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-configure-window ()
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (pcase exwm-class-name
</span></span><span style="display:flex;"><span> ((or <span style="color:#e6db74">&#34;Firefox&#34;</span> <span style="color:#e6db74">&#34;Nightly&#34;</span>)
</span></span><span style="display:flex;"><span> (perspective-exwm-assign-window
</span></span><span style="display:flex;"><span> :workspace-index <span style="color:#ae81ff">2</span>
</span></span><span style="display:flex;"><span> :persp-name <span style="color:#e6db74">&#34;browser&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;Alacritty&#34;</span>
</span></span><span style="display:flex;"><span> (perspective-exwm-assign-window
</span></span><span style="display:flex;"><span> :persp-name <span style="color:#e6db74">&#34;term&#34;</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;exwm-manage-finish-hook</span> <span style="color:#a6e22e">#&#39;</span>my/exwm-configure-window)
</span></span></code></pre></div><p>This hook is run after a new EXWM buffer is created and configured in the context of this buffer, so it seems customary to do such settings there. With this snippet, Firefox will always open in workspace 2 in the perspective named &ldquo;browser&rdquo;, and Alacritty will always open in the current workspace in the perspective named &ldquo;term&rdquo;.</p>
<p>To pull this off for various Emacs apps, it is necessary to open the right EXWM workspace and perspective before opening the app. As I use <a href="https://github.com/noctuid/general.el">general.el</a>, I made a macro to automate that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defmacro my/command-in-persp (command-name persp-name workspace-index <span style="color:#66d9ef">&amp;rest</span> args)
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`&#39;</span>((lambda ()
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (when (and <span style="color:#f92672">,</span>workspace-index (<span style="color:#a6e22e">fboundp</span> <span style="color:#a6e22e">#&#39;</span>exwm-workspace-switch-create))
</span></span><span style="display:flex;"><span> (exwm-workspace-switch-create <span style="color:#f92672">,</span>workspace-index))
</span></span><span style="display:flex;"><span> (persp-switch <span style="color:#f92672">,</span>persp-name)
</span></span><span style="display:flex;"><span> (delete-other-windows)
</span></span><span style="display:flex;"><span> <span style="color:#f92672">,@</span>args)
</span></span><span style="display:flex;"><span> :wk <span style="color:#f92672">,</span>command-name))
</span></span></code></pre></div><p><code>fboundp</code> is meant to provide compatibility with running Emacs without EXWM. Usage of the macro is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my-leader-def
</span></span><span style="display:flex;"><span> :infix <span style="color:#e6db74">&#34;as&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&#34;</span> <span style="color:#f92672">&#39;</span>(:which-key <span style="color:#e6db74">&#34;emms&#34;</span>)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;s&#34;</span> (my/command-in-persp <span style="color:#e6db74">&#34;emms&#34;</span> <span style="color:#e6db74">&#34;EMMS&#34;</span> <span style="color:#ae81ff">0</span> (emms-smart-browse))
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>)
</span></span></code></pre></div><p><code>my-leader-def</code> is a <a href="https://github.com/noctuid/general.el#creating-new-key-definers">custom definer</a>. That way the defined keybinding opens <a href="https://www.gnu.org/software/emms/">EMMS</a> in the workspace 0 in the perspective &ldquo;EMMS&rdquo;. I have this for several other apps, like elfeed, notmuch, dired <code>$HOME</code> and so on.</p>
<h3 id="some-workflow-notes">Some workflow notes</h3>
<p>As I said above, using perspectives in EXWM makes a lot of sense. Because all the EXWM workspace share the same buffer list (sans X windows), and because Emacs becomes the central program (for instance, it can&rsquo;t be easily closed), it is only natural to split the buffer list.</p>
<p>Another aspect of using EXWM is that it becomes very easy to work with code on multiple monitors. While it may signify issues with the code in question if such need arises, having that possibility is still handy and it&rsquo;s not something easily replicable on other tiling WMs. <code>perspective-exwm</code> also presents some features here, for instance, <code>M-x perspective-exwm-copy-to-workspace</code> can be used to copy the current perspective to the adjacent monitor.</p>
<p>Also, in my opinion, Emacs apps like <a href="https://www.gnu.org/software/emms/">EMMS</a> and <a href="https://github.com/skeeto/elfeed">elfeed</a> deserve to be on the same &ldquo;level&rdquo; as &ldquo;proper&rdquo; apps like a browser. On other tiling WMs, something like that can be done with Emacs daemon and multiple Emacs frames, but with EXWM and perspectives this seems natural without much extra work.</p>
<p>As for switching between X windows and perspectives, I ended up preferring to have one perspective for all X windows in the workspace, at least if these windows are full-fledged apps. For instance, all my messengers go to the workspace 3 to the perspective &ldquo;comms&rdquo;, and I switch between them with <code>M-x perspective-exwm-cycle-exwm-buffers-&lt;forward|backward&gt;</code>, bound to <code>s-[</code> and <code>s-]</code>. For switching perspectives, I&rsquo;ve bound <code>s-,</code> and <code>s-.</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq exwm-input-global-keys
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`</span>(
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Switch perspectives</span>
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-,&#34;</span>) <span style="color:#f92672">.</span> persp-prev)
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-.&#34;</span>) <span style="color:#f92672">.</span> persp-next)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; EXWM buffers</span>
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-[&#34;</span>) <span style="color:#f92672">.</span> perspective-exwm-cycle-exwm-buffers-backward)
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-]&#34;</span>) <span style="color:#f92672">.</span> perspective-exwm-cycle-exwm-buffers-forward)
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>)
</span></span></code></pre></div><h2 id="workspaces-on-multiple-monitors">Workspaces on multiple monitors</h2>
<p>Here, <code>exwm-randr</code> provides basic functionality for running EXWM on multiple monitors. For instance, with configuration like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(require <span style="color:#e6db74">&#39;exwm-randr</span>)
</span></span><span style="display:flex;"><span>(exwm-randr-enable)
</span></span><span style="display:flex;"><span><span style="color:#75715e">;; The script is generated by ARandR</span>
</span></span><span style="display:flex;"><span>(start-process-shell-command <span style="color:#e6db74">&#34;xrandr&#34;</span> <span style="color:#66d9ef">nil</span> <span style="color:#e6db74">&#34;~/bin/scripts/screen-layout&#34;</span>)
</span></span><span style="display:flex;"><span>(when (string= (<span style="color:#a6e22e">system-name</span>) <span style="color:#e6db74">&#34;indigo&#34;</span>)
</span></span><span style="display:flex;"><span> (setq exwm-randr-workspace-monitor-plist <span style="color:#f92672">&#39;</span>(<span style="color:#ae81ff">2</span> <span style="color:#e6db74">&#34;DVI-D-0&#34;</span> <span style="color:#ae81ff">3</span> <span style="color:#e6db74">&#34;DVI-D-0&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span>(exwm-init)
</span></span></code></pre></div><p>workspaces 2 and 3 on the machine with hostname &ldquo;indigo&rdquo; will be displayed on the monitor <code>DVI-D-0</code>.</p>
<p>However, some features, common in other tiling WMs, are missing in EXWM out of the box, namely:</p>
<ul>
<li>a command to <a href="https://i3wm.org/docs/userguide.html#%5Ffocusing%5Fmoving%5Fcontainers">switch to another monitor</a>;</li>
<li>a command to <a href="https://i3wm.org/docs/userguide.html#move%5Fto%5Foutputs">move the current workspace to another monitor</a>;</li>
<li>using the same commands to switch between windows and monitors.</li>
</ul>
<p>Here&rsquo;s my take on implementing them.</p>
<h3 id="tracking-recently-used-workspaces">Tracking recently used workspaces</h3>
<p>First up though, we need to track the workspaces in the usage order. I&rsquo;m not sure if there&rsquo;s some built-in functionality in EXWM for that, but it seems simple enough to implement.</p>
<p>Here is a snippet of code that does it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/exwm-last-workspaces <span style="color:#f92672">&#39;</span>(<span style="color:#ae81ff">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/exwm-store-last-workspace ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Save the last workspace to </span><span style="color:#e6db74">`my/exwm-last-workspaces&#39;</span><span style="color:#e6db74">.&#34;</span>
</span></span><span style="display:flex;"><span> (setq my/exwm-last-workspaces
</span></span><span style="display:flex;"><span> (seq-uniq (<span style="color:#a6e22e">cons</span> exwm-workspace-current-index
</span></span><span style="display:flex;"><span> my/exwm-last-workspaces))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;exwm-workspace-switch-hook</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">#&#39;</span>my/exwm-store-last-workspace)
</span></span></code></pre></div><p>The variable <code>my/exwm-last-workspaces</code> stores the workspace indices; the first item is the index of the current workspace, the second item is the index of the previous workspace, and so on.</p>
<p>One note here is that workspaces may also disappear (e.g. after <code>M-x exwm-workspace-delete</code>), so we also need a function to clean the list:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-last-workspaces-clear ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Clean </span><span style="color:#e6db74">`my/exwm-last-workspaces&#39;</span><span style="color:#e6db74"> from deleted workspaces.&#34;</span>
</span></span><span style="display:flex;"><span> (setq my/exwm-last-workspaces
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (i) (<span style="color:#a6e22e">nth</span> i exwm-workspace--list))
</span></span><span style="display:flex;"><span> my/exwm-last-workspaces)))
</span></span></code></pre></div><h3 id="the-monitor-list">The monitor list</h3>
<p>The second piece of the puzzle is getting the monitor list in the right order.</p>
<p>While it is possible to retrieve the monitor list from <code>exwm-randr-workspace-output-plist</code>, this won&rsquo;t scale well beyond two monitors, mainly because changing this variable may screw up the order.</p>
<p>So the easiest way is to just define the variable like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/exwm-monitor-list
</span></span><span style="display:flex;"><span> (pcase (<span style="color:#a6e22e">system-name</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;indigo&#34;</span> <span style="color:#f92672">&#39;</span>(<span style="color:#66d9ef">nil</span> <span style="color:#e6db74">&#34;DVI-D-0&#34;</span>))
</span></span><span style="display:flex;"><span> (_ <span style="color:#f92672">&#39;</span>(<span style="color:#66d9ef">nil</span>))))
</span></span></code></pre></div><p>If you are changing the RandR configuration on the fly, this variable will also need to be changed, but for now, I don&rsquo;t have such a necessity.</p>
<p>A function to get the current monitor:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-get-current-monitor ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Return the current monitor name or nil.&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">plist-get</span> exwm-randr-workspace-output-plist
</span></span><span style="display:flex;"><span> (cl-position (<span style="color:#a6e22e">selected-frame</span>)
</span></span><span style="display:flex;"><span> exwm-workspace--list)))
</span></span></code></pre></div><p>And a function to cycle the monitor list in either direction:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-get-other-monitor (dir)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Cycle the monitor list in the direction DIR.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">DIR is either &#39;left or &#39;right.&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">%</span> (<span style="color:#a6e22e">+</span> (cl-position
</span></span><span style="display:flex;"><span> (my/exwm-get-current-monitor)
</span></span><span style="display:flex;"><span> my/exwm-monitor-list
</span></span><span style="display:flex;"><span> :test <span style="color:#a6e22e">#&#39;string-equal</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">length</span> my/exwm-monitor-list)
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;right</span> <span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;left</span> <span style="color:#ae81ff">-1</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">length</span> my/exwm-monitor-list))
</span></span><span style="display:flex;"><span> my/exwm-monitor-list))
</span></span></code></pre></div><h3 id="switch-to-another-monitor">Switch to another monitor</h3>
<p>With the functions from the previous two sections, we can implement switching to another monitor by switching to the most recently used workspace on that monitor.</p>
<video controls width="100%">
<source src="/ox-hugo/exwm-workspace-switch.mp4" type="video/mp4">
</video>
<p>One caveat here is that on the startup the <code>my/exwm-last-workspaces</code> variable won&rsquo;t have any values from other monitor(s), so this list is concatenated with the list of available workspace indices.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-switch-to-other-monitor (<span style="color:#66d9ef">&amp;optional</span> dir)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Switch to another monitor.&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (my/exwm-last-workspaces-clear)
</span></span><span style="display:flex;"><span> (exwm-workspace-switch
</span></span><span style="display:flex;"><span> (cl-loop with other-monitor <span style="color:#a6e22e">=</span> (my/exwm-get-other-monitor (or dir <span style="color:#e6db74">&#39;right</span>))
</span></span><span style="display:flex;"><span> for i in (<span style="color:#a6e22e">append</span> my/exwm-last-workspaces
</span></span><span style="display:flex;"><span> (cl-loop for i from <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span> for _ in exwm-workspace--list
</span></span><span style="display:flex;"><span> collect i))
</span></span><span style="display:flex;"><span> if (if other-monitor
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">string-equal</span> (<span style="color:#a6e22e">plist-get</span> exwm-randr-workspace-output-plist i)
</span></span><span style="display:flex;"><span> other-monitor)
</span></span><span style="display:flex;"><span> (not (<span style="color:#a6e22e">plist-get</span> exwm-randr-workspace-output-plist i)))
</span></span><span style="display:flex;"><span> return i)))
</span></span></code></pre></div><p>I bind this function to <code>s-q</code>, as I&rsquo;m used from i3.</p>
<h3 id="move-the-workspace-to-another-monitor">Move the workspace to another monitor</h3>
<p>Now, moving the workspace to another monitor.</p>
<video controls width="100%">
<source src="/ox-hugo/exwm-workspace-move.mp4" type="video/mp4">
</video>
<p>This is actually quite easy to pull off - one just has to update <code>exwm-randr-workspace-monitor-plist</code> accordingly and run <code>exwm-randr-refresh</code>. I just add another check there because I don&rsquo;t want some monitor to remain without workspaces at all.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-workspace-switch-monitor ()
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Move the current workspace to another monitor.&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (let ((new-monitor (my/exwm-get-other-monitor <span style="color:#e6db74">&#39;right</span>))
</span></span><span style="display:flex;"><span> (current-monitor (my/exwm-get-current-monitor)))
</span></span><span style="display:flex;"><span> (when (and current-monitor
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">&gt;=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span> (cl-loop for (key value) on exwm-randr-workspace-monitor-plist
</span></span><span style="display:flex;"><span> by <span style="color:#e6db74">&#39;cddr</span>
</span></span><span style="display:flex;"><span> if (<span style="color:#a6e22e">string-equal</span> value current-monitor) sum <span style="color:#ae81ff">1</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">error</span> <span style="color:#e6db74">&#34;Can&#39;t remove the last workspace on the monitor!&#34;</span>))
</span></span><span style="display:flex;"><span> (setq exwm-randr-workspace-monitor-plist
</span></span><span style="display:flex;"><span> (map-delete exwm-randr-workspace-monitor-plist exwm-workspace-current-index))
</span></span><span style="display:flex;"><span> (when new-monitor
</span></span><span style="display:flex;"><span> (setq exwm-randr-workspace-monitor-plist
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">plist-put</span> exwm-randr-workspace-monitor-plist
</span></span><span style="display:flex;"><span> exwm-workspace-current-index
</span></span><span style="display:flex;"><span> new-monitor))))
</span></span><span style="display:flex;"><span> (exwm-randr-refresh))
</span></span></code></pre></div><p>In my configuration this is bound to <code>s-&lt;tab&gt;</code>.</p>
<h3 id="windmove-between-monitors">Windmove between monitors</h3>
<p>And the final (for now) piece of the puzzle is using the same command to switch between windows and monitors. E.g. when the focus is on the right-most window on one monitor, I want the command to switch to the left-most window on the monitor to the right instead of saying &ldquo;No window right from the selected window&rdquo;, as <code>windmove-right</code> does.</p>
<p>So here is my implementation of that. It always does <code>windmove-do-select-window</code> for <code>'down</code> and <code>'up</code>. For <code>'right</code> and <code>'left</code> though, the function calls the previously defined function to switch to other monitor if <code>windmove-find-other-window</code> doesn&rsquo;t return anything.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-windmove (dir)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Move to window or monitor in the direction DIR.&#34;</span>
</span></span><span style="display:flex;"><span> (if (or (<span style="color:#a6e22e">eq</span> dir <span style="color:#e6db74">&#39;down</span>) (<span style="color:#a6e22e">eq</span> dir <span style="color:#e6db74">&#39;up</span>))
</span></span><span style="display:flex;"><span> (windmove-do-window-select dir)
</span></span><span style="display:flex;"><span> (let ((other-window (windmove-find-other-window dir))
</span></span><span style="display:flex;"><span> (other-monitor (my/exwm-get-other-monitor dir))
</span></span><span style="display:flex;"><span> (opposite-dir (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;left</span> <span style="color:#e6db74">&#39;right</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;right</span> <span style="color:#e6db74">&#39;left</span>))))
</span></span><span style="display:flex;"><span> (if other-window
</span></span><span style="display:flex;"><span> (windmove-do-window-select dir)
</span></span><span style="display:flex;"><span> (my/exwm-switch-to-other-monitor dir)
</span></span><span style="display:flex;"><span> (cl-loop while (windmove-find-other-window opposite-dir)
</span></span><span style="display:flex;"><span> do (windmove-do-window-select opposite-dir))))))
</span></span></code></pre></div><p>I bind it to the corresponding keys like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq exwm-input-global-keys
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`</span>(
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Switch windows</span>
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-&lt;left&gt;&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;left</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-&lt;right&gt;&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;right</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-&lt;up&gt;&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;up</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-&lt;down&gt;&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;down</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-h&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;left</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-l&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;right</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-k&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;up</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-j&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-windmove <span style="color:#e6db74">&#39;down</span>)))
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>)
</span></span></code></pre></div><h2 id="managing-windows">Managing windows</h2>
<p>Another thing I want to tackle here is managing windows.</p>
<p>This section of the post depends on <a href="https://github.com/emacs-evil/evil">evil-mode</a>, which provides a reasonable set of vim-like commands to manage windows. But a few points to improve upon remain.</p>
<h3 id="moving-windows">Moving windows</h3>
<p>As I wrote in my <a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/">Emacs and i3</a> post, I want to have a rather specific behavior when moving windows (which does resemble i3 in some way):</p>
<ul>
<li>if there is space in the required direction, move the Emacs window there;</li>
<li>if there is no space in the required direction, but space in two orthogonal directions, move the Emacs window so that there is no more space in the orthogonal directions;</li>
</ul>
<p>I can&rsquo;t say it&rsquo;s better or worse than the built-in functionality or one provided by evil, but I&rsquo;m used to it and I think it fits better for managing a lot of windows.</p>
<p>So, first, we need a predicate that checks whether there is space in the given direction:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-direction-exists-p (dir)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Check if there is space in the direction DIR.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Does not take the minibuffer into account.&#34;</span>
</span></span><span style="display:flex;"><span> (cl-some (lambda (dir)
</span></span><span style="display:flex;"><span> (let ((win (windmove-find-other-window dir)))
</span></span><span style="display:flex;"><span> (and win (not (<span style="color:#a6e22e">window-minibuffer-p</span> win)))))
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;width</span> <span style="color:#f92672">&#39;</span>(left right))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;height</span> <span style="color:#f92672">&#39;</span>(up down)))))
</span></span></code></pre></div><p>And a function to implement that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-move-window (dir)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Move the current window in the direction DIR.&#34;</span>
</span></span><span style="display:flex;"><span> (let ((other-window (windmove-find-other-window dir))
</span></span><span style="display:flex;"><span> (other-direction (my/exwm-direction-exists-p
</span></span><span style="display:flex;"><span> (pcase dir
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;up</span> <span style="color:#e6db74">&#39;width</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;down</span> <span style="color:#e6db74">&#39;width</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;left</span> <span style="color:#e6db74">&#39;height</span>)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#39;right</span> <span style="color:#e6db74">&#39;height</span>)))))
</span></span><span style="display:flex;"><span> (cond
</span></span><span style="display:flex;"><span> ((and other-window (not (<span style="color:#a6e22e">window-minibuffer-p</span> other-window)))
</span></span><span style="display:flex;"><span> (window-swap-states (<span style="color:#a6e22e">selected-window</span>) other-window))
</span></span><span style="display:flex;"><span> (other-direction
</span></span><span style="display:flex;"><span> (evil-move-window dir)))))
</span></span></code></pre></div><p>My preferred keybindings for this part are, of course, <code>s-&lt;H|J|K|L&gt;</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq exwm-input-global-keys
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`</span>(
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Moving windows</span>
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-H&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-move-window <span style="color:#e6db74">&#39;left</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-L&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-move-window <span style="color:#e6db74">&#39;right</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-K&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-move-window <span style="color:#e6db74">&#39;up</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#f92672">,</span>(kbd <span style="color:#e6db74">&#34;s-J&#34;</span>) <span style="color:#f92672">.</span> (lambda () (interactive) (my/exwm-move-window <span style="color:#e6db74">&#39;down</span>)))
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>))
</span></span></code></pre></div><h3 id="resizing-windows">Resizing windows</h3>
<p>I find this odd that there are different commands to resize tiling and floating windows.</p>
<video controls width="100%">
<source src="/ox-hugo/exwm-resize-hydra.mp4" type="video/mp4">
</video>
<p>So let&rsquo;s define one command to perform both resizes depending on the context:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/exwm-resize-value <span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(defun my/exwm-resize-window (dir kind <span style="color:#66d9ef">&amp;optional</span> value)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Resize the current window in the direction DIR.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">DIR is either &#39;height or &#39;width, KIND is either &#39;shrink or
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> &#39;grow. VALUE is </span><span style="color:#e6db74">`my/exwm-resize-value&#39;</span><span style="color:#e6db74"> by default.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">If the window is an EXWM floating window, execute the
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">corresponding command from the exwm-layout group, execute the
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">command from the evil-window group.&#34;</span>
</span></span><span style="display:flex;"><span> (unless value
</span></span><span style="display:flex;"><span> (setq value my/exwm-resize-value))
</span></span><span style="display:flex;"><span> (let* ((is-exwm-floating
</span></span><span style="display:flex;"><span> (and (derived-mode-p <span style="color:#e6db74">&#39;exwm-mode</span>)
</span></span><span style="display:flex;"><span> exwm--floating-frame))
</span></span><span style="display:flex;"><span> (func (if is-exwm-floating
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">intern</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;exwm-layout-&#34;</span>
</span></span><span style="display:flex;"><span> (pcase kind (<span style="color:#e6db74">&#39;shrink</span> <span style="color:#e6db74">&#34;shrink&#34;</span>) (<span style="color:#e6db74">&#39;grow</span> <span style="color:#e6db74">&#34;enlarge&#34;</span>))
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;-window&#34;</span>
</span></span><span style="display:flex;"><span> (pcase dir (<span style="color:#e6db74">&#39;height</span> <span style="color:#e6db74">&#34;&#34;</span>) (<span style="color:#e6db74">&#39;width</span> <span style="color:#e6db74">&#34;-horizontally&#34;</span>))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">intern</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;evil-window&#34;</span>
</span></span><span style="display:flex;"><span> (pcase kind (<span style="color:#e6db74">&#39;shrink</span> <span style="color:#e6db74">&#34;-decrease-&#34;</span>) (<span style="color:#e6db74">&#39;grow</span> <span style="color:#e6db74">&#34;-increase-&#34;</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">symbol-name</span> dir))))))
</span></span><span style="display:flex;"><span> (when is-exwm-floating
</span></span><span style="display:flex;"><span> (setq value (<span style="color:#a6e22e">*</span> <span style="color:#ae81ff">5</span> value)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">funcall</span> func value)))
</span></span></code></pre></div><p>This function will call <code>exwm-layout-&lt;shrink|grow&gt;[-horizontally]</code> for EXWM floating window and <code>evil-window-&lt;decrease|increase&gt;-&lt;width|height&gt;</code> otherwise.</p>
<p>This function can be bound to the required keybindings directly, but I prefer a hydra to emulate the i3 submode:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defhydra my/exwm-resize-hydra (:color pink :hint <span style="color:#66d9ef">nil</span> :foreign-keys run)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^Resize^
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">_l_: Increase width _h_: Decrease width _j_: Increase height _k_: Decrease height
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">_=_: Balance &#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;h&#34;</span> (lambda () (interactive) (my/exwm-resize-window <span style="color:#e6db74">&#39;width</span> <span style="color:#e6db74">&#39;shrink</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;j&#34;</span> (lambda () (interactive) (my/exwm-resize-window <span style="color:#e6db74">&#39;height</span> <span style="color:#e6db74">&#39;grow</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;k&#34;</span> (lambda () (interactive) (my/exwm-resize-window <span style="color:#e6db74">&#39;height</span> <span style="color:#e6db74">&#39;shrink</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;l&#34;</span> (lambda () (interactive) (my/exwm-resize-window <span style="color:#e6db74">&#39;width</span> <span style="color:#e6db74">&#39;grow</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;=&#34;</span> balance-windows)
</span></span><span style="display:flex;"><span> (<span style="color:#e6db74">&#34;q&#34;</span> <span style="color:#66d9ef">nil</span> <span style="color:#e6db74">&#34;quit&#34;</span> :color blue))
</span></span></code></pre></div><h3 id="splitting-windows">Splitting windows</h3>
<p><code>M-x evil-window-[v]split</code> (bound to <code>C-w v</code> and <code>C-w s</code> by default) are the default evil command to do splits.</p>
<p>One EXWM-related issue though is that by default doing such a split &ldquo;copies&rdquo; the current buffer to the new window. But as EXWM buffer cannot be &ldquo;copied&rdquo; like that, some other buffer is displayed in the split, and generally, that&rsquo;s not a buffer I want.</p>
<p>For instance, I prefer to have Chrome DevTools as a separate window. When I click &ldquo;Inspect&rdquo; on something, the DevTools window replaces my Ungoogled Chromium window. I press <code>C-w v</code>, and most often I have something like <code>*scratch*</code> buffer in the opened split instead of the previous Chromium window.</p>
<p>To implement better behavior, I define the following advice:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/exwm-fill-other-window (<span style="color:#66d9ef">&amp;rest</span> _)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Open the most recently used buffer in the next window.&#34;</span>
</span></span><span style="display:flex;"><span> (interactive)
</span></span><span style="display:flex;"><span> (when (and (<span style="color:#a6e22e">eq</span> major-mode <span style="color:#e6db74">&#39;exwm-mode</span>) (not (<span style="color:#a6e22e">eq</span> (<span style="color:#a6e22e">next-window</span>) (<span style="color:#a6e22e">get-buffer-window</span>))))
</span></span><span style="display:flex;"><span> (let ((other-exwm-buffer
</span></span><span style="display:flex;"><span> (cl-loop with <span style="color:#a6e22e">other-buffer</span> <span style="color:#a6e22e">=</span> (persp-other-buffer)
</span></span><span style="display:flex;"><span> for buf in (<span style="color:#a6e22e">sort</span> (persp-current-buffers) (lambda (a _) (<span style="color:#a6e22e">eq</span> a <span style="color:#a6e22e">other-buffer</span>)))
</span></span><span style="display:flex;"><span> with <span style="color:#a6e22e">current-buffer</span> <span style="color:#a6e22e">=</span> (<span style="color:#a6e22e">current-buffer</span>)
</span></span><span style="display:flex;"><span> when (and (not (<span style="color:#a6e22e">eq</span> <span style="color:#a6e22e">current-buffer</span> buf))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">buffer-live-p</span> buf)
</span></span><span style="display:flex;"><span> (not (string-match-p (persp--make-ignore-buffer-rx) (<span style="color:#a6e22e">buffer-name</span> buf)))
</span></span><span style="display:flex;"><span> (not (<span style="color:#a6e22e">get-buffer-window</span> buf)))
</span></span><span style="display:flex;"><span> return buf)))
</span></span><span style="display:flex;"><span> (when other-exwm-buffer
</span></span><span style="display:flex;"><span> (with-selected-window (<span style="color:#a6e22e">next-window</span>)
</span></span><span style="display:flex;"><span> (switch-to-buffer other-exwm-buffer))))))
</span></span></code></pre></div><p>This is meant to be called after doing an either vertical or horizontal split, so it&rsquo;s advised like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(advice-add <span style="color:#e6db74">&#39;evil-window-split</span> :after <span style="color:#a6e22e">#&#39;</span>my/exwm-fill-other-window)
</span></span><span style="display:flex;"><span>(advice-add <span style="color:#e6db74">&#39;evil-window-vsplit</span> :after <span style="color:#a6e22e">#&#39;</span>my/exwm-fill-other-window)
</span></span></code></pre></div><p>This works as follows. If the current buffer is an EXWM buffer and there are other windows open (that is, <code>(next-window)</code> is not the current window), the function tries to find another suitable buffer to be opened in the split. And that also takes the perspectives into account, so buffers are searched only within the current perspective, and the buffer returned by <code>persp-other-buffer</code> will be the top candidate.</p>
<h2 id="notes-on-floating-windows">Notes on floating windows</h2>
<p>Floating windows are not the most stable feature of EXWM.</p>
<p>One story is that closing a floating window often screws up the current perspective, but that&rsquo;s advised away by my <code>perspective-exwm-mode</code>.</p>
<p>Another is that these three settings (which are reasonably <a href="https://github.com/daviwil/emacs-from-scratch/blob/5ebd390119a48cac6258843c7d5e570f4591fdd4/show-notes/Emacs-Desktop-04.org#mouse-warping">recommended</a> in the Emacs Desktop series) seem to increase chances of breaking the current EXWM session:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq exwm-workspace-warp-cursor <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span>(setq mouse-autoselect-window <span style="color:#66d9ef">t</span>)
</span></span><span style="display:flex;"><span>(setq focus-follows-mouse <span style="color:#66d9ef">t</span>)
</span></span></code></pre></div><p>Occasionally they create a loop of mouse warps and focus changes. I found that disabling them just for the floating windows greatly stabilized that part:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/fix-exwm-floating-windows ()
</span></span><span style="display:flex;"><span> (setq-local exwm-workspace-warp-cursor <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span> (setq-local mouse-autoselect-window <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span> (setq-local focus-follows-mouse <span style="color:#66d9ef">nil</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;exwm-floating-setup-hook</span> <span style="color:#a6e22e">#&#39;</span>my/fix-exwm-floating-windows)
</span></span></code></pre></div><p>However, one particularly unfriendly app is the <a href="https://zoom.us/">Zoom app</a>, which proudly creates a million various popups and still manages to break the EXWM sesssion. Fortunately, it can be used from a browser, which is what I advise to do.</p>
<h2 id="what-else-not-to-do">What else not to do</h2>
<p>A couple of final notes to make using EXWM a somewhat better experience.</p>
<p>First, <a href="https://github.com/daviwil/exwm/commit/7b1be884124711af0a02eac740bdb69446bc54cc">this fix</a> by David helped with <a href="https://github.com/ch11ng/exwm/issues/842">one case</a> of EXWM freezing, which I managed to get into a few times.</p>
<p>Second, do not run transients while there&rsquo;s an active EXWM window in the workspace, especially if it&rsquo;s it <code>char-mode</code>. That seems to break the session quite securely.</p>
<p>Third, running <code>shutdown</code> or something like that in the console is not the greatest idea, because things like <code>kill-emacs-hook</code> are not triggered in this case. For instance, EMMS history &amp; elfeed databases are not saved.</p>
<h2 id="p-dot-s-dot">P.S.</h2>
<p>The way how characters aligned in my keybinding for EMMS is coincidental and does not carry any semantic value. The <code>a</code> is for &ldquo;app&rdquo;, <code>s</code> is because <code>e</code> and <code>m</code> were already taken by elfeed and notmuch, and the second <code>s</code> is because it&rsquo;s faster to press the same character twice.</p>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,577 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>A few cases of literate configuration</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<p>A post that arose from the discussion of literate configuration on the <a href="https://systemcrafters.net/">System Crafters</a> Discord.</p>
<p>I am using the <a href="https://leanpub.com/lit-config">literate configuration</a> strategy (based on <a href="https://orgmode.org/">Emacs&rsquo; Org Mode</a>) to manage most of my configuration files. A piece of such a configuration can be as simple as an Org file, which is tangled to one or many plain-text configuration files, but it can be more.</p>
<p>In my opinion, a literate configuration can be more straightforward and concise than a &ldquo;normal&rdquo; one, thanks to Org Mode&rsquo;s capabilities of <a href="https://orgmode.org/manual/Working-with-Source-Code.html">working with source code</a>. So here I present a few examples from my configuration where I think this is the case:</p>
<ul>
<li>Managing system colors</li>
<li>Managing manifests for Guix profiles</li>
<li>Configuring modules in polybar</li>
</ul>
<p>I hope you find something interesting here!</p>
<h2 id="colors">Colors</h2>
<p>Let&rsquo;s start with system colors.</p>
<p>My favorite color theme is Palenight (<a href="https://github.com/JonathanSpeek/palenight-iterm2">color codes</a>), and I want to have one source of truth for these colors. Except for Emacs itself, which has <a href="https://github.com/doomemacs/themes#theme-list">doom-palenight</a> (and in which I occasionally switch to <code>doom-one-light</code>, e.g. when reading a long text), it can be done rather nicely with Org Mode.</p>
<p>First, let&rsquo;s define a table with all the color codes:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+tblname: colors
</span></span><span style="display:flex;"><span>| color | key | value |
</span></span><span style="display:flex;"><span>|---------------+---------+---------|
</span></span><span style="display:flex;"><span>| black | color0 | #292d3e |
</span></span><span style="display:flex;"><span>| red | color1 | #f07178 |
</span></span><span style="display:flex;"><span>| green | color2 | #c3e88d |
</span></span><span style="display:flex;"><span>| yellow | color3 | #ffcb6b |
</span></span><span style="display:flex;"><span>| blue | color4 | #82aaff |
</span></span><span style="display:flex;"><span>| magenta | color5 | #c792ea |
</span></span><span style="display:flex;"><span>| cyan | color6 | #89ddff |
</span></span><span style="display:flex;"><span>| white | color7 | #d0d0d0 |
</span></span><span style="display:flex;"><span>| light-black | color8 | #434758 |
</span></span><span style="display:flex;"><span>| light-red | color9 | #ff8b92 |
</span></span><span style="display:flex;"><span>| light-green | color10 | #ddffa7 |
</span></span><span style="display:flex;"><span>| light-yellow | color11 | #ffe585 |
</span></span><span style="display:flex;"><span>| light-blue | color12 | #9cc4ff |
</span></span><span style="display:flex;"><span>| light-magenta | color13 | #e1acff |
</span></span><span style="display:flex;"><span>| light-cyan | color14 | #a3f7ff |
</span></span><span style="display:flex;"><span>| light-white | color15 | #ffffff |
</span></span><span style="display:flex;"><span>| color-fg | | #000000 |
</span></span></code></pre></div><p>Contents of this table can then be <a href="https://orgmode.org/manual/Environment-of-a-Code-Block.html">accessed from a code block</a>. Let&rsquo;s define one to return the color code based on its name:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: get-color
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :var table=colors name=&#34;black&#34; quote=0
</span></span><span style="display:flex;"><span>(let ((color (seq-some (lambda (e) (and (string= name (car e)) (nth 2 e))) table)))
</span></span><span style="display:flex;"><span> (if (&gt; quote 0)
</span></span><span style="display:flex;"><span> (concat &#34;\&#34;&#34; color &#34;\&#34;&#34;)
</span></span><span style="display:flex;"><span> color))
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>Evaluating this block of code should return color code, corresponding to &ldquo;black&rdquo;.</p>
<p>And the best part is that the results of evaluation of one code block can be included to others with <a href="https://orgmode.org/manual/Noweb-Reference-Syntax.html">noweb</a>. For instance, here&rsquo;s my <a href="https://pwmt.org/projects/zathura/">zathura</a> config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src conf-space :noweb yes :tangle .config/zathura/zathurarc
</span></span><span style="display:flex;"><span>set abort-clear-search false
</span></span><span style="display:flex;"><span>set guioptions cs
</span></span><span style="display:flex;"><span>set selection-clipboard clipboard
</span></span><span style="display:flex;"><span>set recolor true
</span></span><span style="display:flex;"><span>map &lt;C-r&gt; set recolor false
</span></span><span style="display:flex;"><span>map &lt;C-R&gt; set recolor true
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set recolor-lightcolor &lt;&lt;get-color(name=&#34;black&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set completion-bg &lt;&lt;get-color(name=&#34;black&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set completion-fg &lt;&lt;get-color(name=&#34;white&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set completion-group-bg &lt;&lt;get-color(name=&#34;light-black&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set completion-group-fg &lt;&lt;get-color(name=&#34;white&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set completion-highlight-bg &lt;&lt;get-color(name=&#34;magenta&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set completion-highlight-fg &lt;&lt;get-color(name=&#34;black&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set inputbar-bg &lt;&lt;get-color(name=&#34;black&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set inputbar-fg &lt;&lt;get-color(name=&#34;light-magenta&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set statusbar-bg &lt;&lt;get-color(name=&#34;black&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set statusbar-fg &lt;&lt;get-color(name=&#34;light-magenta&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set notification-error-bg &lt;&lt;get-color(name=&#34;red&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set notification-error-fg &lt;&lt;get-color(name=&#34;color-fg&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set notification-warning-bg &lt;&lt;get-color(name=&#34;yellow&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>set notification-warning-fg &lt;&lt;get-color(name=&#34;color-fg&#34;, quote=1)&gt;&gt;
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>Running <code>M-x org-babel-expand-src-block</code> (<code>C-c C-v v</code>) on this code block will open the code buffer with noweb expressions expanded, for instance the line with <code>set recolor-lightcolor</code> will look like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>set recolor-lightcolor &#34;#292d3e&#34;
</span></span></code></pre></div><p><code>M-x org-babel-tangle</code> (<code>C-c C-v t</code>) will also produce <code>zathurarc</code> with the colors set (given that there&rsquo;s <code>:noweb yes</code> somewhere in the code block configuration).</p>
<p>One note is that by default running these commands will require the user to confirm evaluation of each code block. To avoid that, you can set <code>org-confirm-babel-evaluate</code> to <code>nil</code>, for example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(setq my/org-config-files
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(<span style="color:#e6db74">&#34;/home/pavel/Emacs.org&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;/home/pavel/Desktop.org&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;/home/pavel/Console.org&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;/home/pavel/Guix.org&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;/home/pavel/Mail.org&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(add-hook <span style="color:#e6db74">&#39;org-mode-hook</span>
</span></span><span style="display:flex;"><span> (lambda ()
</span></span><span style="display:flex;"><span> (when (<span style="color:#a6e22e">member</span> (<span style="color:#a6e22e">buffer-file-name</span>) my/org-config-files)
</span></span><span style="display:flex;"><span> (setq-local org-confirm-babel-evaluate <span style="color:#66d9ef">nil</span>))))
</span></span></code></pre></div><p>And, to close the loop on colors, let&rsquo;s generate <code>.Xresources</code> from that table:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: get-xresources
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :var table=colors
</span></span><span style="display:flex;"><span>(mapconcat
</span></span><span style="display:flex;"><span> (lambda (elem)
</span></span><span style="display:flex;"><span> (concat &#34;*&#34; (nth 1 elem) &#34;: &#34; (nth 2 elem)))
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (elem) (and (nth 1 elem)
</span></span><span style="display:flex;"><span> (not (string-empty-p (nth 1 elem)))))
</span></span><span style="display:flex;"><span> table)
</span></span><span style="display:flex;"><span> &#34;\n&#34;)
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+begin_src conf-xdefaults :noweb yes :tangle ~/.Xresources
</span></span><span style="display:flex;"><span>&lt;&lt;get-xresources()&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>*background: &lt;&lt;get-color(name=&#34;black&#34;)&gt;&gt;
</span></span><span style="display:flex;"><span>*foreground: &lt;&lt;get-color(name=&#34;white&#34;)&gt;&gt;
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>So, whenever a program is capable of reading <code>.Xresources</code>, it will get colors from there, otherwise, it will get colors from noweb expressions in the literate config. Thus, in both cases, the color is set in a single Org Mode table.</p>
<h2 id="guix-dependencies">Guix dependencies</h2>
<p>Another case I want to cover is <a href="https://guix.gnu.org/en/cookbook/en/html_node/Advanced-package-management.html#Advanced-package-management">using profiles in GNU Guix</a>.</p>
<p>A &ldquo;profile&rdquo; in Guix is a way to group package installations. For instance, I have a &ldquo;music&rdquo; profile that has software like <a href="https://www.musicpd.org/">MPD</a>, <a href="https://github.com/ncmpcpp/ncmpcpp">ncmpcpp</a> that I&rsquo;m still occasionally using because of its tag editor, etc. Corresponding to that profile, there&rsquo;s a manifest named <code>music.scm</code> that looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#a6e22e">specifications-&gt;manifest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#39;</span>(
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;flac&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;cuetools&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;shntool&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;mpd-mpc&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;mpd-watcher&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;picard&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;ncmpcpp&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;mpd&#34;</span>))
</span></span></code></pre></div><p>I could generate this file with <code>org-babel</code> as any other, but that is often not so convenient. For example, I have a <a href="https://github.com/polybar/polybar">polybar</a> module that uses <a href="https://github.com/risacher/sunwait">sunwait</a> to show sunset and sunrise times, and ideally, I want to declare <code>sunwait</code> to be in the &ldquo;desktop-polybar&rdquo; profile in the same section that has the polybar module definition and the bash script.</p>
<p>So here&rsquo;s an approach I came up with. The relevant section of the config looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>*** sun
</span></span><span style="display:flex;"><span>| Category | Guix dependency |
</span></span><span style="display:flex;"><span>|-----------------+-----------------|
</span></span><span style="display:flex;"><span>| desktop-polybar | sunwait |
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Prints out the time of sunrise/sunset. Uses [[https://github.com/risacher/sunwait][sunwait]]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+begin_src bash :tangle ./bin/polybar/sun.sh :noweb yes
</span></span><span style="display:flex;"><span>...script...
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+begin_src conf-windows :noweb yes
</span></span><span style="display:flex;"><span>...polybar module definition...
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p><code>sunwait</code> is declared in an Org table that looks like that:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>desktop-polybar</td>
<td>sunwait</td>
</tr>
</tbody>
</table>
<p>Such tables are spread through my <code>Desktop.org</code>, for instance, here is another one for a polybar module that depends on dateutils:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Guix dependency</th>
</tr>
</thead>
<tbody>
<tr>
<td>desktop-polybar</td>
<td>dateutils</td>
</tr>
</tbody>
</table>
<p>Thus I made a function that extracts packages from all such tables from the current Org buffer. The rules are as follows:</p>
<ul>
<li>If a column name matches <code>[G|g]uix.*dep</code>, its contents are added to the result.</li>
<li>If <code>CATEGORY</code> is passed, a column with name <code>[C|c]ategory</code> is used to filter results. That way, one Org file can be used to produce multiple manifests.</li>
<li>If <code>CATEGORY</code> is not passed, entries with the non-empty category are filtered out</li>
<li>If there is a <code>[D|d]isabled</code> column, entries that have a non-empty value in this column are filtered out.</li>
</ul>
<p>And here is the implementation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/extract-guix-dependencies (<span style="color:#66d9ef">&amp;optional</span> category)
</span></span><span style="display:flex;"><span> (let ((dependencies <span style="color:#f92672">&#39;</span>()))
</span></span><span style="display:flex;"><span> (org-table-map-tables
</span></span><span style="display:flex;"><span> (lambda ()
</span></span><span style="display:flex;"><span> (let* ((table
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (q) (not (<span style="color:#a6e22e">eq</span> q <span style="color:#e6db74">&#39;hline</span>)))
</span></span><span style="display:flex;"><span> (org-table-to-lisp)))
</span></span><span style="display:flex;"><span> (dep-name-index
</span></span><span style="display:flex;"><span> (cl-position
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapcar</span> <span style="color:#a6e22e">#&#39;substring-no-properties</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> table))
</span></span><span style="display:flex;"><span> :test (lambda (_ elem)
</span></span><span style="display:flex;"><span> (string-match-p <span style="color:#e6db74">&#34;[G|g]uix.*dep&#34;</span> elem))))
</span></span><span style="display:flex;"><span> (category-name-index
</span></span><span style="display:flex;"><span> (cl-position
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapcar</span> <span style="color:#a6e22e">#&#39;substring-no-properties</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> table))
</span></span><span style="display:flex;"><span> :test (lambda (_ elem)
</span></span><span style="display:flex;"><span> (string-match-p <span style="color:#e6db74">&#34;.*[C|c]ategory.*&#34;</span> elem))))
</span></span><span style="display:flex;"><span> (disabled-name-index
</span></span><span style="display:flex;"><span> (cl-position
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapcar</span> <span style="color:#a6e22e">#&#39;substring-no-properties</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> table))
</span></span><span style="display:flex;"><span> :test (lambda (_ elem)
</span></span><span style="display:flex;"><span> (string-match-p <span style="color:#e6db74">&#34;.*[D|d]isabled.*&#34;</span> elem)))))
</span></span><span style="display:flex;"><span> (when dep-name-index
</span></span><span style="display:flex;"><span> (dolist (elem (<span style="color:#a6e22e">cdr</span> table))
</span></span><span style="display:flex;"><span> (when
</span></span><span style="display:flex;"><span> (and
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Category</span>
</span></span><span style="display:flex;"><span> (or
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Category is not set and not present in the table</span>
</span></span><span style="display:flex;"><span> (and
</span></span><span style="display:flex;"><span> (or (not category) (string-empty-p category))
</span></span><span style="display:flex;"><span> (not category-name-index))
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Category is set and present in the table</span>
</span></span><span style="display:flex;"><span> (and
</span></span><span style="display:flex;"><span> category-name-index
</span></span><span style="display:flex;"><span> (not (string-empty-p category))
</span></span><span style="display:flex;"><span> (string-match-p category (<span style="color:#a6e22e">nth</span> category-name-index elem))))
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; Not disabled</span>
</span></span><span style="display:flex;"><span> (or
</span></span><span style="display:flex;"><span> (not disabled-name-index)
</span></span><span style="display:flex;"><span> (string-empty-p (<span style="color:#a6e22e">nth</span> disabled-name-index elem))))
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;dependencies</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">substring-no-properties</span> (<span style="color:#a6e22e">nth</span> dep-name-index elem)))))))))
</span></span><span style="display:flex;"><span> dependencies))
</span></span></code></pre></div><p>Let&rsquo;s execute this function in the current buffer:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(my/extract-guix-dependencies <span style="color:#e6db74">&#34;desktop-polybar&#34;</span>)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(<span style="color:#e6db74">&#34;dateutils&#34;</span> <span style="color:#e6db74">&#34;sunwait&#34;</span>)
</span></span></code></pre></div><p>As expected, it found both <code>dateutils</code> and <code>sunwait</code>. To make it work in the configuration, it is necessary to format the list so that Scheme could read it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(defun my/format-guix-dependencies (<span style="color:#66d9ef">&amp;optional</span> category)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapconcat</span>
</span></span><span style="display:flex;"><span> (lambda (e) (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">&#34;\&#34;&#34;</span> e <span style="color:#e6db74">&#34;\&#34;&#34;</span>))
</span></span><span style="display:flex;"><span> (my/extract-guix-dependencies category)
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;\n&#34;</span>))
</span></span></code></pre></div><p>And we need an Org snippet such as this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: packages
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :tangle no :var category=&#34;&#34;
</span></span><span style="display:flex;"><span>(my/format-guix-dependencies category)
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>Now, creating a manifest for the <code>desktop-polybar</code> profile is as simple as:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src scheme :tangle ~/.config/guix/manifests/desktop-polybar.scm :noweb yes
</span></span><span style="display:flex;"><span>(specifications-&gt;manifest
</span></span><span style="display:flex;"><span> &#39;(
</span></span><span style="display:flex;"><span> &lt;&lt;packages(&#34;desktop-polybar&#34;)&gt;&gt;))
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>There&rsquo;s a newline symbol between &ldquo;(&rdquo; and <code>&lt;&lt;packages(&quot;desktop-polybar&quot;)&gt;&gt;</code> because whenever a noweb expression expands into multiple lines, for each new line noweb duplicates contents between the start of the line and the start of the expression.</p>
<p>One reason this is so is to support languages where indentation is a part of the syntax, for instance, Python:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TestClass</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span>class<span style="color:#f92672">-</span>contents<span style="color:#f92672">&gt;&gt;</span>
</span></span></code></pre></div><p>So every line of <code>&lt;&lt;class-contents&gt;&gt;</code> will be indented appropriately. In our case though, it is a minor inconvenience to be aware of.</p>
<h2 id="polybar">Polybar</h2>
<p>Now, the most <del>crazy</del> advanced case I&rsquo;ve come up with so far.</p>
<p>Basically, here is how my <a href="https://github.com/polybar/polybar">polybar</a> currently looks:
<img src="/ox-hugo/literate--polybar.png" alt=""></p>
<p>It has:</p>
<ul>
<li>colors from the general color theme;</li>
<li>powerline-ish decorations between modules.</li>
</ul>
<h3 id="colors">Colors</h3>
<p>The &ldquo;colors&rdquo; part is straightforward enough. Polybar can use <code>Xresources</code>, so we just need to generate the appropriate bindings of Xresources to the polybar variables:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: get-polybar-colors
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :var table=colors :tangle no
</span></span><span style="display:flex;"><span>(mapconcat
</span></span><span style="display:flex;"><span> (lambda (elem)
</span></span><span style="display:flex;"><span> (format &#34;%s = ${xrdb:%s}&#34; (nth 0 elem) (nth 1 elem)))
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (elem) (when-let (name (nth 1 elem))
</span></span><span style="display:flex;"><span> (not (string-empty-p name))))
</span></span><span style="display:flex;"><span> table)
</span></span><span style="display:flex;"><span> &#34;\n&#34;)
</span></span><span style="display:flex;"><span>#+end_src
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>#+begin_src conf-windows :noweb yes
</span></span><span style="display:flex;"><span>[colors]
</span></span><span style="display:flex;"><span>&lt;&lt;get-polybar-colors()&gt;&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>background = ${xrdb:background}
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><h3 id="module-decorations">Module decorations</h3>
<p>As for the module decorations though, I find it ironic that with all this fancy rendering around I have to resort to Unicode glyphs.</p>
<p>Anyhow, the approach is to put a glyph between two blocks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>block1  block2
</span></span></code></pre></div><p>And set the foreground and background colors like that:</p>
<table>
<thead>
<tr>
<th></th>
<th>block1</th>
<th>glyph</th>
<th>block2</th>
</tr>
</thead>
<tbody>
<tr>
<td>foreground</td>
<td>F1</td>
<td>B2</td>
<td>F2</td>
</tr>
<tr>
<td>background</td>
<td>B1</td>
<td>B1</td>
<td>B2</td>
</tr>
</tbody>
</table>
<p>So, that&rsquo;s a start. First, let&rsquo;s define the glyph symbols in the polybar config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[glyph]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">gleft</span> <span style="color:#f92672">=</span> <span style="color:#e6db74"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">gright</span> <span style="color:#f92672">=</span> <span style="color:#e6db74"></span>
</span></span></code></pre></div><h4 id="defining-modules">Defining modules</h4>
<p>As we want to interweave polybar modules with these glyphs in the right order and with the right colors, it is reasonable to define a single source of truth:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: polybar_modules
</span></span><span style="display:flex;"><span>| Index | Module | Color | Glyph |
</span></span><span style="display:flex;"><span>|-------+-------------+---------------+-------|
</span></span><span style="display:flex;"><span>| 1 | pulseaudio | light-magenta | + |
</span></span><span style="display:flex;"><span>| 2 | mpd | magenta | + |
</span></span><span style="display:flex;"><span>| 9 | battery | light-cyan | + |
</span></span><span style="display:flex;"><span>| 3 | cpu | cyan | + |
</span></span><span style="display:flex;"><span>| 4 | ram-memory | light-green | + |
</span></span><span style="display:flex;"><span>| 5 | swap-memory | green | + |
</span></span><span style="display:flex;"><span>| 6 | network | light-red | + |
</span></span><span style="display:flex;"><span>| 7 | openvpn | light-red | |
</span></span><span style="display:flex;"><span>| 8 | xkeyboard | red | + |
</span></span><span style="display:flex;"><span>| 10 | weather | light-yellow | + |
</span></span><span style="display:flex;"><span>| 12 | sun | yellow | + |
</span></span><span style="display:flex;"><span>| 13 | aw-afk | light-blue | + |
</span></span><span style="display:flex;"><span>| 14 | date | blue | + |
</span></span></code></pre></div><p>Also excluding some modules from certain monitors, which for now is about excluding <code>battery</code> from the monitors of my desktop PC:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: polybar_modules_exclude
</span></span><span style="display:flex;"><span>| Monitor | Exclude |
</span></span><span style="display:flex;"><span>|----------+---------|
</span></span><span style="display:flex;"><span>| DVI-D-0 | battery |
</span></span><span style="display:flex;"><span>| HDMI-A-0 | battery |
</span></span></code></pre></div><h4 id="generating-glyphs">Generating glyphs</h4>
<p>To generate the required set of glyphs, we need a glyph for every possible combination of adjacent colors that can occur in polybar.</p>
<p>Most of these combinations can be inferred from the <code>polybar_modules</code> table, the rest are defined in another table:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: polybar_extra_colors
</span></span><span style="display:flex;"><span>| Color 1 | Color 2 |
</span></span><span style="display:flex;"><span>|------------+---------------|
</span></span><span style="display:flex;"><span>| background | white |
</span></span><span style="display:flex;"><span>| background | light-magenta |
</span></span><span style="display:flex;"><span>| blue | background |
</span></span></code></pre></div><p>There&rsquo;s a definition of the source block with the required variables:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: polybar-generate-glyphs
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :var table=polybar_modules exclude-table=polybar_modules_exclude extra=polybar_extra_colors
</span></span><span style="display:flex;"><span>...source...
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>And there is the source block itself (because I want to have some syntax highlighting for this one in the post):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(let* ((monitors
</span></span><span style="display:flex;"><span> (thread-last
</span></span><span style="display:flex;"><span> exclude-table
</span></span><span style="display:flex;"><span> (seq-map (lambda (el) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> el)))
</span></span><span style="display:flex;"><span> (seq-uniq)))
</span></span><span style="display:flex;"><span> (exclude-combinations
</span></span><span style="display:flex;"><span> (seq-map
</span></span><span style="display:flex;"><span> (lambda (monitor)
</span></span><span style="display:flex;"><span> (seq-map
</span></span><span style="display:flex;"><span> (lambda (el) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> el))
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (el) (and (<span style="color:#a6e22e">string-equal</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> el) monitor)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> el)))
</span></span><span style="display:flex;"><span> exclude-table)))
</span></span><span style="display:flex;"><span> <span style="color:#f92672">`</span>(<span style="color:#f92672">,@</span>monitors <span style="color:#e6db74">&#34;&#34;</span>)))
</span></span><span style="display:flex;"><span> (module-glyph-combinations
</span></span><span style="display:flex;"><span> (thread-last
</span></span><span style="display:flex;"><span> exclude-combinations
</span></span><span style="display:flex;"><span> (seq-map
</span></span><span style="display:flex;"><span> (lambda (exclude)
</span></span><span style="display:flex;"><span> (thread-last
</span></span><span style="display:flex;"><span> table
</span></span><span style="display:flex;"><span> (seq-filter
</span></span><span style="display:flex;"><span> (lambda (<span style="color:#a6e22e">elt</span>)
</span></span><span style="display:flex;"><span> (not (or
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">member</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> <span style="color:#a6e22e">elt</span>) exclude)
</span></span><span style="display:flex;"><span> (not (<span style="color:#a6e22e">string-equal</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">3</span> <span style="color:#a6e22e">elt</span>) <span style="color:#e6db74">&#34;+&#34;</span>)))))))))
</span></span><span style="display:flex;"><span> (seq-uniq)))
</span></span><span style="display:flex;"><span> (color-changes <span style="color:#66d9ef">nil</span>))
</span></span><span style="display:flex;"><span> (dolist (e extra)
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;color-changes</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> e) <span style="color:#e6db74">&#34;--&#34;</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> e))))
</span></span><span style="display:flex;"><span> (dolist (comb module-glyph-combinations)
</span></span><span style="display:flex;"><span> (dotimes (i (<span style="color:#a6e22e">1-</span> (<span style="color:#a6e22e">length</span> comb)))
</span></span><span style="display:flex;"><span> (add-to-list
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#39;color-changes</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> (<span style="color:#a6e22e">nth</span> i comb))
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;--&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> (<span style="color:#a6e22e">nth</span> (<span style="color:#a6e22e">1+</span> i) comb))))))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapconcat</span>
</span></span><span style="display:flex;"><span> (lambda (el)
</span></span><span style="display:flex;"><span> (let ((colors (split-string el <span style="color:#e6db74">&#34;--&#34;</span>)))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">[module/glyph-%s--%s]
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">type = custom/text
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">content-background = ${colors.%s}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">content-foreground = ${colors.%s}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">content = ${glyph.gright}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">content-font = 5&#34;</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> colors)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> colors)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> colors)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> colors))))
</span></span><span style="display:flex;"><span> color-changes
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;\n&#34;</span>))
</span></span></code></pre></div><p>Here&rsquo;s a rough outline of how the code works:</p>
<ul>
<li><code>monitors</code> is a list of unique monitors in <code>exclude-table</code></li>
<li><code>exclude-combilnations</code> is a list of lists of module names to be excluded for each monitor</li>
<li><code>module-glyphs-combinations</code> is a list of lists of actual modules for each monitor</li>
<li><code>color-changes</code> is a list of unique adjacent colors across modules in all monitors</li>
</ul>
<p>Finally, <code>color-changes</code> is used to generate glyph modules that look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[module/glyph-light-cyan--cyan]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">type</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">custom/text</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">content-background</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">${colors.light-cyan}</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">content-foreground</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">${colors.cyan}</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">content</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">${glyph.gright}</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">content-font</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">5</span>
</span></span></code></pre></div><p>As of now, 15 of such modules is generated.</p>
<p>And including this in the polybar config itself:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src conf-windows :noweb yes
</span></span><span style="display:flex;"><span>&lt;&lt;polybar-generate-glyphs()&gt;&gt;
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><h4 id="individual-modules">Individual modules</h4>
<p>Another thing we need to do is to set the color of modules in accordance with the <code>polybar_modules</code> table. The background can be determined from the <code>Color</code> column with the following code block:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: get-polybar-bg
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :var table=polybar_modules module=&#34;pulseaudio&#34;
</span></span><span style="display:flex;"><span>(format
</span></span><span style="display:flex;"><span> &#34;${colors.%s}&#34;
</span></span><span style="display:flex;"><span> (nth
</span></span><span style="display:flex;"><span> 2
</span></span><span style="display:flex;"><span> (seq-find
</span></span><span style="display:flex;"><span> (lambda (el) (string-equal (nth 1 el) module))
</span></span><span style="display:flex;"><span> table)))
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>And that block is meant to be invoked in each module definition, e.g. for the <code>cpu</code> module:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+begin_src conf-windows :noweb yes
</span></span><span style="display:flex;"><span>[module/cpu]
</span></span><span style="display:flex;"><span>type = internal/cpu
</span></span><span style="display:flex;"><span>format = &#34;&lt;label&gt;&#34;
</span></span><span style="display:flex;"><span>label = %percentage%%
</span></span><span style="display:flex;"><span>format-background = &lt;&lt;get-polybar-bg(module=&#34;cpu&#34;)&gt;&gt;
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><h4 id="global-polybar-configuration">Global polybar configuration</h4>
<p>To configure polybar itself, we first need to generate a set of modules for each monitor.</p>
<p>Here is the source block definition:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>#+NAME: polybar-generate-modules
</span></span><span style="display:flex;"><span>#+begin_src emacs-lisp :var table=polybar_modules exclude-table=polybar_modules_exclude monitor=&#34;DVI-D-0&#34; first-color=&#34;background&#34; last-color=&#34;background&#34;
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>#+end_src
</span></span></code></pre></div><p>The parameters here, excluding the two required tables, are:</p>
<ul>
<li><code>monitor</code> - the current monitor on which to filter out the blocks by the <code>polybar_modules_exclude</code> table,</li>
<li><code>first-color</code> - the first color of the first glyph,</li>
<li><code>last-color</code> - the second color of the last glyph.</li>
</ul>
<p>And here is the source:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(let* ((exclude-modules
</span></span><span style="display:flex;"><span> (thread-last
</span></span><span style="display:flex;"><span> exclude-table
</span></span><span style="display:flex;"><span> (seq-filter (lambda (el) (<span style="color:#a6e22e">string-equal</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> el) monitor)))
</span></span><span style="display:flex;"><span> (seq-map (lambda (el) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> el)))))
</span></span><span style="display:flex;"><span> (modules
</span></span><span style="display:flex;"><span> (thread-last
</span></span><span style="display:flex;"><span> table
</span></span><span style="display:flex;"><span> (seq-filter (lambda (el) (not (<span style="color:#a6e22e">member</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> el) exclude-modules))))))
</span></span><span style="display:flex;"><span> (prev-color first-color)
</span></span><span style="display:flex;"><span> (ret <span style="color:#66d9ef">nil</span>))
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">concat</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">mapconcat</span>
</span></span><span style="display:flex;"><span> (lambda (el)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">apply</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">#&#39;concat</span>
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">list</span>
</span></span><span style="display:flex;"><span> (when (<span style="color:#a6e22e">string-equal</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">3</span> el) <span style="color:#e6db74">&#34;+&#34;</span>)
</span></span><span style="display:flex;"><span> (setq ret (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34;glyph-%s--%s &#34;</span> prev-color (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> el)))
</span></span><span style="display:flex;"><span> (setq prev-color (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> el))
</span></span><span style="display:flex;"><span> ret)
</span></span><span style="display:flex;"><span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> el))))
</span></span><span style="display:flex;"><span> modules
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34; &#34;</span>)
</span></span><span style="display:flex;"><span> (unless (string-empty-p last-color) (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">&#34; glyph-%s--%s &#34;</span> prev-color last-color))))
</span></span></code></pre></div><p>Here&rsquo;s how it evaluates on my current monitor:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>glyph-background--light-magenta pulseaudio glyph-light-magenta--magenta mpd glyph-magenta--cyan cpu glyph-cyan--light-green ram-memory glyph-light-green--green swap-memory glyph-green--light-red network openvpn glyph-light-red--red xkeyboard glyph-red--light-yellow weather glyph-light-yellow--yellow sun glyph-yellow--light-blue aw-afk glyph-light-blue--blue date glyph-blue--background
</span></span></code></pre></div><p>The polybar config doesn&rsquo;t support conditional statements, but it does support environment variables, so we can pass the parameters from something like a bash script. Here&rsquo;s an excerpt from mine:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>declare -A BLOCKS<span style="color:#f92672">=(</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">[</span><span style="color:#e6db74">&#34;eDP&#34;</span><span style="color:#f92672">]=</span><span style="color:#e6db74">&#34;&lt;&lt;polybar-generate-modules(monitor=&#34;</span>eDP<span style="color:#e6db74">&#34;)&gt;&gt;&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">[</span><span style="color:#e6db74">&#34;eDP-1&#34;</span><span style="color:#f92672">]=</span><span style="color:#e6db74">&#34;&lt;&lt;polybar-generate-modules(monitor=&#34;</span>eDP-1<span style="color:#e6db74">&#34;)&gt;&gt;&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">[</span><span style="color:#e6db74">&#34;DVI-D-0&#34;</span><span style="color:#f92672">]=</span><span style="color:#e6db74">&#34;&lt;&lt;polybar-generate-modules(monitor=&#34;</span>DVI-D-0<span style="color:#e6db74">&#34;)&gt;&gt;&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">[</span><span style="color:#e6db74">&#34;HDMI-A-0&#34;</span><span style="color:#f92672">]=</span><span style="color:#e6db74">&#34;&lt;&lt;polybar-generate-modules(monitor=&#34;</span>HDMI-A-0<span style="color:#e6db74">&#34;)&gt;&gt;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>pkill polybar
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> m in <span style="color:#66d9ef">$(</span>xrandr --query | grep <span style="color:#e6db74">&#34; connected&#34;</span> | cut -d<span style="color:#e6db74">&#34; &#34;</span> -f1<span style="color:#66d9ef">)</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span> export RIGHT_BLOCKS<span style="color:#f92672">=</span><span style="color:#e6db74">${</span>BLOCKS[$MONITOR]<span style="color:#e6db74">}</span>
</span></span><span style="display:flex;"><span> polybar mybar &amp;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>(The full script has a lot of stuff that is not relevant to this post, but you can <a href="https://github.com/SqrtMinusOne/dotfiles/blob/master/Desktop.org#launch-script-1">check here</a> if you are interested.)</p>
<p>So, in the case of polybar, literate configuration allows for implementing a sort of logic that wouldn&rsquo;t be available with the base configuration (also a promise of projects like Guix Home, by the way). Maintaining this configuration, e.g. changing the order of modules, is much easier this way than it would be if everything was hardcoded in the polybar config itself.</p>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Hello, world!</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h2 id="hello-world">Hello, world!</h2>
<p>Eventually, there will be something interesting here. Or not.</p>
<p>Regradless, I&rsquo;ll check if I can write some Python here</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Hello, world&#34;</span>)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Hello, world
</span></span></code></pre></div>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

95
posts/index.html Normal file
View file

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Posts</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/posts/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>Posts</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2022-02-12-literate/">2022-02-12 | A few cases of literate configuration</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2022-01-03-exwm/">2022-01-03 | Using EXWM and perspective.el on multi-monitor setup</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/">2021-10-06 | Getting a consistent set of keybindings between i3 and Emacs</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-09-07-emms/">2021-09-08 | My EMMS and elfeed setup</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-05-01-org-python/">2021-05-01 | Replacing Jupyter Notebook with Org Mode</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-02-27-gmail/">2021-02-27 | Multiple Gmail accounts &amp; labels with Emacs</a></li>
<li><a href="https://sqrtminusone.xyz/posts/hello-world/">2021-02-01 | Hello, world!</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

84
posts/index.xml Normal file
View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Posts on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/posts/</link>
<description>Recent content in Posts on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 12 Feb 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/posts/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>A few cases of literate configuration</title>
<link>https://sqrtminusone.xyz/posts/2022-02-12-literate/</link>
<pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-02-12-literate/</guid>
<description>A post that arose from the discussion of literate configuration on the System Crafters Discord.
I am using the literate configuration strategy (based on Emacs&amp;rsquo; Org Mode) to manage most of my configuration files. A piece of such a configuration can be as simple as an Org file, which is tangled to one or many plain-text configuration files, but it can be more.
In my opinion, a literate configuration can be more straightforward and concise than a &amp;ldquo;normal&amp;rdquo; one, thanks to Org Mode&amp;rsquo;s capabilities of working with source code.</description>
</item>
<item>
<title>Using EXWM and perspective.el on multi-monitor setup</title>
<link>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</link>
<pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</guid>
<description>I wrote about Emacs and i3 integration around two months ago. Shortly after however, I decided to give EXWM another try, mainly because my largest reservation - lack of performance - seems to have been resolved by updates to the native compilation since my first attempt. Or I may have lost some sensitivity to that issue. Regardless, the second dive into EXWM thus far feels successful, and I think it&amp;rsquo;s the right time to share some of my thoughts on the subject.</description>
</item>
<item>
<title>Getting a consistent set of keybindings between i3 and Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</link>
<pubDate>Wed, 06 Oct 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</guid>
<description>Intro One advantage of EXWM for an Emacs user is that EXWM gives one set of keybindings to manage both Emacs windows and X windows. In every other WM, like my preferred i3wm, two orthogonal keymaps seem to be necessary. But, as both programs are quite customizable, I want to see whether I can replicate at least some part of the EXWM goodness in i3.
But why not just use EXWM?</description>
</item>
<item>
<title>My EMMS and elfeed setup</title>
<link>https://sqrtminusone.xyz/posts/2021-09-07-emms/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-09-07-emms/</guid>
<description>Intro This is the current state of my quest to live in Emacs, at least in part of reading RSS and music.
Even before I lost my mind about customizing obscure keyboard-driven software, I tried Inoreader, self-hosted FreshRSS, and then newsboat from the RSS side and ncmpcpp+MPD from the audio player side. At some point, I got curious about whether I can do the same in Emacs.
The respective emacs packages, elfeed and EMMS, proved somewhat tricky to set up, i.</description>
</item>
<item>
<title>Replacing Jupyter Notebook with Org Mode</title>
<link>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</link>
<pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</guid>
<description>Why? Jupyter Notebook and its successor Jupyter Lab providing an interactive development environment for many programming languages are in lots of ways great pieces of software.
But while I was using the former, and then the latter, I was also an as-full-time-as-one-can-get NeoVim user. &amp;ldquo;As one can get&amp;rdquo; is because, of course, there is no sensible way to extend the NeoVim editing experience to the Jupyter ecosystem.
A possibility for change appeared with my discovery of Emacs not so long ago.</description>
</item>
<item>
<title>Multiple Gmail accounts &amp; labels with Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</link>
<pubDate>Sat, 27 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</guid>
<description>Intro For quite some time, e-mail seemed like an anomaly in my workflow. I am a long time Gmail user, and my decade-old account has a somewhat formidable quantity of labels and filters. My messages are often assigned multiple labels, and I also like to keep only a bunch of messages in the inbox.
Although, in my opinion, Gmail web UI was and still is leagues ahead of many of its competitors and even allows keyboard-centric workflow, it&amp;rsquo;s awkward to use with a keyboard-driven browser, and for no money on Earth I would enable browser notifications.</description>
</item>
<item>
<title>Hello, world!</title>
<link>https://sqrtminusone.xyz/posts/hello-world/</link>
<pubDate>Mon, 01 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/hello-world/</guid>
<description>Hello, world! Eventually, there will be something interesting here. Or not.
Regradless, I&amp;rsquo;ll check if I can write some Python here
print(&amp;#34;Hello, world&amp;#34;) Hello, world </description>
</item>
</channel>
</rss>

1
sass/researcher.min.css vendored Normal file
View file

@ -0,0 +1 @@
#content a,.nav-link{color:#dc3545;text-decoration:none}#content a *,.nav-link *{color:#dc3545}#content a:hover,.nav-link:hover{color:#dc3545;text-decoration:underline}#footer a,.navbar-brand{color:#222;text-decoration:none}#footer a *,.navbar-brand *{color:#222}#footer a:hover,.navbar-brand:hover{color:#222;text-decoration:underline}#content table td,#content table th{border:1px solid #ccc;padding:6px 12px;text-align:left}*{color:#222;font-family:Inconsolata;line-height:1.2}.container{max-width:750px}.navbar-brand{font-size:2rem}#content p{margin-bottom:.6rem}#content h1,#content h2,#content h3,#content h4,#content h5,#content h6{font-size:medium;font-weight:700;margin:1rem 0 .6rem}#content h1{font-size:1.8rem}#content h2{font-size:1.6rem}#content h3{font-size:1.4rem}#content h4{font-size:1.2rem}#content img{display:block;margin:1rem auto;max-width:100%}#content .avatar>img{border-radius:50%;float:right;margin:-8px 0 0 16px;height:90px;width:90px}#content ol{counter-reset:list;list-style:none;padding-left:2rem}#content ol>li{display:table-row}#content ol>li:before{content:"[" counter(list,decimal)"] ";counter-increment:list;display:table-cell;text-align:right;padding-right:.5em}#content .container>ol,#content .footnotes>ol{padding-left:0}#content ul{list-style:inside;padding-left:2rem}#content ul>li{list-style-position:outside;margin-left:1em}#content .container>ul,#content .footnotes>ul{padding-left:0}#content table{margin:1rem auto;width:100%}#content table th{font-weight:700}#content table tr:nth-child(2n){background-color:#f8f8f8}#content blockquote{border-left:4px solid;font-style:italic;margin:1rem 0;padding:8px}#content code{color:#222;background-color:#f8f8f8;border:1px solid #ccc;border-radius:10%;padding:0 4px;font-family:inconsolata!important}#content pre code{all:unset;font-size:110%}#content .highlight{margin:1rem auto}#content .highlight>pre{padding:8px}

75
sitemap.xml Normal file
View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://sqrtminusone.xyz/posts/2022-02-12-literate/</loc>
<lastmod>2022-02-12T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/emacs/</loc>
<lastmod>2022-02-12T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/</loc>
<lastmod>2022-02-12T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/org-mode/</loc>
<lastmod>2022-02-12T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/</loc>
<lastmod>2022-02-12T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/</loc>
<lastmod>2022-02-12T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/exwm/</loc>
<lastmod>2022-01-03T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</loc>
<lastmod>2022-01-03T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</loc>
<lastmod>2021-10-06T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/i3wm/</loc>
<lastmod>2021-10-06T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/elfeed/</loc>
<lastmod>2021-09-08T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/emms/</loc>
<lastmod>2021-09-08T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/2021-09-07-emms/</loc>
<lastmod>2021-09-08T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/org/</loc>
<lastmod>2021-05-01T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</loc>
<lastmod>2021-05-01T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/tags/mail/</loc>
<lastmod>2021-02-27T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</loc>
<lastmod>2021-02-27T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/posts/hello-world/</loc>
<lastmod>2021-02-01T00:00:00+00:00</lastmod>
</url><url>
<loc>https://sqrtminusone.xyz/categories/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/console/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/desktop/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/emacs/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/guix/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/mail/</loc>
</url><url>
<loc>https://sqrtminusone.xyz/configs/readme/</loc>
</url>
</urlset>

BIN
stats/all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
stats/emacs-vim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
stats/literate-config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

83
tags/elfeed/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>elfeed</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/elfeed/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>elfeed</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2021-09-07-emms/">2021-09-08 | My EMMS and elfeed setup</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

22
tags/elfeed/index.xml Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>elfeed on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/elfeed/</link>
<description>Recent content in elfeed on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Wed, 08 Sep 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/elfeed/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>My EMMS and elfeed setup</title>
<link>https://sqrtminusone.xyz/posts/2021-09-07-emms/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-09-07-emms/</guid>
<description>Intro This is the current state of my quest to live in Emacs, at least in part of reading RSS and music.
Even before I lost my mind about customizing obscure keyboard-driven software, I tried Inoreader, self-hosted FreshRSS, and then newsboat from the RSS side and ncmpcpp+MPD from the audio player side. At some point, I got curious about whether I can do the same in Emacs.
The respective emacs packages, elfeed and EMMS, proved somewhat tricky to set up, i.</description>
</item>
</channel>
</rss>

93
tags/emacs/index.html Normal file
View file

@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>emacs</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/emacs/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>emacs</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2022-02-12-literate/">2022-02-12 | A few cases of literate configuration</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2022-01-03-exwm/">2022-01-03 | Using EXWM and perspective.el on multi-monitor setup</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/">2021-10-06 | Getting a consistent set of keybindings between i3 and Emacs</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-09-07-emms/">2021-09-08 | My EMMS and elfeed setup</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-05-01-org-python/">2021-05-01 | Replacing Jupyter Notebook with Org Mode</a></li>
<li><a href="https://sqrtminusone.xyz/posts/2021-02-27-gmail/">2021-02-27 | Multiple Gmail accounts &amp; labels with Emacs</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

73
tags/emacs/index.xml Normal file
View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>emacs on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/emacs/</link>
<description>Recent content in emacs on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 12 Feb 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/emacs/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>A few cases of literate configuration</title>
<link>https://sqrtminusone.xyz/posts/2022-02-12-literate/</link>
<pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-02-12-literate/</guid>
<description>A post that arose from the discussion of literate configuration on the System Crafters Discord.
I am using the literate configuration strategy (based on Emacs&amp;rsquo; Org Mode) to manage most of my configuration files. A piece of such a configuration can be as simple as an Org file, which is tangled to one or many plain-text configuration files, but it can be more.
In my opinion, a literate configuration can be more straightforward and concise than a &amp;ldquo;normal&amp;rdquo; one, thanks to Org Mode&amp;rsquo;s capabilities of working with source code.</description>
</item>
<item>
<title>Using EXWM and perspective.el on multi-monitor setup</title>
<link>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</link>
<pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</guid>
<description>I wrote about Emacs and i3 integration around two months ago. Shortly after however, I decided to give EXWM another try, mainly because my largest reservation - lack of performance - seems to have been resolved by updates to the native compilation since my first attempt. Or I may have lost some sensitivity to that issue. Regardless, the second dive into EXWM thus far feels successful, and I think it&amp;rsquo;s the right time to share some of my thoughts on the subject.</description>
</item>
<item>
<title>Getting a consistent set of keybindings between i3 and Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</link>
<pubDate>Wed, 06 Oct 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</guid>
<description>Intro One advantage of EXWM for an Emacs user is that EXWM gives one set of keybindings to manage both Emacs windows and X windows. In every other WM, like my preferred i3wm, two orthogonal keymaps seem to be necessary. But, as both programs are quite customizable, I want to see whether I can replicate at least some part of the EXWM goodness in i3.
But why not just use EXWM?</description>
</item>
<item>
<title>My EMMS and elfeed setup</title>
<link>https://sqrtminusone.xyz/posts/2021-09-07-emms/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-09-07-emms/</guid>
<description>Intro This is the current state of my quest to live in Emacs, at least in part of reading RSS and music.
Even before I lost my mind about customizing obscure keyboard-driven software, I tried Inoreader, self-hosted FreshRSS, and then newsboat from the RSS side and ncmpcpp+MPD from the audio player side. At some point, I got curious about whether I can do the same in Emacs.
The respective emacs packages, elfeed and EMMS, proved somewhat tricky to set up, i.</description>
</item>
<item>
<title>Replacing Jupyter Notebook with Org Mode</title>
<link>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</link>
<pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</guid>
<description>Why? Jupyter Notebook and its successor Jupyter Lab providing an interactive development environment for many programming languages are in lots of ways great pieces of software.
But while I was using the former, and then the latter, I was also an as-full-time-as-one-can-get NeoVim user. &amp;ldquo;As one can get&amp;rdquo; is because, of course, there is no sensible way to extend the NeoVim editing experience to the Jupyter ecosystem.
A possibility for change appeared with my discovery of Emacs not so long ago.</description>
</item>
<item>
<title>Multiple Gmail accounts &amp; labels with Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</link>
<pubDate>Sat, 27 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</guid>
<description>Intro For quite some time, e-mail seemed like an anomaly in my workflow. I am a long time Gmail user, and my decade-old account has a somewhat formidable quantity of labels and filters. My messages are often assigned multiple labels, and I also like to keep only a bunch of messages in the inbox.
Although, in my opinion, Gmail web UI was and still is leagues ahead of many of its competitors and even allows keyboard-centric workflow, it&amp;rsquo;s awkward to use with a keyboard-driven browser, and for no money on Earth I would enable browser notifications.</description>
</item>
</channel>
</rss>

83
tags/emms/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>emms</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/emms/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>emms</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2021-09-07-emms/">2021-09-08 | My EMMS and elfeed setup</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

22
tags/emms/index.xml Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>emms on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/emms/</link>
<description>Recent content in emms on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Wed, 08 Sep 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/emms/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>My EMMS and elfeed setup</title>
<link>https://sqrtminusone.xyz/posts/2021-09-07-emms/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-09-07-emms/</guid>
<description>Intro This is the current state of my quest to live in Emacs, at least in part of reading RSS and music.
Even before I lost my mind about customizing obscure keyboard-driven software, I tried Inoreader, self-hosted FreshRSS, and then newsboat from the RSS side and ncmpcpp+MPD from the audio player side. At some point, I got curious about whether I can do the same in Emacs.
The respective emacs packages, elfeed and EMMS, proved somewhat tricky to set up, i.</description>
</item>
</channel>
</rss>

83
tags/exwm/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>exwm</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/exwm/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>exwm</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2022-01-03-exwm/">2022-01-03 | Using EXWM and perspective.el on multi-monitor setup</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

20
tags/exwm/index.xml Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>exwm on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/exwm/</link>
<description>Recent content in exwm on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Mon, 03 Jan 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/exwm/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Using EXWM and perspective.el on multi-monitor setup</title>
<link>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</link>
<pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-01-03-exwm/</guid>
<description>I wrote about Emacs and i3 integration around two months ago. Shortly after however, I decided to give EXWM another try, mainly because my largest reservation - lack of performance - seems to have been resolved by updates to the native compilation since my first attempt. Or I may have lost some sensitivity to that issue. Regardless, the second dive into EXWM thus far feels successful, and I think it&amp;rsquo;s the right time to share some of my thoughts on the subject.</description>
</item>
</channel>
</rss>

83
tags/i3wm/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>i3wm</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/i3wm/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>i3wm</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/">2021-10-06 | Getting a consistent set of keybindings between i3 and Emacs</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

21
tags/i3wm/index.xml Normal file
View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>i3wm on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/i3wm/</link>
<description>Recent content in i3wm on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Wed, 06 Oct 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/i3wm/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Getting a consistent set of keybindings between i3 and Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</link>
<pubDate>Wed, 06 Oct 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/</guid>
<description>Intro One advantage of EXWM for an Emacs user is that EXWM gives one set of keybindings to manage both Emacs windows and X windows. In every other WM, like my preferred i3wm, two orthogonal keymaps seem to be necessary. But, as both programs are quite customizable, I want to see whether I can replicate at least some part of the EXWM goodness in i3.
But why not just use EXWM?</description>
</item>
</channel>
</rss>

97
tags/index.html Normal file
View file

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Tags</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>Tags</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/tags/emacs/">2022-02-12 | emacs</a></li>
<li><a href="https://sqrtminusone.xyz/tags/org-mode/">2022-02-12 | org-mode</a></li>
<li><a href="https://sqrtminusone.xyz/tags/exwm/">2022-01-03 | exwm</a></li>
<li><a href="https://sqrtminusone.xyz/tags/i3wm/">2021-10-06 | i3wm</a></li>
<li><a href="https://sqrtminusone.xyz/tags/elfeed/">2021-09-08 | elfeed</a></li>
<li><a href="https://sqrtminusone.xyz/tags/emms/">2021-09-08 | emms</a></li>
<li><a href="https://sqrtminusone.xyz/tags/org/">2021-05-01 | org</a></li>
<li><a href="https://sqrtminusone.xyz/tags/mail/">2021-02-27 | mail</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

83
tags/index.xml Normal file
View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Tags on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/</link>
<description>Recent content in Tags on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 12 Feb 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>emacs</title>
<link>https://sqrtminusone.xyz/tags/emacs/</link>
<pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/emacs/</guid>
<description></description>
</item>
<item>
<title>org-mode</title>
<link>https://sqrtminusone.xyz/tags/org-mode/</link>
<pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/org-mode/</guid>
<description></description>
</item>
<item>
<title>exwm</title>
<link>https://sqrtminusone.xyz/tags/exwm/</link>
<pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/exwm/</guid>
<description></description>
</item>
<item>
<title>i3wm</title>
<link>https://sqrtminusone.xyz/tags/i3wm/</link>
<pubDate>Wed, 06 Oct 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/i3wm/</guid>
<description></description>
</item>
<item>
<title>elfeed</title>
<link>https://sqrtminusone.xyz/tags/elfeed/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/elfeed/</guid>
<description></description>
</item>
<item>
<title>emms</title>
<link>https://sqrtminusone.xyz/tags/emms/</link>
<pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/emms/</guid>
<description></description>
</item>
<item>
<title>org</title>
<link>https://sqrtminusone.xyz/tags/org/</link>
<pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/org/</guid>
<description></description>
</item>
<item>
<title>mail</title>
<link>https://sqrtminusone.xyz/tags/mail/</link>
<pubDate>Sat, 27 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/tags/mail/</guid>
<description></description>
</item>
</channel>
</rss>

83
tags/mail/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>mail</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/mail/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>mail</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2021-02-27-gmail/">2021-02-27 | Multiple Gmail accounts &amp; labels with Emacs</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

21
tags/mail/index.xml Normal file
View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>mail on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/mail/</link>
<description>Recent content in mail on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 27 Feb 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/mail/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Multiple Gmail accounts &amp; labels with Emacs</title>
<link>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</link>
<pubDate>Sat, 27 Feb 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-02-27-gmail/</guid>
<description>Intro For quite some time, e-mail seemed like an anomaly in my workflow. I am a long time Gmail user, and my decade-old account has a somewhat formidable quantity of labels and filters. My messages are often assigned multiple labels, and I also like to keep only a bunch of messages in the inbox.
Although, in my opinion, Gmail web UI was and still is leagues ahead of many of its competitors and even allows keyboard-centric workflow, it&amp;rsquo;s awkward to use with a keyboard-driven browser, and for no money on Earth I would enable browser notifications.</description>
</item>
</channel>
</rss>

83
tags/org-mode/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>org-mode</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/org-mode/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>org-mode</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2022-02-12-literate/">2022-02-12 | A few cases of literate configuration</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

22
tags/org-mode/index.xml Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>org-mode on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/org-mode/</link>
<description>Recent content in org-mode on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 12 Feb 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/org-mode/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>A few cases of literate configuration</title>
<link>https://sqrtminusone.xyz/posts/2022-02-12-literate/</link>
<pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2022-02-12-literate/</guid>
<description>A post that arose from the discussion of literate configuration on the System Crafters Discord.
I am using the literate configuration strategy (based on Emacs&amp;rsquo; Org Mode) to manage most of my configuration files. A piece of such a configuration can be as simple as an Org file, which is tangled to one or many plain-text configuration files, but it can be more.
In my opinion, a literate configuration can be more straightforward and concise than a &amp;ldquo;normal&amp;rdquo; one, thanks to Org Mode&amp;rsquo;s capabilities of working with source code.</description>
</item>
</channel>
</rss>

83
tags/org/index.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang=""><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>org</title>
<meta name="description" content="Freedom is a state of mind">
<meta name="author" content='SqrtMinusOne'>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous">
<link rel="stylesheet" href="/sass/researcher.min.css">
<link rel="icon" type="image/ico" href="https://sqrtminusone.xyz/favicon.ico">
<link rel="alternate" type="application/rss+xml" href="https://sqrtminusone.xyz/tags/org/index.xml" title="SqrtMinusOne" />
</head>
<body><div class="container mt-5">
<nav class="navbar navbar-expand-sm flex-column flex-sm-row text-nowrap p-0">
<a class="navbar-brand mx-0 mr-sm-auto" href="https://sqrtminusone.xyz/" title="SqrtMinusOne">
SqrtMinusOne
</a>
<div class="navbar-nav flex-row flex-wrap justify-content-center">
<a class="nav-item nav-link" href="/" title="Index">
Index
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/posts/" title="Posts">
Posts
</a>
<span class="nav-item navbar-text mx-1">/</span>
<a class="nav-item nav-link" href="/configs/readme" title="Configs">
Configs
</a>
</div>
</nav>
</div>
<hr>
<div id="content">
<div class="container">
<h1>org</h1>
<ul>
<li><a href="https://sqrtminusone.xyz/posts/2021-05-01-org-python/">2021-05-01 | Replacing Jupyter Notebook with Org Mode</a></li>
</ul>
</div>
</div><div id="footer" class="mb-5">
<hr>
<div class="container text-center">
</div>
<div class="container text-center">
<a href="https://sqrtminusone.xyz/" title="Pavel Korytov, 2022"><small>Pavel Korytov, 2022</small></a>
</div>
</div>
</body>
</html>

22
tags/org/index.xml Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>org on SqrtMinusOne</title>
<link>https://sqrtminusone.xyz/tags/org/</link>
<description>Recent content in org on SqrtMinusOne</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Sat, 01 May 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://sqrtminusone.xyz/tags/org/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Replacing Jupyter Notebook with Org Mode</title>
<link>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</link>
<pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
<guid>https://sqrtminusone.xyz/posts/2021-05-01-org-python/</guid>
<description>Why? Jupyter Notebook and its successor Jupyter Lab providing an interactive development environment for many programming languages are in lots of ways great pieces of software.
But while I was using the former, and then the latter, I was also an as-full-time-as-one-can-get NeoVim user. &amp;ldquo;As one can get&amp;rdquo; is because, of course, there is no sensible way to extend the NeoVim editing experience to the Jupyter ecosystem.
A possibility for change appeared with my discovery of Emacs not so long ago.</description>
</item>
</channel>
</rss>