Skip to content

Latest commit

 

History

History
672 lines (570 loc) · 18.5 KB

autoload.org

File metadata and controls

672 lines (570 loc) · 18.5 KB

Utils

;; -*- lexical-binding: t -*-

Required Package

Packages and utilities needed for this config.

(require 'dash)
(require 's)
(require 'f)
(require 'noflet)

Macros

Template Literals

(template "2 = <<(+ 1 1)>>")
;;;###autoload
(defmacro template (text)
  "Template literals"
  (let ((pattern "<<\\(.*?\\)>>"))
    ;; The regexp matches anything between delimiters, non-greedily
    (with-temp-buffer
      (save-excursion (insert text))
      (let ((matches '()))
        (while (re-search-forward pattern nil t)
          (push (match-string 1) matches)
          (replace-match "%s" t t))
        `(format ,(buffer-string) ,@(reverse (mapcar 'read matches)))))))

;;;###autoload
(defalias 't! 'template)

Ignore Arguments

;;;###autoload
(defmacro ignore-args (fnc)
  "Return function that ignores its arguments and invokes FNC."
  `(lambda (&rest _rest)
     (funcall ,fnc)))

;;;###autoload
(defalias'ignore-args)

General Utils

Async Command No Window

Prevent async-shell-command displaying a popup or a buffer.

(defun async-shell-command-no-window (command)
  "Execute async command without showing the result buffer."
  (interactive)
  (let ((display-buffer-alist (list (cons "\\*Async Shell Command\\*.*" (cons #'display-buffer-no-window nil)))))
    (async-shell-command command)))

Shell command to list

(defun shell-command-to-list (cmd)
  "Split output from shell-command to list"
  (split-string (shell-command-to-string cmd) "\n" t))

Start Process with Nohup

I don’t want some processes to exit, when I close emacs.

;;;###autoload
(defun +my/start-inferior-process-shell-command (command)
  (let ((name (format "*nohup: %s*" command)))
    (start-process-shell-command name nil (format "nohup %s" command))))

;;;###autoload
(defun +my/start-inferior-process (name buffer program &rest program-args)
  (let ((nohup-name (lambda (&optional text)
                      (if (not (null text))
                          (format "*nohup: %s*" text)
                        nil)))
        (args (-some->> program-args
                (s-join " "))))
    (start-process
     (funcall nohup-name name)
     (funcall nohup-name buffer)
     shell-file-name
     shell-command-switch
     (template "nohup 1>/dev/null 2>/dev/null <<program>> <<args>>"))))

;;;###autoload
(defun +my/nohup-shell-command (cmd &optional &rest args)
  "Launch a shell command, without opening a message buffer.
The proram persists when emacs is closed."
  (let ((args-str (or (-some->> args
                        (s-join " "))
                      "")))
    (call-process-shell-command
       (template "nohup 1>/dev/null 2>/dev/null <<cmd>> <<args-str>> &") nil nil)))

Open Link with MPV

;;;###autoload
(defun +mpv/play-external-url (url)
  (-when-let* ((quality-val
                (-some->> (completing-read
                            "Max height resolution (0 for unlimited): "
                            '("720" "0" "480" "1080"))
                  (string-to-number)))
               (quality-arg (if (> 0 quality-val)
                                (template "--ytdl-format=\"[height<=?<<quality-val>>]\"")
                              "")))
    (message (template "Opening <<url>> at <<quality-val>> with mpv…"))
    (+my/nohup-shell-command "mpv" quality-arg (s-wrap url "\""))))

Utils

General

Noop

;;;###autoload
(defun noop (&optional args) nil)

Kill and Message

;;;###autoload
(defun +my/kill-and-message (x)
  "Executes kill-new but with a message log side effect."
  (kill-new x)
  (message "Copied to clipboard: %s" x))

Without undo

;;;###autoload
(defmacro without-undo (&rest forms)
  "Executes FORMS with a temporary buffer-undo-list that is discarded afterwards.
Taken from http://www.emacswiki.org/emacs/UndoCommands with some
modifications."
  `(let* ((buffer-undo-list)
          (modified (buffer-modified-p))
          (inhibit-read-only t))
     (unwind-protect
         (progn ,@forms)
       (set-buffer-modified-p modified)) ()))

Convert bool to enabled/disabled string

(defun +my/bool-to-enabled-string (x)
  "Convert bool X to string for messaging.
t   -> \"Enabled\")
nil -> \"Disabled\""
  (if x "Enabled" "Disabled"))

Variable t/nil toggle message

(defun +my/bool-state-message (x)
  "Log message if a bool is enabled or not"
  (interactive)
  (message (t! "<<(symbol-name x)>>: <<(+my/bool-to-enabled-string (symbol-value x))>>")))

Dash Extensions

-tap

Run a side effect fn on the initial input x. But Return the original input x.

;;;###autoload
(defun -tap (fn x)
  "Function docstring"
  (funcall fn x)
  x)

;;;###autoload
(defmacro --tap (fn it)
  "Anaphoric form of `-tap'."
  `(-tap (lambda (it) ,fn) ,it))

-log

Log the current input without breaking the pipe.

;;;###autoload
(defun -log (x)
  "Function docstring"
  (--tap (message "%s" it) x))

-when

;;;###autoload
(defun -when (pred fn x)
  "When FN equals t forward X."
  (if pred
      (funcall fn x)
    x))

;;;###autoload
(defmacro --when (pred form xs)
  "Anaphoric form of -id-when"
  (declare (debug (form form)))
  `(let ((it ,xs))
     (if ,pred
         ,form
       ,xs)))

-id-when

;;;###autoload
(defun -id-when (fn x)
  "When FN equals t forward X."
  (when (funcall fn x) x))

;;;###autoload
(defmacro --id-when (form xs)
  "Anaphoric form of -id-when"
  (declare (debug (form form)))
  `(let ((it ,xs))
     (when ,form ,xs)))

-append

;;;###autoload
(defun -append (elem list)
  "Append ELEM to the end of list.

This is like -snoc but it takes the ELEM as the first argument for easier composition"
  (-snoc list elem))

-shuffle

(defun swap-list-items (LIST el1 el2)
  "in LIST swap indices EL1 and EL2 in place"
  (let ((tmp (elt LIST el1)))
    (setf (elt LIST el1) (elt LIST el2))
    (setf (elt LIST el2) tmp)))

;;;###autoload
(defun -shuffle (LIST)
  "Shuffle the elements in LIST.
shuffling is done in place."
  (loop for i in (reverse (number-sequence 1 (1- (length LIST))))
        do (let ((j (random (+ i 1))))
             (swap-list-items LIST i j)))
  LIST)

-f-join

;;;###autoload
(defun -f-join (x path)
  "Reversed argument order for f-join"
  (f-join path x))

-f-tildify

;;;###autoload
(defun f-tildify (path)
  "Replace the HOME directory in path"
  (s-replace-regexp (t! "^<<(getenv \"HOME\")>>") "~" path))

String

String Match

;;;###autoload
(defun s-match-or (regex x)
  "Return match groups or original"
  (interactive)
  (-if-let ((match (s-match regex x)))
      (cdr match)
    (list x)))

;;;###autoload
(defun s-match-or-1 (regex x)
  "Return 1st match group or original."
  (interactive)
  (-if-let ((match (s-match regex x)))
      (car (cdr match))
    x))

Buffer

Check if buffer has line

(defun +my/buffer-contains-line (string)
  (save-excursion
    (save-match-data
      (goto-char (point-min))
      (search-forward string nil t))))

Get the line Indent

;;;###autoload
(defun +my/line-indent ()
  "Get the indent of the current line."
  (interactive)
  (or (-some->> (substring-no-properties (thing-at-point 'line))
        (s-match "^\\(\s*\\).*\n$")
        (nth 1)
        (length))
      0))

Check current line for regex

;;;###autoload
(defun +my/buffer-line-has (regexp)
  "Check for REGEXP at current line."
  (save-excursion
    (goto-char (point-at-bol))
    (search-forward-regexp regexp (point-at-eol) t)))

Delete Current Line

;;;###autoload
(defun +my/delete-current-line ()
  "Delete (not kill) the current line."
  (interactive)
  (save-excursion
    (delete-region
     (progn (forward-visible-line 0) (point))
     (progn (forward-visible-line 1) (point)))))

Map Lines

;;;###autoload
(defun +my/map-lines (fun &optional start end)
  "Map lines in buffer with FUN, fn gets called with the line contents."
  (let ((start (or start (point-min)))
        (end (or end (point-max)))
        (lines (list)))
    (save-excursion
      (goto-char start)
      (while (< (point) end)
        (add-to-list 'lines
          (funcall fun (buffer-substring (line-beginning-position) (line-end-position))))
        (forward-line 1))
      (erase-buffer)
      (->> lines
           reverse
           (s-join "\n")
           insert))))

Files

File Timestamp

;;;###autoload
(defun +file/timestamp (path)
  (->> (file-attributes path)
       (nth 5)))

Get the latest file in directory

Get the last created file in a directory.

;;;###autoload
(defun +file/latest-file-in-dir (path)
  (->> (f-entries path)
       (-sort (lambda (a b) (not (time-less-p (+file/timestamp a)
                                              (+file/timestamp b)))))
       (car)))

Chmod this file

;;;###autoload
(defun +file|chmod-this-file ()
  "Chmod +x the current file."
  (interactive)
  (shell-command (template "chmod +x \"<<(buffer-file-name)>>\"")))

Has hidden entry

(defun f-has-hidden-entry (dir)
  "Check if a DIR has any hidden entries.
Return the first found file when one is found."
  (--find (s-starts-with-p "." (f-filename it)) (f-entries dir)))

Window management

Toggle split style

;;;###autoload
(defun +my|toggle-window-split-direction ()
  "Toggle current window split between horizontal and vertical."
  (interactive)
  (if (= (count-windows) 2)
      (let* ((this-win-buffer (window-buffer))
             (next-win-buffer (window-buffer (next-window)))
             (this-win-edges (window-edges (selected-window)))
             (next-win-edges (window-edges (next-window)))
             (this-win-2nd (not (and (<= (car this-win-edges)
                                         (car next-win-edges))
                                     (<= (cadr this-win-edges)
                                         (cadr next-win-edges)))))
             (splitter
              (if (= (car this-win-edges)
                     (car (window-edges (next-window))))
                  'split-window-horizontally
                'split-window-vertically)))
        (delete-other-windows)
        (let ((first-win (selected-window)))
          (funcall splitter)
          (if this-win-2nd (other-window 1))
          (set-window-buffer (selected-window) this-win-buffer)
          (set-window-buffer (next-window) next-win-buffer)
          (select-window first-win)
          (if this-win-2nd (other-window 1))))))

Toggle dedicated

Lock a window so the buffer can’t be changed or it cant be deleted.

;;;###autoload
(defun +my|toggle-window-dedicated ()
  "Control whether or not Emacs is allowed to display another
buffer in current window."
  (interactive)
  (let* ((window (get-buffer-window (current-buffer)))
         (is-locked (window-dedicated-p window))
         (txt (if is-locked "Window unlocked" "Window locked")))
    (set-window-dedicated-p window (not is-locked))
    (message (template "<<(current-buffer)>>: <<txt>>!"))))

UI

(defun +ui/get-display-dpi (&optional display)
  "Get the DPI of DISPLAY.
DISPLAY is a display name, frame or terminal, as in
`display-monitor-attributes-list'."
  (cl-flet ((pyth (lambda (w h)
                    (sqrt (+ (* w w)
                             (* h h)))))
            (mm2in (lambda (mm)
                     (/ mm 25.4))))
    (let* ((atts (frame-monitor-attributes))
           (pix-w (cl-fourth (assoc 'geometry atts)))
           (pix-h (cl-fifth (assoc 'geometry atts)))
           (pix-d (pyth pix-w pix-h))
           (mm-w (cl-second (assoc 'mm-size atts)))
           (mm-h (cl-third (assoc 'mm-size atts)))
           (mm-d (pyth mm-w mm-h)))
      (/ pix-d (mm2in mm-d)))))

(defun +ui/adjust-font (size line-space &optional font-family)
  (let* ((font-family (or font-family)))
    (setq-default line-spacing line-space)
    (setq-default doom-font (font-spec :family font-family :size size))
    (setq-default doom--font-scale nil)
    (set-frame-font doom-font 'keep-size t)
    (doom/reload-font)
    (run-hooks 'after-setting-font-hook)))

(defun +ui/active-display-name ()
  (interactive)
  (-some->> (shell-command-to-string "xrandr | grep \"connected primary\"")
    (s-split " ")
    (nth 0)
    (s-replace "-" "")))

(defun +ui|adjust-ui-to-display ()
  "Adjust the UI to the current attached display."
  (interactive)
  (cond
   ((string= (system-name) "mbair")
    ;; Not actually a retina display, but this looks best
    (+ui/adjust-font 13 5 "Fira Code"))
   ((string= (system-name) "Florians-iMac.local")
    (+ui/adjust-font 14 10 "Menlo"))
   ((string= (system-name) "thinknix")
    (if (string= (+ui/active-display-name) "eDP1")
        ;; Internal Screen
        (+ui/adjust-font 15 7 "Fira Code")
      ;; HiDPI External
      (+ui/adjust-font 18 8 "Fira Code")))
   ((string= (system-name) "Florians-MacBook-Air.local")
    (+ui/adjust-font 14 10 "Menlo"))))

Bookmarks

Bookmarks for projects with using a bookmarks.json file at the project root.

(defvar +bookmarks:local-bookmarks-file nil)
(setq +bookmarks:local-bookmarks-file (f-join doom-local-dir "bookmarks.json"))

(defun +bookmarks/find-bookmarks (x &optional &key other-window?)
  "Jump to a X relative to the project root, go to character POS."
  (-when-let* ((find-fn (if other-window? 'find-file-other-window 'find-file))
               (item (car x)))
    (let* ((file (-some->> (alist-get 'file item)
                   (--when (alist-get 'relative item)
                           (f-join (projectile-project-root) it))))
           (buffer-is-open (when file (get-file-buffer file))))
      (when file
        (funcall find-fn file)
        (+workspaces-add-current-buffer-h))
      (when-let ((fn (alist-get 'fn item)))
        (funcall fn))

      ;; goto, disable-relocation
      ;; Go to a location matched by regex
      ;; Unless disable-relocation is enabled and the file is already visited
      (-some--> (alist-get 'goto item)
        (--id-when (or (not buffer-is-open)
                       (not (alist-get 'disable-relocation item))) it)
        (--tap (progn
                 (goto-char (point-min))
                 (cond
                  ((eq 'integer (type-of it))
                   (goto-line it))
                  ((eq 'string (type-of it))
                   (search-forward it))))
               it))
      ;; action
      ;; Execute a function after find file
      (-some--> (alist-get 'action item)
        (call-interactively it))
      ;; goto-bol
      ;; Go to the beginning, since the regex search
      ;; will leave the cursor at the end of the search
      (-some--> (alist-get 'goto-bol item)
        (evil-first-non-blank)))))

(defun +bookmarks/local/save (xs)
  "Save an alist as json to the +bookmarks:local-bookmarks-file"
  (->> xs
       (json-encode)
       ((lambda (x) (f-write x 'utf-8 +bookmarks:local-bookmarks-file)))))

(defun +bookmarks/find-bookmarks-other-window (x)
  "Open bookmark X in other window, used with ivy action 'j'."
  (+bookmarks/find-bookmarks (cdr x) :other-window? t))

(defun +bookmarks/local|save ()
  (interactive)
  (let ((entry `((file . ,(buffer-file-name))
                 (goto . ,(line-number-at-pos))
                 (name . ,(read-string "Bookmark Name: ")))))
    (->> (+bookmarks/local/list)
      (-append entry)
      (+bookmarks/local/save))))

(defun +bookmarks/local/kill (x)
  (->>
   (--remove (cl-equalp (car (cdr x)) it) (+bookmarks/local/list))
   (+bookmarks/local/save)))

(defun +bookmarks/local/rename (x)
  (require 'a)
  (-let ((item (car (cdr x))))
    (-log (a-get (cdr item) :name))
    (->>
     (+bookmarks/local/list)
     (--map-first (cl-equalp item it) (a-update it 'name (lambda (x) (read-string "Rename Bookmark: " x))))
     (+bookmarks/local/save))))

(defun +bookmarks/local/list ()
  (-some->> +bookmarks:local-bookmarks-file
    (-id-when #'f-exists?)
    (json-read-file)
    (-map 'identity)))

;;;###autoload
(defun +bookmarks (&optional bookmarks-list)
  "Either take alist BOOKMARKS-LIST or look for bookmarks.json in project root.
If found, show an ivy window with the bookmarks"
  (interactive)
  (-if-let*
      ((bookmarks
        (or bookmarks-list
            (+bookmarks/local/list)))
       (bookmarks (--map (list (alist-get 'name it) it) bookmarks)))
      (ivy-read "Bookmark: " bookmarks
                :action (lambda (x) (+bookmarks/find-bookmarks (cdr x)))
                :caller #'+bookmarks|find-bookmarks)
    (user-error "No bookmarks file found at project root.")))

(after! ivy
  (ivy-set-actions
   '+bookmarks|find-bookmarks
   '(("j" +bookmarks/find-bookmarks-other-window "open in other window")
     ("k" +bookmarks/local/kill "Remove")
     ("r" +bookmarks/local/rename "Rename"))))