diff --git a/.doom.d/config.el b/.doom.d/config.el new file mode 100644 index 0000000..e022362 --- /dev/null +++ b/.doom.d/config.el @@ -0,0 +1,580 @@ +(doom-load-envvars-file (concat doom-private-dir "env.el")) + +(setq doom-theme 'doom-catppuccin) + +(setq doom-font (font-spec :family "monospace" :size 13) + doom-big-font (font-spec :family "monospace" :size 13) + doom-variable-pitch-font (font-spec :family "sans-serif" :size 13)) + +(set-frame-parameter (selected-frame) 'alpha-background 85) +(add-to-list 'default-frame-alist '(alpha-background . 85)) + +(setq evil-want-fine-undo t) + +(defun my/line-numbers-relative () + (setq display-line-numbers 'relative)) +(defun my/line-numbers-absolute () + (setq display-line-numbers 'absolute)) +(add-hook 'evil-insert-state-entry-hook #'my/line-numbers-absolute) +(add-hook 'evil-insert-state-exit-hook #'my/line-numbers-relative) + +(use-package copilot + :commands (copilot-complete)) + +(defun my/copilot-complete () + (interactive) + (copilot-complete) + (my/hydra-copilot/body) + (copilot-clear-overlay)) + +(defhydra my/hydra-copilot () + "Copilot" + ("" copilot-accept-completion "Accept" :color blue ) + ("" copilot-next-completion "Next" ) + ("" copilot-previous-completion "Prev") + ("" copilot-clear-overlay "Cancel" :color blue)) + +(setq doom-scratch-initial-major-mode 'lisp-interaction-mode) + +(map! :leader + "b" nil + "f" nil + "h" nil + "p" nil + "t" nil) + +(map! :map evil-org-mode-map + :n "zc" nil) + +(map! + :desc "Increase font size" :ni "C-=" #'text-scale-increase + :desc "Decrease font size" :ni "C--" #'text-scale-decrease) + +(map! + :desc "Copilot" :i "C-," #'my/copilot-complete) + + + +(map! :map minibuffer-mode-map + :desc "Next history" "C-j" #'next-history-element + :desc "Prev history" "C-k" #'previous-history-element) + +(map! :leader + :desc "M-x" "x" #'counsel-M-x + :desc "M-:" ";" #'pp-eval-expression) + +(map! :leader + :desc "Find file" "." #'counsel-find-file + :desc "Find dir" ">" #'+default/dired + + :desc "Find in project" "SPC" #'+ivy/projectile-find-file + :desc "Find in project uncached" "C-SPC" #'my/projectile-find-file-nocache) + +(defun my/projectile-find-file-nocache () + (interactive) + (projectile-invalidate-cache nil) + (+ivy/projectile-find-file)) + +(map! :leader + :desc "Switch buffer" "," #'+vertico/switch-workspace-buffer + :desc "Switch all buffers" ">" #'consult-buffer) + +(map! :leader + :desc "Search online" "/" #'counsel-search) + +(map! :leader + :prefix ("b" . "buffers") + + :desc "Switch buffer" "b" #'consult-buffer + :desc "ibuffer" "i" #'ibuffer + + :desc "Kill buffer" "d" #'kill-current-buffer + :desc "Kill all buffers" "D" #'doom/kill-all-buffers) + +(map! :leader + :prefix ("f" . "files") + + :desc "Recent files" "r" #'consult-recent-file + + :desc "Find file" "f" #'counsel-find-file + :desc "Find file as root" "u" #'doom/sudo-find-file + :desc "Find package" "p" #'counsel-find-library + + :desc "Copy this file" "c" #'doom/copy-this-file + :desc "Delete this file" "d" #'doom/delete-this-file + :desc "Move this file" "m" #'doom/move-this-file + :desc "Revert this file" "l" #'revert-buffer + + :desc "Copy file path" "y" #'+default/yank-buffer-path + :desc "Copy project file path" "Y" #'+default/yank-buffer-path-relative-to-project + + :desc "Open scratch" "x" #'doom/open-scratch-buffer) + +(map! :leader + :prefix ("f s" . "snippets") + :desc "New snippet" "n" #'yas-new-snippet + :desc "Edit snippet" "e" #'yas-visit-snippet-file + :desc "Browse docs" "?" #'my/yas-browse-docs) + +(defun my/yas-browse-docs () + (interactive) + (browse-url "https://joaotavora.github.io/yasnippet")) + +(map! :leader + :prefix ("f e" . "emacs") + :desc "Find in config" "f" #'doom/find-file-in-private-config + :desc "Reload config" "r" #'doom/reload + + :desc "Edit config" "c" #'my/edit-config + :desc "Edit packages" "p" #'my/edit-packages + :desc "Edit env" "e" #'my/edit-env + :desc "Edit init" "i" #'my/edit-init) + +(defun my/edit-config () + (interactive) + (find-file (concat doom-private-dir "config.org"))) +(defun my/edit-packages () + (interactive) + (find-file (concat doom-private-dir "packages.el"))) +(defun my/edit-init () + (interactive) + (find-file (concat doom-private-dir "init.el"))) +(defun my/edit-env () + (interactive) + (find-file (concat doom-private-dir "env.el"))) + +(define-derived-mode org-config-mode org-mode "Org config mode") +(add-to-list 'auto-mode-alist '("config\\.org" . org-config-mode)) + +(map! :leader + :prefix ("h" . "help") + + :desc "Apropos" "/" #'consult-apropos + :desc "Apropos docs" "?" #'apropos-documentation + + :desc "Help at point" "p" #'helpful-at-point + :desc "Help info" "h" #'info + :desc "Help for help" "H" #'help-for-help + + :desc "Describe function" "f" #'counsel-describe-function + :desc "Describe function key" "F" #'where-is + :desc "Describe variable" "v" #'counsel-describe-variable + :desc "Describe custom variable" "V" #'doom/help-custom-variable + :desc "Describe command" "x" #'helpful-command + :desc "Describe key" "k" #'describe-key-briefly + :desc "Describe key fully" "K" #'describe-key + :desc "Describe char" "'" #'describe-char + :desc "Describe coding system" "\"" #'describe-coding-system + :desc "Describe input method" "i" #'describe-input-method + + :desc "Emacs manual" "e" #'info-emacs-manual + :desc "ASCII table" "a" #'my/ascii-table + + :desc "View messages" "e" #'view-echo-area-messages + :desc "View keystrokes" "l" #'view-lossage) + +(defface my/ascii-table-highlight-face + '((t (:foreground "pink"))) + "Face for highlighting ASCII chars.") + +(defun my/ascii-table () + "Display basic ASCII table (0 thru 128)." + (interactive) + (pop-to-buffer "*ASCII*") + (erase-buffer) + (setq buffer-read-only nil) + (my/buffer-local-set-key "q" #'+popup/quit-window) + (setq lower32 '("nul" "soh" "stx" "etx" "eot" "enq" "ack" "bel" + "bs" "ht" "nl" "vt" "np" "cr" "so" "si" + "dle" "dc1" "dc2" "dc3" "dc4" "nak" "syn" "etb" + "can" "em" "sub" "esc" "fs" "gs" "rs" "us")) + (save-excursion (let ((i -1)) + (insert " Hex Dec Char | Hex Dec Char | Hex Dec Char | Hex Dec Char\n") + (insert " ---------------+-----------------+-----------------+----------------\n") + (while (< i 31) + (insert (format "%4x %4d %4s | %4x %4d %4s | %4x %4d %4s | %4x %4d %4s\n" + (setq i (+ 1 i)) i (elt lower32 i) + (setq i (+ 32 i)) i (single-key-description i) + (setq i (+ 32 i)) i (single-key-description i) + (setq i (+ 32 i)) i (single-key-description i))) + (overlay-put (make-overlay (- (point) 4) (- (point) 1)) 'face 'my/ascii-table-highlight-face) + (overlay-put (make-overlay (- (point) 22) (- (point) 19)) 'face 'my/ascii-table-highlight-face) + (overlay-put (make-overlay (- (point) 40) (- (point) 37)) 'face 'my/ascii-table-highlight-face) + (overlay-put (make-overlay (- (point) 58) (- (point) 55)) 'face 'my/ascii-table-highlight-face) + (setq i (- i 96)) + )))) + +(set-popup-rule! "^\\*ASCII" + :side 'right + :select t + :width 70) + +(map! :leader + :prefix ("h d" . "doom") + + :desc "Doom manual" "d" #'doom/help + :desc "Doom FAQ" "f" #'doom/help-faq + :desc "Doom modules" "m" #'doom/help-modules + :desc "Doom news" "n" #'doom/help-news + :desc "Doom help search" "/" #'doom/help-search-headings + + :desc "Doom version" #'doom/version + + :desc "Doom package configuration" "p" #'doom/help-package-config + :desc "Doom sandbox" "x" #'doom/sandbox) + +(map! :leader + :prefix ("p" . "projects") + :desc "Switch project" "p" #'counsel-projectile-switch-project + :desc "Add new project" "a" #'projectile-add-known-project + :desc "Remove project" "d" #'projectile-remove-known-project + + :desc "Find in project root" "." #'counsel-projectile-find-file + :desc "Search in project" "/" #'+default/search-project + + :desc "Invalidate project cache" "i" #'projectile-invalidate-cache + + :desc "Run cmd in project root" "!" #'projectile-run-shell-command-in-root + :desc "Run async cmd in project root" "&" #'projectile-run-async-shell-command-in-root) + +(defun my/projectile-find-in-root () + (interactive) + (counsel-find-file nil projectile-project-root)) + +(map! :leader + :prefix ("t" . "toggle") + ;; Wrap + :desc "Auto Wrap" "a" #'auto-fill-mode + :desc "Wrap Indicator" "c" #'global-display-fill-column-indicator-mode + :desc "Wrap Column" "C" #'set-fill-column + :desc "Line Wrap" "w" #'visual-line-mode + ;; Modes + :desc "Flycheck" "f" #'flycheck-mode + ;; Files + :desc "Read-only" "r" #'read-only-mode) + +(defun my/auto-fill-mode (cols) + (interactive)) + +(map! :leader + :prefix-map ("w" . "window") + ;; Navigation + :desc "Go..." "w" #'ace-window + :desc "Go left" "h" #'evil-window-left + :desc "Go down" "j" #'evil-window-down + :desc "Go up" "k" #'evil-window-up + :desc "Go right" "l" #'evil-window-right + :desc "Go other" "o" #'other-window + ;; Layout + :desc "Rotate up" "K" #'evil-window-rotate-upwards + :desc "Rotate down" "J" #'evil-window-rotate-downwards + ;; Splits + :desc "VSplit" "=" #'+evil/window-vsplit-and-follow + :desc "HSplit" "-" #'+evil/window-split-and-follow + :desc "Tear off" "t" #'tear-off-window + ;; History + :desc "Undo" "u" #'winner-undo + :desc "Redo" "U" #'winner-redo + ;; Misc + :desc "Resize" "r" #'my/hydra-window-resize/body + :desc "Balance" "b" #'balance-windows + ;; Management + :desc "Kill window" "d" #'+workspace/close-window-or-workspace) +;; TODO: Maybe check out: +;; evil-window-mru + +(setq my/window-resize-step 3) + +(defun my/window-increase-height () + (interactive) + (evil-window-increase-height my/window-resize-step)) +(defun my/window-decrease-height () + (interactive) + (evil-window-decrease-height my/window-resize-step)) +(defun my/window-increase-width () + (interactive) + (evil-window-increase-width my/window-resize-step)) +(defun my/window-decrease-width () + (interactive) + (evil-window-decrease-width my/window-resize-step)) + +(defhydra my/hydra-window-resize () + "Resize window" + ("=" my/window-increase-height "++Height") + ("-" my/window-decrease-height "--Height") + ("<" my/window-decrease-width "--Width") + (">" my/window-increase-width "++Width")) + +(map! :map org-config-mode-map + :localleader + :v :desc "Eval Region" "e" #'eval-region + :n :desc "Eval Source" "e" #'my/org-config-eval-source) + +(defun my/org-config-eval-source () + (interactive) + (org-ctrl-c-ctrl-c) + (org-babel-remove-result)) + +(map! :map rustic-mode-map + :localleader + :desc "Debug..." "d" #'my/rust/dap-hydra/body) + +(map! :map rustic-mode-map + :desc "Pluralize import" "," #'my/rust/import-pluralize + :desc "Singularize import" "" #'my/rust/import-singularize + :desc "Singularize import" "C-" #'my/rust/import-c-singularize) + +(defhydra my/rust/dap-hydra (:color pink :hint nil :foreign-keys run) + " +^Stepping^ ^Switch^ ^Breakpoints^ ^Debug^ ^Eval +^^^^^^^^---------------------------------------------------------------------------------------------------------------- +_n_: Next _ss_: Session _bb_: Toggle _dd_: Debug binary _ee_: Eval +_i_: Step in _st_: Thread _bd_: Delete _dr_: Restart debugging _es_: Eval thing at point +_o_: Step out _sf_: Stack frame _ba_: Add _ea_: Add expression +_c_: Continue _su_: Up stack frame _bc_: Set condition +_Q_: Disconnect _sd_: Down stack frame _bh_: Set hit count + _sl_: List locals _bl_: Set log message + _sb_: List breakpoints + _sS_: List sessions +" + ("n" dap-next) + ("i" dap-step-in) + ("o" dap-step-out) + ("c" dap-continue) + ("r" dap-restart-frame) + ("ss" dap-switch-session) + ("st" dap-switch-thread) + ("sf" dap-switch-stack-frame) + ("su" dap-up-stack-frame) + ("sd" dap-down-stack-frame) + ("sl" dap-ui-locals) + ("sb" dap-ui-breakpoints) + ("sS" dap-ui-sessions) + ("bb" dap-breakpoint-toggle) + ("ba" dap-breakpoint-add) + ("bd" dap-breakpoint-delete) + ("bc" dap-breakpoint-condition) + ("bh" dap-breakpoint-hit-condition) + ("bl" dap-breakpoint-log-message) + ("dd" my/rust/debug-binary) + ("dr" dap-debug-restart) + ("ee" dap-eval) + ("ea" dap-ui-expressions-add) + ("es" dap-eval-thing-at-point) + ("q" nil "quit" :color blue) + ("Q" dap-disconnect :color red)) + +(map! :prefix "z" + :desc "Kill buffer" :n "x" #'kill-current-buffer + :desc "Kill window" :n "c" #'+workspace/close-window-or-workspace) + +(map! :prefix "[" + :desc "Start of fn" :n "f" #'beginning-of-defun) + +(map! :prefix "]" + :desc "End of fn" :n "f" #'end-of-defun) + + + +(add-to-list 'projectile-globally-ignored-files "Cargo.lock") + +(defun my/rust/import-pluralize () + "Convert a singular import into a brace-wrapped plural import." + (interactive) + (if (and + (not (my/kbd!-p)) + (my/insert-mode-p) + (my/line-match-p + ;; use foo::bar::baz; + (rx line-start "use " + (+ (+ word) "::") + (+ word) + (? ";") line-end))) + (kbd! "ESC vb S} f} i,") + (insert ","))) + +(defun my/rust/import-singularize () + "Convert a brace-wrapped plural import into a singular import." + (interactive) + (if (and + (not (my/kbd!-p)) + (my/insert-mode-p) + (my/line-match-p + ;; use foo::bar::baz::{qux::quo,}; + (rx line-start "use " + (+ (+ word) "::") + "{" (* (+ word) "::") (+ word) ",}" + (? ";") line-end))) + (kbd! "ESC l dF, ds} $i") + (evil-delete-backward-char-and-join 1))) + +(defun my/rust/import-c-singularize () + "Convert a brace-wrapped plural import into a singular import." + (interactive) + (if (and + (not (my/kbd!-p)) + (my/insert-mode-p) + (my/line-match-p + ;; use foo::bar::baz::{qux::quo, }; + (rx line-start + "use " + (+ (+ word) "::") + "{" (* (+ word) "::") (+ word) "," (* whitespace) "}" + (? ";") line-end))) + (kbd! "ESC l dF, ds} $i") + (backward-kill-word 1))) + +(defun my/rust/debug-config (args) + (append + `(:type "lldb-vscode" + ;; `(:type "lldb" + :request "launch" + :dap-server-path ,(list (executable-find "lldb-vscode")) + ;; :dap-server-path ,(list (executable-find "rust-lldb")) + ,@args))) + +;; use a::TestThin +;; (:MIMode "gdb" +;; :miDebuggerPath "gdb" +;; :stopAtEntry t +;; :externalConsole +;; :json-false +;; :type "cppdbg" +;; :request "launch" +;; :name "test test2" +;; :args ["test2" "--exact" "--nocapture"] +;; :cwd "/home/lain/Code/test/rust/debug" +;; :sourceLanguages ["rust"] +;; :program "/home/lain/Code/test/rust/debug/target/debug/deps/...") + +;; (require 'dap-cpptools) +(defun my/rust/debug-binary (args) + (interactive "sArgs: ") + (let* ((root (projectile-project-root)) + (name (projectile-project-name)) + (target (concat root "target/debug/" name))) + ;; (rustic-cargo-build) + (dap-debug + (my/rust/debug-config + `(:program ,target + :cwd ,root + :args ,(apply #'vector (split-string-and-unquote args))))))) + +(defun my/rust/debug-lsp-runnable (runnable) + "Select and debug a RUNNABLE action." + (interactive (list (lsp-rust-analyzer--select-runnable))) + (-let (((&rust-analyzer:Runnable + :args (&rust-analyzer:RunnableArgs :cargo-args :workspace-root? :executable-args) + :label) runnable)) + (pcase (aref cargo-args 0) + ("run" (aset cargo-args 0 "build")) + ("test" (when (-contains? (append cargo-args ()) "--no-run") + (cl-callf append cargo-args (list "--no-run"))))) + (->> (append (list (executable-find "cargo")) + cargo-args + (list "--message-format=json")) + (s-join " ") + (shell-command-to-string) + (s-lines) + (-keep (lambda (s) + (condition-case nil + (-let* ((json-object-type 'plist) + ((msg &as &plist :reason :executable) (json-read-from-string s))) + (when (and executable (string= "compiler-artifact" reason)) + executable)) + (error)))) + (funcall + (lambda (artifact-spec) + (pcase artifact-spec + (`() (user-error "No compilation artifacts or obtaining the runnable artifacts failed")) + (`(,spec) spec) + (_ (user-error "Multiple compilation artifacts are not supported"))))) + (list :name label + :args executable-args + :cwd workspace-root? + ;; :sourceLanguages ["rust"] + :stopAtEntry t + :stopAtEntry :json-true + :externalConsole :json-false + :program) + (my/rust/debug-config) + (dap-debug)))) +(advice-add #'lsp-rust-analyzer-debug :override #'my/rust/debug-lsp-runnable) + +;; (setq projectile-project-search-path +;; '("~/Code")) + +(let ((found (nth 0 (auth-source-search :host "kagi.com")))) + (when found + (let ((token (plist-get found :secret))) + (when (functionp token) + (setq token (funcall token))) + (add-to-list + 'counsel-search-engines-alist + `(kagi + "https://duckduckgo.com/ac/" + ,(format "https://kagi.com/search?token=%s&q=" token) + counsel--search-request-data-ddg)) + (setq counsel-search-engine 'kagi)))) + +(load! "lisp/emacs-everywhere.el") +(setq emacs-everywhere-paste-command '("xdotool" "key" "--clearmodifiers" "ctrl+v")) +(setq emacs-everywhere-frame-parameters + '((title . "Emacs Everywhere") + (width . 120) + (height . 36))) + +(defun my/line () + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + +(defun my/line-match-p (regexp) + (string-match-p regexp (my/line))) + +(defun my/insert-mode-p () + (eq evil-state 'insert)) +(defun my/normal-mode-p () + (eq evil-state 'normal)) + +(defun my/kbd-replace (str) + "Convert STR into a keyboard macro string by replacing terminal key sequences with GUI keycodes." + (let ((kbd-regex '(("ESC" . "") + ("DEL" . "" ) + ("BS" . "") + ("RET" . "") + ("SPC" . "") + ("TAB" . "")))) + (my/replace-regexps-in-string str kbd-regex))) + +(setq my//kbd-p nil) +(defun my/kbd!-p () (eq my//kbd-p t)) + +(defun kbd! (str) + "Execute the key sequence defined by STR like a VIM macro." + (let ((minibuffer-message-timeout 0)) + (setq my//kbd-p t) + (execute-kbd-macro (read-kbd-macro (my/kbd-replace str))) + (setq my//kbd-p nil))) + +(defun my/buffer-local-set-key (key fn) + (let ((mode (intern (format "%s-local-mode" (buffer-name)))) + (map (intern (format "%s-local-mode-map" (buffer-name))))) + (unless (boundp map) + (set map (make-sparse-keymap)) + (evil-make-overriding-map map 'normal)) + (eval + `(define-minor-mode ,mode + "A minor mode for buffer-local keybinds." + :keymap ,map)) + (eval + `(define-key ,map ,key #',fn)) + (funcall mode t))) + +(defun my/replace-regexps-in-string (str regexps) + "Replace all pairs of (regex . replacement) defined by REGEXPS in STR." + (if (null regexps) + str + (my/replace-regexps-in-string + (replace-regexp-in-string (caar regexps) (cdar regexps) str t) + (cdr regexps)))) diff --git a/.doom.d/config.org b/.doom.d/config.org new file mode 100644 index 0000000..fc94395 --- /dev/null +++ b/.doom.d/config.org @@ -0,0 +1,733 @@ +#+title: Config + +* Setup +** Environment Variables +#+BEGIN_SRC elisp +(doom-load-envvars-file (concat doom-private-dir "env.el")) +#+END_SRC +* Appearance +Set a nice theme and font. +#+BEGIN_SRC elisp +(setq doom-theme 'doom-catppuccin) + +(setq doom-font (font-spec :family "monospace" :size 13) + doom-big-font (font-spec :family "monospace" :size 13) + doom-variable-pitch-font (font-spec :family "sans-serif" :size 13)) +#+END_SRC + +Sprinkle in a little background transparency. Instead of making the entire frame +transparent (including the text) with =alpha=, we use the =alpha-background= +frame parameter which just landed in the Emacs 29 development branch. +#+BEGIN_SRC elisp +(set-frame-parameter (selected-frame) 'alpha-background 85) +(add-to-list 'default-frame-alist '(alpha-background . 85)) +#+END_SRC +* Editing +** Evil +#+BEGIN_SRC elisp +(setq evil-want-fine-undo t) +#+END_SRC + +*** Line Numbers +Use relative line numbers in normal mode, and absolute line numbers in insert +mode. +#+BEGIN_SRC emacs-lisp +(defun my/line-numbers-relative () + (setq display-line-numbers 'relative)) +(defun my/line-numbers-absolute () + (setq display-line-numbers 'absolute)) +(add-hook 'evil-insert-state-entry-hook #'my/line-numbers-absolute) +(add-hook 'evil-insert-state-exit-hook #'my/line-numbers-relative) +#+END_SRC +** Copilot +Add support for GitHub Copilot ([[*Copilot][keybinds]]). + +#+BEGIN_SRC elisp +(use-package copilot + :commands (copilot-complete)) + +(defun my/copilot-complete () + (interactive) + (copilot-complete) + (my/hydra-copilot/body) + (copilot-clear-overlay)) + +(defhydra my/hydra-copilot () + "Copilot" + ("" copilot-accept-completion "Accept" :color blue ) + ("" copilot-next-completion "Next" ) + ("" copilot-previous-completion "Prev") + ("" copilot-clear-overlay "Cancel" :color blue)) +#+END_SRC +** Scratch +Use =lisp-interaction-mode= by default for the scratch buffer. +#+BEGIN_SRC elisp +(setq doom-scratch-initial-major-mode 'lisp-interaction-mode) +#+END_SRC +* Keybinds +** Unmaps +Unmap a bunch of the default keybindings. +#+BEGIN_SRC elisp +(map! :leader + "b" nil + "f" nil + "h" nil + "p" nil + "t" nil) + +(map! :map evil-org-mode-map + :n "zc" nil) +#+END_SRC + +** Global +*** Font Size +#+BEGIN_SRC elisp +(map! + :desc "Increase font size" :ni "C-=" #'text-scale-increase + :desc "Decrease font size" :ni "C--" #'text-scale-decrease) +#+END_SRC + +*** Copilot +#+BEGIN_SRC elisp +(map! + :desc "Copilot" :i "C-," #'my/copilot-complete) +#+END_SRC +*** LSP +#+BEGIN_SRC elisp +#+END_SRC +*** Minibuffer +#+BEGIN_SRC elisp +(map! :map minibuffer-mode-map + :desc "Next history" "C-j" #'next-history-element + :desc "Prev history" "C-k" #'previous-history-element) +#+END_SRC + +** Leader +*** Root +**** Eval +#+BEGIN_SRC elisp +(map! :leader + :desc "M-x" "x" #'counsel-M-x + :desc "M-:" ";" #'pp-eval-expression) +#+END_SRC + +**** Files +#+BEGIN_SRC elisp +(map! :leader + :desc "Find file" "." #'counsel-find-file + :desc "Find dir" ">" #'+default/dired + + :desc "Find in project" "SPC" #'+ivy/projectile-find-file + :desc "Find in project uncached" "C-SPC" #'my/projectile-find-file-nocache) + +(defun my/projectile-find-file-nocache () + (interactive) + (projectile-invalidate-cache nil) + (+ivy/projectile-find-file)) +#+END_SRC +**** Buffers +#+BEGIN_SRC elisp +(map! :leader + :desc "Switch buffer" "," #'+vertico/switch-workspace-buffer + :desc "Switch all buffers" ">" #'consult-buffer) +#+END_SRC + +**** Search +#+BEGIN_SRC elisp +(map! :leader + :desc "Search online" "/" #'counsel-search) +#+END_SRC + +*** b: Buffers +#+BEGIN_SRC elisp +(map! :leader + :prefix ("b" . "buffers") + + :desc "Switch buffer" "b" #'consult-buffer + :desc "ibuffer" "i" #'ibuffer + + :desc "Kill buffer" "d" #'kill-current-buffer + :desc "Kill all buffers" "D" #'doom/kill-all-buffers) +#+END_SRC + +*** f: Files +#+BEGIN_SRC elisp +(map! :leader + :prefix ("f" . "files") + + :desc "Recent files" "r" #'consult-recent-file + + :desc "Find file" "f" #'counsel-find-file + :desc "Find file as root" "u" #'doom/sudo-find-file + :desc "Find package" "p" #'counsel-find-library + + :desc "Copy this file" "c" #'doom/copy-this-file + :desc "Delete this file" "d" #'doom/delete-this-file + :desc "Move this file" "m" #'doom/move-this-file + :desc "Revert this file" "l" #'revert-buffer + + :desc "Copy file path" "y" #'+default/yank-buffer-path + :desc "Copy project file path" "Y" #'+default/yank-buffer-path-relative-to-project + + :desc "Open scratch" "x" #'doom/open-scratch-buffer) +#+END_SRC + +**** s: Snippets +#+BEGIN_SRC emacs-lisp +(map! :leader + :prefix ("f s" . "snippets") + :desc "New snippet" "n" #'yas-new-snippet + :desc "Edit snippet" "e" #'yas-visit-snippet-file + :desc "Browse docs" "?" #'my/yas-browse-docs) +#+END_SRC + +Add a command to open the YASnippet docs. +#+BEGIN_SRC elisp +(defun my/yas-browse-docs () + (interactive) + (browse-url "https://joaotavora.github.io/yasnippet")) +#+END_SRC + +**** e: Emacs Files +#+BEGIN_SRC elisp +(map! :leader + :prefix ("f e" . "emacs") + :desc "Find in config" "f" #'doom/find-file-in-private-config + :desc "Reload config" "r" #'doom/reload + + :desc "Edit config" "c" #'my/edit-config + :desc "Edit packages" "p" #'my/edit-packages + :desc "Edit env" "e" #'my/edit-env + :desc "Edit init" "i" #'my/edit-init) +#+END_SRC + +#+BEGIN_SRC elisp +(defun my/edit-config () + (interactive) + (find-file (concat doom-private-dir "config.org"))) +(defun my/edit-packages () + (interactive) + (find-file (concat doom-private-dir "packages.el"))) +(defun my/edit-init () + (interactive) + (find-file (concat doom-private-dir "init.el"))) +(defun my/edit-env () + (interactive) + (find-file (concat doom-private-dir "env.el"))) +#+END_SRC + +Define a derived mode for editing the literate config so we can specify some +keybindings specific to =config.org=. +#+BEGIN_SRC elisp +(define-derived-mode org-config-mode org-mode "Org config mode") +(add-to-list 'auto-mode-alist '("config\\.org" . org-config-mode)) +#+END_SRC + +*** h: Help +#+BEGIN_SRC elisp +(map! :leader + :prefix ("h" . "help") + + :desc "Apropos" "/" #'consult-apropos + :desc "Apropos docs" "?" #'apropos-documentation + + :desc "Help at point" "p" #'helpful-at-point + :desc "Help info" "h" #'info + :desc "Help for help" "H" #'help-for-help + + :desc "Describe function" "f" #'counsel-describe-function + :desc "Describe function key" "F" #'where-is + :desc "Describe variable" "v" #'counsel-describe-variable + :desc "Describe custom variable" "V" #'doom/help-custom-variable + :desc "Describe command" "x" #'helpful-command + :desc "Describe key" "k" #'describe-key-briefly + :desc "Describe key fully" "K" #'describe-key + :desc "Describe char" "'" #'describe-char + :desc "Describe coding system" "\"" #'describe-coding-system + :desc "Describe input method" "i" #'describe-input-method + + :desc "Emacs manual" "e" #'info-emacs-manual + :desc "ASCII table" "a" #'my/ascii-table + + :desc "View messages" "e" #'view-echo-area-messages + :desc "View keystrokes" "l" #'view-lossage) +#+END_SRC + +**** a: Ascii Table +#+BEGIN_SRC elisp +(defface my/ascii-table-highlight-face + '((t (:foreground "pink"))) + "Face for highlighting ASCII chars.") + +(defun my/ascii-table () + "Display basic ASCII table (0 thru 128)." + (interactive) + (pop-to-buffer "*ASCII*") + (erase-buffer) + (setq buffer-read-only nil) + (my/buffer-local-set-key "q" #'+popup/quit-window) + (setq lower32 '("nul" "soh" "stx" "etx" "eot" "enq" "ack" "bel" + "bs" "ht" "nl" "vt" "np" "cr" "so" "si" + "dle" "dc1" "dc2" "dc3" "dc4" "nak" "syn" "etb" + "can" "em" "sub" "esc" "fs" "gs" "rs" "us")) + (save-excursion (let ((i -1)) + (insert " Hex Dec Char | Hex Dec Char | Hex Dec Char | Hex Dec Char\n") + (insert " ---------------+-----------------+-----------------+----------------\n") + (while (< i 31) + (insert (format "%4x %4d %4s | %4x %4d %4s | %4x %4d %4s | %4x %4d %4s\n" + (setq i (+ 1 i)) i (elt lower32 i) + (setq i (+ 32 i)) i (single-key-description i) + (setq i (+ 32 i)) i (single-key-description i) + (setq i (+ 32 i)) i (single-key-description i))) + (overlay-put (make-overlay (- (point) 4) (- (point) 1)) 'face 'my/ascii-table-highlight-face) + (overlay-put (make-overlay (- (point) 22) (- (point) 19)) 'face 'my/ascii-table-highlight-face) + (overlay-put (make-overlay (- (point) 40) (- (point) 37)) 'face 'my/ascii-table-highlight-face) + (overlay-put (make-overlay (- (point) 58) (- (point) 55)) 'face 'my/ascii-table-highlight-face) + (setq i (- i 96)) + )))) + +(set-popup-rule! "^\\*ASCII" + :side 'right + :select t + :width 70) +#+END_SRC + +**** d: Doom +#+BEGIN_SRC elisp +(map! :leader + :prefix ("h d" . "doom") + + :desc "Doom manual" "d" #'doom/help + :desc "Doom FAQ" "f" #'doom/help-faq + :desc "Doom modules" "m" #'doom/help-modules + :desc "Doom news" "n" #'doom/help-news + :desc "Doom help search" "/" #'doom/help-search-headings + + :desc "Doom version" #'doom/version + + :desc "Doom package configuration" "p" #'doom/help-package-config + :desc "Doom sandbox" "x" #'doom/sandbox) +#+END_SRC + +*** p: Projects +#+BEGIN_SRC elisp +(map! :leader + :prefix ("p" . "projects") + :desc "Switch project" "p" #'counsel-projectile-switch-project + :desc "Add new project" "a" #'projectile-add-known-project + :desc "Remove project" "d" #'projectile-remove-known-project + + :desc "Find in project root" "." #'counsel-projectile-find-file + :desc "Search in project" "/" #'+default/search-project + + :desc "Invalidate project cache" "i" #'projectile-invalidate-cache + + :desc "Run cmd in project root" "!" #'projectile-run-shell-command-in-root + :desc "Run async cmd in project root" "&" #'projectile-run-async-shell-command-in-root) + +(defun my/projectile-find-in-root () + (interactive) + (counsel-find-file nil projectile-project-root)) +#+END_SRC + +*** t: Toggle +#+BEGIN_SRC elisp +(map! :leader + :prefix ("t" . "toggle") + ;; Wrap + :desc "Auto Wrap" "a" #'auto-fill-mode + :desc "Wrap Indicator" "c" #'global-display-fill-column-indicator-mode + :desc "Wrap Column" "C" #'set-fill-column + :desc "Line Wrap" "w" #'visual-line-mode + ;; Modes + :desc "Flycheck" "f" #'flycheck-mode + ;; Files + :desc "Read-only" "r" #'read-only-mode) +#+END_SRC + +#+BEGIN_SRC elisp +(defun my/auto-fill-mode (cols) + (interactive)) +#+END_SRC +*** w: Window +#+BEGIN_SRC elisp +(map! :leader + :prefix-map ("w" . "window") + ;; Navigation + :desc "Go..." "w" #'ace-window + :desc "Go left" "h" #'evil-window-left + :desc "Go down" "j" #'evil-window-down + :desc "Go up" "k" #'evil-window-up + :desc "Go right" "l" #'evil-window-right + :desc "Go other" "o" #'other-window + ;; Layout + :desc "Rotate up" "K" #'evil-window-rotate-upwards + :desc "Rotate down" "J" #'evil-window-rotate-downwards + ;; Splits + :desc "VSplit" "=" #'+evil/window-vsplit-and-follow + :desc "HSplit" "-" #'+evil/window-split-and-follow + :desc "Tear off" "t" #'tear-off-window + ;; History + :desc "Undo" "u" #'winner-undo + :desc "Redo" "U" #'winner-redo + ;; Misc + :desc "Resize" "r" #'my/hydra-window-resize/body + :desc "Balance" "b" #'balance-windows + ;; Management + :desc "Kill window" "d" #'+workspace/close-window-or-workspace) +;; TODO: Maybe check out: +;; evil-window-mru +#+END_SRC + +#+BEGIN_SRC elisp +(setq my/window-resize-step 3) + +(defun my/window-increase-height () + (interactive) + (evil-window-increase-height my/window-resize-step)) +(defun my/window-decrease-height () + (interactive) + (evil-window-decrease-height my/window-resize-step)) +(defun my/window-increase-width () + (interactive) + (evil-window-increase-width my/window-resize-step)) +(defun my/window-decrease-width () + (interactive) + (evil-window-decrease-width my/window-resize-step)) + +(defhydra my/hydra-window-resize () + "Resize window" + ("=" my/window-increase-height "++Height") + ("-" my/window-decrease-height "--Height") + ("<" my/window-decrease-width "--Width") + (">" my/window-increase-width "++Width")) +#+END_SRC + +** Local Leader +*** Org Config +#+BEGIN_SRC elisp +(map! :map org-config-mode-map + :localleader + :v :desc "Eval Region" "e" #'eval-region + :n :desc "Eval Source" "e" #'my/org-config-eval-source) + +(defun my/org-config-eval-source () + (interactive) + (org-ctrl-c-ctrl-c) + (org-babel-remove-result)) +#+END_SRC +*** Rust +#+BEGIN_SRC elisp +(map! :map rustic-mode-map + :localleader + :desc "Debug..." "d" #'my/rust/dap-hydra/body) + +(map! :map rustic-mode-map + :desc "Pluralize import" "," #'my/rust/import-pluralize + :desc "Singularize import" "" #'my/rust/import-singularize + :desc "Singularize import" "C-" #'my/rust/import-c-singularize) +#+END_SRC + +#+BEGIN_SRC elisp +(defhydra my/rust/dap-hydra (:color pink :hint nil :foreign-keys run) + " +^Stepping^ ^Switch^ ^Breakpoints^ ^Debug^ ^Eval +^^^^^^^^---------------------------------------------------------------------------------------------------------------- +_n_: Next _ss_: Session _bb_: Toggle _dd_: Debug binary _ee_: Eval +_i_: Step in _st_: Thread _bd_: Delete _dr_: Restart debugging _es_: Eval thing at point +_o_: Step out _sf_: Stack frame _ba_: Add _ea_: Add expression +_c_: Continue _su_: Up stack frame _bc_: Set condition +_Q_: Disconnect _sd_: Down stack frame _bh_: Set hit count + _sl_: List locals _bl_: Set log message + _sb_: List breakpoints + _sS_: List sessions +" + ("n" dap-next) + ("i" dap-step-in) + ("o" dap-step-out) + ("c" dap-continue) + ("r" dap-restart-frame) + ("ss" dap-switch-session) + ("st" dap-switch-thread) + ("sf" dap-switch-stack-frame) + ("su" dap-up-stack-frame) + ("sd" dap-down-stack-frame) + ("sl" dap-ui-locals) + ("sb" dap-ui-breakpoints) + ("sS" dap-ui-sessions) + ("bb" dap-breakpoint-toggle) + ("ba" dap-breakpoint-add) + ("bd" dap-breakpoint-delete) + ("bc" dap-breakpoint-condition) + ("bh" dap-breakpoint-hit-condition) + ("bl" dap-breakpoint-log-message) + ("dd" my/rust/debug-binary) + ("dr" dap-debug-restart) + ("ee" dap-eval) + ("ea" dap-ui-expressions-add) + ("es" dap-eval-thing-at-point) + ("q" nil "quit" :color blue) + ("Q" dap-disconnect :color red)) +#+END_SRC + +** Modes +*** Evil +**** z +#+BEGIN_SRC elisp +(map! :prefix "z" + :desc "Kill buffer" :n "x" #'kill-current-buffer + :desc "Kill window" :n "c" #'+workspace/close-window-or-workspace) +#+END_SRC + +**** g +**** [ +#+BEGIN_SRC elisp +(map! :prefix "[" + :desc "Start of fn" :n "f" #'beginning-of-defun) + +(map! :prefix "]" + :desc "End of fn" :n "f" #'end-of-defun) +#+END_SRC +*** Dap +#+BEGIN_SRC elisp +#+END_SRC + +* Languages +** Rust +*** General +#+BEGIN_SRC elisp +(add-to-list 'projectile-globally-ignored-files "Cargo.lock") +#+END_SRC + +*** Editing +#+BEGIN_SRC elisp +(defun my/rust/import-pluralize () + "Convert a singular import into a brace-wrapped plural import." + (interactive) + (if (and + (not (my/kbd!-p)) + (my/insert-mode-p) + (my/line-match-p + ;; use foo::bar::baz; + (rx line-start "use " + (+ (+ word) "::") + (+ word) + (? ";") line-end))) + (kbd! "ESC vb S} f} i,") + (insert ","))) + +(defun my/rust/import-singularize () + "Convert a brace-wrapped plural import into a singular import." + (interactive) + (if (and + (not (my/kbd!-p)) + (my/insert-mode-p) + (my/line-match-p + ;; use foo::bar::baz::{qux::quo,}; + (rx line-start "use " + (+ (+ word) "::") + "{" (* (+ word) "::") (+ word) ",}" + (? ";") line-end))) + (kbd! "ESC l dF, ds} $i") + (evil-delete-backward-char-and-join 1))) + +(defun my/rust/import-c-singularize () + "Convert a brace-wrapped plural import into a singular import." + (interactive) + (if (and + (not (my/kbd!-p)) + (my/insert-mode-p) + (my/line-match-p + ;; use foo::bar::baz::{qux::quo, }; + (rx line-start + "use " + (+ (+ word) "::") + "{" (* (+ word) "::") (+ word) "," (* whitespace) "}" + (? ";") line-end))) + (kbd! "ESC l dF, ds} $i") + (backward-kill-word 1))) +#+END_SRC + +*** Debugging +#+BEGIN_SRC elisp +(defun my/rust/debug-config (args) + (append + `(:type "lldb-vscode" + ;; `(:type "lldb" + :request "launch" + :dap-server-path ,(list (executable-find "lldb-vscode")) + ;; :dap-server-path ,(list (executable-find "rust-lldb")) + ,@args))) + +;; use a::TestThin +;; (:MIMode "gdb" +;; :miDebuggerPath "gdb" +;; :stopAtEntry t +;; :externalConsole +;; :json-false +;; :type "cppdbg" +;; :request "launch" +;; :name "test test2" +;; :args ["test2" "--exact" "--nocapture"] +;; :cwd "/home/lain/Code/test/rust/debug" +;; :sourceLanguages ["rust"] +;; :program "/home/lain/Code/test/rust/debug/target/debug/deps/...") + +;; (require 'dap-cpptools) +(defun my/rust/debug-binary (args) + (interactive "sArgs: ") + (let* ((root (projectile-project-root)) + (name (projectile-project-name)) + (target (concat root "target/debug/" name))) + ;; (rustic-cargo-build) + (dap-debug + (my/rust/debug-config + `(:program ,target + :cwd ,root + :args ,(apply #'vector (split-string-and-unquote args))))))) + +(defun my/rust/debug-lsp-runnable (runnable) + "Select and debug a RUNNABLE action." + (interactive (list (lsp-rust-analyzer--select-runnable))) + (-let (((&rust-analyzer:Runnable + :args (&rust-analyzer:RunnableArgs :cargo-args :workspace-root? :executable-args) + :label) runnable)) + (pcase (aref cargo-args 0) + ("run" (aset cargo-args 0 "build")) + ("test" (when (-contains? (append cargo-args ()) "--no-run") + (cl-callf append cargo-args (list "--no-run"))))) + (->> (append (list (executable-find "cargo")) + cargo-args + (list "--message-format=json")) + (s-join " ") + (shell-command-to-string) + (s-lines) + (-keep (lambda (s) + (condition-case nil + (-let* ((json-object-type 'plist) + ((msg &as &plist :reason :executable) (json-read-from-string s))) + (when (and executable (string= "compiler-artifact" reason)) + executable)) + (error)))) + (funcall + (lambda (artifact-spec) + (pcase artifact-spec + (`() (user-error "No compilation artifacts or obtaining the runnable artifacts failed")) + (`(,spec) spec) + (_ (user-error "Multiple compilation artifacts are not supported"))))) + (list :name label + :args executable-args + :cwd workspace-root? + ;; :sourceLanguages ["rust"] + :stopAtEntry t + :stopAtEntry :json-true + :externalConsole :json-false + :program) + (my/rust/debug-config) + (dap-debug)))) +(advice-add #'lsp-rust-analyzer-debug :override #'my/rust/debug-lsp-runnable) +#+END_SRC +** Go +*** Debugging +Setup: run =M-x dap-go-setup= +* Tools +** Projectile +#+BEGIN_SRC elisp +;; (setq projectile-project-search-path +;; '("~/Code")) +#+END_SRC +** Counsel Search +#+BEGIN_SRC elisp +(let ((found (nth 0 (auth-source-search :host "kagi.com")))) + (when found + (let ((token (plist-get found :secret))) + (when (functionp token) + (setq token (funcall token))) + (add-to-list + 'counsel-search-engines-alist + `(kagi + "https://duckduckgo.com/ac/" + ,(format "https://kagi.com/search?token=%s&q=" token) + counsel--search-request-data-ddg)) + (setq counsel-search-engine 'kagi)))) +#+END_SRC + +* Apps +** Emacs Everywhere +#+BEGIN_SRC elisp +(load! "lisp/emacs-everywhere.el") +(setq emacs-everywhere-paste-command '("xdotool" "key" "--clearmodifiers" "ctrl+v")) +(setq emacs-everywhere-frame-parameters + '((title . "Emacs Everywhere") + (width . 120) + (height . 36))) +#+END_SRC +* Lisp +** Editing +#+BEGIN_SRC elisp +(defun my/line () + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + +(defun my/line-match-p (regexp) + (string-match-p regexp (my/line))) +#+END_SRC + +#+BEGIN_SRC elisp +(defun my/insert-mode-p () + (eq evil-state 'insert)) +(defun my/normal-mode-p () + (eq evil-state 'normal)) +#+END_SRC + +** Keybindings +#+BEGIN_SRC elisp +(defun my/kbd-replace (str) + "Convert STR into a keyboard macro string by replacing terminal key sequences with GUI keycodes." + (let ((kbd-regex '(("ESC" . "") + ("DEL" . "" ) + ("BS" . "") + ("RET" . "") + ("SPC" . "") + ("TAB" . "")))) + (my/replace-regexps-in-string str kbd-regex))) + +(setq my//kbd-p nil) +(defun my/kbd!-p () (eq my//kbd-p t)) + +(defun kbd! (str) + "Execute the key sequence defined by STR like a VIM macro." + (let ((minibuffer-message-timeout 0)) + (setq my//kbd-p t) + (execute-kbd-macro (read-kbd-macro (my/kbd-replace str))) + (setq my//kbd-p nil))) +#+END_SRC + + +#+BEGIN_SRC elisp +(defun my/buffer-local-set-key (key fn) + (let ((mode (intern (format "%s-local-mode" (buffer-name)))) + (map (intern (format "%s-local-mode-map" (buffer-name))))) + (unless (boundp map) + (set map (make-sparse-keymap)) + (evil-make-overriding-map map 'normal)) + (eval + `(define-minor-mode ,mode + "A minor mode for buffer-local keybinds." + :keymap ,map)) + (eval + `(define-key ,map ,key #',fn)) + (funcall mode t))) +#+END_SRC + +** Regex +#+BEGIN_SRC elisp +(defun my/replace-regexps-in-string (str regexps) + "Replace all pairs of (regex . replacement) defined by REGEXPS in STR." + (if (null regexps) + str + (my/replace-regexps-in-string + (replace-regexp-in-string (caar regexps) (cdar regexps) str t) + (cdr regexps)))) +#+END_SRC diff --git a/.doom.d/env.el b/.doom.d/env.el new file mode 100644 index 0000000..828ecfb --- /dev/null +++ b/.doom.d/env.el @@ -0,0 +1,2 @@ +("DISPLAY=:0" + "SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh") diff --git a/.doom.d/init.el b/.doom.d/init.el new file mode 100644 index 0000000..e48cfad --- /dev/null +++ b/.doom.d/init.el @@ -0,0 +1,189 @@ +;;; init.el -*- lexical-binding: t; -*- + +;; This file controls what Doom modules are enabled and what order they load +;; in. Remember to run 'doom sync' after modifying it! + +;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's +;; documentation. There you'll find a "Module Index" link where you'll find +;; a comprehensive list of Doom's modules and what flags they support. + +;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or +;; 'C-c c k' for non-vim users) to view its documentation. This works on +;; flags as well (those symbols that start with a plus). +;; +;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its +;; directory (for easy access to its source code). + +(doom! :input + ;;chinese + ;;japanese + ;;layout ; auie,ctsrnm is the superior home row + + :completion + company ; the ultimate code completion backend + ;;helm ; the *other* search engine for love and life + ;;ido ; the other *other* search engine... + (ivy +prescient +icons) ; a search engine for love and life + vertico ; the search engine of the future + + :ui + ;;deft ; notational velocity for Emacs + doom ; what makes DOOM look the way it does + doom-dashboard ; a nifty splash screen for Emacs + doom-quit ; DOOM quit-message prompts when you quit Emacs + (emoji +unicode) ; 🙂 + hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW + hydra + ;;indent-guides ; highlighted indent columns + ;;ligatures ; ligatures and symbols to make your code pretty again + ;;minimap ; show a map of the code on the side + modeline ; snazzy, Atom-inspired modeline, plus API + ;;nav-flash ; blink cursor line after big motions + ;;neotree ; a project drawer, like NERDTree for vim + ophints ; highlight the region an operation acts on + (popup +defaults) ; tame sudden yet inevitable temporary windows + ;;tabs ; a tab bar for Emacs + (treemacs +lsp) ; a project drawer, like neotree but cooler + ;;unicode ; extended unicode support for various languages + vc-gutter ; vcs diff in the fringe + vi-tilde-fringe ; fringe tildes to mark beyond EOB + ;;window-select ; visually switch windows + workspaces ; tab emulation, persistence & separate workspaces + ;;zen ; distraction-free coding or writing + + :editor + (evil +everywhere); come to the dark side, we have cookies + ;;file-templates ; auto-snippets for empty files + fold ; (nigh) universal code folding + format ; (+onsave) automated prettiness + ;;god ; run Emacs commands without modifier keys + lispy ; vim for lisp, for people who don't like vim + multiple-cursors ; editing in many places at once + ;;objed ; text object editing for the innocent + ;;parinfer ; turn lisp into python, sort of + ;;rotate-text ; cycle region at point between text candidates + snippets ; my elves. They type so I don't have to + ;;word-wrap ; soft wrapping with language-aware indent + + :emacs + dired ; making dired pretty [functional] + electric ; smarter, keyword-based electric-indent + ;;ibuffer ; interactive buffer management + undo ; persistent, smarter undo for your inevitable mistakes + vc ; version-control and Emacs, sitting in a tree + + :term + eshell ; the elisp shell that works everywhere + ;;shell ; simple shell REPL for Emacs + ;;term ; basic terminal emulator for Emacs + ;;vterm ; the best terminal emulation in Emacs + + :checkers + syntax ; tasing you for every semicolon you forget + ;;(spell +flyspell) ; tasing you for misspelling mispelling + ;;grammar ; tasing grammar mistake every you make + + :tools + ;;ansible + (debugger +lsp) ; FIXME stepping through code, to help you add bugs + ;;direnv + docker + editorconfig ; let someone else argue about tabs vs spaces + ;;ein ; tame Jupyter notebooks with emacs + (eval +overlay) ; run code, run (also, repls) + ;;gist ; interacting with github gists + lookup ; navigate your code and its documentation + lsp ; M-x vscode + (magit +forge) ; a git porcelain for Emacs + make ; run make tasks from Emacs + ;;pass ; password manager for nerds + pdf ; pdf enhancements + ;;prodigy ; FIXME managing external services & code builders + ;;rgb ; creating color strings + taskrunner ; taskrunner for all your projects + terraform ; infrastructure as code + tmux ; an API for interacting with tmux + ;;upload ; map local to remote projects via ssh/ftp + + :os + (:if IS-MAC macos) ; improve compatibility with macOS + ;;tty ; improve the terminal Emacs experience + + :lang + ;;agda ; types of types of types of types... + ;;beancount ; mind the GAAP + (cc +lsp) ; C > C++ == 1 + ;;clojure ; java with a lisp + ;;common-lisp ; if you've seen one lisp, you've seen them all + ;;coq ; proofs-as-programs + ;;crystal ; ruby at the speed of c + ;;csharp ; unity, .NET, and mono shenanigans + ;;data ; config/data formats + ;;(dart +flutter) ; paint ui and not much else + ;;dhall + elixir ; erlang done right + ;;elm ; care for a cup of TEA? + emacs-lisp ; drown in parentheses + ;;erlang ; an elegant language for a more civilized age + ;;ess ; emacs speaks statistics + ;;factor + ;;faust ; dsp, but you get to keep your soul + ;;fsharp ; ML stands for Microsoft's Language + ;;fstar ; (dependent) types and (monadic) effects and Z3 + ;;gdscript ; the language you waited for + (go +lsp) ; the hipster dialect + (haskell +lsp) ; a language that's lazier than I am + ;;hy ; readability of scheme w/ speed of python + ;;idris ; a language you can depend on + json ; At least it ain't XML + (java +meghanada) ; the poster child for carpal tunnel syndrome + javascript ; all(hope(abandon(ye(who(enter(here)))))) + julia ; a better, faster MATLAB + ;;kotlin ; a better, slicker Java(Script) + latex ; writing papers in Emacs has never been so fun + ;;lean ; for folks with too much to prove + ;;ledger ; be audit you can be + lua ; one-based indices? one-based indices + markdown ; writing docs for people to ignore + ;;nim ; python + lisp at the speed of c + ;;nix ; I hereby declare "nix geht mehr!" + ;;ocaml ; an objective camel + org ; organize your plain life in plain text + php ; perl's insecure younger brother + ;;plantuml ; diagrams for confusing people more + ;;purescript ; javascript, but functional + (python +lsp +pyright) ; beautiful is better than ugly + ;;qt ; the 'cutest' gui framework ever + ;;racket ; a DSL for DSLs + ;;raku ; the artist formerly known as perl6 + ;;rest ; Emacs as a REST client + ;;rst ; ReST in peace + ;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} + (rust +lsp) ; Fe2O3.unwrap().unwrap().unwrap().unwrap() + ;;scala ; java, but good + (scheme +guile) ; a fully conniving family of lisps + (sh +fish) ; she sells {ba,z,fi}sh shells on the C xor + ;;sml + ;;solidity ; do you need a blockchain? No. + ;;swift ; who asked for emoji variables? + ;;terra ; Earth and Moon in alignment for performance. + web ; the tubes + yaml ; JSON, but readable + ;;zig ; C, but simpler + + :email + ;;(mu4e +org +gmail) + ;;notmuch + ;;(wanderlust +gmail) + + :app + calendar + ;;emms + everywhere ; *leave* Emacs!? You must be joking + irc ; how neckbeards socialize + ;;(rss +org) ; emacs as an RSS reader + ;;twitter ; twitter client https://twitter.com/vnought + + :config + literate + (default +bindings +smartparens)) diff --git a/.doom.d/lisp/emacs-everywhere.el b/.doom.d/lisp/emacs-everywhere.el new file mode 100644 index 0000000..673ef3b --- /dev/null +++ b/.doom.d/lisp/emacs-everywhere.el @@ -0,0 +1,546 @@ +;;; emacs-everywhere.el --- System-wide popup windows for quick edits -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 TEC + +;; Author: TEC +;; Maintainer: TEC +;; Created: February 06, 2021 +;; Modified: February 06, 2021 +;; Version: 0.1.0 +;; Keywords: conenience, frames +;; Homepage: https://github.com/tecosaur/emacs-everywhere +;; Package-Requires: ((emacs "26.3")) + +;;; License: + +;; This file is part of org-pandoc-import, which is not part of GNU Emacs. +;; SPDX-License-Identifier: GPL-3.0-or-later + +;;; Commentary: + +;; System-wide popup Emacs windows for quick edits + +;;; Code: + +(require 'cl-lib) +(require 'server) + +(defgroup emacs-everywhere () + "Customise group for Emacs-everywhere." + :group 'convenience) + +(define-obsolete-variable-alias + 'emacs-everywhere-paste-p 'emacs-everywhere-paste-command "0.1.0") + +(defvar emacs-everywhere--display-server + (cond + ((eq system-type 'darwin) 'quartz) + ((memq system-type '(ms-dos windows-nt cygwin)) 'windows) + ((executable-find "loginctl") + (pcase (string-trim + (shell-command-to-string "loginctl show-session $(loginctl | grep $(whoami) | awk '{print $1}') -p Type") + "Type=" "\n") + ("x11" 'x11) + ("wayland" 'wayland) + (_ 'unknown))) + (t 'unknown)) + "The detected display server.") + +(defcustom emacs-everywhere-paste-command + (pcase emacs-everywhere--display-server + ('quartz (list "osascript" "-e" "tell application \"System Events\" to keystroke \"v\" using command down")) + ('x11 (list "xdotool" "key" "--clearmodifiers" "Shift+Insert")) + ((or 'wayland 'unknown) + (list "notify-send" + "No paste command defined for emacs-everywhere" + "-a" "Emacs" "-i" "emacs"))) + "Command to trigger a system paste from the clipboard. +This is given as a list in the form (CMD ARGS...). +To not run any command, set to nil." + :type '(set (repeat string) (const nil)) + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-copy-command + (pcase emacs-everywhere--display-server + ('x11 (list "xclip" "-selection" "clipboard" "%f")) + ((and 'wayland (guard (executable-find "wl-copy"))) + (list "sh" "-c" "wl-copy < %f"))) + "Command to write to the system clipboard from a file (%f). +This is given as a list in the form (CMD ARGS...). +In the arguments, \"%f\" is treated as a placeholder for the path +to the file. +When nil, nothing is executed. +`gui-select-text' is always called on the buffer content, however experience +suggests that this can be somewhat flakey, and so an extra step to make sure +it worked can be a good idea." + :type '(set (repeat string) (const nil)) + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-window-focus-command + (pcase emacs-everywhere--display-server + ('quartz (list "osascript" "-e" "tell application \"%w\" to activate")) + ('x11 (list "xdotool" "windowactivate" "--sync" "%w"))) + "Command to refocus the active window when emacs-everywhere was triggered. +This is given as a list in the form (CMD ARGS...). +In the arguments, \"%w\" is treated as a placeholder for the window ID, +as returned by `emacs-everywhere-app-id'. +When nil, nothing is executed, and pasting is not attempted." + :type '(set (repeat string) (const nil)) + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-markdown-windows + '("Reddit" "Stack Exchange" "Stack Overflow" ; Sites + "Discord" "Element" "Slack" "HedgeDoc" "HackMD" "Zulip" ; Web Apps + "Pull Request" "Issue" "Comparing .*\\.\\.\\.") ; Github + "For use with `emacs-everywhere-markdown-p'. +Patterns which are matched against the window title." + :type '(rep string) + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-markdown-apps + '("Discord" "Element" "Fractal" "NeoChat" "Slack") + "For use with `emacs-everywhere-markdown-p'. +Patterns which are matched against the app name." + :type '(rep string) + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-frame-name-format "Emacs Everywhere :: %s — %s" + "Format string used to produce the frame name. +Formatted with the app name, and truncated window name." + :type 'string + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-init-hooks + `(emacs-everywhere-set-frame-name + emacs-everywhere-set-frame-position + ,(cond + ((executable-find "pandoc") #'org-mode) + ((fboundp 'markdown-mode) #'emacs-everywhere-major-mode-org-or-markdown) + (t #'text-mode)) + emacs-everywhere-insert-selection + emacs-everywhere-remove-trailing-whitespace + emacs-everywhere-init-spell-check) + "Hooks to be run before function `emacs-everywhere-mode'." + :type 'hook + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-final-hooks + '(emacs-everywhere-remove-trailing-whitespace + emacs-everywhere-return-converted-org-to-gfm) + "Hooks to be run just before content is copied." + :type 'hook + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-frame-parameters + `((name . "emacs-everywhere") + (width . 80) + (height . 12)) + "Parameters `make-frame' recognises to apply to the emacs-everywhere frame." + :type 'list + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-file-dir + temporary-file-directory + "The default directory for temp files generated by `emacs-everywhere-filename-function'." + :type 'string + :group 'emacs-everywhere) + +(defcustom emacs-everywhere-file-patterns + (let ((default-directory emacs-everywhere-file-dir)) + (list (concat "^" (regexp-quote (file-truename "emacs-everywhere-"))) + ;; For qutebrowser 'editor.command' support + (concat "^" (regexp-quote (file-truename "qutebrowser-editor-"))))) + "A list of file regexps to activate `emacs-everywhere-mode' for." + :type '(repeat regexp) + :group 'emacs-everywhere) + +(defun emacs-everywhere-temp-filename (app-info) + "Generate a temp file." + (concat "emacs-everywhere-" + (format-time-string "%Y%m%d-%H%M%S-" (current-time)) + (emacs-everywhere-app-class app-info))) + +(defcustom emacs-everywhere-filename-function + #'emacs-everywhere-temp-filename + "A function which generates a file name for the buffer. +The function is passed the result of `emacs-everywhere-app-info'. +Make sure that it will be matched by `emacs-everywhere-file-patterns'." + :type 'function + :group 'emacs-everywhere) + +;; Semi-internal variables + +(defconst emacs-everywhere-osascript-accessibility-error-message + "osascript is not allowed assistive access" + "String to search for to determine if Emacs does not have accessibility rights.") + +(defvar-local emacs-everywhere-current-app nil + "The current `emacs-everywhere-app'") +;; Prevents buffer-local variable from being unset by major mode changes +(put 'emacs-everywhere-current-app 'permanent-local t) + +(defvar-local emacs-everywhere--contents nil) + +;; Make the byte-compiler happier + +(declare-function org-in-src-block-p "org") +(declare-function org-ctrl-c-ctrl-c "org") +(declare-function org-export-to-buffer "ox") +(declare-function evil-insert-state "evil-states") +(declare-function spell-fu-buffer "spell-fu") +(declare-function markdown-mode "markdown-mode") + +;;; Primary functionality + +;;;###autoload +(defun emacs-everywhere (&optional file line column) + "Lanuch the emacs-everywhere frame from emacsclient." + (let ((app-info (emacs-everywhere-app-info))) + (apply #'call-process "emacsclient" nil 0 nil + (delq + nil (list + (when (server-running-p) + (if server-use-tcp + (concat "--server-file=" + (shell-quote-argument + (expand-file-name server-name server-auth-dir))) + (concat "--socket-name=" + (shell-quote-argument + (expand-file-name server-name server-socket-dir))))) + "-c" "-F" + (prin1-to-string + (cons (cons 'emacs-everywhere-app app-info) + emacs-everywhere-frame-parameters)) + (cond ((and line column) (format "+%d:%d" line column)) + (line (format "+%d" line))) + (or file + (expand-file-name + (funcall emacs-everywhere-filename-function app-info) + emacs-everywhere-file-dir))))))) + +(defun emacs-everywhere-file-p (file) + "Return non-nil if FILE should be handled by emacs-everywhere. +This matches FILE against `emacs-everywhere-file-patterns'." + (let ((file (file-truename file))) + (cl-some (lambda (pattern) (string-match-p pattern file)) + emacs-everywhere-file-patterns))) + +;;;###autoload +(defun emacs-everywhere-initialise () + "Entry point for the executable. +APP is an `emacs-everywhere-app' struct." + (let ((file (buffer-file-name (buffer-base-buffer)))) + (when (and file (emacs-everywhere-file-p file)) + (let ((app (or (frame-parameter nil 'emacs-everywhere-app) + (emacs-everywhere-app-info)))) + (setq-local emacs-everywhere-current-app app) + (with-demoted-errors "Emacs Everywhere: error running init hooks, %s" + (run-hooks 'emacs-everywhere-init-hooks)) + (emacs-everywhere-mode 1) + (setq emacs-everywhere--contents (buffer-string)))))) + +;;;###autoload +(add-hook 'server-visit-hook #'emacs-everywhere-initialise) +(add-hook 'server-done-hook #'emacs-everywhere-finish) + +(defvar emacs-everywhere-mode-initial-map + (let ((keymap (make-sparse-keymap))) + (define-key keymap (kbd "DEL") #'emacs-everywhere-erase-buffer) + (define-key keymap (kbd "C-SPC") #'emacs-everywhere-erase-buffer) + keymap) + "Transient keymap invoked when an emacs-everywhere buffer is first created. +Set to `nil' to prevent this transient map from activating in emacs-everywhere +buffers.") + +(define-minor-mode emacs-everywhere-mode + "Tweak the current buffer to add some emacs-everywhere considerations." + :init-value nil + :keymap `((,(kbd "C-c C-c") . emacs-everywhere-finish-or-ctrl-c-ctrl-c) + (,(kbd "C-x 5 0") . emacs-everywhere-finish) + (,(kbd "C-c C-k") . emacs-everywhere-abort)) + ;; line breaking + (turn-off-auto-fill) + (visual-line-mode t) + ;; DEL/C-SPC to clear (first keystroke only) + (when (keymapp emacs-everywhere-mode-initial-map) + (set-transient-map emacs-everywhere-mode-initial-map))) + +(defun emacs-everywhere-erase-buffer () + "Delete the contents of the current buffer." + (interactive) + (delete-region (point-min) (point-max))) + +(defun emacs-everywhere-finish-or-ctrl-c-ctrl-c () + "Finish emacs-everywhere session or invoke `org-ctrl-c-ctrl-c' in org-mode." + (interactive) + (if (and (eq major-mode 'org-mode) + (org-in-src-block-p)) + (org-ctrl-c-ctrl-c) + (emacs-everywhere-finish))) + +(defun emacs-everywhere-finish (&optional abort) + "Copy buffer content, close emacs-everywhere window, and maybe paste. +Must only be called within a emacs-everywhere buffer. +Never paste content when ABORT is non-nil." + (interactive) + (when emacs-everywhere-mode + (when (equal emacs-everywhere--contents (buffer-string)) + (setq abort t)) + (unless abort + (run-hooks 'emacs-everywhere-final-hooks) + (gui-select-text (buffer-string)) + (when emacs-everywhere-copy-command ; handle clipboard finicklyness + (let ((inhibit-message t) + (require-final-newline nil) + write-file-functions) + (write-file buffer-file-name) + (apply #'call-process (car emacs-everywhere-copy-command) + nil nil nil + (mapcar (lambda (arg) + (replace-regexp-in-string "%f" buffer-file-name arg)) + (cdr emacs-everywhere-copy-command)))))) + (sleep-for 0.01) ; prevents weird multi-second pause, lets clipboard info propagate + (when emacs-everywhere-window-focus-command + (let* ((window-id (emacs-everywhere-app-id emacs-everywhere-current-app)) + (window-id-str (if (numberp window-id) (number-to-string window-id) window-id))) + (apply #'call-process (car emacs-everywhere-window-focus-command) + nil nil nil + (mapcar (lambda (arg) + (replace-regexp-in-string "%w" window-id-str arg)) + (cdr emacs-everywhere-window-focus-command))) + ;; The frame only has this parameter if this package initialized the temp + ;; file its displaying. Otherwise, it was created by another program, likely + ;; a browser with direct EDITOR support, like qutebrowser. + (when (and (frame-parameter nil 'emacs-everywhere-app) + emacs-everywhere-paste-command + (not abort)) + (apply #'call-process (car emacs-everywhere-paste-command) + nil nil nil (cdr emacs-everywhere-paste-command))))) + ;; Clean up after ourselves in case the buffer survives `server-buffer-done' + ;; (b/c `server-existing-buffer' is non-nil). + (emacs-everywhere-mode -1) + (server-buffer-done (current-buffer)))) + +(defun emacs-everywhere-abort () + "Abort current emacs-everywhere session." + (interactive) + (set-buffer-modified-p nil) + (emacs-everywhere-finish t)) + +;;; Window info + +(cl-defstruct emacs-everywhere-app + "Metadata about the last focused window before emacs-everywhere was invoked." + id class title geometry) + +(defun emacs-everywhere-app-info () + "Return information on the active window." + (let ((w (pcase system-type + (`darwin (emacs-everywhere-app-info-osx)) + (_ (emacs-everywhere-app-info-linux))))) + (setf (emacs-everywhere-app-title w) + (replace-regexp-in-string + (format " ?-[A-Za-z0-9 ]*%s" + (regexp-quote (emacs-everywhere-app-class w))) + "" + (replace-regexp-in-string + "[^[:ascii:]]+" "-" (emacs-everywhere-app-title w)))) + w)) + +(defun emacs-everywhere-call (command &rest args) + "Execute COMMAND with ARGS synchronously." + (with-temp-buffer + (apply #'call-process command nil t nil (remq nil args)) + (when (and (eq system-type 'darwin) + (string-match-p emacs-everywhere-osascript-accessibility-error-message (buffer-string))) + (call-process "osascript" nil nil nil + "-e" (format "display alert \"emacs-everywhere\" message \"Emacs has not been granted accessibility permissions, cannot run emacs-everywhere! +Please go to 'System Preferences > Security & Privacy > Privacy > Accessibility' and allow Emacs.\"" )) + (error "MacOS accessibility error, aborting.")) + (string-trim (buffer-string)))) + +(defun emacs-everywhere-app-info-linux () + "Return information on the active window, on linux." + (let ((window-id (emacs-everywhere-call "xdotool" "getactivewindow"))) + (let ((app-name + (car (split-string-and-unquote + (string-trim-left + (emacs-everywhere-call "xprop" "-id" window-id "WM_CLASS") + "[^ ]+ = \"[^\"]+\", ")))) + (window-title + (car (split-string-and-unquote + (string-trim-left + (emacs-everywhere-call "xprop" "-id" window-id "_NET_WM_NAME") + "[^ ]+ = ")))) + (window-geometry + (let ((info (mapcar (lambda (line) + (split-string line ":" nil "[ \t]+")) + (split-string + (emacs-everywhere-call "xwininfo" "-id" window-id) "\n")))) + (mapcar #'string-to-number + (list (cadr (assoc "Absolute upper-left X" info)) + (cadr (assoc "Absolute upper-left Y" info)) + (cadr (assoc "Relative upper-left X" info)) + (cadr (assoc "Relative upper-left Y" info)) + (cadr (assoc "Width" info)) + (cadr (assoc "Height" info))))))) + (make-emacs-everywhere-app + :id (string-to-number window-id) + :class app-name + :title window-title + :geometry (list + (if (= (nth 0 window-geometry) (nth 2 window-geometry)) + (nth 0 window-geometry) + (- (nth 0 window-geometry) (nth 2 window-geometry))) + (if (= (nth 1 window-geometry) (nth 3 window-geometry)) + (nth 1 window-geometry) + (- (nth 1 window-geometry) (nth 3 window-geometry))) + (nth 4 window-geometry) + (nth 5 window-geometry)))))) + +(defvar emacs-everywhere--dir (file-name-directory load-file-name)) + +(defun emacs-everywhere-app-info-osx () + "Return information on the active window, on osx." + (emacs-everywhere-ensure-oscascript-compiled) + (let ((default-directory emacs-everywhere--dir)) + (let ((app-name (emacs-everywhere-call + "osascript" "app-name")) + (window-title (emacs-everywhere-call + "osascript" "window-title")) + (window-geometry (mapcar #'string-to-number + (split-string + (emacs-everywhere-call + "osascript" "window-geometry") ", ")))) + (make-emacs-everywhere-app + :id app-name + :class app-name + :title window-title + :geometry window-geometry)))) + +(defun emacs-everywhere-ensure-oscascript-compiled (&optional force) + "Ensure that compiled oscascript files are present. +Will always compile when FORCE is non-nil." + (unless (and (file-exists-p "app-name") + (file-exists-p "window-geometry") + (file-exists-p "window-title") + (not force)) + (let ((default-directory emacs-everywhere--dir) + (app-name + "tell application \"System Events\" + set frontAppName to name of first application process whose frontmost is true +end tell +return frontAppName") + (window-geometry + "tell application \"System Events\" + set frontWindow to front window of (first application process whose frontmost is true) + set windowPosition to (get position of frontWindow) + set windowSize to (get size of frontWindow) +end tell +return windowPosition & windowSize") + (window-title + "set windowTitle to \"\" +tell application \"System Events\" + set frontAppProcess to first application process whose frontmost is true +end tell +tell frontAppProcess + if count of windows > 0 then + set windowTitle to name of front window + end if +end tell +return windowTitle")) + (dolist (script `(("app-name" . ,app-name) + ("window-geometry" . ,window-geometry) + ("window-title" . ,window-title))) + (write-region (cdr script) nil (concat (car script) ".applescript")) + (shell-command (format "osacompile -r scpt:128 -t osas -o %s %s" + (car script) (concat (car script) ".applescript"))))))) + +;;; Secondary functionality + +(defun emacs-everywhere-set-frame-name () + "Set the frame name based on `emacs-everywhere-frame-name-format'." + (set-frame-name + (format emacs-everywhere-frame-name-format + (emacs-everywhere-app-class emacs-everywhere-current-app) + (truncate-string-to-width + (emacs-everywhere-app-title emacs-everywhere-current-app) + 45 nil nil "…")))) + +(defun emacs-everywhere-remove-trailing-whitespace () + "Move point to the end of the buffer, and remove all trailing whitespace." + (goto-char (max-char)) + (delete-trailing-whitespace) + (delete-char (- (skip-chars-backward "\n")))) + +(defun emacs-everywhere-set-frame-position () + "Set the size and position of the emacs-everywhere frame." + (cl-destructuring-bind (x . y) (mouse-absolute-pixel-position) + (set-frame-position (selected-frame) + (- x 100) + (- y 50)))) + +(defun emacs-everywhere-insert-selection () + "Insert the last text selection into the buffer." + (if (eq system-type 'darwin) + (progn + (call-process "osascript" nil nil nil + "-e" "tell application \"System Events\" to keystroke \"c\" using command down") + (sleep-for 0.01) ; lets clipboard info propagate + (yank)) + (when-let ((selection (gui-get-selection 'PRIMARY 'UTF8_STRING))) + (gui-backend-set-selection 'PRIMARY "") + (insert selection))) + (when (and (eq major-mode 'org-mode) + (emacs-everywhere-markdown-p) + (executable-find "pandoc")) + (shell-command-on-region (point-min) (point-max) + "pandoc -f markdown-auto_identifiers -t org" nil t) + (deactivate-mark) (goto-char (point-max))) + (cond ((bound-and-true-p evil-local-mode) (evil-insert-state)))) + +(defun emacs-everywhere-init-spell-check () + "Run a spell check function on the buffer, using a relevant enabled mode." + (cond ((bound-and-true-p spell-fu-mode) (spell-fu-buffer)) + ((bound-and-true-p flyspell-mode) (flyspell-buffer)))) + +(defun emacs-everywhere-markdown-p () + "Return t if the original window is recognised as markdown-flavoured." + (let ((title (emacs-everywhere-app-title emacs-everywhere-current-app)) + (class (emacs-everywhere-app-class emacs-everywhere-current-app))) + (or (cl-some (lambda (pattern) + (string-match-p pattern title)) + emacs-everywhere-markdown-windows) + (cl-some (lambda (pattern) + (string-match-p pattern class)) + emacs-everywhere-markdown-apps)))) + +(defun emacs-everywhere-major-mode-org-or-markdown () + "Use markdow-mode, when window is recognised as markdown-flavoured. +Otherwise use `org-mode'." + (if (emacs-everywhere-markdown-p) + (markdown-mode) + (org-mode))) + +(defcustom emacs-everywhere-org-export-options + "#+property: header-args :exports both +#+options: toc:nil\n" + "A string inserted at the top of the Org buffer prior to export. +This is with the purpose of setting #+property and #+options parameters. +Should end in a newline to avoid interfering with the buffer content." + :type 'string + :group 'emacs-everywhere) + +(defvar org-export-show-temporary-export-buffer) +(defun emacs-everywhere-return-converted-org-to-gfm () + "When appropriate, convert org buffer to markdown." + (when (and (eq major-mode 'org-mode) + (emacs-everywhere-markdown-p)) + (goto-char (point-min)) + (insert emacs-everywhere-org-export-options) + (let (org-export-show-temporary-export-buffer) + (require 'ox-md) + (org-export-to-buffer (if (featurep 'ox-gfm) 'gfm 'md) (current-buffer))))) + +(provide 'emacs-everywhere) +;;; emacs-everywhere.el ends here diff --git a/.doom.d/packages.el b/.doom.d/packages.el new file mode 100644 index 0000000..60cf23a --- /dev/null +++ b/.doom.d/packages.el @@ -0,0 +1,57 @@ +;; -*- no-byte-compile: t; -*- +;;; $DOOMDIR/packages.el + +;; To install a package with Doom you must declare them here and run 'doom sync' +;; on the command line, then restart Emacs for the changes to take effect -- or +;; use 'M-x doom/reload'. + + +;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror: +;(package! some-package) + +;; To install a package directly from a remote git repo, you must specify a +;; `:recipe'. You'll find documentation on what `:recipe' accepts here: +;; https://github.com/raxod502/straight.el#the-recipe-format +;(package! another-package +; :recipe (:host github :repo "username/repo")) + +;; If the package you are trying to install does not contain a PACKAGENAME.el +;; file, or is located in a subdirectory of the repo, you'll need to specify +;; `:files' in the `:recipe': +;(package! this-package +; :recipe (:host github :repo "username/repo" +; :files ("some-file.el" "src/lisp/*.el"))) + +;; If you'd like to disable a package included with Doom, you can do so here +;; with the `:disable' property: +;(package! builtin-package :disable t) + +;; You can override the recipe of a built in package without having to specify +;; all the properties for `:recipe'. These will inherit the rest of its recipe +;; from Doom or MELPA/ELPA/Emacsmirror: +;(package! builtin-package :recipe (:nonrecursive t)) +;(package! builtin-package-2 :recipe (:repo "myfork/package")) + +;; Specify a `:branch' to install a package from a particular branch or tag. +;; This is required for some packages whose default branch isn't 'master' (which +;; our package manager can't deal with; see raxod502/straight.el#279) +;(package! builtin-package :recipe (:branch "develop")) + +;; Use `:pin' to specify a particular commit to install. +;(package! builtin-package :pin "1a2b3c4d5e") + + +;; Doom's packages are pinned to a specific commit and updated from release to +;; release. The `unpin!' macro allows you to unpin single packages... +;(unpin! pinned-package) +;; ...or multiple packages +;(unpin! pinned-package another-pinned-package) +;; ...Or *all* packages (NOT RECOMMENDED; will likely break things) +;(unpin! t) + +(package! emacs-everywhere :ignore t) +(package! doom-snippets :ignore t) + +(package! copilot + :recipe (:host github :repo "zerolfx/copilot.el" + :files ("dist" "copilot.el"))) diff --git a/.doom.d/snippets/org-config-mode/.yas-parents b/.doom.d/snippets/org-config-mode/.yas-parents new file mode 100644 index 0000000..e69de29 diff --git a/.doom.d/snippets/org-mode/src_elisp b/.doom.d/snippets/org-mode/src_elisp new file mode 100644 index 0000000..292f0f6 --- /dev/null +++ b/.doom.d/snippets/org-mode/src_elisp @@ -0,0 +1,8 @@ +# -*- mode: snippet -*- +# name: src_elisp +# key: / scss-mode + (css-proprietary-property :foreground orange) + (css-property :foreground magenta) + (css-selector :foreground blue) + ;;;; doom-modeline + (doom-modeline-bar :background (if doom-catppuccin-brighter-modeline modeline-bg highlight)) + (doom-modeline-buffer-file :inherit 'mode-line-buffer-id :weight 'bold) + (doom-modeline-buffer-path :inherit 'mode-line-emphasis :weight 'bold) + (doom-modeline-buffer-project-root :foreground magenta :weight 'bold) + ;;;; ivy + (ivy-current-match :background dark-blue :distant-foreground base0 :weight 'normal) + ;;;; LaTeX-mode + (font-latex-math-face :foreground magenta) + ;;;; markdown-mode + (markdown-markup-face :foreground base5) + (markdown-header-face :inherit 'bold :foreground red) + ((markdown-code-face &override) :background (doom-lighten base3 0.05)) + ;;;; rjsx-mode + ) + + ;;;; Base theme variable overrides- + ()) + +;;; doom-catppuccin-theme.el ends here