A *TON* of changes
This commit is contained in:
parent
003bc783d7
commit
b54abacff3
@ -3,6 +3,14 @@
|
||||
;;; Code:
|
||||
(require 'eglot)
|
||||
|
||||
(defconst ltex-eglot-supported-languages
|
||||
'("ar" "ast-ES" "be-BY" "br-FR" "ca-ES" "ca-ES-valencia" "da-DK" "de" "de-AT"
|
||||
"de-CH" "de-DE" "de-DE-x-simple-language" "el-GR" "en" "en-AU" "en-CA" "en-GB"
|
||||
"en-NZ" "en-US" "en-ZA" "eo" "es" "es-AR" "fa" "fr" "ga-IE" "gl-ES" "it"
|
||||
"ja-JP" "km-KH" "nl" "nl-BE" "pl-PL" "pt" "pt-AO" "pt-BR" "pt-MZ" "pt-PT"
|
||||
"ro-RO" "ru-RU" "sk-SK" "sl-SI" "sv" "ta-IN" "tl-PH" "uk-UA" "zh-CN")
|
||||
"List of languages supportd by LTeX.")
|
||||
|
||||
(defcustom ltex-eglot-server-binary "ltex-ls"
|
||||
"The binary to use for the LTeX LSP server."
|
||||
:group 'ltex-eglot
|
||||
@ -43,6 +51,14 @@
|
||||
(const :tag "Enabled" t)
|
||||
(const :tag "Disabled" nil)))
|
||||
|
||||
(defcustom ltex-eglot-spell-check-rules
|
||||
'(:en-US ["EN_CONTRACTION_SPELLING" "MORFOLOGIK_RULE_EN_US"])
|
||||
"Rules to disable if `ltex-eglot-enable-spell-check' is nil."
|
||||
:group 'ltex-eglot
|
||||
:type '(plist :tag "Entries by language"
|
||||
:key-type (string :tag "Language Code")
|
||||
:value-type (repeat :tag "Rules" string)))
|
||||
|
||||
(defun ltex-eglot--entry-file-p (entry)
|
||||
"Check if ENTRY would be concidered a file by LTex LSP."
|
||||
(when (stringp entry)
|
||||
@ -81,8 +97,8 @@ This is meant to check file-local saftey for the likes of
|
||||
:value-type (repeat :tag "Words" string))
|
||||
:safe 'ltex-eglot--non-file-settings-plist-p)
|
||||
|
||||
(defun ltex-eglot--valid-latex-plist-p (plist)
|
||||
"Return non-nil if PLIST is an OK value for LaTeX options."
|
||||
(defun ltex-eglot--valid-latex-environments-p (plist)
|
||||
"Check if PLIST is an OK value for the `ltex-eglot-latex-environemnts'."
|
||||
(cl-loop for (name handling) on plist by 'cddr
|
||||
unless (and (stringp name)
|
||||
(member handling '("ignore" "default")))
|
||||
@ -100,6 +116,15 @@ This is meant to check file-local saftey for the likes of
|
||||
(const :tag "Check" "default")))
|
||||
:safe 'ltex-eglot--valid-latex-plist-p)
|
||||
|
||||
(defun ltex-eglot--valid-latex-commands-p (plist)
|
||||
"Check if PLIST is an OK value for the `ltex-eglot-latex-commands'."
|
||||
(cl-loop for (name handling) on plist by 'cddr
|
||||
unless (and (stringp name)
|
||||
(member handling '("ignore" "default" "dummy"
|
||||
"pluralDummy" "vowelDummy")))
|
||||
do (cl-return)
|
||||
finally return t))
|
||||
|
||||
(defcustom ltex-eglot-latex-commands ()
|
||||
"Plist controlling the handling of LaTeX commands."
|
||||
:group 'ltex-eglot
|
||||
@ -107,8 +132,13 @@ This is meant to check file-local saftey for the likes of
|
||||
:tag "Commands"
|
||||
:key-type (string :tag "Name")
|
||||
:value-type (choice :tag "Handling"
|
||||
(const :tag "Default" "default")
|
||||
(const :tag "Ignore" "ignore")
|
||||
(const :tag "Check" "default")))
|
||||
(const :tag "Replace with dummy word" "dummy")
|
||||
(const :tag "Replace with dummy plural word"
|
||||
"pluralDummy")
|
||||
(const :tag "Replace with dummy vowel word"
|
||||
"vowelDummy")))
|
||||
:safe 'ltex-eglot--valid-latex-plist-p)
|
||||
|
||||
(defun ltex-eglot--valid-bibtex-plist-p (plist)
|
||||
@ -138,6 +168,19 @@ This is meant to check file-local saftey for the likes of
|
||||
(const :tag "Disabled" nil))
|
||||
:safe 'booleanp)
|
||||
|
||||
(defcustom ltex-eglot-variable-save-method 'dir
|
||||
"How to save variables added by quick fixes.
|
||||
This is one of the following:
|
||||
- \\='dir\tSave in .dir-locals.el
|
||||
- \\='file\tSave as a file local variable
|
||||
- nil\tJust set the buffer local value, don't save the variable"
|
||||
:group 'ltex-eglot
|
||||
:type '(choice :tag "Save method"
|
||||
(const :tag "Directory local (saved)" dir)
|
||||
(const :tag "File local (saved)" file)
|
||||
(const :tag "Buffer local (not saved)" nil))
|
||||
:safe 'symbolp)
|
||||
|
||||
(defvar ltex-eglot-hidden-false-positives nil
|
||||
"List of hidden false positives.
|
||||
This is intented to be set from .dir-locals.el.")
|
||||
@ -203,7 +246,9 @@ well."
|
||||
(dictionary :initform nil
|
||||
:accessor ltex-eglot-server--dictionary)
|
||||
(disabled-rules :initform nil
|
||||
:accessor ltex-eglot-server--disabled-rules))
|
||||
:accessor ltex-eglot-server--disabled-rules)
|
||||
(language :initform nil
|
||||
:accessor ltex-eglot-server--language))
|
||||
"LTeX server class.")
|
||||
|
||||
(cl-defmethod ltex-eglot--disabled-rules-plist ((server ltex-eglot-server))
|
||||
@ -213,7 +258,7 @@ SERVER is the server from which to get the rules."
|
||||
(default-value 'ltex-eglot-disabled-rules)
|
||||
(ltex-eglot-server--disabled-rules server)
|
||||
(and (not ltex-eglot-enable-spell-check)
|
||||
'(:en-US ["EN_CONTRACTION_SPELLING" "MORFOLOGIK_RULE_EN_US"]))))
|
||||
ltex-eglot-spell-check-rules)))
|
||||
|
||||
(cl-defmethod ltex-eglot--setup-server ((server ltex-eglot-server))
|
||||
"Setup up SERVER for the first time."
|
||||
@ -235,6 +280,7 @@ SERVER is the server from which to get the rules."
|
||||
(if (local-variable-p 'ltex-eglot-dictionary)
|
||||
ltex-eglot-dictionary
|
||||
'(t))
|
||||
(ltex-eglot-server--language server) ltex-eglot-language
|
||||
(ltex-eglot-server--setup-done-p server) t)))
|
||||
|
||||
(cl-defmethod ltex-eglot--build-workspace-settings-plist ((server ltex-eglot-server))
|
||||
@ -242,7 +288,7 @@ SERVER is the server from which to get the rules."
|
||||
(unless (ltex-eglot-server--setup-done-p server)
|
||||
(ltex-eglot--setup-server server))
|
||||
(list
|
||||
:language ltex-eglot-language
|
||||
:language (ltex-eglot-server--language server)
|
||||
:dictionary (ltex-eglot--process-and-add-global
|
||||
(default-value 'ltex-eglot-dictionary)
|
||||
(ltex-eglot-server--dictionary server))
|
||||
@ -276,6 +322,16 @@ SERVER is the server from which to get the rules."
|
||||
(cl-callf nconc output (list t)))
|
||||
finally return output))
|
||||
|
||||
(cl-defmethod ltex-eglot--set-variable ((server ltex-eglot-server)
|
||||
variable value)
|
||||
"Set VARIABLE to VALUE in each buffer for SERVER.
|
||||
Also, maybe save VARIABLE in .dir-locals.el or as a file local variable."
|
||||
(cl-case ltex-eglot-variable-save-method
|
||||
(dir (add-dir-local-variable nil variable value))
|
||||
(file (add-file-local-variable variable value)))
|
||||
(dolist (buf (eglot--managed-buffers server))
|
||||
(setf (buffer-local-value variable buf) value)))
|
||||
|
||||
(defun ltex-eglot--handle-client-action (server command slot)
|
||||
"Handle the client side action COMMAND for SERVER.
|
||||
SLOT is a slot in SERVER."
|
||||
@ -291,15 +347,12 @@ SLOT is a slot in SERVER."
|
||||
(newval (ltex-eglot--merge-options-plists
|
||||
'list
|
||||
(slot-value server slot) (plist-get args arg))))
|
||||
(add-dir-local-variable nil local-var
|
||||
(ltex-eglot--cleanup-plist-for-dir-locals newval))
|
||||
(setf (slot-value server slot) newval)
|
||||
(dolist (buf (eglot--managed-buffers server))
|
||||
(setf (buffer-local-value local-var buf) newval))
|
||||
(ltex-eglot--set-variable server local-var newval)
|
||||
(eglot-signal-didChangeConfiguration server)))
|
||||
|
||||
(cl-defmethod eglot-execute ((server ltex-eglot-server) action)
|
||||
"Handelr for LTeX actions.
|
||||
"Handler for LTeX actions.
|
||||
ACTION is the action which to run on SERVER."
|
||||
(let ((kind (plist-get action :kind)))
|
||||
(pcase kind
|
||||
@ -319,7 +372,7 @@ ACTION is the action which to run on SERVER."
|
||||
PATH is the same as for OLDFUN, which is probably
|
||||
`eglot--workspace-configuration-plist'."
|
||||
(let ((conf (funcall oldfun server path)))
|
||||
(when (object-of-class-p server 'ltex-eglot-server)
|
||||
(when (ltex-eglot-server-p server)
|
||||
(let ((ltex-conf (plist-get conf :ltex)))
|
||||
(cl-loop for (prop val) on
|
||||
(ltex-eglot--build-workspace-settings-plist server)
|
||||
@ -329,13 +382,31 @@ PATH is the same as for OLDFUN, which is probably
|
||||
(setf (plist-get conf :ltex) ltex-conf)))
|
||||
conf))
|
||||
|
||||
(defun ltex-eglot-set-language (language server &optional no-save)
|
||||
"Set the SERVER's language to LANGUAGE.
|
||||
When called interactively, prompt for LANGUAGE. With NO-SAVE, don't save the
|
||||
language setting in any file."
|
||||
(interactive (list (completing-read "Language"
|
||||
ltex-eglot-supported-languages)
|
||||
(eglot-current-server)
|
||||
current-prefix-arg))
|
||||
(unless (ltex-eglot-server-p server)
|
||||
(user-error "Current server is not an LTeX server!"))
|
||||
(when-let ((server (eglot-current-server)))
|
||||
(setf (ltex-eglot-server--language server) language)
|
||||
(let ((ltex-eglot-variable-save-method
|
||||
(and (not no-save)
|
||||
ltex-eglot-variable-save-method)))
|
||||
(ltex-eglot--set-variable server 'ltex-eglot-language language))
|
||||
(eglot-signal-didChangeConfiguration server)))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'eglot-server-programs
|
||||
(cons ltex-eglot-modes
|
||||
(list
|
||||
'ltex-eglot-server
|
||||
ltex-eglot-server-binary "--server-type" "TcpSocket"
|
||||
"--port" :autoport)))
|
||||
"--no-endless" "--port" :autoport)))
|
||||
|
||||
;;;###autoload
|
||||
(advice-add 'eglot--workspace-configuration-plist :around
|
||||
|
354
elisp/org-mu4e-compose.el
Normal file
354
elisp/org-mu4e-compose.el
Normal file
@ -0,0 +1,354 @@
|
||||
;;; org-mu4e-compose.el --- Write mu4e messages with org-mode. -*- lexical-binding: t; -*-
|
||||
;;; Commentary:
|
||||
|
||||
;; I use evil. This file does not depend on evil, but some of these keybindings
|
||||
;; shadow useful org keybinding with message mode keybindings because the org
|
||||
;; bindings being shadowed are available with evil under some other key sequence.
|
||||
|
||||
;;; Code:
|
||||
(require 'mu4e)
|
||||
(require 'org-mime)
|
||||
(require 'shr)
|
||||
(require 'dom)
|
||||
(require 'sgml-mode)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar-local org-mu4e--html-message-p t
|
||||
"Weather or not the current message should be htmlized.")
|
||||
|
||||
(defvar-local org-mu4e--override-org-mode-check nil
|
||||
"Internal variable.
|
||||
See `org-mu4e--override-org-mode-check-advice' for information about what this
|
||||
does.")
|
||||
|
||||
(defvar org-mu4e--internal-message-mode-function
|
||||
(symbol-function 'mu4e-compose-mode)
|
||||
"The `message-mode' (or derived mode) used by `org-mu4e-compose-mode'.")
|
||||
|
||||
(defun org-mu4e--override-org-mode-check-advice (oldfun &rest r)
|
||||
"Around advice for various org mode functions.
|
||||
This function will call OLDFUN with arguments R with `major-mode' let-bound to
|
||||
\\='org-mode when `org-mu4e--override-org-mode-check' is t."
|
||||
(let ((major-mode (if org-mu4e--override-org-mode-check
|
||||
'org-mode
|
||||
major-mode)))
|
||||
(apply oldfun r)))
|
||||
|
||||
(advice-add 'org-element-at-point :around
|
||||
'org-mu4e--override-org-mode-check-advice)
|
||||
|
||||
(defun org-mu4e-toggle-htmlize-mssage (&optional arg no-message)
|
||||
"Toggle weather the current message should be htmlized.
|
||||
If ARG is a positive number or zero, enable htmlization, if it is negative,
|
||||
disable it. Otherwise, toggle it. With NO-MESSAGE, don't display a message
|
||||
about this change."
|
||||
(interactive "P")
|
||||
(setq org-mu4e--html-message-p (or (wholenump arg)
|
||||
(and (not arg)
|
||||
(not org-mu4e--html-message-p))))
|
||||
(unless no-message
|
||||
(message "Message will be %ssent with an HTML part."
|
||||
(if org-mu4e--html-message-p "" "not ")))
|
||||
(force-mode-line-update))
|
||||
|
||||
(defun org-mu4e--bounds-of-mime-part (type)
|
||||
"Find the bounds of the mime part for TYPE in the current buffer."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(when (and
|
||||
(re-search-forward (rx bol (literal mail-header-separator) eol)
|
||||
nil t)
|
||||
(re-search-forward (rx "<#multipart" (* any) ">")
|
||||
nil t)
|
||||
(re-search-forward (rx "<#part " (* any)
|
||||
"type=" (literal type) (* any) ">")
|
||||
nil t))
|
||||
(let ((start (match-end 0))
|
||||
(end (point-max)))
|
||||
(when (re-search-forward
|
||||
(rx (or (and "<#/" (or "part" "multipart") ">")
|
||||
(and "<#part" (* any) ">")))
|
||||
nil t)
|
||||
(setq end (match-beginning 0)))
|
||||
(cons (1+ start) end)))))
|
||||
|
||||
(defun org-mu4e--pretty-print-fontify-html-part ()
|
||||
"Pretty print and fontify the HTML part of the current buffer."
|
||||
(when-let ((bounds (org-mu4e--bounds-of-mime-part "text/html"))
|
||||
(real-buf (current-buffer)))
|
||||
(save-excursion
|
||||
(let ((content
|
||||
(with-temp-buffer
|
||||
(insert-buffer-substring real-buf (car bounds) (cdr bounds))
|
||||
(let (sgml-mode-hook html-mode-hook text-mode-hook)
|
||||
(html-mode))
|
||||
(sgml-pretty-print (point-min) (point-max))
|
||||
(indent-region (point-min) (point-max))
|
||||
(put-text-property (point-min) (point-max) 'fontified nil)
|
||||
(font-lock-ensure)
|
||||
(buffer-string))))
|
||||
(delete-region (car bounds) (cdr bounds))
|
||||
(goto-char (car bounds))
|
||||
(insert content)))))
|
||||
|
||||
(defun org-mu4e--htmlize-and-cleanup ()
|
||||
"HTMLize and cleanup the visible portion of the buffer.
|
||||
This moves point, wrap it in `save-excursion' if that is a problem."
|
||||
(org-mime-htmlize)
|
||||
;; IDK why, but the above function adds a bunch of newlines to the end
|
||||
;; of the buffer.
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward (rx (group (* "\n")) "\n" eos) nil t)
|
||||
(delete-region (match-beginning 1)
|
||||
(match-end 1)))
|
||||
(font-lock-ensure)
|
||||
(org-mu4e--pretty-print-fontify-html-part))
|
||||
|
||||
(defun org-mu4e-preview-html ()
|
||||
"Preview the HTML version of the current buffer in a new buffer.
|
||||
Return the newly created buffer."
|
||||
(interactive)
|
||||
(let ((msg-buffer (current-buffer))
|
||||
(buffer (get-buffer-create "*Org-Mu4e HTML Preview*"))
|
||||
(bounds (point-min))
|
||||
(cur-max (point-max)))
|
||||
(without-restriction
|
||||
(with-current-buffer buffer
|
||||
(special-mode)
|
||||
(setq-local org-mu4e--override-org-mode-check t)
|
||||
;; Setup font-lock without all the other pesky major mode stuff
|
||||
(org-set-font-lock-defaults)
|
||||
(font-lock-add-keywords nil message-font-lock-keywords)
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(insert-buffer-substring msg-buffer)
|
||||
(narrow-to-region bounds cur-max)
|
||||
(org-mu4e--htmlize-and-cleanup))
|
||||
(goto-char (point-min))))
|
||||
(switch-to-buffer-other-window buffer)
|
||||
buffer))
|
||||
|
||||
(defun org-mu4e-render-preview ()
|
||||
"Render a preview of the HTML message."
|
||||
(interactive)
|
||||
(let ((msg-buffer (current-buffer))
|
||||
(buffer (get-buffer-create "*Org-Mu4e Render Preview*")))
|
||||
(save-excursion
|
||||
(without-restriction
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward (rx bol (literal mail-header-separator) eol)
|
||||
nil t)
|
||||
(let* ((start (1+ (match-end 0)))
|
||||
(org-export-with-latex org-mime-org-html-with-latex-default)
|
||||
(org-preview-latex-image-directory
|
||||
(expand-file-name "ltximg/" mm-tmp-directory))
|
||||
(default-directory org-preview-latex-image-directory)
|
||||
(org-html-postamble nil))
|
||||
(narrow-to-region start (point-max))
|
||||
(if-let ((export-data (org-export-as
|
||||
'html nil t nil
|
||||
org-mime-export-options)))
|
||||
(progn
|
||||
(with-current-buffer buffer
|
||||
(special-mode)
|
||||
(let ((inhibit-read-only t)
|
||||
(default-directory
|
||||
org-preview-latex-image-directory))
|
||||
(erase-buffer)
|
||||
(insert export-data)
|
||||
(shr-render-region (point-min) (point-max))
|
||||
;; The above function inserts a text directionality
|
||||
;; character and then two newlines, just to be safe,
|
||||
;; check for them, then hide them
|
||||
(goto-char (point-min))
|
||||
(let ((new-start (point-min)))
|
||||
(when (or (eq (char-after) #x200e)
|
||||
(eq (char-after) #x200f))
|
||||
(cl-incf new-start))
|
||||
(dotimes (_ 2)
|
||||
(forward-char)
|
||||
(when (eq (char-after) ?\n)
|
||||
(cl-incf new-start)))
|
||||
(narrow-to-region new-start (point-max)))))
|
||||
(switch-to-buffer-other-window buffer))
|
||||
(user-error "HTML export failed")))
|
||||
(user-error "Can't find message start in current buffer"))))))
|
||||
|
||||
(defun org-mu4e-send (&optional arg)
|
||||
"HTMLize and send the message in the current buffer.
|
||||
ARG is passed directly to `message-send'."
|
||||
;; This has to return a non-nil value so that org knows we handled the C-c C-c
|
||||
(interactive "P")
|
||||
(let ((modified (buffer-modified-p))
|
||||
;; we only restore the restriction if the sending below fails
|
||||
(old-rest (cons (point-min) (point-max))))
|
||||
(widen)
|
||||
(let ((save-text (buffer-substring-no-properties (point-min)
|
||||
(point-max))))
|
||||
(condition-case _
|
||||
(progn
|
||||
(when org-mu4e--html-message-p
|
||||
(org-mu4e--htmlize-and-cleanup))
|
||||
(message-send arg)
|
||||
'sent)
|
||||
((or error quit)
|
||||
(erase-buffer)
|
||||
(insert save-text)
|
||||
(narrow-to-region (car old-rest) (cdr old-rest))
|
||||
(restore-buffer-modified-p modified)
|
||||
'failed)))))
|
||||
|
||||
(defun org-mu4e-send-and-exit (&optional arg)
|
||||
"Call `org-mu4e-send', the save and kill the buffer.
|
||||
ARG is passed directly to `message-send'."
|
||||
(interactive "P")
|
||||
(when (eq (org-mu4e-send arg) 'sent)
|
||||
(message-kill-buffer))
|
||||
t ;; this tells org that we have handled the C-c C-c
|
||||
)
|
||||
|
||||
;;;###autoload
|
||||
(defun org-mu4e-compose-new (&rest r)
|
||||
"This is like `mu4e-compose-new', but it utilizes `org-mu4e-compose-mode'.
|
||||
Each of the arguments in R are the same as `mu4e-compose-new', and are directly
|
||||
passed to it."
|
||||
(interactive)
|
||||
;; Save local variables set by `mu4e-compose-new'
|
||||
(let ((org-mu4e--internal-message-mode-function
|
||||
(symbol-function 'mu4e-compose-mode)))
|
||||
(cl-letf (((symbol-function 'mu4e-compose-mode) 'org-mu4e-compose-mode))
|
||||
(apply 'mu4e-compose-new r))))
|
||||
|
||||
;;;###autoload
|
||||
(defvar-keymap org-mu4e-compose-mode-map
|
||||
:parent org-mode-map
|
||||
;; These come straight from `message-mode-map' and override `org-mode-map'
|
||||
"C-c C-f C-t" #'message-goto-to
|
||||
"C-c C-f C-o" #'message-goto-from
|
||||
"C-c C-f C-b" #'message-goto-bcc
|
||||
"C-c C-f C-w" #'message-goto-fcc
|
||||
"C-c C-f C-c" #'message-goto-cc
|
||||
"C-c C-f C-s" #'message-goto-subject
|
||||
"C-c C-f C-r" #'message-goto-reply-to
|
||||
"C-c C-f C-d" #'message-goto-distribution
|
||||
"C-c C-f C-f" #'message-goto-followup-to
|
||||
"C-c C-f C-m" #'message-goto-mail-followup-to
|
||||
"C-c C-f C-k" #'message-goto-keywords
|
||||
"C-c C-f C-u" #'message-goto-summary
|
||||
"C-c C-f C-i" #'message-insert-or-toggle-importance
|
||||
"C-c C-f C-a" #'message-generate-unsubscribed-mail-followup-to
|
||||
|
||||
;; modify headers (and insert notes in body)
|
||||
"C-c C-f s" #'message-change-subject
|
||||
;;
|
||||
"C-c C-f x" #'message-cross-post-followup-to
|
||||
;; prefix+message-cross-post-followup-to = same without cross-post
|
||||
"C-c C-f t" #'message-reduce-to-to-cc
|
||||
"C-c C-f a" #'message-add-archive-header
|
||||
;; mark inserted text
|
||||
"C-c M-m" #'message-mark-inserted-region
|
||||
"C-c M-f" #'message-mark-insert-file
|
||||
|
||||
"C-c C-b" #'message-goto-body
|
||||
"C-c C-i" #'message-goto-signature
|
||||
|
||||
"C-c C-t" #'message-insert-to
|
||||
"C-c C-f w" #'message-insert-wide-reply
|
||||
"C-c C-f C-e" #'message-insert-expires
|
||||
"C-c M-u" #'message-insert-or-toggle-importance
|
||||
"C-c M-n" #'message-insert-disposition-notification-to
|
||||
|
||||
"C-c C-y" #'message-yank-original
|
||||
"C-c C-M-y" #'message-yank-buffer
|
||||
"C-c C-S-q" #'message-fill-yanked-message
|
||||
"C-c M-s" #'message-insert-signature
|
||||
"C-c M-h" #'message-insert-headers
|
||||
"C-c M-o" #'message-sort-headers
|
||||
|
||||
;; C-c C-c to send and exit is handled by `org-ctrl-c-ctrl-c-hook'
|
||||
"C-c C-s" #'org-mu4e-send
|
||||
"C-c C-k" #'message-kill-buffer
|
||||
"C-c C-d" #'message-dont-send
|
||||
|
||||
"C-c M-k" #'message-kill-address
|
||||
"C-c M-e" #'message-elide-region
|
||||
"C-c M-v" #'message-delete-not-region
|
||||
"C-c M-z" #'message-kill-to-signature
|
||||
"<remap> <split-line>" #'message-split-line
|
||||
"<remap> <beginning-of-buffer>" #'mu4e-compose-goto-top
|
||||
"<remap> <end-of-buffer>" #'mu4e-compose-goto-bottom
|
||||
|
||||
"C-c M-r" #'message-insert-screenshot
|
||||
|
||||
"M-n" #'message-display-abbrev
|
||||
|
||||
"C-c M-t" #'org-mu4e-toggle-htmlize-mssage
|
||||
"C-c M-p C-p" #'org-mu4e-preview-html
|
||||
"C-c M-p C-w" #'org-mu4e-render-preview
|
||||
"C-c C-;" #'mu4e-compose-context-switch)
|
||||
|
||||
(defun org-mu4e--compose-mode-command-predicate (symbol buffer)
|
||||
"`org-mu4e-compose-mode' local value for command completion predicate.
|
||||
This will defer to the user's global value for the same, except that it allows
|
||||
for commands who are designed for `message-mode' and `mu4e-mode' as well.
|
||||
SYMBOL is the command to check. BUFFER is the buffer in which checking
|
||||
happens. BUFFER is ignored."
|
||||
(let ((real-fun (default-value 'read-extended-command-predicate))
|
||||
(modes (command-modes symbol)))
|
||||
(or (not real-fun)
|
||||
(member 'message-mode modes)
|
||||
(member 'mu4e-compose-mode modes)
|
||||
(funcall real-fun symbol buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode org-mu4e-compose-mode org-mode "mu4e:org-compose"
|
||||
"Major mode for editing mu4e messages with `org-mode' syntax.
|
||||
This is derived from `org-mode', but it also essentially runs
|
||||
`mu4e-compose-mode' and `message-mode'. Therefore, it runs their hooks too."
|
||||
;; Enable all the things from `mu4e-compose-mode' (which derives from
|
||||
;; `message-mode'), but don't let it change the major mode (or other things we
|
||||
;; care about).
|
||||
(when org-mu4e--internal-message-mode-function
|
||||
(let ((major-mode major-mode)
|
||||
(mode-name mode-name)
|
||||
(local-abbrev-table local-abbrev-table)
|
||||
(font-lock-defaults font-lock-defaults)
|
||||
;; some of these are not actually changed, but they are here just in
|
||||
;; case they change in the future...
|
||||
(comment-start comment-start)
|
||||
(comment-end comment-end)
|
||||
(comment-start-skip comment-start-skip)
|
||||
(comment-add comment-add)
|
||||
(comment-style comment-style))
|
||||
(cl-letf (((symbol-function 'kill-all-local-variables) 'ignore)
|
||||
((symbol-function 'use-local-map) 'ignore)
|
||||
((symbol-function 'set-syntax-table) 'ignore))
|
||||
(funcall org-mu4e--internal-message-mode-function))))
|
||||
;; Add `message-mode' keyword and quote highlighting on top of the org syntax
|
||||
;; highlighting
|
||||
(font-lock-add-keywords nil message-font-lock-keywords)
|
||||
(setq-local org-mu4e--override-org-mode-check t
|
||||
;; This does not work, but I am leaving it here to figure out later
|
||||
read-extended-command-predicate
|
||||
'org-mu4e--compose-mode-command-predicate)
|
||||
(add-to-list (make-local-variable 'org-ctrl-c-ctrl-c-final-hook)
|
||||
'org-mu4e-send-and-exit)
|
||||
(add-to-list (make-local-variable 'mode-line-misc-info)
|
||||
'(:eval (if org-mu4e--html-message-p
|
||||
"Text/HTML "
|
||||
"Text Only "))))
|
||||
|
||||
;;;###autoload
|
||||
(define-mail-user-agent 'org-mu4e-user-agent
|
||||
#'org-mu4e-compose-new
|
||||
#'org-mu4e-send-and-exit
|
||||
#'message-kill-buffer
|
||||
'message-send-hook)
|
||||
|
||||
;;;###autoload
|
||||
(defun org-mu4e-user-agent ()
|
||||
"Return `org-mu4e-user-agent'."
|
||||
'org-mu4e-user-agent)
|
||||
|
||||
(provide 'org-mu4e-compose)
|
||||
;;; org-mu4e-compose.el ends here
|
112
init.el
112
init.el
@ -126,6 +126,12 @@
|
||||
;; Make some commands easier to enter multiple times
|
||||
(repeat-mode 1)
|
||||
|
||||
;; Easier buffer navigation
|
||||
(keymap-global-set "C-c <" #'previous-buffer)
|
||||
(keymap-global-set "C-c >" #'next-buffer)
|
||||
(keymap-global-set "C-c k" #'previous-buffer)
|
||||
(keymap-global-set "C-c j" #'next-buffer)
|
||||
|
||||
;; Set fonts
|
||||
(add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono-12"))
|
||||
(add-hook 'server-after-make-frame-hook
|
||||
@ -142,6 +148,12 @@
|
||||
comment-multi-line t
|
||||
comment-empty-lines 'eol)
|
||||
(add-to-list 'auto-mode-alist '("\\.[cC][nN][fF]\\'" . conf-mode))
|
||||
(keymap-set emacs-lisp-mode-map "C-c C-r" #'eval-region)
|
||||
(defun my/-fix-emacs-lisp-mode-system-files ()
|
||||
(when (string-prefix-p lisp-directory buffer-file-name)
|
||||
;; system Emacs files use tab characters and look weird without this.
|
||||
(setq-local tab-width 8)))
|
||||
(add-hook 'emacs-lisp-mode-hook #'my/-fix-emacs-lisp-mode-system-files)
|
||||
|
||||
;; Tree sitter download locations
|
||||
(setq treesit-language-source-alist
|
||||
@ -356,8 +368,22 @@ directory. Otherwise, run `find-file' on that file."
|
||||
|
||||
;; kitty keyboard protocol
|
||||
(use-package kkp
|
||||
:bind ("C-q" . my/quoted-insert)
|
||||
:config
|
||||
(global-kkp-mode 1)
|
||||
(defun my/quoted-insert (arg)
|
||||
"Insert the next character using read-key, not read-char."
|
||||
(interactive "*p")
|
||||
;; Source: https://github.com/benjaminor/kkp/issues/11
|
||||
(let ((char (read-key)))
|
||||
;; Ensure char is treated as a character code for insertion
|
||||
(unless (characterp char)
|
||||
(user-error "%s is not a valid character"
|
||||
(key-description (vector char))))
|
||||
(when (numberp char)
|
||||
(while (> arg 0)
|
||||
(insert-and-inherit char)
|
||||
(setq arg (1- arg))))))
|
||||
(defun my/-kkp-fix-save-some-buffers (oldfun &optional arg pred)
|
||||
"Fix `save-some-buffers' when used in a terminal with kkp enabled."
|
||||
(let ((status (kkp--terminal-has-active-kkp-p)))
|
||||
@ -410,7 +436,11 @@ directory. Otherwise, run `find-file' on that file."
|
||||
(evil-define-key '(normal visual motion) dired-mode-map
|
||||
"u" #'dired-unmark)
|
||||
(evil-define-key '(normal visual motion) profiler-report-mode-map
|
||||
(kbd "TAB") #'profiler-report-toggle-entry))
|
||||
(kbd "TAB") #'profiler-report-toggle-entry)
|
||||
(eldoc-add-command 'evil-insert
|
||||
'evil-append
|
||||
'evil-insert-line
|
||||
'evil-append-line))
|
||||
(use-package evil-collection
|
||||
:after evil
|
||||
:diminish evil-collection-unimpaired-mode
|
||||
@ -454,7 +484,12 @@ directory. Otherwise, run `find-file' on that file."
|
||||
'paredit-open-angled
|
||||
'paredit-open-bracket
|
||||
'paredit-open-angled
|
||||
'paredit-open-parenthesis)
|
||||
'paredit-open-parenthesis
|
||||
'delete-indentation
|
||||
'evil-cp-insert
|
||||
'evil-cp-append
|
||||
'evil-cp-insert-at-beginning-of-form
|
||||
'evil-cp-insert-at-end-of-form)
|
||||
(define-key evil-cleverparens-mode-map (kbd "<normal-state> M-o") nil t)
|
||||
(defun my/-enable-evil-cleverparens ()
|
||||
(if (member major-mode '(lisp-mode emacs-lisp-mode
|
||||
@ -567,6 +602,10 @@ in the region and indents once)."
|
||||
(goto-char end))))
|
||||
(back-to-indentation)
|
||||
(lisp-dedent-adjust-parens)))
|
||||
(eldoc-add-command 'my/lisp-indent-adjust-parens
|
||||
'my/lisp-dedent-adjust-parens
|
||||
'lisp-indent-adjust-parens
|
||||
'lisp-dedent-adjust-parens)
|
||||
(evil-define-key '(normal visual) adjust-parens-mode-map
|
||||
(kbd "<tab>") #'my/lisp-indent-adjust-parens
|
||||
(kbd "<backtab>") #'my/lisp-dedent-adjust-parens))
|
||||
@ -577,7 +616,7 @@ in the region and indents once)."
|
||||
:config
|
||||
(require 'vlf-setup))
|
||||
|
||||
;; allow copy from termainl
|
||||
;; allow copy from terminal
|
||||
(use-package xclip
|
||||
:config
|
||||
(xclip-mode 1))
|
||||
@ -628,6 +667,8 @@ visual states."
|
||||
;; vertico
|
||||
(use-package vertico
|
||||
:bind (:map vertico-map
|
||||
("C-RET" . vertico-exit-input)
|
||||
("C-<return>" . vertico-exit-input)
|
||||
("C-S-k" . kill-line)
|
||||
("C-k" . vertico-previous)
|
||||
("C-j" . vertico-next)
|
||||
@ -647,7 +688,7 @@ visual states."
|
||||
(setq vertico-cycle t
|
||||
enable-recursive-minibuffers t
|
||||
read-extended-command-predicate #'command-completion-default-include-p
|
||||
minibuffer-prompt-properties '(read-only t
|
||||
minibuffer-prompt-properties '(read-only t ;; noindent 3
|
||||
cursor-intangible t
|
||||
face minibuffer-prompt))
|
||||
(vertico-mode 1)
|
||||
@ -983,8 +1024,12 @@ With PROJECT, give diagnostics for all buffers in the current project."
|
||||
(use-package eglot
|
||||
:demand t
|
||||
:pin gnu ;; try to force Elpa version to fix warnings
|
||||
:hook (eglot-managed-mode . my/-eglot-setup)
|
||||
:hook ((eglot-managed-mode . my/-eglot-setup)
|
||||
(text-mode . my/eglot-in-text-mode-only))
|
||||
:init
|
||||
(defun my/eglot-in-text-mode-only ()
|
||||
(when (eq major-mode 'text-mode)
|
||||
(eglot-ensure)))
|
||||
(defvar my/-eglot-documentation-buffer nil
|
||||
"Buffer for showing documentation for `my/eglot-documentation-at-point'.")
|
||||
(defun my/eglot-documentation-at-point ()
|
||||
@ -1889,6 +1934,7 @@ If no name is given, list all bookmarks instead."
|
||||
|
||||
;; org-mode
|
||||
(use-package org
|
||||
:pin gnu
|
||||
:bind (("C-c c" . org-capture)
|
||||
("C-c a" . org-agenda)
|
||||
("C-c l" . org-store-link)
|
||||
@ -1896,6 +1942,11 @@ If no name is given, list all bookmarks instead."
|
||||
("C-c t" . org-table-create))
|
||||
:hook (org-mode . org-table-header-line-mode)
|
||||
:init
|
||||
(font-lock-add-keywords 'org-mode
|
||||
`((,(rx bol (* " ") (group "-") " ")
|
||||
(0 (prog1 nil
|
||||
(compose-region (match-beginning 1)
|
||||
(match-end 1) "•"))))))
|
||||
(setq org-directory "~/org"
|
||||
org-agenda-files '("~/org/")
|
||||
org-log-into-drawer t
|
||||
@ -1903,13 +1954,27 @@ If no name is given, list all bookmarks instead."
|
||||
org-log-redeadline 'time
|
||||
org-log-reschedule 'time
|
||||
org-preview-latex-default-process 'dvisvgm
|
||||
org-highlight-latex-and-related '(native entities)
|
||||
org-startup-with-inline-images t
|
||||
org-adapt-indentation t
|
||||
org-hide-leading-stars t
|
||||
org-html-with-latex 'dvisvgm
|
||||
org-preview-latex-process-alist
|
||||
'((dvisvgm
|
||||
:image-input-type "dvi"
|
||||
:image-output-type "svg"
|
||||
:image-size-adjust (1.7 . 1.5)
|
||||
:latex-compiler ("pdflatex -interaction nonstopmode -output-format=dvi -output-directory=%o %f")
|
||||
:image-converter ("dvisvgm %o%b.dvi --no-fonts --exact-bbox --scale=%S --output=%O")))))
|
||||
:image-converter ("dvisvgm %o%b.dvi --no-fonts --exact-bbox --scale=%S --output=%O"))))
|
||||
(defun my/-org-allow-in-derived-mode (oldfun &rest r)
|
||||
"Allow OLDFUN to run, even if `major-mode' is only derived from `org-mode'.
|
||||
R is rest of the arguments to OLDFUN."
|
||||
(let ((major-mode (if (derived-mode-p 'org-mode)
|
||||
'org-mode
|
||||
major-mode)))
|
||||
(apply oldfun r)))
|
||||
(advice-add 'org-element-at-point :around 'my/-org-allow-in-derived-mode)
|
||||
(advice-add 'org-table-header-line-mode :around 'my/-org-allow-in-derived-mode))
|
||||
(use-package evil-org
|
||||
:after org
|
||||
:hook (org-mode . evil-org-mode)
|
||||
@ -1947,7 +2012,6 @@ The name is compared with the field name using TESTFN (defaults to `equal')."
|
||||
(mu4e-view-mode . my/-mu4e-setup-view-mode)
|
||||
(mu4e-compose-mode . my/-mu4e-setup-compose-mode))
|
||||
:bind (("C-x C-m" . mu4e)
|
||||
("C-x m" . mu4e-compose-new)
|
||||
:map message-mode-map
|
||||
("C-c k" . khard-insert-email-contact)
|
||||
:map mu4e-headers-mode-map
|
||||
@ -2004,8 +2068,8 @@ The name is compared with the field name using TESTFN (defaults to `equal')."
|
||||
(concat "flag:unread AND NOT flag:trashed AND NOT "
|
||||
"maildir:/protonmail/Trash AND NOT maildir:/protonmail/Spam")
|
||||
"Flag for mail which will appear as \"unread\" and will be notified.")
|
||||
(setq mail-user-agent 'mu4e-user-agent
|
||||
message-kill-buffer-on-exit t
|
||||
(setq message-kill-buffer-on-exit t
|
||||
message-confirm-send t
|
||||
message-send-mail-function 'sendmail-send-it
|
||||
mu4e-change-filenames-when-moving t
|
||||
mu4e-context-policy 'pick-first
|
||||
@ -2048,13 +2112,26 @@ The name is compared with the field name using TESTFN (defaults to `equal')."
|
||||
(mu4e t)
|
||||
(mu4e-context-switch nil "Personal")
|
||||
|
||||
;; org-msg
|
||||
(use-package org-msg
|
||||
:init
|
||||
(setq org-msg-default-alternatives '((new . (text html))
|
||||
(reply-to-html . (text html))
|
||||
(reply-to-text . (text)))
|
||||
org-msg-convert-citation t))
|
||||
;; mu4e compose HTML messages
|
||||
(use-package org-mime)
|
||||
(require 'org-mu4e-compose)
|
||||
(setq mail-user-agent 'org-mu4e-user-agent
|
||||
org-mime-org-html-with-latex-default 'dvisvgm
|
||||
org-mime-export-options '(:with-latex dvisvgm :with-footnotes t))
|
||||
(evil-define-key '(normal visual) org-mu4e-compose-mode-map
|
||||
"G" #'mu4e-compose-goto-bottom
|
||||
"gg" #'mu4e-compose-goto-top)
|
||||
(evil-define-key 'normal org-mu4e-compose-mode-map
|
||||
"ZZ" #'message-send-and-exit
|
||||
"ZD" #'message-dont-send
|
||||
"ZQ" #'message-kill-buffer
|
||||
"ZF" #'mml-attach-file)
|
||||
(defun my/-setup-org-mu4e-compose-mode ()
|
||||
"Setup up stuff in `org-mu4e-compose' buffers."
|
||||
(setq-local ltex-eglot-variable-save-method 'file)
|
||||
;; this should come last so it can pick up the above
|
||||
(eglot-ensure))
|
||||
(add-hook 'org-mu4e-compose-mode-hook #'my/-setup-org-mu4e-compose-mode)
|
||||
|
||||
;; elfeed
|
||||
(use-package elfeed
|
||||
@ -2089,7 +2166,8 @@ The name is compared with the field name using TESTFN (defaults to `equal')."
|
||||
(defun my/-helpful-setup-emacs-lisp-mode ()
|
||||
(setq-local evil-lookup-func #'helpful-at-point))
|
||||
(defun my/-setup-helpful-mode ()
|
||||
(setq-local evil-lookup-func #'helpful-at-point))
|
||||
(setq-local evil-lookup-func #'helpful-at-point
|
||||
tab-width 8))
|
||||
(defvar my/helpful-symbol-history-size 50
|
||||
"Max size of `my/helpful-symbol-history'.")
|
||||
(defvar my/helpful-symbol-history '()
|
||||
|
Loading…
Reference in New Issue
Block a user