]> shtarkov.net Git - shtarkov.net.git/commitdiff
Site skeleton
authorChristian Shtarkov <ops+git@shtarkov.net>
Sun, 20 Oct 2024 16:53:20 +0000 (17:53 +0100)
committerChristian Shtarkov <ops+git@shtarkov.net>
Sun, 20 Oct 2024 19:06:03 +0000 (20:06 +0100)
LICENSE [new file with mode: 0644]
assets/cat/cat1.jpg [new file with mode: 0644]
assets/cat/cat2.jpg [new file with mode: 0644]
assets/cat/cat3.jpg [new file with mode: 0644]
assets/cat/cat4.jpg [new file with mode: 0644]
assets/cat/cat5.jpg [new file with mode: 0644]
assets/emacs/transpose-regions.webm [new file with mode: 0644]
assets/main.css [moved from assets/one.css with 56% similarity]
one.org [deleted file]
onerc.el [new file with mode: 0644]
shtarkov.net.org [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..67aa147
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,5 @@
+Except where otherwise noted, site content is licensed under the Creative Commons Attribution 4.0 International license:
+https://creativecommons.org/licenses/by/4.0/
+
+All else is licensed under the GNU General Public License, either version 3 of the License, or (at your option) any later version:
+https://www.gnu.org/licenses/gpl-3.0.en.html#license-text
diff --git a/assets/cat/cat1.jpg b/assets/cat/cat1.jpg
new file mode 100644 (file)
index 0000000..502c7f8
Binary files /dev/null and b/assets/cat/cat1.jpg differ
diff --git a/assets/cat/cat2.jpg b/assets/cat/cat2.jpg
new file mode 100644 (file)
index 0000000..af77c25
Binary files /dev/null and b/assets/cat/cat2.jpg differ
diff --git a/assets/cat/cat3.jpg b/assets/cat/cat3.jpg
new file mode 100644 (file)
index 0000000..78e5395
Binary files /dev/null and b/assets/cat/cat3.jpg differ
diff --git a/assets/cat/cat4.jpg b/assets/cat/cat4.jpg
new file mode 100644 (file)
index 0000000..5c22c66
Binary files /dev/null and b/assets/cat/cat4.jpg differ
diff --git a/assets/cat/cat5.jpg b/assets/cat/cat5.jpg
new file mode 100644 (file)
index 0000000..1c8d267
Binary files /dev/null and b/assets/cat/cat5.jpg differ
diff --git a/assets/emacs/transpose-regions.webm b/assets/emacs/transpose-regions.webm
new file mode 100644 (file)
index 0000000..6004fd3
Binary files /dev/null and b/assets/emacs/transpose-regions.webm differ
similarity index 56%
rename from assets/one.css
rename to assets/main.css
index b99d34f6c5e857bff9938eb4f19051a3bf52e216..1d2264f965517ca5171ff19a76a3aac68e9cf6ee 100644 (file)
@@ -1,4 +1,5 @@
-@import url('https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400&family=Noto+Sans:wght@400;700&display=swap');
+@import url('https://fonts.googleapis.com/css?family=Cantarell');
+@import url('https://fonts.googleapis.com/css?family=Fira Mono');
 
 html, body, p, ol, ul, li, dl, dt, dd,
 blockquote, figure, fieldset, legend, textarea,
@@ -52,7 +53,7 @@ body {
   background: #151515;
   color: #dedede;
   font-family: "Noto Sans",sans-serif;
-       font-size: 106%;
+  font-size: 106%;
   line-height: 1.5;
   word-wrap: break-word;
 }
@@ -95,33 +96,43 @@ a:visited {
 
 img {
   display: block;
+  width: 80%;
   margin-left: auto;
   margin-right: auto;
   border: 1px solid;
 }
 
+img[src="/cat.png"] {
+  width: auto;
+}
+
+video {
+  width: 100%;
+  border: 1px solid;
+}
+
 /* ------- '.one' classes used by 'one-ox' org backend ------- */
 
 .one-hl {
-       font-family: 'Fira Mono', monospace;
-  font-size: 80%;
-  border-radius: 6px;
+  font-family: "Fira Mono", monospace;
+  font-size: 90%;
 }
 
 .one-hl-inline {
-       background: #31424a;
+  background: #31424a;
   padding: 0.2em 0.4em;
   margin: 0;
   white-space: break-spaces;
 }
 
 .one-hl-block {
-  background: #161f22;
+  background: #000000;
   color: #c5c5c5;
   display: block;
   overflow: auto;
   padding: 16px;
   line-height: 1.45;
+  border: 1px solid;
 }
 
 .one-blockquote {
@@ -133,6 +144,7 @@ img {
 }
 
 .one-blockquote > p:last-child {
+  text-align: right;
   margin-bottom: 0;
 }
 
@@ -187,17 +199,17 @@ img {
   background: #31424a;
 }
 
-/* -------- specific to the default render functions -------- */
+/* -------- render functions -------- */
 
 .header {
   color: #ffffff;
+  font-family: "Cantarell", sans-serif;
   font-size: 2em;
   font-weight: bold;
   padding: 0 16px 0 16px;
   background: #151515;
   width: 100%;
   height: 3.5rem;
-  position: fixed;
   top: 0;
   left: 0;
   border-bottom: 1px solid #1d272b;
@@ -217,9 +229,8 @@ img {
 }
 
 .content {
-  margin: 3.5rem auto;
-  padding-top: 1.8rem;
-  max-width: 740px;
+  margin: 0 auto;
+  max-width: 852px;
   padding: 0 16px;
 }
 
@@ -228,20 +239,8 @@ img {
   padding: 1.8rem 0;
 }
 
-.title-empty {
-  padding-top: 1rem;
-}
-
-/* -------- one-default-home -------- */
-
-#home {
-  margin: 5rem 0 1.5rem 0;
-}
-
-/* -------- one-default-home-list-pages -------- */
-
 #home-list-pages {
-  margin: 5rem 0 1.5rem 0;
+  margin: 0 0 1.5rem 0;
 }
 
 #pages ul {
@@ -264,217 +263,28 @@ img {
   color: #ffffff;
 }
 
-/* -------- one-default, one-default-with-toc, one-default-with-sidebar, one-default-doc -------- */
-
-.nav {
-  border-top: 1px solid #c5c5c5;
-  margin-top: 3em;
-  padding: 2em 0;
+.article-header {
   display: flex;
-  justify-content: center;
-  gap: 0.5em;
-  font-weight: bold;
+  justify-content: space-between;
 }
 
-.nav a {
-  display: block;
-  background: #dedede;
-  border-radius: 6px;
-  padding: 0.2em 0.8em;
-  color: #151515;
-  width: 20%;
-  text-align: center;
+.article-header #category {
 }
 
-@media (max-width:600px) {
-  .nav a {
-    width: auto;
-  }
+.article-header #date {
 }
 
-/* -------- one-default-with-toc, one-default-doc -------- */
-
-.toc {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 1.8rem;
-  color: #d1d1d1;
-}
-
-.toc > div {
-  padding: 0 1em;
+.category {
 }
 
-.toc a {
-  color: #d1d1d1;
-}
-
-.toc > div > div:first-child {
-  text-decoration: underline 1px;
+#footer {
+  margin: 3rem 0;
   text-align: center;
-  font-size: 1.2em;
-  margin-bottom: 16px;
-}
-
-/* --------- one-default-with-sidebar, one-default-doc --------- */
-
-#sidebar-header {
-  color: #ffffff;
-  font-size: 2em;
-  font-weight: bold;
-  padding: 0 16px 0 16px;
-  background: #151515;
-  width: 100%;
-  height: 3.5rem;
-  position: fixed;
-  top: 0;
-  left: 0;
-  border-bottom: 1px solid #1d272b;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-
-#sidebar-header > a {
-  color: inherit;
-  cursor: pointer;
-  text-decoration: none;
-}
-
-#sidebar-header > a:visited {
-  color: inherit;
-}
-
-#hamburger {
-  cursor: pointer;
-  height: 1em;
-  fill: #dedede;
-  display: none;
-  font-weight: normal;
-  margin-right: 0.3em;
-}
-
-#sidebar-content {
-  margin: 3.5rem auto;
-  display: flex;
-  margin-left: auto;
-  margin-right: auto;
-  max-width: 1140px;
-  width: 100%;
-  padding: 1em 16px;
-}
-
-#sidebar {
-  border-right: 2px solid #31424a;
-  top: 4.5rem;
-  position: sticky;
-  padding-top: 2.2em;
-  padding-bottom: 6em;
-  width: 250px;
-  max-height: 100vh;
-  overflow-y: auto;
-}
-
-#sidebar a {
-  display: block;
-  color: #dedede;
-}
-
-#sidebar a:hover {
-  text-decoration: none;
-}
-
-#sidebar ul {
-  list-style: none;
-  padding:0;
-}
-
-#sidebar li {
-  padding: 0.5em 0.6em;
-}
-
-#sidebar li:hover {
-  background: #31424a;
-}
-
-article {
-  padding: 0 1.5em;
-  max-width: 640px;
-  width: 100%;
-}
-
-#sidebar-left {
-  width: 0;
-  height: 100%;
-  position: fixed;
-  z-index: 3;
-  top: 0;
-  left: 0;
-  transition: 0.25s;
-       background: #2c444f;
-  overflow: hidden; /* to make the children disappear when width is 0 */
-  overflow-y: auto;
-}
-
-#sidebar-left > div:first-child {
-  height: 3.5rem;
-  font-size: 2em;
-  font-weight: bold;
-  border-bottom: 1px solid #b8b8b8;
-  padding-left: 16px;
-  margin-bottom: 16px;
-  display: flex;
-  align-items: center;
+  font-size: 70%;
 }
 
-#sidebar-left > ul {
-  padding: 0 16px 0 16px;
-}
-
-#sidebar-left > ul ul {
-  padding-left: 0.8em;
-  margin-left: 3px;
-  border-left: 1px solid #b8b8b8;
-}
-
-#sidebar-left a {
-  color: #dedede;
-  text-decoration: none;
-}
-
-#sidebar-left li {
-  padding: 0.5em 0;
-  list-style-type: none;
-}
-
-#sidebar-main {
-  display: none;
-  top: 0;
-  right: 0;
-  width: 100%;
-  height: 100%;
-  position: fixed;
-  background: #080808;
-  opacity: 0.80;
-  z-index: 2;
-}
-
-@media (max-width: 840px) {
-  #hamburger {
-    display: block;
-  }
-  #sidebar {
-    display: none;
-  }
-  #sidebar-content {
-    justify-content: center;
-  }
-  #sidebar-header {
-    justify-content: left;
-  }
-  article {
-    padding: 0;
-  }
+#footer p {
+  margin-bottom: 0;
 }
 
 /* Local Variables: */
diff --git a/one.org b/one.org
deleted file mode 100644 (file)
index 9b8c4c2..0000000
--- a/one.org
+++ /dev/null
@@ -1,219 +0,0 @@
-* shtarkov.net
-:PROPERTIES:
-:ONE: one-default-home-list-pages
-:CUSTOM_ID: /
-:END:
-
-[[./assets/cat.png][Cat]]
-
-* List all website's pages on the home page
-:PROPERTIES:
-:ONE: one-default-home-list-pages
-:CUSTOM_ID: /blog/default-home-list-pages/
-:END:
-
-This page is rendered with the default render function
-~one-default-home-list-pages~ specified in ~ONE~ org property which lists
-below all the pages on the website.  You can use it instead of
-~one-default-home~ for your home page.
-
-* The default page
-:PROPERTIES:
-:ONE: one-default
-:CUSTOM_ID: /blog/default/
-:END:
-
-This page is rendered with the default render function ~one-default~
-specified in ~ONE~ org property.
-
-** Do you want a table of content?
-
-As you can see, ~one-default~ doesn't add a table of content (TOC).  If
-you want a default render function that adds the TOC to the page you can
-use the render function ~one-default-with-toc~ presented in [[#/blog/one-default-with-toc/][The default
-page with a TOC]].
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│   └── one.css
-├── one.org
-└── public
-    ├── blog
-    │   ├── default
-    │   │   └── index.html
-    │   ├── default-home-list-pages
-    │   │   └── index.html
-    │   ├── one-default-doc
-    │   │   └── index.html
-    │   ├── one-default-with-sidebar
-    │   │   └── index.html
-    │   └── one-default-with-toc
-    │       └── index.html
-    ├── index.html
-    └── one.css
-
-8 directories, 9 files
-#+end_example
-
-* The default page with a TOC
-:PROPERTIES:
-:ONE: one-default-with-toc
-:CUSTOM_ID: /blog/one-default-with-toc/
-:END:
-
-This page is rendered with the render function ~one-default-with-toc~
-specified in the org property ~ONE~.
-
-** Do you want a sidebar?
-
-Perhaps you want a sidebar listing all the pages on your website, as
-many modern documentation sites do.  If so, you can use the default
-render function ~one-default-with-sidebar~ presented in [[#/blog/one-default-with-sidebar/][The default page
-with a sidebar]].
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│   └── one.css
-├── one.org
-└── public
-    ├── blog
-    │   ├── default
-    │   │   └── index.html
-    │   ├── default-home-list-pages
-    │   │   └── index.html
-    │   ├── one-default-doc
-    │   │   └── index.html
-    │   ├── one-default-with-sidebar
-    │   │   └── index.html
-    │   └── one-default-with-toc
-    │       └── index.html
-    ├── index.html
-    └── one.css
-
-8 directories, 9 files
-#+end_example
-
-* The default page with a sidebar
-:PROPERTIES:
-:ONE: one-default-with-sidebar
-:CUSTOM_ID: /blog/one-default-with-sidebar/
-:END:
-
-This page is rendered with the render function ~one-default-with-sidebar~
-specified in the org property ~ONE~.
-
-** Do you want a sidebar and a TOC?
-
-Perhaps you want a sidebar listing all the pages on your website and a
-table of content, as many modern documentation sites do.  If so, you
-can use the default render function ~one-default-doc~ presented in [[#/blog/one-default-doc/][The
-default page with TOC and sidebar]].
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│   └── one.css
-├── one.org
-└── public
-    ├── blog
-    │   ├── default
-    │   │   └── index.html
-    │   ├── default-home-list-pages
-    │   │   └── index.html
-    │   ├── one-default-doc
-    │   │   └── index.html
-    │   ├── one-default-with-sidebar
-    │   │   └── index.html
-    │   └── one-default-with-toc
-    │       └── index.html
-    ├── index.html
-    └── one.css
-
-8 directories, 9 files
-#+end_example
-
-* The default page with TOC and sidebar
-:PROPERTIES:
-:ONE: one-default-doc
-:CUSTOM_ID: /blog/one-default-doc/
-:END:
-
-This page is rendered with the function ~one-default-doc~ specified
-in the org property ~ONE~.
-
-** Do you want to know more about one.el?
-
-Check the documentation at https://one.tonyaldon.com.
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│   └── one.css
-├── one.org
-└── public
-    ├── blog
-    │   ├── default
-    │   │   └── index.html
-    │   ├── default-home-list-pages
-    │   │   └── index.html
-    │   ├── one-default-doc
-    │   │   └── index.html
-    │   ├── one-default-with-sidebar
-    │   │   └── index.html
-    │   └── one-default-with-toc
-    │       └── index.html
-    ├── index.html
-    └── one.css
-
-8 directories, 9 files
-#+end_example
diff --git a/onerc.el b/onerc.el
new file mode 100644 (file)
index 0000000..20f13cd
--- /dev/null
+++ b/onerc.el
@@ -0,0 +1,171 @@
+;; -*- lexical-binding: t; -*-
+(org-export-define-backend 'shtarkov.net-ox
+  '((headline . one-ox-headline)
+    (section . one-ox-section)
+    (paragraph . one-ox-paragraph)
+
+    (plain-text . one-ox-plain-text)
+
+    (bold . one-ox-bold)
+    (italic . one-ox-italic)
+    (strike-through . one-ox-strike-through)
+    (underline . one-ox-underline)
+    (code . one-ox-code)
+    (verbatim . one-ox-verbatim)
+
+    (subscript . one-ox-no-subscript)
+    (superscript . one-ox-no-superscript)
+
+    (plain-list . one-ox-plain-list)
+    (item . one-ox-item)
+
+    (src-block . one-ox-src-block)
+    (example-block . one-ox-example-block)
+    (fixed-width . one-ox-fixed-width)
+    (quote-block . one-ox-quote-block)
+
+    (link . shtarkov.net-link)))
+
+(defun shtarkov.net-head (website-name)
+  `(:head
+    (:meta (@ :charset "UTF-8"))
+    (:meta (@ :name "viewport" :content "width=device-width,initial-scale=1"))
+    (:link (@ :rel "stylesheet" :type "text/css" :href "/main.css"))
+    (:title ,website-name)))
+
+(setq shtarkov.net-footer
+      '(:div/footer
+        (:p
+         "© 2024 Christian Shtarkov. Except where otherwise noted, content on this site is licensed under the "
+         (:a (@ :href "https://creativecommons.org/licenses/by/4.0/") "Creative Commons Attribution 4.0 International license")
+         ".")
+        (:p
+         "Built with " (:a (@ :href "https://one.tonyaldon.com") "one.el")
+         " (" (:a (@ :href "/source/") "source") ")")))
+
+(defun shtarkov.net-category (custom-id)
+  (cadr (string-split custom-id "/")))
+
+(defun shtarkov.net-home (page-tree pages _global)
+  (let* ((website-name (one-default-website-name pages))
+         (content (org-export-data-with-backend
+                   (org-element-contents page-tree)
+                   'shtarkov.net-ox nil))
+         (pages-list (one-default-pages pages "^/[^/]+/$")))
+    (jack-html
+     "<!DOCTYPE html>"
+     `(:html
+       (@ :lang "en")
+       ,(shtarkov.net-head website-name)
+       (:body
+        (:div.header (:a (@ :href "/") ,website-name))
+        (:div.content
+         (:div/home-list-pages ,content)
+         (:div/pages (:ul ,(reverse pages-list))))
+        ,shtarkov.net-footer)))))
+
+(defun shtarkov.net-articles (page-tree pages _global)
+  (let* ((website-name (one-default-website-name pages))
+         (title (org-element-property :raw-value page-tree))
+         (content (org-export-data-with-backend
+                   (org-element-contents page-tree)
+                   'shtarkov.net-ox nil))
+         (pages-list
+          (one-default-pages
+           pages
+           (format "^%s[^$]" (org-element-property :CUSTOM_ID page-tree)))))
+    (jack-html
+     "<!DOCTYPE html>"
+     `(:html
+       (@ :lang "en")
+       ,(shtarkov.net-head website-name)
+       (:body
+        (:div.header (:a (@ :href "/") ,website-name))
+        (:div.content
+         (:div.title (:h1 ,title))
+         (:div/home-list-pages ,content)
+         (:div/pages (:ul ,(reverse pages-list)))))
+       ,shtarkov.net-footer))))
+
+(defun shtarkov.net-default (page-tree pages _global)
+  (let* ((website-name (one-default-website-name pages))
+         (title (org-element-property :raw-value page-tree))
+         (content (org-export-data-with-backend
+                   (org-element-contents page-tree)
+                   'shtarkov.net-ox nil)))
+    (jack-html
+     "<!DOCTYPE html>"
+     `(:html
+       (@ :lang "en")
+       ,(shtarkov.net-head website-name)
+       (:body
+        (:div.header (:a (@ :href "/") ,website-name))
+        (:div.content
+         (:div.title (:h1 ,title))
+         ,content))
+       ,shtarkov.net-footer))))
+
+(defun shtarkov.net-article (page-tree pages _global)
+  (let* ((website-name (one-default-website-name pages))
+         (title (org-element-property :raw-value page-tree))
+         (category
+          (shtarkov.net-category (org-element-property :CUSTOM_ID page-tree)))
+         (date (org-element-property :DATE page-tree))
+         (content (org-export-data-with-backend
+                   (org-element-contents page-tree)
+                   'shtarkov.net-ox nil)))
+    (jack-html
+     "<!DOCTYPE html>"
+     `(:html
+       (@ :lang "en")
+       ,(shtarkov.net-head website-name)
+       (:body
+        (:div.header (:a (@ :href "/") ,website-name))
+        (:div.content
+         (:div.title (:h1 ,title))
+         (:div.article-header
+          (:a/category (@ :href ,(format "/%s/" category)) "#" ,category)
+          (:p/date ,date))
+         ,content))
+       ,shtarkov.net-footer))))
+
+(defun shtarkov.net-link (link desc info)
+  "Transcode a LINK object from Org to HTML.
+DESC is the description part of the link, or the empty string.
+INFO is a plist holding contextual information."
+  (let* ((type (org-element-property :type link))
+         (path (org-element-property :path link))
+         (raw-link (org-element-property :raw-link link))
+         (custom-type-link
+          (let ((export-func (org-link-get-parameter type :export)))
+            (and (functionp export-func)
+                 (funcall export-func path desc 'shtarkov.net-ox info))))
+         (href (cond
+                ((string= type "custom-id") path)
+                ((string= type "fuzzy")
+                 (let ((beg (org-element-property :begin link)))
+                   (signal 'one-link-broken
+                           `(,raw-link
+                             "fuzzy links not supported"
+                             ,(format "goto-char: %s" beg)))))
+                ((string= type "file")
+                 (or
+                  ;; ./assets/images/image-1.png --> /images/image-1.png
+                  ;; ./public/blog/page-1.md     --> /blog/page-1.md
+                  (and (string-match "\\`\\./\\(assets\\|public\\)" path)
+                       (replace-match "" nil nil path))
+                  (let ((beg (org-element-property :begin link)))
+                    (signal 'one-link-broken
+                            `(,raw-link ,(format "goto-char: %s" beg))))))
+                (t raw-link))))
+    (or custom-type-link
+        (and
+         (string-match one-ox-link-image-extensions path)
+         (format "<p><img src=\"%s\" alt=\"%s\" /></p>"
+                 href (or (org-string-nw-p desc) href)) )
+        (and
+         (string-match "\\.webm$" path)
+         (format "<video controls><source src=\"%s\" alt=\"%s\" type=\"video/webm\"></video>"
+                 href (or (org-string-nw-p desc) href)))
+        (format "<a href=\"%s\">%s</a>"
+                href (or (org-string-nw-p desc) href)))))
diff --git a/shtarkov.net.org b/shtarkov.net.org
new file mode 100644 (file)
index 0000000..ddc018b
--- /dev/null
@@ -0,0 +1,87 @@
+* (shtarkov)
+:properties:
+:one: shtarkov.net-home
+:custom_id: /
+:end:
+
+[[file+emacs:./assets/cat.png][Cat]]
+
+* Emacs
+:properties:
+:one: shtarkov.net-articles
+:custom_id: /emacs/
+:end:
+
+* Cat
+:properties:
+:one: shtarkov.net-default
+:custom_id: /cat/
+:end:
+
+#+begin_quote
+A good movie is precisely the one in which the camera disappears, when
+we are no longer aware of watching a movie, while an avant-garde film
+tries to turn the camera to itself and show the hidden device. A
+jocular way of understanding this would be to compare the reaction of
+a human and that of a cat when we point to an object with the finger;
+while the human looks beyond the finger trying to discover the object
+to which the finger points, the cat stares at the finger; in this
+sense, the cat is avant-garde, it is more interested in the medium
+(the finger, the camera) than the object pointed.
+
+Julio Cabrera
+#+end_quote
+
+[[file+emacs:./assets/cat/cat1.jpg]]
+[[file+emacs:./assets/cat/cat2.jpg]]
+[[file+emacs:./assets/cat/cat3.jpg]]
+[[file+emacs:./assets/cat/cat4.jpg]]
+[[file+emacs:./assets/cat/cat5.jpg]]
+
+* Transpose Regions
+:properties:
+:one: shtarkov.net-article
+:custom_id: /emacs/transpose-regions/
+:date: <2024-10-20 Sun>
+:end:
+
+Often times I find myself indecisive about how to lay out function
+arguments.
+
+#+begin_src c++
+dump(repository, file);
+#+end_src
+
+Which one should go first? It's easy enough to reorder them with
+=C-M-t (transpose-sexps)= by placing point between the two.
+
+But, until now, I never had a good solution for the function
+declaration:
+
+#+begin_src c++
+void dump(repository::Repository &repository, const std::filesystem::path &file);
+#+end_src
+
+~transpose-sexps~ does not produce the desired result:
+
+#+begin_src c++
+void dump(repository::Repository &const, repository std::filesystem::path &file);
+#+end_src
+
+A Web search reveals that the usual approaches are defining Elisp
+commands in advance, installing packages or mucking about with the
+kill ring. Same goes for Vim!
+
+Well, there's also ~transpose-regions~, unbound by default. Let's see
+how that can work:
+
+[[./assets/emacs/transpose-regions.webm][Transpose Regions Demo]]
+
+Did you see it? The trick is to save the mark 3 times — at the
+beginning and end of the first region, and at the beginning of the
+second region. Then leave point at the end of the second region and do
+~transpose-regions~, which I've bound to =C-c t=. Isearch comes in
+handy here, because it saves the mark where the search starts. I've
+modified it to [[https://endlessparentheses.com/leave-the-cursor-at-start-of-match-after-isearch.html][leave point at the start of the match]], which makes it
+even more convenient. If you find Isearch too cumbersome you can use
+something like [[https://www.emacswiki.org/emacs/iy-go-to-char.el][iy-go-to-char.el]] or [[#/emacs/do-not-zap-up-to-char][do-not-zap-up-to-char]].