#+begin_src emacs-lisp :comments no
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
#+end_src
* Personal information
#+begin_src emacs-lisp
(setq user-full-name "Óscar Nájera"
      user-mail-address "hi@oscarnajera.com")
#+end_src
* Editor
** Theme
#+begin_src emacs-lisp
(setq doom-font (font-spec :family "DejaVu Sans Mono" :size 18 :weight 'semi-light))
(setq doom-theme 'doom-gruvbox)
(add-hook 'prog-mode-hook 'prettify-symbols-mode)

(defun on/prettify-symbols ()
  "Common added symbols."
  (mapc (lambda (p)
          (cl-pushnew p prettify-symbols-alist :test #'equal))
        '(("<=" . ?≤)
          (">=" . ?≥)
          ("->" . ?→)
          ("->>" . ?↠)
          ("=>" . ?⇒)
          ("/=" . ?≠)   ("!=" . ?≠)
          ("::" . ?∷)
          ("lambda" . ?λ))))

(add-hook 'prog-mode-hook 'on/prettify-symbols)
#+end_src
** Frame title includes visited file
#+BEGIN_SRC emacs-lisp
  ;;http://emacs-fu.blogspot.fr/2011/01/setting-frame-title.html
  (setq frame-title-format
    '("" invocation-name ": "(:eval (if (buffer-file-name)
                (abbreviate-file-name (buffer-file-name))
                 "%b"))))
#+END_SRC
** Package Manager
Doom manages packages separately. Keep that file separate
#+begin_src emacs-lisp :tangle "packages.el"
;; -*- no-byte-compile: t; -*-
;;; $DOOMDIR/packages.el
#+end_src
** SSH agent
#+begin_src emacs-lisp
(setenv "SSH_AUTH_SOCK" (concat (getenv "XDG_RUNTIME_DIR")"/gnupg/S.gpg-agent.ssh"))
#+end_src
** Auth mechanisms
#+begin_src emacs-lisp :tangle "packages.el"
(package! pass)
(package! password-store)
(package! password-store-otp)
#+end_src

#+begin_src emacs-lisp
(use-package! pass
  :init
  (require 'auth-source)
  (auth-source-pass-enable)
  (defun on/fetch-password (&rest params)
    (if-let* ((match (car (apply #'auth-source-search params)))
              (secret (plist-get match :secret)))
        (values
         (if (functionp secret)
             (funcall secret)
           secret)
         match)
      (user-error "Password not found for %S" params))))
#+end_src
** Keybindings
#+begin_src emacs-lisp
;; (map! :leader
;;       :desc "M-x"                   "x"    #'execute-extended-command
;;       )
(after! evil
  (setq evil-default-state 'emacs)
  (set-evil-initial-state! '(prog-mode text-mode fundamental-mode conf-mode pass-view-mode) 'normal))
#+end_src
** Avy
This allows me to jump to buffer positions using my home row ordering
#+begin_src emacs-lisp
(after! avy
  (setq avy-all-windows t
        avy-all-windows-alt nil)
  (setq avy-keys '(?r ?t ?i ?e ?a ?o ?n ?s)))
#+end_src
** Ace window
#+begin_src emacs-lisp
(after! ace-window
  (setq aw-keys '(?r ?t ?i ?e ?a ?o ?n ?s)))
#+end_src
** Which-key
Because I always need help and it should come up quickly
#+begin_src emacs-lisp
(setq which-key-idle-delay 0.1)
#+end_src
** Smartparens
#+begin_src emacs-lisp
(after! smartparens
  (map!
   :map smartparens-mode-map
   :nvie "C-)" #'sp-forward-slurp-sexp
   :nvie "C-}" #'sp-forward-barf-sexp
   :nvie "C-(" #'sp-backward-slurp-sexp
   :nvie "C-{" #'sp-backward-barf-sexp
   :nie "M-s" #'sp-split-sexp
   :nie "M-j" #'sp-join-sexp)
  (map! :map (emacs-lisp-mode-map scheme-mode-map lisp-mode-map clojure-mode-map)
        :nv ")" #'sp-next-sexp
        :nv "(" #'sp-backward-sexp))
#+end_src
** View emacs structures
#+begin_src emacs-lisp
(defun on/display-obj (obj)
  "Display in new buffer the given OBJ pretty printed."
  (with-current-buffer (get-buffer-create "*Lisp Display*")
    (erase-buffer)
    (pp obj (current-buffer))
    (emacs-lisp-mode)
    (display-buffer (current-buffer))))
#+end_src
#+begin_src emacs-lisp
 (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p)
#+end_src
** AI queries
#+begin_src emacs-lisp
(use-package! ai-query
  :load-path "~/dev/dotfiles/elisp/"
  :commands (ai-query-request))
#+end_src

** Track Deliveries
#+begin_src emacs-lisp
(use-package! delivery-track
  :load-path "~/dev/dotfiles/elisp/"
  :commands (delivery-track-update-dhl))
#+end_src
** Fixes
#+begin_src emacs-lisp
(after! vtable
  (defun vtable-header-line-sort (e)
    "Sort a vtable from the header line."
    (interactive "e")
    (let* ((pos (event-start e))
           (obj (posn-object pos)))
      (with-current-buffer (window-buffer (posn-window pos))
        (vtable-beginning-of-table) ;; This line is the fix
        (vtable-goto-column
         (get-text-property (if obj (cdr obj) (posn-point pos))
                            'vtable-column
                            (car obj)))
        (vtable-sort-by-current-column)))))
#+end_src
* Dictionary
#+begin_src emacs-lisp :tangle "packages.el"
(package! lexic)
#+end_src
Shamelessly copied from https://tecosaur.github.io/emacs-config/#dictionary
#+begin_src emacs-lisp
(use-package! lexic
  :commands lexic-search lexic-list-dictionary)
#+end_src
#+begin_src emacs-lisp
(defadvice! +lookup/dictionary-definition-lexic (identifier &optional arg)
  "Look up the definition of the word at point (or selection) using `lexic-search'."
  :override #'+lookup/dictionary-definition
  (interactive
   (list (or (doom-thing-at-point-or-region 'word)
             (read-string "Look up in dictionary: "))
         current-prefix-arg))
  (lexic-search identifier nil nil t))
#+end_src
** Shavian transliterate
#+begin_src emacs-lisp
(defun on/shavian-transliterate (start end)
  "Convert text from START to END to Shavian in new buffer."
  (interactive "r")
  (let ((url-request-method "POST")
        (url-request-extra-headers
         '(("Content-Type" . "application/x-www-form-urlencoded")))
        (url-request-data
         (thread-last
           (buffer-substring-no-properties start end)
           (url-hexify-string)
           (format "text=%s"))))

    (url-retrieve
     "http://localhost:4242/shaw"
     (lambda (status)
       (set-buffer-multibyte t)
       ;; (decode-coding-region (point) (point-max) 'utf-8)
       (pop-to-buffer (current-buffer))))))
#+end_src
* Orgmode
#+begin_src emacs-lisp
(after! org
  (setq org-tags-column (- fill-column))
  (setcdr (assoc "j" org-capture-templates)
          '("Journal" entry (file+olp+datetree +org-capture-journal-file)
            "* %(format-time-string \"%H:%M\") %?\n%a\n%i"
            :clock-in t :clock-resume t))

  (add-to-list 'org-capture-templates
               `("e" "Event" entry (file ,(expand-file-name
                                           "caldav.org" org-directory))
                 "* %?\n%^T\n%i\n%a"))
  (add-to-list 'org-capture-templates
               `("m" "NBG-TM Contact" entry
                 (file+headline ,(expand-file-name "toastmasters/NBG-membership.org" org-directory) "Guest")
                 "* INTRS [[notmuch-search:%:reply-to][%:fromname]]
:PROPERTIES:
:EMAIL: %:reply-to
:END:
Invited: %^t
"))

  (setq org-roam-capture-templates '(("d" "default" plain "%i\n%?"
                                      :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
                                                         "#+title: ${title}")
                                      :unnarrowed t))
        org-roam-capture-ref-templates org-roam-capture-templates)

  ;; This is a pass through. Need it to export on my block html code loads
  (defun org-babel-execute:html (body _params)
    "Execute BODY of HTML code.
This function is called by `org-babel-execute-src-block'."
    body))

(use-package! semantic-search
  :after org
  :load-path "~/dev/dotfiles/scratch/semgrep"
  :commands (semantic-search)
  :config
  (setq  semantic-server-url "http://localhost:8080"))
#+end_src
** Alerts
This is to set the reminders of calendar events.
Using appt is quite fine. I get a remainder just above the minibuffer
There is no loud sound or anything disturbing, just the appearance of
this new window.
#+BEGIN_SRC emacs-lisp
(after! org-agenda
  (setq
   appt-display-mode-line t   ;; show in the modeline
   appt-display-format 'echo) ;; use our func
  (run-at-time "5 minutes" (* 3600 2) 'org-agenda-to-appt)
  (appt-activate 1)             ;; active appt (appointment notification)
  (display-time)                ;; time display is required for this...

  ;; update appt each time agenda opened
  (add-hook 'org-agenda-finalize-hook 'org-agenda-to-appt))
#+END_SRC
** Calendar sync
#+begin_src emacs-lisp
(use-package! cal-sync
  :load-path "~/dev/org-caldav"
  :commands (cal-sync-push cal-sync-import-file cal-sync-delete on/calendar-fetch)
  :init (map! :leader
              (:prefix-map "mc"
               "p" #'cal-sync-push
               "f" #'cal-sync-import-file))
  :config
  (defun on/calendar-fetch ()
    (interactive)
    (call-process "org_agenda_sync"
                  nil (list :file (expand-file-name "~/org/caldav.org")) nil
                  "--format" "org"))

  (setq org-icalendar-include-todo 'unblocked)
  (setq cal-sync-connection
        (cal-sync-calendar-create
         "https://cloud.oscarnajera.com/remote.php/dav/calendars/oscar/personal-1/"
         "oscar" "~/org/caldav.org")
        org-icalendar-date-time-format ":%Y%m%dT%H%M%SZ"))
#+end_src
** References
#+begin_src emacs-lisp :tangle "packages.el"
(package! helm-bibtex)
(package! org-ref)
#+end_src
#+begin_src emacs-lisp
(use-package! helm-bibtex
  :commands helm-bibtex
  :after org
  :init
  (setq bibtex-completion-bibliography (expand-file-name "biblio.bib" org-directory)
        bibtex-completion-pdf-field "file"
        bibtex-completion-library-path (expand-file-name "bibtex-pdf/" org-directory)
        bibtex-completion-notes-path (expand-file-name "roam/" org-directory)
        bibtex-completion-notes-template-multiple-files "#+TITLE: ${title} (${year})\n#+author: ${author-or-editor}\n")
  :config
  (defun bibtex-completion-open-notes-and-pdf (keys)
    (bibtex-completion-open-pdf keys)
    (bibtex-completion-edit-notes keys))

  (helm-bibtex-helmify-action bibtex-completion-open-notes-and-pdf helm-bibtex-open-notes-and-pdf)
                                        ;(helm-delete-action-from-source "Edit notes with PDF" helm-source-bibtex)
  (helm-add-action-to-source "Edit notes with PDF" 'helm-bibtex-open-notes-and-pdf helm-source-bibtex 1))

(use-package! org-ref
  :after helm-bibtex)
#+end_src
** Letters
#+BEGIN_SRC emacs-lisp
(use-package ox-latex
  :ensure org
  :after org
  :config
  ;; APS journals
  (add-to-list 'org-latex-classes '("revtex4-1"
        "\\documentclass{revtex4-1}
        [PACKAGES]
        [EXTRA]"
        ("\\section{%s}" . "\\section*{%s}")
        ("\\subsection{%s}" . "\\subsection*{%s}")
        ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
        ("\\paragraph{%s}" . "\\paragraph*{%s}")
        ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

  (eval-after-load 'ox
    '(require 'ox-koma-letter))

  (add-to-list 'org-latex-packages-alist '("AUTO" "babel" nil))
)
#+END_SRC
** Feeds
#+begin_src emacs-lisp
(after! elfeed
  (setq elfeed-feeds
        '("http://feeds.feedburner.com/MeltingAsphalt"
          "http://feeds.feedburner.com/TroyHunt"
          "http://kitchingroup.cheme.cmu.edu/blog/feed/index.xml"
          "http://www.howardism.org/index.xml"
          "https://www.reddit.com/r/Common_Lisp/.rss"
          "https://ag91.github.io/rss.xml"
          "https://architectelevator.com/feed.xml"
          "https://bitcoinops.org/feed.xml"
          "https://cyberwardog.blogspot.com/feeds/posts/default"
          "https://daryl.wakatara.com/rss.xml"
          "https://guix.gnu.org/feeds/blog.atom"
          "https://hboeck.de/rss.xml"
          "https://jackrusher.com/feed.xml"
          "https://nullprogram.com/feed/"
          "https://oscarnajera.com/post/index.xml"
          "https://paulstamatiou.com/posts.xml"
          "https://planet.archlinux.org/atom.xml"
          "https://reyify.com/api/rss"
          "https://sachachua.com/blog/feed"
          "https://serokell.io/blog.rss.xml"
          "https://stevelosh.com/rss.xml"
          "https://www.archlinux.org/feeds/news/"
          "https://www.n16f.net/blog/index.xml"
          "https://www.schneier.com/feed/atom"
          "https://www.tweag.io/rss.xml")))
#+end_src
* Email
** Notmuch
#+begin_src emacs-lisp :tangle "packages.el"
(package! notmuch :pin "c769658360e10a6d01a4134e680e2f498741bc5c")
(package! org-mime :pin "cc00afcf0291633324364c1c83bfe2833cfdc1bf")
(package! ol-notmuch :pin "2704345ac8c4558c7c787d2ea0a4777607789a62")
#+end_src
#+BEGIN_SRC emacs-lisp
(use-package! notmuch
  :init (after! org (add-to-list 'org-modules 'ol-notmuch))
  (map! :leader
        (:prefix-map ("o" . "open")
         :desc "notmuch" "m" #'notmuch))
  :commands notmuch
  :bind (:map notmuch-hello-mode-map
         ("l" . notmuch-jump-search)
         ("/" . notmuch-tree)
         :map notmuch-search-mode-map
         ("f" . notmuch-search-filter-by-tag)
         ("/" . notmuch-tree)
         ("d" . notmuch-search-delete-thread)
         ("D" . notmuch-search-delete-all)
         :map notmuch-show-mode-map
         ("d" . notmuch-show-delete-message)
         ("D" . notmuch-show-delete-thread-then-exit)
         ("i" . notmuch-show-tag-spam)
         ("cb" . on/notmuch-spam-test)
         ("cr" . org-store-link)
         )
  :config
  (set-company-backend! 'notmuch-message-mode
    'notmuch-company '(company-ispell company-yasnippet))
  (setq-default notmuch-search-oldest-first nil)
  (setq message-directory "~/.mail/"
        message-auto-save-directory "/tmp/"
        org-email-link-description-format "%c: %s")

  (setq notmuch-saved-searches
        '((:name "inbox" :query "tag:inbox" :key "i" :sort-order newest-first)
          (:name "Isar-Speak" :query "(tag:Isar-Speak or tag:lists/isar-speak-officers) and tag:unread" :key "t")
          (:name "flagged" :query "tag:flagged" :key "f")
          (:name "sent"    :query "tag:sent"    :key "s")
          (:name "unread"  :query "tag:unread and not tag:fail2ban and not tag:CheckMK-Alerts" :key "u")
          (:name "drafts"  :query "tag:draft" :key "d")))

  (setq notmuch-show-all-tags-list t
        notmuch-hello-tag-list-make-query "tag:unread and not tag:killed")

  (setq notmuch-fcc-dirs '((".*@oscarnajera.com" . "hi_pers/Sent")
                           ;;(".*@gmail.com" . "\"gmail/[Gmail]/.Sent Mail\"")
                           ))

  (setq notmuch-crypto-process-mime t)
  (setq message-kill-buffer-on-exit t)

  (setq notmuch-archive-tags '("-inbox" "-unread"))

  (setq notmuch-search-line-faces
        `(("deleted" . (:strike-through "red"))
          ("flagged" . notmuch-search-flagged-face)
          ("unread" . notmuch-search-unread-face)))

  (add-hook! 'doom-real-buffer-functions
    (defun notmuch-interesting-buffer (b)
      "Whether the current buffer's major-mode is a notmuch mode."
      (with-current-buffer b
        (memq major-mode '(notmuch-show-mode
                           notmuch-search-mode
                           notmuch-tree-mode
                           notmuch-hello-mode
                           notmuch-message-mode)))))

  (defun notmuch-toggle-tag (tag tags-present)
    (concat (if (member tag tags-present) "-" "+") tag))

  (defun notmuch-show-delete-message ()
    "Delete current message if already marked as deleted undo."
    (interactive)
    (notmuch-show-tag-message (notmuch-toggle-tag "deleted" (notmuch-show-get-tags))))

  (defun on/bogofilter (&rest args)
    "Call bogofilter on current notmuch-show-file with ARGS."
    (let ((msg-file (notmuch-show-get-filename)))
      (with-temp-buffer
        (apply #'call-process "bogofilter" msg-file t nil args)
        (message (string-trim (buffer-string))))))

  (defun on/notmuch-spam-test ()
    "Evaluate spaminess of message."
    (interactive)
    (on/bogofilter "-v"))

  (defun on/bogofilter-set-spam (spam-p)
    "Set spam or ham depending according to SPAM-P."
    (on/bogofilter "-v" (if spam-p "-s" "-n")))

  (defun notmuch-show-tag-spam ()
    "Tag spam on current message if already marked as spam undo."
    (interactive)
    (let ((tag (notmuch-toggle-tag "spam" (notmuch-show-get-tags))))
      (notmuch-show-tag-message tag)
      (on/bogofilter-set-spam (string-prefix-p "+" tag))))

  (defun notmuch-show-delete-thread-then-exit ()
    "Delete all messages in the current buffer, then exit back to search results."
    (interactive)
    (notmuch-show-tag-all '("+deleted"))
    (notmuch-show-next-thread))

  (defun notmuch-search-delete-thread()
    "Delete all messages in the current thread or undelete"
    (interactive)
    (notmuch-search-tag
     (list (notmuch-toggle-tag "deleted" (notmuch-search-get-tags)))))

  (defun notmuch-search-delete-all()
    "Delete all messages in the current buffer"
    (interactive)
    (notmuch-search-tag-all
     '("+deleted" "-inbox" "-unread")))

  (define-key notmuch-tree-mode-map "d"
    (lambda ()
      "delete message"
      (interactive)
      (notmuch-tree-tag
       (list (notmuch-toggle-tag "deleted" (notmuch-tree-get-tags)))))))

(use-package! org-mime
  :after (org notmuch)
  :config (setq org-mime-library 'mml))

(require 'comint)
(require 'ansi-color)

(defun on/registered-mail-accounts ()
  "Read mbsync config to extract IMAPAccounts."
  (with-temp-buffer
    (insert-file-contents (expand-file-name "~/.mbsyncrc"))
    (cl-loop while (search-forward-regexp (rx bol "IMAPAccount " (group (+ any)) eol) nil t)
             collect (match-string 1))))


(define-derived-mode mail-sync-log-mode comint-mode "mail-sync-log"
  "Major mode for reading mail sync."
  ;; code for syntax highlighting
  (setq font-lock-defaults `(((,(rx bol "[" (1+ (or letter space)) "]") . font-lock-warning-face)
                              (,(rx bol " [mv]") . font-lock-constant-face)
                              (,(rx bol " [rm]") . font-lock-keyword-face)))))

(defun on/fetch-all-email (mailbox)
  "Start tagmail fetch on a subprocess for MAILBOX."
  (interactive (list (completing-read
                      "Which mailbox? "
                      (on/registered-mail-accounts)
                      nil nil nil nil "all")))
  (with-current-buffer (get-buffer-create "*E-mail fetch*")
    (pop-to-buffer (current-buffer))
    (mail-sync-log-mode)
    (make-process
     :name "E-mail fetch"
     :buffer (current-buffer)
     :command (thread-last
                (if (string= mailbox "all") nil (list mailbox))
                (cons "tagmail"))
     :filter #'comint-output-filter)))
#+END_SRC
** Sending email
#+begin_src emacs-lisp
(after! notmuch
  (setq send-mail-function 'smtpmail-send-it
        message-send-mail-function 'message-smtpmail-send-it
        smtpmail-local-domain "oscarnajera.com")

  (defun set-smtp-server ()
    (-let* (((user host)
             (thread-first
               (message-field-value "from" )
               (mail-extract-address-components)
               (cadr)
               (string-split "@")))
            (smtp-servers '("oscarnajera.com" "mail.oscarnajera.com"
                            "gmail.com" "smtp.googlemail.com"
                            "googlemail.com" "smtp.googlemail.com"
                            "byteplant.com" "smtp.byteplant.com"))
            ((&plist :host :user :port :secret)
             (car
              (auth-source-search :host
                                  (or (plist-get smtp-servers host #'string=)
                                      "mail.oscarnajera.com")
                                  :user user
                                  :max 1))))
      (setq smtpmail-smtp-server host
            smtpmail-smtp-service (string-to-number port)
            smtpmail-stream-type 'starttls
            smtpmail-smtp-user user)
      (message "SMTP server changed to %s %s %s" host port user)))

  (add-hook 'message-send-mail-hook 'set-smtp-server))
#+end_src

** Contacts
#+begin_src emacs-lisp :tangle "packages.el"
(package! khardel)
#+end_src
#+begin_src emacs-lisp
(use-package! khardel
  :config
  (after! tree-sitter-langs
    (add-to-list 'tree-sitter-major-mode-language-alist '(khardel-edit-mode . yaml)))
  (add-hook 'khardel-edit-mode-hook
            (lambda ()
              (tree-sitter-mode)
              (setq imenu-create-index-function #'on/imenu-nested-path-elements))))
#+end_src
* IRC
#+begin_src emacs-lisp
(after! circe
  (set-irc-server! "irc.libera.chat"
    '(:tls t
      :port 6697
      :nick "titan-c"
      :sasl-username "titan-c"
      :sasl-password (lambda (&rest _) (on/fetch-password :user "titan-c" :host "freenode"))
      :channels ("#emacs" "#guix"))))
#+end_src

* Ledger
Emacs mode for managing ledger text files
#+BEGIN_SRC emacs-lisp
(after! ledger-mode
  (setq ledger-default-date-format ledger-iso-date-format)

  (defun on/ledger-link-invoice ()
    "Attach an invoice file to this posting."
    (interactive)
    (ledger-navigate-beginning-of-xact)
    (end-of-line)
    (when-let* ((date  (ledger-xact-date))
                (payee (thread-last
                         (ledger-xact-payee)
                         (string-trim)
                         (replace-regexp-in-string (rx (+ (or space punctuation))) "_")))
                (src-file (read-file-name "Attach: "))
                (file-name (concat "Empresa_DE/" date "_" payee "." (file-name-extension src-file))))
      (insert "\n    ; Invoice: " file-name)
      (rename-file src-file (expand-file-name file-name)))))
#+end_src
** CSV
#+begin_src emacs-lisp :tangle "packages.el"
(package! parse-csv)
#+end_src
** Crypto
#+begin_src emacs-lisp
(use-package! cardano-tx
  :commands (cardano-tx-new cardano-tx-cli-tip)
  :load-path "~/dev/cardano/emacs-wallet"
  :init
  (defun on/cardano-set-network ()
    (interactive)
    (let* ((networks '((:mainnet "/run/cardano-node-mainnet/socket" "http://localhost:8090" "--mainnet")
                       (:preview "/run/cardano-node-preview/socket" "http://localhost:8091" "--testnet-magic" "2")))
           (pick
            (alist-get
             (completing-read "Pick network" networks)
             networks nil nil #'string=)))
      (setq cardano-tx-cli-node-socket (expand-file-name (car pick))
            cardano-wallet-url (cadr pick)
            cardano-tx-cli-network-args (cddr pick))))
  :config
  (add-hook! 'doom-real-buffer-functions
    (defun cardano-interesting-buffer (b)
      "Whether the current buffer's major-mode is a cardano mode."
      (with-current-buffer b
        (memq major-mode '(cardano-tx-db-addresses-mode
                           cardano-tx-db-files-mode
                           cardano-tx-mode
                           cardano-wallet-tx-log-mode)))))
  (setq cardano-tx-log-level 'debug)
  ;; (setq cardano-cli-command (expand-file-name "~/dev/cardano/cardano-node/cli"))
  (setq cardano-tx-cli-command "/usr/bin/cardano-cli")
  (setq cardano-tx-db-keyring-dir "~/dev/cardano/emacs-wallet/keys/demo"))

(use-package! cardano-wallet
  :commands (cardano-wallet-balances)
  :load-path "~/dev/cardano/emacs-wallet")
#+end_src

#+RESULTS:

* Books
#+begin_src emacs-lisp :tangle "packages.el"
(package! nov)
(package! calibredb)
#+end_src
#+begin_src emacs-lisp
(use-package! nov
  :mode (("\\.epub\\'" . nov-mode)))
(use-package! calibredb
  :commands calibredb
  :config
  (setq calibredb-root-dir "/run/media/titan/ext_backup/personal/Libros/CalibreManaged")
  (setq calibredb-db-dir (expand-file-name "metadata.db" calibredb-root-dir)))
#+end_src
* Programming languages
** YAML
#+begin_src emacs-lisp :tangle "packages.el"
(package! yaml)
#+end_src
#+begin_src emacs-lisp
(use-package! libyaml
  :commands yaml-read-file
  :load-path "~/dev/emacs-lisp/emacs-libyaml/")

(use-package! yaml)

(after! yaml-mode
  ;; Inspired by this blog post to use tree sitter to create a imenu list.
  ;; https://blog.meain.io/2022/navigating-config-files-using-tree-sitter/
  (defun on/ts-elements ()
    "Tree sitter matches for all entries in data buffer."
    (let ((query (thread-last
                   (cond
                    ((derived-mode-p 'json-mode)
                     "(object (pair (string (string_content) @key) (_)) @item)")
                    ((derived-mode-p 'yaml-mode)
                     "(block_mapping_pair (flow_node) @key (_)) @item"))
                   (tsc-make-query tree-sitter-language)))
          (root-node (tsc-root-node tree-sitter-tree)))
      (tsc-query-matches query root-node #'tsc--buffer-substring-no-properties)))

  (defun on/pos-items (ts-match)
    "From a TS-MATCH get element key name and region in buffer."
    (pcase-let ((`[(_ . ,item) (_ . ,key)] (cdr ts-match)))
      (list
       (tsc-node-text key)
       (tsc-node-byte-range item)
       ;; (destructuring-bind (s . e) (tsc-node-byte-range key)
       ;;   (cons (byte-to-position s) (byte-to-position e)))
       )))

  (defun on/nested-item-names (items)
    "From named ITEMS figure out the nested hierarchy for names."
    (let ((node-stack '(("" 0)))
          result)
      (dolist (item items (nreverse result))
        (pcase-let ((`(,item-name (,item-start . ,item-end)) item)
                    (`((_ ,parent-end)) node-stack))
          (when (< parent-end item-end)
            (setq node-stack (seq-filter (lambda (y)
                                           (<= item-end (cadr y)))
                                         node-stack)))
          (push (list item-name item-end) node-stack)
          (push (list (nreverse (mapcar #'car node-stack))
                      item-start)
                result)))))

  (defun on/imenu-nested-path-elements ()
    "On a yaml or json buffer get the element positions for imenu."
    (thread-last
      (seq-map #'on/pos-items (on/ts-elements))
      (on/nested-item-names)
      (seq-map (lambda (item)
                 (seq-let (name-chain start) item
                   (cons (string-join name-chain ".") (byte-to-position start)))))))

  (remove-hook 'yaml-mode-hook 'yaml-set-imenu-generic-expression) ;; don't use default one
  (add-hook 'yaml-mode-hook
            (lambda ()
              (setq imenu-create-index-function #'on/imenu-nested-path-elements)))

  (ert-deftest on/tree-sitter-imenu ()
    "Test the imenu generation."
    (with-temp-buffer
      (yaml-mode)
      (tree-sitter-mode)
      (insert "one: Ñaño
skills:
  run: 3
  jump:
    high: 7
    low: 2

")
      (should
       (equal '(("one" . 1) ("skills" . 13) ("skills.run" . 23) ("skills.jump" . 32) ("skills.jump.high" . 43) ("skills.jump.low" . 55))
              (on/imenu-nested-path-elements)))
      ))
  )
#+end_src

** Databases
#+begin_src emacs-lisp
(after! format-all
  (define-format-all-formatter sqlformat
    (:executable "pg_format")
    (:install)
    (:modes sql-mode)
    (:format
     (format-all--buffer-easy
      executable
      "-s2" "-g" "-U2"
      "-"))))
#+end_src
** WEB
#+begin_src emacs-lisp :tangle "packages.el"
(package! impatient-mode)
#+end_src
#+begin_src emacs-lisp
(after! simple-httpd
  (setq httpd-port 8095))

(use-package! impatient-mode
  :commands impatient-mode)

(use-package! trident-mode
  :commands trident-mode
  :load-path "~/dev/emacs-lisp/trident-mode.el/"
  :bind (:map trident-mode-map
              ("C-M-x" . trident-eval-dwim)
              ("C-c C-e" . trident-expand-dwim)))
#+end_src
** Haskell
#+begin_src emacs-lisp
(after! haskell-mode
  (setq-hook! 'haskell-mode-hook +format-with :none)
  (setq haskell-stylish-on-save t
        lsp-haskell-formatting-provider "stylish-haskell"))
#+end_src
** Clojure
#+begin_src emacs-lisp
(setq-hook! 'clojure-mode-hook +format-with :none)
#+end_src
** Common Lisp
#+begin_src emacs-lisp
(after! sly
  ;; for parenscript
  (put 'for-in 'sly-common-lisp-indent-function '(as dolist))
  (put 'for-of 'sly-common-lisp-indent-function '(as dolist))
  (put 'dom-el 'sly-common-lisp-indent-function '(4 &body))
  (setq sly-lisp-implementations
        '((sbcl-large ("sbcl" "--dynamic-space-size" "2048"))
          (sbcl ("sbcl")))))
#+end_src
** C
#+begin_src emacs-lisp
(after! lsp-clangd
  (setq lsp-clients-clangd-args
        '("-j=3"
          "--background-index"
          "--clang-tidy"
          "--completion-style=detailed"
          "--header-insertion=never"
          "--header-insertion-decorators=0"))
  (set-lsp-priority! 'clangd 2))
#+end_src

* Video Editing
#+begin_src emacs-lisp :tangle "packages.el"
(package! subed
  :recipe (:host github :repo "sachac/subed" :files ("subed/*.el")))
#+end_src

#+begin_src emacs-lisp
(use-package! subed)
#+end_src

* Admin
#+begin_src emacs-lisp :tangle "packages.el"
(package! guix)
#+end_src
#+begin_src emacs-lisp
(use-package! guix)
#+end_src
#+begin_src emacs-lisp :tangle "packages.el"
(package! json-rpc)
#+end_src
#+begin_src emacs-lisp
(use-package! btc-explorer
  :load-path "~/dev/emacs-lisp/btc-explorer/"
  :commands (on/bitcoin-rpc-connect)
  :config
  (use-package json-rpc)
  (defun on/bitcoin-rpc-connect ()
    (interactive)
    (let* ((networks '(mainnet 8332
                       mainnet-r 8335
                       testnet 18332
                       testnet-r 18335
                       regtest 18443
                       ))
           (pick (completing-read "Pick network: " networks))
           (host (thread-first pick (split-string "-") car))
           (port (plist-get networks pick #'string-equal))
           (user "rpc-emacs"))
      (when (and btc-explorer-bitcoind
                 (json-rpc-live-p btc-explorer-bitcoind))
        (json-rpc-close btc-explorer-bitcoind))
      (setq btc-explorer-bitcoind
            (cl-multiple-value-bind
                (secret match) (on/fetch-password :host host :user user)
              (json-rpc-connect
               "localhost" port (plist-get match :user) secret))))))

(use-package! lnd
  :load-path "~/dev/emacs-lisp/btc-explorer/"
  :commands (lnd-pick-node)
  :config
  (setq lnd-nodes '(("local" "https://localhost:8480/v1/"
                     "/run/media/titan/ext_backup/personal/bitcoin/lnd/data/chain/bitcoin/testnet/admin.macaroon")
                    ("reg-ali" "https://localhost:8001/v1/"
                     "/scratch/titan/lnd/reg-ali/data/chain/bitcoin/regtest/admin.macaroon")
                    ("reg-carl" "https://localhost:8002/v1/"
                     "/scratch/titan/lnd/reg-carl/data/chain/bitcoin/regtest/admin.macaroon")
                    ("reg-bob" "https://localhost:8003/v1/"
                     "/scratch/titan/lnd/reg-bob/data/chain/bitcoin/regtest/admin.macaroon")
                    ("remote" "https://localhost:8481/v1/"
                     "~/dev/emacs-lisp/btc-explorer/admin.macaroon"))))

#+end_src

#+begin_src emacs-lisp
(use-package! shepherd
  :load-path "~/dev/dotfiles/elisp/"
  :commands (shepherd))
#+end_src

#+begin_src emacs-lisp
(use-package! mpc)
#+end_src

#+begin_src emacs-lisp
(use-package! journalctl
  :load-path "~/dev/dotfiles/elisp"
  :commands (journalctl))
#+end_src
#+begin_src emacs-lisp
(setq netstat-program-options '("-aptu"))
#+end_src