Removed .doom.d, moved to .config/doom
This commit is contained in:
parent
6bd3184b2f
commit
fb6ba70d71
@ -1,431 +0,0 @@
|
||||
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Place your private configuration here! Remember, you do not need to run 'doom
|
||||
;; sync' after modifying this file!
|
||||
|
||||
|
||||
;; Some functionality uses this to identify you, e.g. GPG configuration, email
|
||||
;; clients, file templates and snippets.
|
||||
(setq user-full-name "Roger Gonzalez"
|
||||
user-mail-address "roger@rogs.me")
|
||||
|
||||
;; Add custom packages
|
||||
(add-to-list 'load-path "~/.doom.d/custom-packages")
|
||||
|
||||
;; Load custom packages
|
||||
(require 'screenshot)
|
||||
(require 'ox-slack)
|
||||
(require 'deferred)
|
||||
|
||||
;; Doom exposes five (optional) variables for controlling fonts in Doom. Here
|
||||
;; are the three important ones:
|
||||
;;
|
||||
;; + `doom-font'
|
||||
;; + `doom-variable-pitch-font'
|
||||
;; + `doom-big-font' -- used for `doom-big-font-mode'; use this for
|
||||
;; presentations or streaming.
|
||||
;;
|
||||
;; They all accept either a font-spec, font string ("Input Mono-12"), or xlfd
|
||||
;; font string. You generally only need these two:
|
||||
;; (setq doom-font (font-spec :family "monospace" :size 12 :weight 'semi-light)
|
||||
;; doom-variable-pitch-font (font-spec :family "sans" :size 13))
|
||||
|
||||
;; (setq doom-font (font-spec :family "Mononoki Nerd Font" :size 14)
|
||||
;; doom-variable-pitch-font (font-spec :family "sans")
|
||||
;; doom-big-font (font-spec :family "Mononoki Nerd Font" :size 24))
|
||||
(setq doom-font (font-spec :family "MesloLGS NF" :size 14)
|
||||
doom-variable-pitch-font (font-spec :family "sans")
|
||||
doom-big-font (font-spec :family "MesloLGS NF" :size 24))
|
||||
|
||||
;; There are two ways to load a theme. Both assume the theme is installed and
|
||||
;; available. You can either set `doom-theme' or manually load a theme with the
|
||||
;; `load-theme' function. This is the default:
|
||||
(after! doom-themes
|
||||
(setq doom-themes-enable-bold t
|
||||
doom-themes-enable-italic t))
|
||||
(custom-set-faces!
|
||||
'(font-lock-comment-face :slant italic)
|
||||
'(font-lock-keyword-face :slant italic))
|
||||
(setq doom-theme 'doom-badger)
|
||||
|
||||
;; If you use `org' and don't want your org files in the default location below,
|
||||
;; change `org-directory'. It must be set before org loads!
|
||||
(setq org-directory "~/org/")
|
||||
|
||||
;; This determines the style of line numbers in effect. If set to `nil', line
|
||||
;; numbers are disabled. For relative line numbers, set this to `relative'.
|
||||
(setq display-line-numbers-type 'relative)
|
||||
|
||||
|
||||
;; Here are some additional functions/macros that could help you configure Doom:
|
||||
;;
|
||||
;; - `load!' for loading external *.el files relative to this one
|
||||
;; - `use-package!' for configuring packages
|
||||
;; - `after!' for running code after a package has loaded
|
||||
;; - `add-load-path!' for adding directories to the `load-path', relative to
|
||||
;; this file. Emacs searches the `load-path' when you load packages with
|
||||
;; `require' or `use-package'.
|
||||
;; - `map!' for binding new keys
|
||||
;;
|
||||
;; To get information about any of these functions/macros, move the cursor over
|
||||
;; the highlighted symbol at press 'K' (non-evil users must press 'C-c c k').
|
||||
;; This will open documentation for it, including demos of how they are used.
|
||||
;;
|
||||
;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how
|
||||
;; they are implemented.
|
||||
;; HTTP Statuses for Helm
|
||||
(defvar helm-httpstatus-source
|
||||
'((name . "HTTP STATUS")
|
||||
(candidates . (("100 Continue") ("101 Switching Protocols")
|
||||
("102 Processing") ("200 OK")
|
||||
("201 Created") ("202 Accepted")
|
||||
("203 Non-Authoritative Information") ("204 No Content")
|
||||
("205 Reset Content") ("206 Partial Content")
|
||||
("207 Multi-Status") ("208 Already Reported")
|
||||
("300 Multiple Choices") ("301 Moved Permanently")
|
||||
("302 Found") ("303 See Other")
|
||||
("304 Not Modified") ("305 Use Proxy")
|
||||
("307 Temporary Redirect") ("400 Bad Request")
|
||||
("401 Unauthorized") ("402 Payment Required")
|
||||
("403 Forbidden") ("404 Not Found")
|
||||
("405 Method Not Allowed") ("406 Not Acceptable")
|
||||
("407 Proxy Authentication Required") ("408 Request Timeout")
|
||||
("409 Conflict") ("410 Gone")
|
||||
("411 Length Required") ("412 Precondition Failed")
|
||||
("413 Request Entity Too Large")
|
||||
("414 Request-URI Too Large")
|
||||
("415 Unsupported Media Type")
|
||||
("416 Request Range Not Satisfiable")
|
||||
("417 Expectation Failed") ("418 I'm a teapot")
|
||||
("421 Misdirected Request")
|
||||
("422 Unprocessable Entity") ("423 Locked")
|
||||
("424 Failed Dependency") ("425 No code")
|
||||
("426 Upgrade Required") ("428 Precondition Required")
|
||||
("429 Too Many Requests")
|
||||
("431 Request Header Fields Too Large")
|
||||
("449 Retry with") ("500 Internal Server Error")
|
||||
("501 Not Implemented") ("502 Bad Gateway")
|
||||
("503 Service Unavailable") ("504 Gateway Timeout")
|
||||
("505 HTTP Version Not Supported")
|
||||
("506 Variant Also Negotiates")
|
||||
("507 Insufficient Storage") ("509 Bandwidth Limit Exceeded")
|
||||
("510 Not Extended")
|
||||
("511 Network Authentication Required")))
|
||||
(action . message)))
|
||||
|
||||
(defun helm-httpstatus ()
|
||||
(interactive)
|
||||
(helm-other-buffer '(helm-httpstatus-source) "*helm httpstatus*"))
|
||||
|
||||
;; Org Mode
|
||||
(after! org
|
||||
;; Include diary
|
||||
(setq org-agenda-include-diary t)
|
||||
;; Logs
|
||||
(setq org-log-state-notes-insert-after-drawers nil
|
||||
org-log-into-drawer "LOGBOOK"
|
||||
org-log-done 'time
|
||||
org-log-repeat 'time
|
||||
org-log-redeadline 'note
|
||||
org-log-reschedule 'note)
|
||||
;; Keyword and faces
|
||||
(setq-default org-todo-keywords
|
||||
'((sequence "REPEAT(r)" "NEXT(n@/!)" "DELEGATED(e@/!)" "TODO(t@/!)" "WAITING(w@/!)" "SOMEDAY(s@/!)" "PROJ(p)" "|" "DONE(d@)" "CANCELLED(c@/!)" "FORWARDED(f@)")))
|
||||
(setq-default org-todo-keyword-faces
|
||||
'(
|
||||
( "REPEAT" . (:foreground "white" :background "indigo" :weight bold))
|
||||
( "NEXT" . (:foreground "red" :background "orange" :weight bold))
|
||||
( "DELEGATED" . (:foreground "white" :background "blue" :weight bold))
|
||||
( "TODO" . (:foreground "white" :background "violet" :weight bold))
|
||||
( "WAITING" (:foreground "white" :background "#A9BE00" :weight bold))
|
||||
( "SOMEDAY" . (:foreground "white" :background "#00807E" :weight bold))
|
||||
( "PROJ" . (:foreground "white" :background "deeppink3" :weight bold))
|
||||
( "DONE" . (:foreground "white" :background "forest green" :weight bold))
|
||||
( "CANCELLED" . (:foreground "light gray" :slant italic))
|
||||
( "FORWARDED" . (:foreground "light gray" :slant italic))
|
||||
))
|
||||
(setq org-fontify-done-headline t)
|
||||
(setq org-fontify-todo-headline t)
|
||||
;; Priorities
|
||||
;; A: Do it now
|
||||
;; B: Decide when to do it
|
||||
;; C: Delegate it
|
||||
;; D: Just an idea
|
||||
(setq org-highest-priority ?A)
|
||||
(setq org-lowest-priority ?D)
|
||||
(setq org-default-priority ?B)
|
||||
(setq org-priority-faces '((?A . (:foreground "white" :background "dark red" :weight bold))
|
||||
(?B . (:foreground "white" :background "dark green" :weight bold))
|
||||
(?C . (:foreground "yellow"))
|
||||
(?D . (:foreground "gray"))))
|
||||
;; Capture templates
|
||||
(setq org-capture-templates
|
||||
(quote
|
||||
(
|
||||
("G" "Define a goal" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/goal.org") :empty-lines-after 2)
|
||||
("R" "REPEAT entry" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/repeat.org") :empty-lines-before 2)
|
||||
("N" "NEXT entry" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/next.org") :empty-lines-before 2)
|
||||
("T" "TODO entry" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/todo.org") :empty-lines-before 2)
|
||||
("W" "WAITING entry" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/waiting.org") :empty-lines-before 2)
|
||||
("S" "SOMEDAY entry" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/someday.org") :empty-lines-before 2)
|
||||
("P" "PROJ entry" entry (file+headline "~/org/capture.org" "Capture") (file "~/org/templates/proj.org") :empty-lines-before 2)
|
||||
("B" "Book on the to-read-list" entry (file+headline "~/org/private.org" "Libros para leer") (file "~/org/templates/book.org") :empty-lines-after 2)
|
||||
("p" "Create a daily plan")
|
||||
("pP" "Daily plan private" plain (file+olp+datetree "~/org/plan-free.org") (file "~/org/templates/dailyplan.org") :immediate-finish t :jump-to-captured t)
|
||||
("pW" "Daily plan work" plain (file+olp+datetree "~/org/plan-work.org") (file "~/org/templates/dailyplan.org") :immediate-finish t :jump-to-captured t)
|
||||
("j" "Journal entry")
|
||||
("jP" "Journal entry private private" entry (file+olp+datetree "~/org/journal-private.org") "** %U - %^{Heading}")
|
||||
("jW" "Journal entry work " entry (file+olp+datetree "~/org/journal-work.org") "** %U - %^{Heading}")
|
||||
("d" "Create a deployment")
|
||||
("dF" "Deploy features" plain (file+olp+datetree "~/org/deploy-features.org") (file "~/org/templates/deployment.org") :immediate-finish t :jump-to-captured t)
|
||||
("dB" "Deploy bugs" plain (file+olp+datetree "~/org/deploy-bugs.org") (file "~/org/templates/deployment.org") :immediate-finish t :jump-to-captured t)
|
||||
)))
|
||||
;; Custom agenda views
|
||||
(setq org-agenda-custom-commands
|
||||
(quote
|
||||
(
|
||||
("A" . "Agendas")
|
||||
("AT" "Daily overview"
|
||||
((tags-todo "URGENT"
|
||||
((org-agenda-overriding-header "Urgent Tasks")))
|
||||
(tags-todo "RADAR"
|
||||
((org-agenda-overriding-header "On my radar")))
|
||||
(tags-todo "PHONE+TODO=\"NEXT\""
|
||||
((org-agenda-overriding-header "Phone Calls")))
|
||||
(tags-todo "COMPANY"
|
||||
((org-agenda-overriding-header "Cuquitoni")))
|
||||
(tags-todo "SHOPPING"
|
||||
((org-agenda-overriding-header "Shopping")))
|
||||
(tags-todo "Depth=\"Deep\"/NEXT"
|
||||
((org-agenda-overriding-header "Next Actions requiring deep work")))
|
||||
(agenda ""
|
||||
((org-agenda-overriding-header "Today")
|
||||
(org-agenda-span 1)
|
||||
(org-agenda-start-day "1d")
|
||||
(org-agenda-sorting-strategy
|
||||
(quote
|
||||
(time-up priority-down)))))
|
||||
nil nil))
|
||||
("AW" "Weekly overview" agenda ""
|
||||
((org-agenda-overriding-header "Weekly overview")))
|
||||
("AM" "Monthly overview" agenda ""
|
||||
((org-agenda-overriding-header "Monthly overview"))
|
||||
(org-agenda-span
|
||||
(quote month))
|
||||
(org-deadline-warning-days 0)
|
||||
(org-agenda-sorting-strategy
|
||||
(quote
|
||||
(time-up priority-down tag-up))))
|
||||
("W" . "Weekly Review Helper")
|
||||
("Wn" "New tasks" tags "NEW"
|
||||
((org-agenda-overriding-header "NEW Tasks")))
|
||||
("Wd" "Check DELEGATED tasks" todo "DELEGATED"
|
||||
((org-agenda-overriding-header "DELEGATED tasks")))
|
||||
("Ww" "Check WAITING tasks" todo "WAITING"
|
||||
((org-agenda-overriding-header "WAITING tasks")))
|
||||
("Ws" "Check SOMEDAY tasks" todo "SOMEDAY"
|
||||
((org-agenda-overriding-header "SOMEDAY tasks")))
|
||||
("Wf" "Check finished tasks" todo "DONE|CANCELLED|FORWARDED"
|
||||
((org-agenda-overriding-header "Finished tasks")))
|
||||
("WP" "Planing ToDos (unscheduled) only" todo "TODO|NEXT"
|
||||
((org-agenda-overriding-header "To plan")
|
||||
(org-agenda-skip-function
|
||||
(quote
|
||||
(org-agenda-skip-entry-if
|
||||
(quote scheduled)
|
||||
(quote deadline)))))))
|
||||
))
|
||||
;;
|
||||
;; Enforce ordered tasks
|
||||
(setq org-enforce-todo-dependencies t)
|
||||
(setq org-enforce-todo-checkbox-dependencies t)
|
||||
(setq org-track-ordered-property-with-tag t)
|
||||
|
||||
;; Org bullets
|
||||
(require 'org-bullets)
|
||||
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))
|
||||
|
||||
;; Org recur
|
||||
(use-package org-recur
|
||||
:hook ((org-mode . org-recur-mode)
|
||||
(org-agenda-mode . org-recur-agenda-mode))
|
||||
:demand t
|
||||
:config
|
||||
(define-key org-recur-mode-map (kbd "C-c d") 'org-recur-finish)
|
||||
|
||||
;; Rebind the 'd' key in org-agenda (default: `org-agenda-day-view').
|
||||
(define-key org-recur-agenda-mode-map (kbd "C-c d") 'org-recur-finish)
|
||||
(define-key org-recur-agenda-mode-map (kbd "C-c 0") 'org-recur-schedule-today)
|
||||
|
||||
(setq org-recur-finish-done t
|
||||
org-recur-finish-archive t))
|
||||
|
||||
;; Truncate lines to 105 chars
|
||||
;; Why 105 chars? Because that's the max my screen can handle on vertical split
|
||||
(add-hook 'org-mode-hook #'auto-fill-mode)
|
||||
(setq-default fill-column 105)
|
||||
|
||||
;; Custom ORG functions
|
||||
;; Refresh org-agenda after rescheduling a task.
|
||||
(defun org-agenda-refresh ()
|
||||
"Refresh all `org-agenda' buffers."
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(when (derived-mode-p 'org-agenda-mode)
|
||||
(org-agenda-maybe-redo)))))
|
||||
|
||||
(defadvice org-schedule (after refresh-agenda activate)
|
||||
"Refresh org-agenda."
|
||||
(org-agenda-refresh))
|
||||
|
||||
(defun org-focus-private() "Set focus on private things."
|
||||
(interactive)
|
||||
(setq org-agenda-files '("~/org/private.org")))
|
||||
(defun org-focus-work() "Set focus on work things."
|
||||
(interactive)
|
||||
(setq org-agenda-files '("~/org/work.org")))
|
||||
(defun org-focus-all() "Set focus on all things."
|
||||
(interactive)
|
||||
(setq org-agenda-files '("~/org/")))
|
||||
|
||||
(defun my/org-add-ids-to-headlines-in-file ()
|
||||
"Add ID properties to all headlines in the current file which
|
||||
do not already have one."
|
||||
(interactive)
|
||||
(org-map-entries 'org-id-get-create))
|
||||
(add-hook 'org-mode-hook
|
||||
(lambda ()
|
||||
(add-hook 'before-save-hook
|
||||
'my/org-add-ids-to-headlines-in-file nil 'local)))
|
||||
(defun my/copy-idlink-to-clipboard() "Copy an ID link with the
|
||||
headline to killring, if no ID is there then create a new unique
|
||||
ID. This function works only in org-mode or org-agenda buffers.
|
||||
|
||||
The purpose of this function is to easily construct id:-links to
|
||||
org-mode items. If its assigned to a key it saves you marking the
|
||||
text and copying to the killring."
|
||||
(interactive)
|
||||
(when (eq major-mode 'org-agenda-mode) ;if we are in agenda mode we switch to orgmode
|
||||
(org-agenda-show)
|
||||
(org-agenda-goto))
|
||||
(when (eq major-mode 'org-mode) ; do this only in org-mode buffers
|
||||
(setq mytmphead (nth 4 (org-heading-components)))
|
||||
(setq mytmpid (funcall 'org-id-get-create))
|
||||
(setq mytmplink (format "[[id:%s][%s]]" mytmpid mytmphead))
|
||||
(kill-new mytmplink)
|
||||
(message "Copied %s to killring (clipboard)" mytmplink)
|
||||
))
|
||||
|
||||
(global-set-key (kbd "<f5>") 'my/copy-idlink-to-clipboard)
|
||||
|
||||
(defun org-reset-checkbox-state-maybe ()
|
||||
"Reset all checkboxes in an entry if the `RESET_CHECK_BOXES' property is set"
|
||||
(interactive "*")
|
||||
(if (org-entry-get (point) "RESET_CHECK_BOXES")
|
||||
(org-reset-checkbox-state-subtree)))
|
||||
|
||||
(defun org-checklist ()
|
||||
(when (member org-state org-done-keywords) ;; org-state dynamically bound in org.el/org-todo
|
||||
(org-reset-checkbox-state-maybe)))
|
||||
|
||||
(add-hook 'org-after-todo-state-change-hook 'org-checklist)
|
||||
|
||||
;; Save all org buffers on each save
|
||||
(add-hook 'auto-save-hook 'org-save-all-org-buffers)
|
||||
(add-hook 'after-save-hook 'org-save-all-org-buffers))
|
||||
|
||||
;; My own menu
|
||||
(map! :leader
|
||||
(:prefix-map ("a" . "applications")
|
||||
:desc "HTTP Status cheatsheet" "h" #'helm-httpstatus)
|
||||
(:prefix-map ("ao" . "org")
|
||||
:desc "Org focus work" "w" #'org-focus-work
|
||||
:desc "Org focus private" "p" #'org-focus-private
|
||||
:desc "Org focus all" "a" #'org-focus-all
|
||||
))
|
||||
|
||||
;; Python
|
||||
|
||||
(require 'auto-virtualenv)
|
||||
(after! python
|
||||
:init
|
||||
(add-hook 'python-mode-hook 'auto-virtualenv-set-virtualenv)
|
||||
(setq enable-local-variables :all))
|
||||
|
||||
(elpy-enable)
|
||||
(after! elpy
|
||||
(set-company-backend! 'elpy-mode
|
||||
'(elpy-company-backend :with company-files company-yasnippet)))
|
||||
(setq elpy-rpc-timeout 10)
|
||||
(remove-hook 'elpy-modules 'elpy-module-flymake)
|
||||
|
||||
(use-package flycheck
|
||||
:config
|
||||
(setq-default flycheck-disabled-checkers '(python-pylint)))
|
||||
|
||||
;; LSP config
|
||||
(after! lsp-mode
|
||||
(setq lsp-diagnostic-package :none)
|
||||
(setq lsp-headerline-breadcrumb-enable t)
|
||||
(setq lsp-headerline-breadcrumb-icons-enable t))
|
||||
|
||||
(after! lsp-ui
|
||||
(setq lsp-ui-doc-enable t))
|
||||
|
||||
;; (add-hook 'prog-mode-hook (lambda () (symbol-overlay-mode t)))
|
||||
|
||||
|
||||
;; Create new spikes, saved for later
|
||||
;; (defun certn/new-spike ()
|
||||
;; "Create a new org spike in ~/org/Lazer/Certn/."
|
||||
;; (interactive)
|
||||
;; (let ((name (read-string "Ticket: ")))
|
||||
;; (expand-file-name (format "%s.org" name) "~/org/Lazer/Certn/Spikes")))
|
||||
|
||||
|
||||
;; Dashboard mode
|
||||
(use-package dashboard
|
||||
:init ;; tweak dashboard config before loading it
|
||||
(setq dashboard-set-heading-icons t)
|
||||
(setq dashboard-set-file-icons t)
|
||||
(setq dashboard-center-content nil) ;; set to 't' for centered content
|
||||
(setq dashboard-items '((recents . 5)
|
||||
(bookmarks . 5)
|
||||
(projects . 5)))
|
||||
(setq dashboard-set-navigator t)
|
||||
:config
|
||||
(dashboard-setup-startup-hook)
|
||||
(dashboard-modify-heading-icons '((recents . "file-text")
|
||||
(bookmarks . "book"))))
|
||||
(setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
|
||||
(setq doom-fallback-buffer-name "*dashboard*")
|
||||
|
||||
(defun my/html2org-clipboard ()
|
||||
"Convert clipboard contents from HTML to Org and then paste (yank)."
|
||||
(interactive)
|
||||
(kill-new (shell-command-to-string "timeout 1 xclip -selection clipboard -o -t text/html | pandoc -f html -t json | pandoc -f json -t org --wrap=none"))
|
||||
(yank)
|
||||
(message "Pasted HTML in org"))
|
||||
(define-key org-mode-map (kbd "<f4>") 'my/html2org-clipboard)
|
||||
|
||||
;; Clipmon as emacs clipboard manager
|
||||
(global-set-key (kbd "M-y") 'helm-show-kill-ring)
|
||||
(add-to-list 'after-init-hook 'clipmon-mode-start)
|
||||
(defadvice clipmon--on-clipboard-change (around stop-clipboard-parsing activate) (let ((interprogram-cut-function nil)) ad-do-it))
|
||||
(setq clipmon-timer-interval 1)
|
||||
|
||||
;; Jenkins
|
||||
(require 'butler)
|
||||
(add-to-list 'butler-server-list
|
||||
'(jenkins "prometeo-jenkins"
|
||||
(server-address . "http://jenkins.prometeoapi")
|
||||
(auth-file . "~/.authinfo.gpg")))
|
||||
|
||||
(defun my/jenkins-verify ()
|
||||
"Check if my current Jenkinsfile has the correct format"
|
||||
(interactive)
|
||||
(projectile-with-default-dir (projectile-acquire-root)
|
||||
(message (shell-command-to-string "/usr/bin/python ~/.doom.d/scripts/check_jenkinsfile.py"))))
|
||||
|
||||
(after! groovy-mode
|
||||
(define-key groovy-mode-map (kbd "<f4>") 'my/jenkins-verify))
|
@ -1,971 +0,0 @@
|
||||
;;; deferred.el --- Simple asynchronous functions for emacs lisp -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2010-2016 SAKURAI Masashi
|
||||
|
||||
;; Author: SAKURAI Masashi <m.sakurai at kiwanami.net>
|
||||
;; Version: 0.5.1
|
||||
;; Keywords: deferred, async
|
||||
;; Package-Requires: ((emacs "24.4"))
|
||||
;; URL: https://github.com/kiwanami/emacs-deferred
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; 'deferred.el' is a simple library for asynchronous tasks.
|
||||
;; [https://github.com/kiwanami/emacs-deferred]
|
||||
|
||||
;; The API is almost the same as JSDeferred written by cho45. See the
|
||||
;; JSDeferred and Mochikit.Async web sites for further documentations.
|
||||
;; [https://github.com/cho45/jsdeferred]
|
||||
;; [http://mochikit.com/doc/html/MochiKit/Async.html]
|
||||
|
||||
;; A good introduction document (JavaScript)
|
||||
;; [http://cho45.stfuawsc.com/jsdeferred/doc/intro.en.html]
|
||||
|
||||
;;; Samples:
|
||||
|
||||
;; ** HTTP Access
|
||||
|
||||
;; (require 'url)
|
||||
;; (deferred:$
|
||||
;; (deferred:url-retrieve "http://www.gnu.org")
|
||||
;; (deferred:nextc it
|
||||
;; (lambda (buf)
|
||||
;; (insert (with-current-buffer buf (buffer-string)))
|
||||
;; (kill-buffer buf))))
|
||||
|
||||
;; ** Invoking command tasks
|
||||
|
||||
;; (deferred:$
|
||||
;; (deferred:process "wget" "-O" "a.jpg" "http://www.gnu.org/software/emacs/tour/images/splash.png")
|
||||
;; (deferred:nextc it
|
||||
;; (lambda (x) (deferred:process "convert" "a.jpg" "-resize" "100x100" "jpg:b.jpg")))
|
||||
;; (deferred:nextc it
|
||||
;; (lambda (x)
|
||||
;; (insert-image (create-image (expand-file-name "b.jpg") 'jpeg nil)))))
|
||||
|
||||
;; See the readme for further API documentation.
|
||||
|
||||
;; ** Applications
|
||||
|
||||
;; *Inertial scrolling for Emacs
|
||||
;; [https://github.com/kiwanami/emacs-inertial-scroll]
|
||||
|
||||
;; This program makes simple multi-thread function, using
|
||||
;; deferred.el.
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'subr-x)
|
||||
|
||||
(declare-function pp-display-expression 'pp)
|
||||
|
||||
(defvar deferred:version nil "deferred.el version")
|
||||
(setq deferred:version "0.5.0")
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defmacro deferred:aand (test &rest rest)
|
||||
"[internal] Anaphoric AND."
|
||||
(declare (debug ("test" form &rest form)))
|
||||
`(let ((it ,test))
|
||||
(if it ,(if rest `(deferred:aand ,@rest) 'it))))
|
||||
|
||||
(defmacro deferred:$ (&rest elements)
|
||||
"Anaphoric function chain macro for deferred chains."
|
||||
(declare (debug (&rest form)))
|
||||
`(let (it)
|
||||
,@(cl-loop for i in elements
|
||||
collect
|
||||
`(setq it ,i))
|
||||
it))
|
||||
|
||||
(defmacro deferred:lambda (args &rest body)
|
||||
"Anaphoric lambda macro for self recursion."
|
||||
(declare (debug ("args" form &rest form)))
|
||||
(let ((argsyms (cl-loop repeat (length args) collect (cl-gensym))))
|
||||
`(lambda (,@argsyms)
|
||||
(let (self)
|
||||
(setq self (lambda( ,@args ) ,@body))
|
||||
(funcall self ,@argsyms)))))
|
||||
|
||||
(cl-defmacro deferred:try (d &key catch finally)
|
||||
"Try-catch-finally macro. This macro simulates the
|
||||
try-catch-finally block asynchronously. CATCH and FINALLY can be
|
||||
nil. Because of asynchrony, this macro does not ensure that the
|
||||
task FINALLY should be called."
|
||||
(let ((chain
|
||||
(if catch `((deferred:error it ,catch)))))
|
||||
(when finally
|
||||
(setq chain (append chain `((deferred:watch it ,finally)))))
|
||||
`(deferred:$ ,d ,@chain)))
|
||||
|
||||
(defun deferred:setTimeout (f msec)
|
||||
"[internal] Timer function that emulates the `setTimeout' function in JS."
|
||||
(run-at-time (/ msec 1000.0) nil f))
|
||||
|
||||
(defun deferred:cancelTimeout (id)
|
||||
"[internal] Timer cancellation function that emulates the `cancelTimeout' function in JS."
|
||||
(cancel-timer id))
|
||||
|
||||
(defun deferred:run-with-idle-timer (sec f)
|
||||
"[internal] Wrapper function for run-with-idle-timer."
|
||||
(run-with-idle-timer sec nil f))
|
||||
|
||||
(defun deferred:call-lambda (f &optional arg)
|
||||
"[internal] Call a function with one or zero argument safely.
|
||||
The lambda function can define with zero and one argument."
|
||||
(condition-case err
|
||||
(funcall f arg)
|
||||
('wrong-number-of-arguments
|
||||
(display-warning 'deferred "\
|
||||
Callback that takes no argument may be specified.
|
||||
Passing callback with no argument is deprecated.
|
||||
Callback must take one argument.
|
||||
Or, this error is coming from somewhere inside of the callback: %S" err)
|
||||
(condition-case nil
|
||||
(funcall f)
|
||||
('wrong-number-of-arguments
|
||||
(signal 'wrong-number-of-arguments (cdr err))))))) ; return the first error
|
||||
|
||||
;; debug
|
||||
|
||||
(eval-and-compile
|
||||
(defvar deferred:debug nil "Debug output switch."))
|
||||
(defvar deferred:debug-count 0 "[internal] Debug output counter.")
|
||||
|
||||
(defmacro deferred:message (&rest args)
|
||||
"[internal] Debug log function."
|
||||
(when deferred:debug
|
||||
`(progn
|
||||
(with-current-buffer (get-buffer-create "*deferred:debug*")
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert (format "%5i %s\n" deferred:debug-count (format ,@args)))))
|
||||
(cl-incf deferred:debug-count))))
|
||||
|
||||
(defun deferred:message-mark ()
|
||||
"[internal] Debug log function."
|
||||
(interactive)
|
||||
(deferred:message "==================== mark ==== %s"
|
||||
(format-time-string "%H:%M:%S" (current-time))))
|
||||
|
||||
(defun deferred:pp (d)
|
||||
(require 'pp)
|
||||
(deferred:$
|
||||
(deferred:nextc d
|
||||
(lambda (x)
|
||||
(pp-display-expression x "*deferred:pp*")))
|
||||
(deferred:error it
|
||||
(lambda (e)
|
||||
(pp-display-expression e "*deferred:pp*")))
|
||||
(deferred:nextc it
|
||||
(lambda (_x) (pop-to-buffer "*deferred:pp*")))))
|
||||
|
||||
(defvar deferred:debug-on-signal nil
|
||||
"If non nil, the value `debug-on-signal' is substituted this
|
||||
value in the `condition-case' form in deferred
|
||||
implementations. Then, Emacs debugger can catch an error occurred
|
||||
in the asynchronous tasks.")
|
||||
|
||||
(defmacro deferred:condition-case (var protected-form &rest handlers)
|
||||
"[internal] Custom condition-case. See the comment for
|
||||
`deferred:debug-on-signal'."
|
||||
(declare (debug condition-case)
|
||||
(indent 2))
|
||||
`(let ((debug-on-signal
|
||||
(or debug-on-signal deferred:debug-on-signal)))
|
||||
(condition-case ,var
|
||||
,protected-form
|
||||
,@handlers)))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Back end functions of deferred tasks
|
||||
|
||||
(defvar deferred:tick-time 0.001
|
||||
"Waiting time between asynchronous tasks (second).
|
||||
The shorter waiting time increases the load of Emacs. The end
|
||||
user can tune this parameter. However, applications should not
|
||||
modify it because the applications run on various environments.")
|
||||
|
||||
(defvar deferred:queue nil
|
||||
"[internal] The execution queue of deferred objects.
|
||||
See the functions `deferred:post-task' and `deferred:worker'.")
|
||||
|
||||
(defmacro deferred:pack (a b c)
|
||||
`(cons ,a (cons ,b ,c)))
|
||||
|
||||
(defun deferred:schedule-worker ()
|
||||
"[internal] Schedule consuming a deferred task in the execution queue."
|
||||
(run-at-time deferred:tick-time nil 'deferred:worker))
|
||||
|
||||
(defun deferred:post-task (d which &optional arg)
|
||||
"[internal] Add a deferred object to the execution queue
|
||||
`deferred:queue' and schedule to execute.
|
||||
D is a deferred object. WHICH is a symbol, `ok' or `ng'. ARG is
|
||||
an argument value for execution of the deferred task."
|
||||
(push (deferred:pack d which arg) deferred:queue)
|
||||
(deferred:message "QUEUE-POST [%s]: %s"
|
||||
(length deferred:queue) (deferred:pack d which arg))
|
||||
(deferred:schedule-worker)
|
||||
d)
|
||||
|
||||
(defun deferred:clear-queue ()
|
||||
"Clear the execution queue. For test and debugging."
|
||||
(interactive)
|
||||
(deferred:message "QUEUE-CLEAR [%s -> 0]" (length deferred:queue))
|
||||
(setq deferred:queue nil))
|
||||
|
||||
(defun deferred:worker ()
|
||||
"[internal] Consume a deferred task.
|
||||
Mainly this function is called by timer asynchronously."
|
||||
(when deferred:queue
|
||||
(let* ((pack (car (last deferred:queue)))
|
||||
(d (car pack))
|
||||
(which (cadr pack))
|
||||
(arg (cddr pack)) value)
|
||||
(setq deferred:queue (nbutlast deferred:queue))
|
||||
(condition-case err
|
||||
(setq value (deferred:exec-task d which arg))
|
||||
(error
|
||||
(deferred:message "ERROR : %s" err)
|
||||
(message "deferred error : %s" err)))
|
||||
value)))
|
||||
|
||||
(defun deferred:flush-queue! ()
|
||||
"Call all deferred tasks synchronously. For test and debugging."
|
||||
(let (value)
|
||||
(while deferred:queue
|
||||
(setq value (deferred:worker)))
|
||||
value))
|
||||
|
||||
(defun deferred:sync! (d)
|
||||
"Wait for the given deferred task. For test and debugging.
|
||||
Error is raised if it is not processed within deferred chain D."
|
||||
(progn
|
||||
(let ((last-value 'deferred:undefined*)
|
||||
uncaught-error)
|
||||
(deferred:try
|
||||
(deferred:nextc d
|
||||
(lambda (x) (setq last-value x)))
|
||||
:catch
|
||||
(lambda (err) (setq uncaught-error err)))
|
||||
(while (and (eq 'deferred:undefined* last-value)
|
||||
(not uncaught-error))
|
||||
(sit-for 0.05)
|
||||
(sleep-for 0.05))
|
||||
(when uncaught-error
|
||||
(deferred:resignal uncaught-error))
|
||||
last-value)))
|
||||
|
||||
|
||||
|
||||
;; Struct: deferred
|
||||
;;
|
||||
;; callback : a callback function (default `deferred:default-callback')
|
||||
;; errorback : an errorback function (default `deferred:default-errorback')
|
||||
;; cancel : a canceling function (default `deferred:default-cancel')
|
||||
;; next : a next chained deferred object (default nil)
|
||||
;; status : if 'ok or 'ng, this deferred has a result (error) value. (default nil)
|
||||
;; value : saved value (default nil)
|
||||
;;
|
||||
(cl-defstruct deferred
|
||||
(callback 'deferred:default-callback)
|
||||
(errorback 'deferred:default-errorback)
|
||||
(cancel 'deferred:default-cancel)
|
||||
next status value)
|
||||
|
||||
(defun deferred:default-callback (i)
|
||||
"[internal] Default callback function."
|
||||
(identity i))
|
||||
|
||||
(defun deferred:default-errorback (err)
|
||||
"[internal] Default errorback function."
|
||||
(deferred:resignal err))
|
||||
|
||||
(defun deferred:resignal (err)
|
||||
"[internal] Safely resignal ERR as an Emacs condition.
|
||||
|
||||
If ERR is a cons (ERROR-SYMBOL . DATA) where ERROR-SYMBOL has an
|
||||
`error-conditions' property, it is re-signaled unchanged. If ERR
|
||||
is a string, it is signaled as a generic error using `error'.
|
||||
Otherwise, ERR is formatted into a string as if by `print' before
|
||||
raising with `error'."
|
||||
(cond ((and (listp err)
|
||||
(symbolp (car err))
|
||||
(get (car err) 'error-conditions))
|
||||
(signal (car err) (cdr err)))
|
||||
((stringp err)
|
||||
(error "%s" err))
|
||||
(t
|
||||
(error "%S" err))))
|
||||
|
||||
(defun deferred:default-cancel (d)
|
||||
"[internal] Default canceling function."
|
||||
(deferred:message "CANCEL : %s" d)
|
||||
(setf (deferred-callback d) 'deferred:default-callback)
|
||||
(setf (deferred-errorback d) 'deferred:default-errorback)
|
||||
(setf (deferred-next d) nil)
|
||||
d)
|
||||
|
||||
(defvar deferred:onerror nil
|
||||
"Default error handler. This value is nil or a function that
|
||||
have one argument for the error message.")
|
||||
|
||||
(defun deferred:exec-task (d which &optional arg)
|
||||
"[internal] Executing deferred task. If the deferred object has
|
||||
next deferred task or the return value is a deferred object, this
|
||||
function adds the task to the execution queue.
|
||||
D is a deferred object. WHICH is a symbol, `ok' or `ng'. ARG is
|
||||
an argument value for execution of the deferred task."
|
||||
(deferred:message "EXEC : %s / %s / %s" d which arg)
|
||||
(when (null d) (error "deferred:exec-task was given a nil."))
|
||||
(let ((callback (if (eq which 'ok)
|
||||
(deferred-callback d)
|
||||
(deferred-errorback d)))
|
||||
(next-deferred (deferred-next d)))
|
||||
(cond
|
||||
(callback
|
||||
(deferred:condition-case err
|
||||
(let ((value (deferred:call-lambda callback arg)))
|
||||
(cond
|
||||
((deferred-p value)
|
||||
(deferred:message "WAIT NEST : %s" value)
|
||||
(if next-deferred
|
||||
(deferred:set-next value next-deferred)
|
||||
value))
|
||||
(t
|
||||
(if next-deferred
|
||||
(deferred:post-task next-deferred 'ok value)
|
||||
(setf (deferred-status d) 'ok)
|
||||
(setf (deferred-value d) value)
|
||||
value))))
|
||||
(error
|
||||
(cond
|
||||
(next-deferred
|
||||
(deferred:post-task next-deferred 'ng err))
|
||||
(deferred:onerror
|
||||
(deferred:call-lambda deferred:onerror err))
|
||||
(t
|
||||
(deferred:message "ERROR : %S" err)
|
||||
(message "deferred error : %S" err)
|
||||
(setf (deferred-status d) 'ng)
|
||||
(setf (deferred-value d) err)
|
||||
err)))))
|
||||
(t ; <= (null callback)
|
||||
(cond
|
||||
(next-deferred
|
||||
(deferred:exec-task next-deferred which arg))
|
||||
((eq which 'ok) arg)
|
||||
(t ; (eq which 'ng)
|
||||
(deferred:resignal arg)))))))
|
||||
|
||||
(defun deferred:set-next (prev next)
|
||||
"[internal] Connect deferred objects."
|
||||
(setf (deferred-next prev) next)
|
||||
(cond
|
||||
((eq 'ok (deferred-status prev))
|
||||
(setf (deferred-status prev) nil)
|
||||
(let ((ret (deferred:exec-task
|
||||
next 'ok (deferred-value prev))))
|
||||
(if (deferred-p ret) ret
|
||||
next)))
|
||||
((eq 'ng (deferred-status prev))
|
||||
(setf (deferred-status prev) nil)
|
||||
(let ((ret (deferred:exec-task next 'ng (deferred-value prev))))
|
||||
(if (deferred-p ret) ret
|
||||
next)))
|
||||
(t
|
||||
next)))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Basic functions for deferred objects
|
||||
|
||||
(defun deferred:new (&optional callback)
|
||||
"Create a deferred object."
|
||||
(if callback
|
||||
(make-deferred :callback callback)
|
||||
(make-deferred)))
|
||||
|
||||
(defun deferred:callback (d &optional arg)
|
||||
"Start deferred chain with a callback message."
|
||||
(deferred:exec-task d 'ok arg))
|
||||
|
||||
(defun deferred:errorback (d &optional arg)
|
||||
"Start deferred chain with an errorback message."
|
||||
(deferred:exec-task d 'ng arg))
|
||||
|
||||
(defun deferred:callback-post (d &optional arg)
|
||||
"Add the deferred object to the execution queue."
|
||||
(deferred:post-task d 'ok arg))
|
||||
|
||||
(defun deferred:errorback-post (d &optional arg)
|
||||
"Add the deferred object to the execution queue."
|
||||
(deferred:post-task d 'ng arg))
|
||||
|
||||
(defun deferred:cancel (d)
|
||||
"Cancel all callbacks and deferred chain in the deferred object."
|
||||
(deferred:message "CANCEL : %s" d)
|
||||
(funcall (deferred-cancel d) d)
|
||||
d)
|
||||
|
||||
(defun deferred:status (d)
|
||||
"Return a current status of the deferred object. The returned value means following:
|
||||
`ok': the callback was called and waiting for next deferred.
|
||||
`ng': the errorback was called and waiting for next deferred.
|
||||
nil: The neither callback nor errorback was not called."
|
||||
(deferred-status d))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Basic utility functions
|
||||
|
||||
(defun deferred:succeed (&optional arg)
|
||||
"Create a synchronous deferred object."
|
||||
(let ((d (deferred:new)))
|
||||
(deferred:exec-task d 'ok arg)
|
||||
d))
|
||||
|
||||
(defun deferred:fail (&optional arg)
|
||||
"Create a synchronous deferred object."
|
||||
(let ((d (deferred:new)))
|
||||
(deferred:exec-task d 'ng arg)
|
||||
d))
|
||||
|
||||
(defun deferred:next (&optional callback arg)
|
||||
"Create a deferred object and schedule executing. This function
|
||||
is a short cut of following code:
|
||||
(deferred:callback-post (deferred:new callback))."
|
||||
(let ((d (if callback
|
||||
(make-deferred :callback callback)
|
||||
(make-deferred))))
|
||||
(deferred:callback-post d arg)
|
||||
d))
|
||||
|
||||
(defun deferred:nextc (d callback)
|
||||
"Create a deferred object with OK callback and connect it to the given deferred object."
|
||||
(let ((nd (make-deferred :callback callback)))
|
||||
(deferred:set-next d nd)))
|
||||
|
||||
(defun deferred:error (d callback)
|
||||
"Create a deferred object with errorback and connect it to the given deferred object."
|
||||
(let ((nd (make-deferred :errorback callback)))
|
||||
(deferred:set-next d nd)))
|
||||
|
||||
(defun deferred:watch (d callback)
|
||||
"Create a deferred object with watch task and connect it to the given deferred object.
|
||||
The watch task CALLBACK can not affect deferred chains with
|
||||
return values. This function is used in following purposes,
|
||||
simulation of try-finally block in asynchronous tasks, progress
|
||||
monitoring of tasks."
|
||||
(let* ((callback callback)
|
||||
(normal (lambda (x) (ignore-errors (deferred:call-lambda callback x)) x))
|
||||
(err (lambda (e)
|
||||
(ignore-errors (deferred:call-lambda callback e))
|
||||
(deferred:resignal e))))
|
||||
(let ((nd (make-deferred :callback normal :errorback err)))
|
||||
(deferred:set-next d nd))))
|
||||
|
||||
(defun deferred:wait (msec)
|
||||
"Return a deferred object scheduled at MSEC millisecond later."
|
||||
(let ((d (deferred:new)) (start-time (float-time)) timer)
|
||||
(deferred:message "WAIT : %s" msec)
|
||||
(setq timer (deferred:setTimeout
|
||||
(lambda ()
|
||||
(deferred:exec-task d 'ok
|
||||
(* 1000.0 (- (float-time) start-time)))
|
||||
nil) msec))
|
||||
(setf (deferred-cancel d)
|
||||
(lambda (x)
|
||||
(deferred:cancelTimeout timer)
|
||||
(deferred:default-cancel x)))
|
||||
d))
|
||||
|
||||
(defun deferred:wait-idle (msec)
|
||||
"Return a deferred object which will run when Emacs has been
|
||||
idle for MSEC millisecond."
|
||||
(let ((d (deferred:new)) (start-time (float-time)) timer)
|
||||
(deferred:message "WAIT-IDLE : %s" msec)
|
||||
(setq timer
|
||||
(deferred:run-with-idle-timer
|
||||
(/ msec 1000.0)
|
||||
(lambda ()
|
||||
(deferred:exec-task d 'ok
|
||||
(* 1000.0 (- (float-time) start-time)))
|
||||
nil)))
|
||||
(setf (deferred-cancel d)
|
||||
(lambda (x)
|
||||
(deferred:cancelTimeout timer)
|
||||
(deferred:default-cancel x)))
|
||||
d))
|
||||
|
||||
(defun deferred:call (f &rest args)
|
||||
"Call the given function asynchronously."
|
||||
(deferred:next
|
||||
(lambda (_x)
|
||||
(apply f args))))
|
||||
|
||||
(defun deferred:apply (f &optional args)
|
||||
"Call the given function asynchronously."
|
||||
(deferred:next
|
||||
(lambda (_x)
|
||||
(apply f args))))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Utility functions
|
||||
|
||||
(defun deferred:empty-p (times-or-seq)
|
||||
"[internal] Return non-nil if TIMES-OR-SEQ is the number zero or nil."
|
||||
(or (and (numberp times-or-seq) (<= times-or-seq 0))
|
||||
(and (sequencep times-or-seq) (= (length times-or-seq) 0))))
|
||||
|
||||
(defun deferred:loop (times-or-seq func)
|
||||
"Return a iteration deferred object."
|
||||
(deferred:message "LOOP : %s" times-or-seq)
|
||||
(if (deferred:empty-p times-or-seq) (deferred:next)
|
||||
(let* (items (rd
|
||||
(cond
|
||||
((numberp times-or-seq)
|
||||
(cl-loop for i from 0 below times-or-seq
|
||||
with ld = (deferred:next)
|
||||
do
|
||||
(push ld items)
|
||||
(setq ld
|
||||
(let ((i i))
|
||||
(deferred:nextc ld
|
||||
(lambda (_x) (deferred:call-lambda func i)))))
|
||||
finally return ld))
|
||||
((sequencep times-or-seq)
|
||||
(cl-loop for i in (append times-or-seq nil) ; seq->list
|
||||
with ld = (deferred:next)
|
||||
do
|
||||
(push ld items)
|
||||
(setq ld
|
||||
(let ((i i))
|
||||
(deferred:nextc ld
|
||||
(lambda (_x) (deferred:call-lambda func i)))))
|
||||
finally return ld)))))
|
||||
(setf (deferred-cancel rd)
|
||||
(lambda (x) (deferred:default-cancel x)
|
||||
(cl-loop for i in items
|
||||
do (deferred:cancel i))))
|
||||
rd)))
|
||||
|
||||
(defun deferred:trans-multi-args (args self-func list-func main-func)
|
||||
"[internal] Check the argument values and dispatch to methods."
|
||||
(cond
|
||||
((and (= 1 (length args)) (consp (car args)) (not (functionp (car args))))
|
||||
(let ((lst (car args)))
|
||||
(cond
|
||||
((or (null lst) (null (car lst)))
|
||||
(deferred:next))
|
||||
((deferred:aand lst (car it) (or (functionp it) (deferred-p it)))
|
||||
;; a list of deferred objects
|
||||
(funcall list-func lst))
|
||||
((deferred:aand lst (consp it))
|
||||
;; an alist of deferred objects
|
||||
(funcall main-func lst))
|
||||
(t (error "Wrong argument type. %s" args)))))
|
||||
(t (funcall self-func args))))
|
||||
|
||||
(defun deferred:parallel-array-to-alist (lst)
|
||||
"[internal] Translation array to alist."
|
||||
(cl-loop for d in lst
|
||||
for i from 0 below (length lst)
|
||||
collect (cons i d)))
|
||||
|
||||
(defun deferred:parallel-alist-to-array (alst)
|
||||
"[internal] Translation alist to array."
|
||||
(cl-loop for pair in
|
||||
(sort alst (lambda (x y)
|
||||
(< (car x) (car y))))
|
||||
collect (cdr pair)))
|
||||
|
||||
(defun deferred:parallel-func-to-deferred (alst)
|
||||
"[internal] Normalization for parallel and earlier arguments."
|
||||
(cl-loop for pair in alst
|
||||
for d = (cdr pair)
|
||||
collect
|
||||
(progn
|
||||
(unless (deferred-p d)
|
||||
(setf (cdr pair) (deferred:next d)))
|
||||
pair)))
|
||||
|
||||
(defun deferred:parallel-main (alst)
|
||||
"[internal] Deferred alist implementation for `deferred:parallel'. "
|
||||
(deferred:message "PARALLEL<KEY . VALUE>" )
|
||||
(let ((nd (deferred:new))
|
||||
(len (length alst))
|
||||
values)
|
||||
(cl-loop for pair in
|
||||
(deferred:parallel-func-to-deferred alst)
|
||||
with cd ; current child deferred
|
||||
do
|
||||
(let ((name (car pair)))
|
||||
(setq cd
|
||||
(deferred:nextc (cdr pair)
|
||||
(lambda (x)
|
||||
(push (cons name x) values)
|
||||
(deferred:message "PARALLEL VALUE [%s/%s] %s"
|
||||
(length values) len (cons name x))
|
||||
(when (= len (length values))
|
||||
(deferred:message "PARALLEL COLLECTED")
|
||||
(deferred:post-task nd 'ok (nreverse values)))
|
||||
nil)))
|
||||
(deferred:error cd
|
||||
(lambda (e)
|
||||
(push (cons name e) values)
|
||||
(deferred:message "PARALLEL ERROR [%s/%s] %s"
|
||||
(length values) len (cons name e))
|
||||
(when (= (length values) len)
|
||||
(deferred:message "PARALLEL COLLECTED")
|
||||
(deferred:post-task nd 'ok (nreverse values)))
|
||||
nil))))
|
||||
nd))
|
||||
|
||||
(defun deferred:parallel-list (lst)
|
||||
"[internal] Deferred list implementation for `deferred:parallel'. "
|
||||
(deferred:message "PARALLEL<LIST>" )
|
||||
(let* ((pd (deferred:parallel-main (deferred:parallel-array-to-alist lst)))
|
||||
(rd (deferred:nextc pd 'deferred:parallel-alist-to-array)))
|
||||
(setf (deferred-cancel rd)
|
||||
(lambda (x) (deferred:default-cancel x)
|
||||
(deferred:cancel pd)))
|
||||
rd))
|
||||
|
||||
(defun deferred:parallel (&rest args)
|
||||
"Return a deferred object that calls given deferred objects or
|
||||
functions in parallel and wait for all callbacks. The following
|
||||
deferred task will be called with an array of the return
|
||||
values. ARGS can be a list or an alist of deferred objects or
|
||||
functions."
|
||||
(deferred:message "PARALLEL : %s" args)
|
||||
(deferred:trans-multi-args args
|
||||
'deferred:parallel 'deferred:parallel-list 'deferred:parallel-main))
|
||||
|
||||
(defun deferred:earlier-main (alst)
|
||||
"[internal] Deferred alist implementation for `deferred:earlier'. "
|
||||
(deferred:message "EARLIER<KEY . VALUE>" )
|
||||
(let ((nd (deferred:new))
|
||||
(len (length alst))
|
||||
value results)
|
||||
(cl-loop for pair in
|
||||
(deferred:parallel-func-to-deferred alst)
|
||||
with cd ; current child deferred
|
||||
do
|
||||
(let ((name (car pair)))
|
||||
(setq cd
|
||||
(deferred:nextc (cdr pair)
|
||||
(lambda (x)
|
||||
(push (cons name x) results)
|
||||
(cond
|
||||
((null value)
|
||||
(setq value (cons name x))
|
||||
(deferred:message "EARLIER VALUE %s" (cons name value))
|
||||
(deferred:post-task nd 'ok value))
|
||||
(t
|
||||
(deferred:message "EARLIER MISS [%s/%s] %s" (length results) len (cons name value))
|
||||
(when (eql (length results) len)
|
||||
(deferred:message "EARLIER COLLECTED"))))
|
||||
nil)))
|
||||
(deferred:error cd
|
||||
(lambda (e)
|
||||
(push (cons name e) results)
|
||||
(deferred:message "EARLIER ERROR [%s/%s] %s" (length results) len (cons name e))
|
||||
(when (and (eql (length results) len) (null value))
|
||||
(deferred:message "EARLIER FAILED")
|
||||
(deferred:post-task nd 'ok nil))
|
||||
nil))))
|
||||
nd))
|
||||
|
||||
(defun deferred:earlier-list (lst)
|
||||
"[internal] Deferred list implementation for `deferred:earlier'. "
|
||||
(deferred:message "EARLIER<LIST>" )
|
||||
(let* ((pd (deferred:earlier-main (deferred:parallel-array-to-alist lst)))
|
||||
(rd (deferred:nextc pd (lambda (x) (cdr x)))))
|
||||
(setf (deferred-cancel rd)
|
||||
(lambda (x) (deferred:default-cancel x)
|
||||
(deferred:cancel pd)))
|
||||
rd))
|
||||
|
||||
|
||||
(defun deferred:earlier (&rest args)
|
||||
"Return a deferred object that calls given deferred objects or
|
||||
functions in parallel and wait for the first callback. The
|
||||
following deferred task will be called with the first return
|
||||
value. ARGS can be a list or an alist of deferred objects or
|
||||
functions."
|
||||
(deferred:message "EARLIER : %s" args)
|
||||
(deferred:trans-multi-args args
|
||||
'deferred:earlier 'deferred:earlier-list 'deferred:earlier-main))
|
||||
|
||||
(defmacro deferred:timeout (timeout-msec timeout-form d)
|
||||
"Time out macro on a deferred task D. If the deferred task D
|
||||
does not complete within TIMEOUT-MSEC, this macro cancels the
|
||||
deferred task and return the TIMEOUT-FORM."
|
||||
`(deferred:earlier
|
||||
(deferred:nextc (deferred:wait ,timeout-msec)
|
||||
(lambda (x) ,timeout-form))
|
||||
,d))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Application functions
|
||||
|
||||
(defvar deferred:uid 0 "[internal] Sequence number for some utilities. See the function `deferred:uid'.")
|
||||
|
||||
(defun deferred:uid ()
|
||||
"[internal] Generate a sequence number."
|
||||
(cl-incf deferred:uid))
|
||||
|
||||
(defun deferred:buffer-string (strformat buf)
|
||||
"[internal] Return a string in the buffer with the given format."
|
||||
(format strformat
|
||||
(with-current-buffer buf (buffer-string))))
|
||||
|
||||
(defun deferred:process (command &rest args)
|
||||
"A deferred wrapper of `start-process'. Return a deferred
|
||||
object. The process name and buffer name of the argument of the
|
||||
`start-process' are generated by this function automatically.
|
||||
The next deferred object receives stdout and stderr string from
|
||||
the command process."
|
||||
(deferred:process-gen 'start-process command args))
|
||||
|
||||
(defun deferred:process-shell (command &rest args)
|
||||
"A deferred wrapper of `start-process-shell-command'. Return a deferred
|
||||
object. The process name and buffer name of the argument of the
|
||||
`start-process-shell-command' are generated by this function automatically.
|
||||
The next deferred object receives stdout and stderr string from
|
||||
the command process."
|
||||
(deferred:process-gen 'start-process-shell-command command args))
|
||||
|
||||
(defun deferred:process-buffer (command &rest args)
|
||||
"A deferred wrapper of `start-process'. Return a deferred
|
||||
object. The process name and buffer name of the argument of the
|
||||
`start-process' are generated by this function automatically.
|
||||
The next deferred object receives stdout and stderr buffer from
|
||||
the command process."
|
||||
(deferred:process-buffer-gen 'start-process command args))
|
||||
|
||||
(defun deferred:process-shell-buffer (command &rest args)
|
||||
"A deferred wrapper of `start-process-shell-command'. Return a deferred
|
||||
object. The process name and buffer name of the argument of the
|
||||
`start-process-shell-command' are generated by this function automatically.
|
||||
The next deferred object receives stdout and stderr buffer from
|
||||
the command process."
|
||||
(deferred:process-buffer-gen 'start-process-shell-command command args))
|
||||
|
||||
(defun deferred:process-gen (f command args)
|
||||
"[internal]"
|
||||
(let ((pd (deferred:process-buffer-gen f command args)) d)
|
||||
(setq d (deferred:nextc pd
|
||||
(lambda (buf)
|
||||
(prog1
|
||||
(with-current-buffer buf (buffer-string))
|
||||
(kill-buffer buf)))))
|
||||
(setf (deferred-cancel d)
|
||||
(lambda (_x)
|
||||
(deferred:default-cancel d)
|
||||
(deferred:default-cancel pd)))
|
||||
d))
|
||||
|
||||
(defun deferred:process-buffer-gen (f command args)
|
||||
"[internal]"
|
||||
(let ((d (deferred:next)) (uid (deferred:uid)))
|
||||
(let ((proc-name (format "*deferred:*%s*:%s" command uid))
|
||||
(buf-name (format " *deferred:*%s*:%s" command uid))
|
||||
(pwd default-directory)
|
||||
(env process-environment)
|
||||
(con-type process-connection-type)
|
||||
(nd (deferred:new)) proc-buf proc)
|
||||
(deferred:nextc d
|
||||
(lambda (_x)
|
||||
(setq proc-buf (get-buffer-create buf-name))
|
||||
(condition-case err
|
||||
(let ((default-directory pwd)
|
||||
(process-environment env)
|
||||
(process-connection-type con-type))
|
||||
(setq proc
|
||||
(if (null (car args))
|
||||
(apply f proc-name buf-name command nil)
|
||||
(apply f proc-name buf-name command args)))
|
||||
(set-process-sentinel
|
||||
proc
|
||||
(lambda (proc event)
|
||||
(unless (process-live-p proc)
|
||||
(if (zerop (process-exit-status proc))
|
||||
(deferred:post-task nd 'ok proc-buf)
|
||||
(let ((msg (format "Deferred process exited abnormally:\n command: %s\n exit status: %s %s\n event: %s\n buffer contents: %S"
|
||||
command
|
||||
(process-status proc)
|
||||
(process-exit-status proc)
|
||||
(string-trim-right event)
|
||||
(if (buffer-live-p proc-buf)
|
||||
(with-current-buffer proc-buf
|
||||
(buffer-string))
|
||||
"(unavailable)"))))
|
||||
(kill-buffer proc-buf)
|
||||
(deferred:post-task nd 'ng msg))))))
|
||||
(setf (deferred-cancel nd)
|
||||
(lambda (x) (deferred:default-cancel x)
|
||||
(when proc
|
||||
(kill-process proc)
|
||||
(kill-buffer proc-buf)))))
|
||||
(error (deferred:post-task nd 'ng err)))
|
||||
nil))
|
||||
nd)))
|
||||
|
||||
(defmacro deferred:processc (d command &rest args)
|
||||
"Process chain of `deferred:process'."
|
||||
`(deferred:nextc ,d
|
||||
(lambda (,(cl-gensym)) (deferred:process ,command ,@args))))
|
||||
|
||||
(defmacro deferred:process-bufferc (d command &rest args)
|
||||
"Process chain of `deferred:process-buffer'."
|
||||
`(deferred:nextc ,d
|
||||
(lambda (,(cl-gensym)) (deferred:process-buffer ,command ,@args))))
|
||||
|
||||
(defmacro deferred:process-shellc (d command &rest args)
|
||||
"Process chain of `deferred:process'."
|
||||
`(deferred:nextc ,d
|
||||
(lambda (,(cl-gensym)) (deferred:process-shell ,command ,@args))))
|
||||
|
||||
(defmacro deferred:process-shell-bufferc (d command &rest args)
|
||||
"Process chain of `deferred:process-buffer'."
|
||||
`(deferred:nextc ,d
|
||||
(lambda (,(cl-gensym)) (deferred:process-shell-buffer ,command ,@args))))
|
||||
|
||||
;; Special variables defined in url-vars.el.
|
||||
(defvar url-request-data)
|
||||
(defvar url-request-method)
|
||||
(defvar url-request-extra-headers)
|
||||
|
||||
(declare-function url-http-symbol-value-in-buffer "url-http"
|
||||
(symbol buffer &optional unbound-value))
|
||||
|
||||
(declare-function deferred:url-param-serialize "request" (params))
|
||||
|
||||
(declare-function deferred:url-escape "request" (val))
|
||||
|
||||
(eval-after-load "url"
|
||||
;; for url package
|
||||
;; TODO: proxy, charaset
|
||||
;; List of gloabl variables to preserve and restore before url-retrieve call
|
||||
'(let ((url-global-variables '(url-request-data
|
||||
url-request-method
|
||||
url-request-extra-headers)))
|
||||
|
||||
(defun deferred:url-retrieve (url &optional cbargs silent inhibit-cookies)
|
||||
"A wrapper function for url-retrieve. The next deferred
|
||||
object receives the buffer object that URL will load
|
||||
into. Values of dynamically bound 'url-request-data', 'url-request-method' and
|
||||
'url-request-extra-headers' are passed to url-retrieve call."
|
||||
(let ((nd (deferred:new))
|
||||
buf
|
||||
(local-values (mapcar (lambda (symbol) (symbol-value symbol)) url-global-variables)))
|
||||
(deferred:next
|
||||
(lambda (_x)
|
||||
(cl-progv url-global-variables local-values
|
||||
(condition-case err
|
||||
(setq buf
|
||||
(url-retrieve
|
||||
url (lambda (_xx) (deferred:post-task nd 'ok buf))
|
||||
cbargs silent inhibit-cookies))
|
||||
(error (deferred:post-task nd 'ng err)))
|
||||
nil)))
|
||||
(setf (deferred-cancel nd)
|
||||
(lambda (_x)
|
||||
(when (buffer-live-p buf)
|
||||
(kill-buffer buf))))
|
||||
nd))
|
||||
|
||||
(defun deferred:url-delete-header (buf)
|
||||
(with-current-buffer buf
|
||||
(let ((pos (url-http-symbol-value-in-buffer
|
||||
'url-http-end-of-headers buf)))
|
||||
(when pos
|
||||
(delete-region (point-min) (1+ pos)))))
|
||||
buf)
|
||||
|
||||
(defun deferred:url-delete-buffer (buf)
|
||||
(when (and buf (buffer-live-p buf))
|
||||
(kill-buffer buf))
|
||||
nil)
|
||||
|
||||
(defun deferred:url-get (url &optional params &rest args)
|
||||
"Perform a HTTP GET method with `url-retrieve'. PARAMS is
|
||||
a parameter list of (key . value) or key. ARGS will be appended
|
||||
to deferred:url-retrieve args list. The next deferred
|
||||
object receives the buffer object that URL will load into."
|
||||
(when params
|
||||
(setq url
|
||||
(concat url "?" (deferred:url-param-serialize params))))
|
||||
(let ((d (deferred:$
|
||||
(apply 'deferred:url-retrieve url args)
|
||||
(deferred:nextc it 'deferred:url-delete-header))))
|
||||
(deferred:set-next
|
||||
d (deferred:new 'deferred:url-delete-buffer))
|
||||
d))
|
||||
|
||||
(defun deferred:url-post (url &optional params &rest args)
|
||||
"Perform a HTTP POST method with `url-retrieve'. PARAMS is
|
||||
a parameter list of (key . value) or key. ARGS will be appended
|
||||
to deferred:url-retrieve args list. The next deferred
|
||||
object receives the buffer object that URL will load into."
|
||||
(let ((url-request-method "POST")
|
||||
(url-request-extra-headers
|
||||
(append url-request-extra-headers
|
||||
'(("Content-Type" . "application/x-www-form-urlencoded"))))
|
||||
(url-request-data (deferred:url-param-serialize params)))
|
||||
(let ((d (deferred:$
|
||||
(apply 'deferred:url-retrieve url args)
|
||||
(deferred:nextc it 'deferred:url-delete-header))))
|
||||
(deferred:set-next
|
||||
d (deferred:new 'deferred:url-delete-buffer))
|
||||
d)))
|
||||
|
||||
(defun deferred:url-escape (val)
|
||||
"[internal] Return a new string that is VAL URI-encoded."
|
||||
(unless (stringp val)
|
||||
(setq val (format "%s" val)))
|
||||
(url-hexify-string
|
||||
(encode-coding-string val 'utf-8)))
|
||||
|
||||
(defun deferred:url-param-serialize (params)
|
||||
"[internal] Serialize a list of (key . value) cons cells
|
||||
into a query string."
|
||||
(when params
|
||||
(mapconcat
|
||||
'identity
|
||||
(cl-loop for p in params
|
||||
collect
|
||||
(cond
|
||||
((consp p)
|
||||
(concat
|
||||
(deferred:url-escape (car p)) "="
|
||||
(deferred:url-escape (cdr p))))
|
||||
(t
|
||||
(deferred:url-escape p))))
|
||||
"&")))
|
||||
))
|
||||
|
||||
|
||||
(provide 'deferred)
|
||||
;;; deferred.el ends here
|
@ -1,366 +0,0 @@
|
||||
;;; ox-slack.el --- Slack Exporter for org-mode -*- lexical-binding: t; -*-
|
||||
|
||||
|
||||
;; Copyright (C) 2018 Matt Price
|
||||
|
||||
;; Author: Matt Price
|
||||
;; Keywords: org, slack, outlines
|
||||
;; Package-Version: 0.1.1
|
||||
;; Package-Requires: ((emacs "24") (org "9.1.4") (ox-gfm "1.0"))
|
||||
;; URL: https://github.com/titaniumbones/ox-slack
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This library implements a Slack backend for the Org
|
||||
;; exporter, based on the `md' and `gfm' back-ends.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ox-gfm)
|
||||
|
||||
(org-export-define-derived-backend 'slack 'gfm
|
||||
;; for now, I just have this commented out
|
||||
;; might be better to create a defcustom to
|
||||
;; decide whether to add this to the export dispatcher
|
||||
;; :menu-entry
|
||||
;; '(?s "Export to Slack syntax"
|
||||
;; ((?s "To temporary buffer"
|
||||
;; (lambda (a s v b) (org-slack-export-as-slack a s v)))
|
||||
;; (?S "To file" (lambda (a s v b) (org-slack-export-to-slack a s v)))
|
||||
;; (?o "To file and open"
|
||||
;; (lambda (a s v b)
|
||||
;; (if a (org-slack-export-to-slack t s v)
|
||||
;; (org-open-file (org-slack-export-to-slack nil s v)))))))
|
||||
:translate-alist
|
||||
'(
|
||||
(bold . org-slack-bold)
|
||||
(code . org-slack-code)
|
||||
(headline . org-slack-headline)
|
||||
(inner-template . org-slack-inner-template)
|
||||
(italic . org-slack-italic)
|
||||
(link . org-slack-link)
|
||||
(plain-text . org-slack-plain-text)
|
||||
(src-block . org-slack-src-block)
|
||||
(strike-through . org-slack-strike-through)
|
||||
(timestamp . org-slack-timestamp)))
|
||||
|
||||
;; timestamp
|
||||
(defun org-slack-timestamp (timestamp contents info)
|
||||
"Transcode TIMESTAMP element into Slack format.
|
||||
CONTENTS is the timestamp contents. INFO is a plist used as a
|
||||
ocmmunications channel."
|
||||
(org-html-plain-text (org-timestamp-translate timestamp) info))
|
||||
|
||||
;; headline
|
||||
(defun org-slack-headline (headline contents info)
|
||||
"Transcode HEADLINE element into Markdown format.
|
||||
CONTENTS is the headline contents. INFO is a plist used as
|
||||
a communication channel."
|
||||
(unless (org-element-property :footnote-section-p headline)
|
||||
(let* ((level (org-export-get-relative-level headline info))
|
||||
(title (org-export-data (org-element-property :title headline) info))
|
||||
(todo (and (plist-get info :with-todo-keywords)
|
||||
(let ((todo (org-element-property :todo-keyword
|
||||
headline)))
|
||||
(and todo (concat (org-export-data todo info) " ")))))
|
||||
(tags (and (plist-get info :with-tags)
|
||||
(let ((tag-list (org-export-get-tags headline info)))
|
||||
(and tag-list
|
||||
(concat " " (org-make-tag-string tag-list))))))
|
||||
(priority
|
||||
(and (plist-get info :with-priority)
|
||||
(let ((char (org-element-property :priority headline)))
|
||||
(and char (format "[#%c] " char)))))
|
||||
;; Headline text without tags.
|
||||
(heading (concat todo priority title)))
|
||||
(format "*%s*\n\n%s" title contents)
|
||||
)))
|
||||
|
||||
;; link
|
||||
(defun org-slack-link (link contents info)
|
||||
"Transcode LINK object into Markdown format.
|
||||
CONTENTS is the link's description. INFO is a plist used as
|
||||
a communication channel."
|
||||
(let ((link-org-files-as-md
|
||||
(lambda (raw-path)
|
||||
;; Treat links to `file.org' as links to `file.md'.
|
||||
(if (string= ".org" (downcase (file-name-extension raw-path ".")))
|
||||
(concat (file-name-sans-extension raw-path) ".md")
|
||||
raw-path)))
|
||||
(type (org-element-property :type link)))
|
||||
(cond
|
||||
;; Link type is handled by a special function.
|
||||
((org-export-custom-protocol-maybe link contents 'md))
|
||||
((member type '("custom-id" "id" "fuzzy"))
|
||||
(let ((destination (if (string= type "fuzzy")
|
||||
(org-export-resolve-fuzzy-link link info)
|
||||
(org-export-resolve-id-link link info))))
|
||||
(pcase (org-element-type destination)
|
||||
(`plain-text ; External file.
|
||||
(let ((path (funcall link-org-files-as-md destination)))
|
||||
(if (not contents) (format "%s>" path)
|
||||
(format "[%s](%s)" contents path))))
|
||||
(`headline
|
||||
(format
|
||||
;; "[%s](#%s)"
|
||||
"[%s]"
|
||||
;; Description.
|
||||
(cond ((org-string-nw-p contents))
|
||||
((org-export-numbered-headline-p destination info)
|
||||
(mapconcat #'number-to-string
|
||||
(org-export-get-headline-number destination info)
|
||||
"."))
|
||||
(t (org-export-data (org-element-property :title destination)
|
||||
info)))
|
||||
;; Reference.
|
||||
;; (or (org-element-property :CUSTOM_ID destination)
|
||||
;; (org-export-get-reference destination info))
|
||||
))
|
||||
(_
|
||||
(let ((description
|
||||
(or (org-string-nw-p contents)
|
||||
(let ((number (org-export-get-ordinal destination info)))
|
||||
(cond
|
||||
((not number) nil)
|
||||
((atom number) (number-to-string number))
|
||||
(t (mapconcat #'number-to-string number ".")))))))
|
||||
(when description
|
||||
(format "[%s]"
|
||||
description
|
||||
;; (org-export-get-reference destination info)
|
||||
)))))))
|
||||
((org-export-inline-image-p link org-html-inline-image-rules)
|
||||
(let ((path (let ((raw-path (org-element-property :path link)))
|
||||
(cond ((not (equal "file" type)) (concat type ":" raw-path))
|
||||
((not (file-name-absolute-p raw-path)) raw-path)
|
||||
(t (expand-file-name raw-path)))))
|
||||
(caption (org-export-data
|
||||
(org-export-get-caption
|
||||
(org-export-get-parent-element link)) info)))
|
||||
(format ""
|
||||
(if (not (org-string-nw-p caption)) path
|
||||
(format "%s \"%s\"" path caption)))))
|
||||
((string= type "coderef")
|
||||
(let ((ref (org-element-property :path link)))
|
||||
(format (org-export-get-coderef-format ref contents)
|
||||
(org-export-resolve-coderef ref info))))
|
||||
((equal type "radio") contents)
|
||||
(t (let* ((raw-path (org-element-property :path link))
|
||||
(path
|
||||
(cond
|
||||
((member type '("http" "https" "ftp" "mailto"))
|
||||
(concat type ":" raw-path))
|
||||
((string= type "file")
|
||||
(org-export-file-uri (funcall link-org-files-as-md raw-path)))
|
||||
(t raw-path))))
|
||||
(if (not contents) (format "%s" path)
|
||||
(format "[%s](%s)" contents path)))))))
|
||||
|
||||
(defun org-slack-verbatim (_verbatim contents _info)
|
||||
"Transcode VERBATIM from Org to Slack.
|
||||
CONTENTS is the text with bold markup. INFO is a plist holding
|
||||
contextual information."
|
||||
(format "`%s`" contents))
|
||||
|
||||
(defun org-slack-code (code _contents info)
|
||||
"Return a CODE object from Org to SLACK.
|
||||
CONTENTS is nil. INFO is a plist holding contextual
|
||||
information."
|
||||
(format "`%s`"
|
||||
(org-element-property :value code)))
|
||||
|
||||
;;;; Italic
|
||||
|
||||
(defun org-slack-italic (_italic contents _info)
|
||||
"Transcode italic from Org to SLACK.
|
||||
CONTENTS is the text with italic markup. INFO is a plist holding
|
||||
contextual information."
|
||||
(format "_%s_" contents))
|
||||
|
||||
;;; Bold
|
||||
(defun org-slack-bold (_bold contents _info)
|
||||
"Transcode bold from Org to SLACK.
|
||||
CONTENTS is the text with bold markup. INFO is a plist holding
|
||||
contextual information."
|
||||
(format "*%s*" contents))
|
||||
|
||||
|
||||
;;;; Strike-through
|
||||
(defun org-slack-strike-through (_strike-through contents _info)
|
||||
"Transcode STRIKE-THROUGH from Org to SLACK.
|
||||
CONTENTS is text with strike-through markup. INFO is a plist
|
||||
holding contextual information."
|
||||
(format "~%s~" contents))
|
||||
|
||||
|
||||
(defun org-slack-inline-src-block (inline-src-block _contents info)
|
||||
"Transcode an INLINE-SRC-BLOCK element from Org to SLACK.
|
||||
CONTENTS holds the contents of the item. INFO is a plist holding
|
||||
contextual information."
|
||||
(format "`%s`"
|
||||
(org-element-property :value inline-src-block)))
|
||||
|
||||
;;;; Src Block
|
||||
(defun org-slack-src-block (src-block contents info)
|
||||
"Transcode SRC-BLOCK element into Github Flavored Markdown format.
|
||||
CONTENTS is nil. INFO is a plist used as a communication
|
||||
channel."
|
||||
(let* ((lang (org-element-property :language src-block))
|
||||
(code (org-export-format-code-default src-block info))
|
||||
(prefix (concat "```" "\n"))
|
||||
(suffix "```"))
|
||||
(concat prefix code suffix)))
|
||||
|
||||
;;;; Quote Block
|
||||
(defun org-slack-quote-block (_quote-block contents info)
|
||||
"Transcode a QUOTE-BLOCK element from Org to SLACK.
|
||||
CONTENTS holds the contents of the block. INFO is a plist
|
||||
holding contextual information."
|
||||
(org-slack--indent-string contents (plist-get info :slack-quote-margin)))
|
||||
|
||||
|
||||
(defun org-slack-inner-template (contents info)
|
||||
"Return body of document after converting it to Markdown syntax.
|
||||
CONTENTS is the transcoded contents string. INFO is a plist
|
||||
holding export options."
|
||||
;; Make sure CONTENTS is separated from table of contents and
|
||||
;; footnotes with at least a blank line.
|
||||
(concat
|
||||
;; Table of contents.
|
||||
;; (let ((depth (plist-get info :with-toc)))
|
||||
;; (when depth
|
||||
;; (concat (org-md--build-toc info (and (wholenump depth) depth)) "\n")))
|
||||
;; Document contents.
|
||||
contents
|
||||
"\n"
|
||||
;; Footnotes section.
|
||||
(org-md--footnote-section info)))
|
||||
|
||||
;;;; Plain text
|
||||
(defun org-slack-plain-text (text info)
|
||||
"Transcode a TEXT string into Markdown format.
|
||||
TEXT is the string to transcode. INFO is a plist holding
|
||||
contextual information."
|
||||
;; (when (plist-get info :with-smart-quotes)
|
||||
;; (setq text (org-export-activate-smart-quotes text :html info)))
|
||||
;; The below series of replacements in `text' is order sensitive.
|
||||
;; Protect `, *, _, and \
|
||||
;; (setq text (replace-regexp-in-string "[`*_\\]" "\\\\\\&" text))
|
||||
;; Protect ambiguous #. This will protect # at the beginning of
|
||||
;; a line, but not at the beginning of a paragraph. See
|
||||
;; `org-md-paragraph'.
|
||||
(setq text (replace-regexp-in-string "\n#" "\n\\\\#" text))
|
||||
;; Protect ambiguous !
|
||||
(setq text (replace-regexp-in-string "\\(!\\)\\[" "\\\\!" text nil nil 1))
|
||||
;; ;; Handle special strings, if required.
|
||||
;; (when (plist-get info :with-special-strings)
|
||||
;; (setq text (org-html-convert-special-strings text)))
|
||||
;; Handle break preservation, if required.
|
||||
(when (plist-get info :preserve-breaks)
|
||||
(setq text (replace-regexp-in-string "[ \t]*\n" " \n" text)))
|
||||
;; Return value.
|
||||
text)
|
||||
|
||||
;;; End-user functions
|
||||
|
||||
;;;###autoload
|
||||
(defun org-slack-export-as-slack
|
||||
(&optional async subtreep visible-only body-only ext-plist)
|
||||
"Export current buffer to a text buffer.
|
||||
|
||||
If narrowing is active in the current buffer, only export its
|
||||
narrowed part.
|
||||
|
||||
If a region is active, export that region.
|
||||
|
||||
A non-nil optional argument ASYNC means the process should happen
|
||||
asynchronously. The resulting buffer should be accessible
|
||||
through the `org-export-stack' interface.
|
||||
|
||||
When optional argument SUBTREEP is non-nil, export the sub-tree
|
||||
at point, extracting information from the headline properties
|
||||
first.
|
||||
|
||||
When optional argument VISIBLE-ONLY is non-nil, don't export
|
||||
contents of hidden elements.
|
||||
|
||||
When optional argument BODY-ONLY is non-nil, strip title and
|
||||
table of contents from output.
|
||||
|
||||
EXT-PLIST, when provided, is a property list with external
|
||||
parameters overriding Org default settings, but still inferior to
|
||||
file-local settings.
|
||||
|
||||
Export is done in a buffer named \"*Org SLACK Export*\", which
|
||||
will be displayed when `org-export-show-temporary-export-buffer'
|
||||
is non-nil."
|
||||
(interactive)
|
||||
(org-export-to-buffer 'slack "*Org SLACK Export*"
|
||||
async subtreep visible-only body-only ext-plist (lambda () (text-mode))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-slack-export-to-slack
|
||||
(&optional async subtreep visible-only body-only ext-plist)
|
||||
"Export current buffer to a text file.
|
||||
|
||||
If narrowing is active in the current buffer, only export its
|
||||
narrowed part.
|
||||
|
||||
If a region is active, export that region.
|
||||
|
||||
A non-nil optional argument ASYNC means the process should happen
|
||||
asynchronously. The resulting file should be accessible through
|
||||
the `org-export-stack' interface.
|
||||
|
||||
When optional argument SUBTREEP is non-nil, export the sub-tree
|
||||
at point, extracting information from the headline properties
|
||||
first.
|
||||
|
||||
When optional argument VISIBLE-ONLY is non-nil, don't export
|
||||
contents of hidden elements.
|
||||
|
||||
When optional argument BODY-ONLY is non-nil, strip title and
|
||||
table of contents from output.
|
||||
|
||||
EXT-PLIST, when provided, is a property list with external
|
||||
parameters overriding Org default settings, but still inferior to
|
||||
file-local settings.
|
||||
|
||||
Return output file's name."
|
||||
(interactive)
|
||||
(let ((file (org-export-output-file-name ".txt" subtreep)))
|
||||
(org-export-to-file 'slack file
|
||||
async subtreep visible-only body-only ext-plist)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-slack-export-to-clipboard-as-slack ()
|
||||
"Export region to slack, and copy to the kill ring for pasting into other programs."
|
||||
(interactive)
|
||||
(let* ((org-export-with-toc nil)
|
||||
(org-export-with-smart-quotes nil))
|
||||
(kill-new (org-export-as 'slack) ))
|
||||
)
|
||||
|
||||
;; (org-export-register-backend 'slack)
|
||||
(provide 'ox-slack)
|
||||
|
||||
;; Local variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
|
||||
;;; ox-slack.el ends here
|
@ -1,460 +0,0 @@
|
||||
;;; screenshot.el --- Swiftly grab images of your code -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020 TEC
|
||||
|
||||
;; Author: TEC <http://github/tecosaur>
|
||||
;; Maintainer: TEC <tec@tecosaur.com>
|
||||
;; Homepage: https://github.com/tecosaur/screenshot
|
||||
;; Version: 0.1.0
|
||||
;; Keywords: convenience, screenshot
|
||||
;; Package-Requires: ((emacs "27") (transient "0.2.0") (posframe "0.8.3"))
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;;; License:
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Convenience package for creating images of the current region or buffer.
|
||||
;; Requires `imagemagick' for some visual post-processing, and `xclip' for
|
||||
;; copying images to the clipboard.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'transient)
|
||||
(require 'posframe)
|
||||
|
||||
(defgroup screenshot ()
|
||||
"Customise group for Screenshot."
|
||||
:group 'convenience)
|
||||
|
||||
(defvar screenshot--buffer nil
|
||||
"The buffer last used to create a screenshot.")
|
||||
|
||||
(defcustom screenshot-buffer-creation-hook nil
|
||||
"Hook run after creating a buffer for screenshots.
|
||||
Run after hardcoded setup, but before the screenshot is captured."
|
||||
:type 'hook
|
||||
:group 'screenshot)
|
||||
|
||||
(defvar screenshot--region-beginning nil
|
||||
"Start of the region forming the screenshot.")
|
||||
(defvar screenshot--region-end nil
|
||||
"End of the region forming the screenshot.")
|
||||
|
||||
(defvar screenshot--tmp-file nil
|
||||
"An intermediate target file for the screenshot.")
|
||||
|
||||
(defvar screenshot--first-line-number nil
|
||||
"The first line contained in the screenshot.")
|
||||
|
||||
(defvar screenshot--total-lines nil
|
||||
"The total number of lines contained in the screenshot.")
|
||||
|
||||
;;; Generated variables
|
||||
|
||||
;;; Screenshot parameters
|
||||
|
||||
(eval-when-compile
|
||||
(defmacro screenshot--define-infix (key name description type default
|
||||
&rest reader)
|
||||
"Define infix with KEY, NAME, DESCRIPTION, TYPE, DEFAULT and READER as arguments."
|
||||
`(progn
|
||||
(defcustom ,(intern (concat "screenshot-" name)) ,default
|
||||
,description
|
||||
:type ,type
|
||||
:group 'screenshot)
|
||||
(transient-define-infix ,(intern (concat "screenshot--set-" name)) ()
|
||||
"Set `screenshot--theme' from a popup buffer."
|
||||
:class 'transient-lisp-variable
|
||||
:variable ',(intern (concat "screenshot-" name))
|
||||
:key ,key
|
||||
:description ,description
|
||||
:argument ,(concat "--" name)
|
||||
:reader (lambda (&rest _) ,@reader))))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-l" "line-numbers-p" "Show line numbers"
|
||||
'boolean nil
|
||||
(not screenshot-line-numbers-p))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-L" "relative-line-numbers-p" "Relative line numbers within the screenshot"
|
||||
'boolean nil
|
||||
(not screenshot-relative-line-numbers-p))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-t" "text-only-p" "Use a text-only version of the buffer"
|
||||
'boolean nil
|
||||
(not screenshot-text-only-p))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-T" "truncate-lines-p" "Truncate lines beyond the screenshot width"
|
||||
'boolean nil
|
||||
(not screenshot-truncate-lines-p))
|
||||
|
||||
(declare-function counsel-fonts "ext:counsel-fonts")
|
||||
|
||||
(declare-function ivy-read "ext:ivy-read")
|
||||
|
||||
(screenshot--define-infix
|
||||
"-ff" "font-family" "Font family to use"
|
||||
'string (let ((font (face-attribute 'default :font)))
|
||||
(if (eq font 'unspecified) "monospace"
|
||||
(symbol-name (font-get font :family))))
|
||||
(if (fboundp #'counsel-fonts)
|
||||
(ivy-read "Font: " (delete-dups (font-family-list))
|
||||
:preselect screenshot-font-family
|
||||
:require-match t
|
||||
:history 'counsel-fonts-history
|
||||
:caller 'counsel-fonts)
|
||||
(completing-read "Font: " (delete-dups (font-family-list)))))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-fs" "font-size" "Font size (pt)"
|
||||
'number 14
|
||||
(read-number "Font size in pt: " screenshot-font-size))
|
||||
|
||||
;;;; Frame
|
||||
|
||||
(screenshot--define-infix
|
||||
"-b" "border-width" "Border width in pixels"
|
||||
'integer 20
|
||||
(read-number "Border width in px: " screenshot-border-width))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-r" "radius" "Rounded corner radius"
|
||||
'integer 10
|
||||
(read-number "Border radius in px: " screenshot-radius))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-w" "min-width" "Minimum width, in columns"
|
||||
'integer 40
|
||||
(read-number "Minimum width (columns): " screenshot-min-width))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-W" "max-width" "Maximum width, in columns"
|
||||
'integer 120
|
||||
(read-number "Maximum width (columns): " screenshot-max-width))
|
||||
|
||||
;;;; Shadow
|
||||
|
||||
(screenshot--define-infix
|
||||
"-s" "shadow-radius" "Radius of the shadow in pixels"
|
||||
'integer 12
|
||||
(read-number "Shadow width in px: " screenshot-shadow-radius))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-i" "shadow-intensity" "Intensity of the shadow"
|
||||
'integer 80
|
||||
(read-number "Shadow intensity: " screenshot-shadow-intensity))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-c" "shadow-color" "Color of the shadow"
|
||||
'color "#333"
|
||||
(read-string "Shadow color: " screenshot-shadow-color))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-x" "shadow-offset-horizontal" "Shadow horizontal offset"
|
||||
'integer -8
|
||||
(read-number "Shadow horizontal offset in px: " screenshot-shadow-offset-horizontal))
|
||||
|
||||
(screenshot--define-infix
|
||||
"-y" "shadow-offset-vertical" "Shadow vertical offset"
|
||||
'integer 5
|
||||
(read-number "Shadow vertical offset in px: " screenshot-shadow-offset-vertical)))
|
||||
|
||||
;;; Main function
|
||||
|
||||
;;;###autoload
|
||||
(defun screenshot (beg end &optional upload-text)
|
||||
"Take a screenshot of the current region or buffer.
|
||||
|
||||
Region included in screenshot is the active selection, interactively,
|
||||
or given by BEG and END. Buffer is used if region spans 0-1 characters.
|
||||
|
||||
When a universal argument is given, UPLOAD-TEXT is non-nil.
|
||||
Then the text of the region/buffer is uploaded, and the URL is copied to clipboard."
|
||||
(interactive (if (region-active-p)
|
||||
(list (region-beginning) (region-end) (when current-prefix-arg t))
|
||||
(list (point-min) (point-max) (when current-prefix-arg t))))
|
||||
|
||||
(if upload-text
|
||||
(screenshot-text-upload beg end)
|
||||
(deactivate-mark)
|
||||
(screenshot--set-screenshot-region beg end)
|
||||
(setq screenshot--tmp-file
|
||||
(make-temp-file "screenshot-" nil ".png"))
|
||||
(call-interactively #'screenshot-transient)))
|
||||
|
||||
(defvar screenshot-text-upload-function #'screenshot-ixio-upload
|
||||
"Function to use to upload text.
|
||||
|
||||
Must take a start and end position for the current buffer, and
|
||||
return a URL.")
|
||||
|
||||
(defun screenshot-text-upload (beg end)
|
||||
"Upload the region from BEG to END, and copy the upload URL to the clipboard."
|
||||
(message "Uploading text...")
|
||||
(let ((url
|
||||
(funcall screenshot-text-upload-function beg end)))
|
||||
(gui-select-text url)
|
||||
(message "Screenshot uploaded, link copied to clipboard (%s)" url)))
|
||||
|
||||
(defun screenshot-ixio-upload (beg end)
|
||||
"Upload the region from BEG to END to ix.io, and return the URL."
|
||||
(let ((output (generate-new-buffer "ixio")) url)
|
||||
(shell-command-on-region beg end
|
||||
(format "curl -F 'ext:1=%s' -F 'f:1=<-' ix.io 2>/dev/null"
|
||||
(file-name-extension (or (buffer-file-name) " .txt")))
|
||||
output)
|
||||
(setq url (string-trim-right (with-current-buffer output (buffer-string))))
|
||||
(kill-buffer output)
|
||||
url))
|
||||
|
||||
;;; Screenshot capturing
|
||||
|
||||
(defun screenshot--set-screenshot-region (beg end)
|
||||
"Use the region from BEG to END to determine the relevant region to capture.
|
||||
Also records useful information like the total number of lines contained,
|
||||
and the line number of the first line of the region."
|
||||
(when (or (= beg end) (= (1+ beg) end))
|
||||
(setq beg (point-min)
|
||||
end (point-max)))
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(when (string-match-p "\\`\\s-*$" (thing-at-point 'line))
|
||||
(forward-line 1)
|
||||
(setq beg (line-beginning-position)))
|
||||
(back-to-indentation)
|
||||
(when (= beg (point))
|
||||
(setq beg (line-beginning-position)))
|
||||
(goto-char end)
|
||||
(when (string-match-p "\\`\\s-*$" (thing-at-point 'line))
|
||||
(forward-line -1)
|
||||
(setq end (line-end-position))))
|
||||
(setq screenshot--region-beginning beg
|
||||
screenshot--region-end end
|
||||
screenshot--first-line-number (line-number-at-pos beg)
|
||||
screenshot--total-lines (- (line-number-at-pos end) (line-number-at-pos beg) -1)))
|
||||
|
||||
(defun screenshot--setup-buffer ()
|
||||
"Modify the current buffer to make it appropriate for screenshotting."
|
||||
(setq-local face-remapping-alist '((line-number-current-line line-number)
|
||||
(show-paren-match nil)
|
||||
(region nil))
|
||||
line-spacing 0.1)
|
||||
(when (bound-and-true-p hl-line-mode) (hl-line-mode -1))
|
||||
(when (bound-and-true-p solaire-mode) (solaire-mode -1))
|
||||
(run-hooks 'screenshot-buffer-creation-hook))
|
||||
|
||||
(defvar screenshot--text-only-buffer
|
||||
(with-current-buffer (generate-new-buffer " *screenshot")
|
||||
(screenshot--setup-buffer)
|
||||
(when prettify-symbols-mode
|
||||
(prettify-symbols-mode -1))
|
||||
(current-buffer))
|
||||
"A text-only buffer for use in creating screenshots.")
|
||||
|
||||
(defun screenshot--format-text-only-buffer (beg end)
|
||||
"Insert text from BEG to END in the current buffer, into the screenshot text-only buffer."
|
||||
;; include indentation if `beg' is where indentation starts
|
||||
(let ((s (string-trim-right (buffer-substring beg end))))
|
||||
(with-current-buffer (setq screenshot--buffer screenshot--text-only-buffer)
|
||||
(buffer-face-set :family screenshot-font-family
|
||||
:height (* 10 screenshot-font-size))
|
||||
(erase-buffer)
|
||||
(insert s)
|
||||
(indent-rigidly (point-min) (point-max)
|
||||
(- (indent-rigidly--current-indentation
|
||||
(point-min) (point-max))))
|
||||
(current-buffer))))
|
||||
|
||||
(defun screenshot--narrowed-clone-buffer (beg end)
|
||||
"Create a clone of the current buffer, narrowed to the region from BEG to END.
|
||||
This buffer then then set up to be used for a screenshot."
|
||||
(with-current-buffer (clone-indirect-buffer " *screenshot-clone" nil t)
|
||||
(narrow-to-region beg end)
|
||||
(screenshot--setup-buffer)
|
||||
(buffer-face-set :family screenshot-font-family
|
||||
:height (* 10 screenshot-font-size))
|
||||
(current-buffer)))
|
||||
|
||||
;;; Screenshot processing
|
||||
|
||||
(defun screenshot--process ()
|
||||
"Perform the screenshot process.
|
||||
|
||||
Create a buffer for the screenshot, use `x-export-frames' to create the image,
|
||||
and process it."
|
||||
(setq screenshot--buffer
|
||||
(if screenshot-text-only-p
|
||||
(screenshot--format-text-only-buffer screenshot--region-beginning screenshot--region-end)
|
||||
(screenshot--narrowed-clone-buffer screenshot--region-beginning screenshot--region-end)))
|
||||
|
||||
(let ((frame (posframe-show
|
||||
screenshot--buffer
|
||||
:position (point-min)
|
||||
:internal-border-width screenshot-border-width
|
||||
:min-width screenshot-min-width
|
||||
:width screenshot-max-width
|
||||
:min-height screenshot--total-lines
|
||||
:lines-truncate screenshot-truncate-lines-p
|
||||
:poshandler #'posframe-poshandler-point-bottom-left-corner
|
||||
:hidehandler #'posframe-hide)))
|
||||
(with-current-buffer screenshot--buffer
|
||||
(setq-local display-line-numbers screenshot-line-numbers-p)
|
||||
(when screenshot-text-only-p
|
||||
(setq-local display-line-numbers-offset
|
||||
(if screenshot-relative-line-numbers-p
|
||||
0 (1- screenshot--first-line-number))))
|
||||
(font-lock-ensure (point-min) (point-max))
|
||||
(redraw-frame frame)
|
||||
(with-temp-file screenshot--tmp-file
|
||||
(insert (x-export-frames frame 'png))))
|
||||
(posframe-hide screenshot--buffer))
|
||||
(unless screenshot-text-only-p
|
||||
(kill-buffer screenshot--buffer))
|
||||
(screenshot--post-process screenshot--tmp-file))
|
||||
|
||||
(defcustom screenshot-post-process-hook
|
||||
(when (executable-find "pngquant")
|
||||
(list (defun screenshot--compress-file (file)
|
||||
(call-process "pngquant" nil nil nil "-f" "-o" file file))))
|
||||
"Functions to be called on the output file after processing.
|
||||
Must take a single argument, the file name, and operate in-place."
|
||||
:type 'function
|
||||
:group 'screenshot)
|
||||
|
||||
(defun screenshot--post-process (file)
|
||||
"Apply any image post-processing to FILE."
|
||||
(when (or (> screenshot-radius 0)
|
||||
(> screenshot-shadow-radius 0))
|
||||
(let ((result
|
||||
(shell-command-to-string
|
||||
(format "convert '%1$s' \\( +clone -alpha extract \\
|
||||
\\( -size %2$dx%2$d xc:black -draw 'fill white circle %2$d,%2$d %2$d,0' -write mpr:arc +delete \\) \\
|
||||
\\( mpr:arc \\) -gravity northwest -composite \\
|
||||
\\( mpr:arc -flip \\) -gravity southwest -composite \\
|
||||
\\( mpr:arc -flop \\) -gravity northeast -composite \\
|
||||
\\( mpr:arc -rotate 180 \\) -gravity southeast -composite \\) \\
|
||||
-alpha off -compose CopyOpacity -composite -compose over \\
|
||||
\\( +clone -background '%3$s' -shadow %4$dx%5$d+%6$d+%7$d \\) \\
|
||||
+swap -background none -layers merge '%1$s'"
|
||||
file
|
||||
screenshot-radius
|
||||
screenshot-shadow-color
|
||||
screenshot-shadow-intensity
|
||||
screenshot-shadow-radius
|
||||
screenshot-shadow-offset-horizontal
|
||||
screenshot-shadow-offset-vertical))))
|
||||
(unless (string= result "")
|
||||
(error "Could not apply imagemagick commands to image:\n%s" result))))
|
||||
(run-hook-with-args 'screenshot-post-process-hook file))
|
||||
|
||||
;;; Screenshot actions
|
||||
|
||||
(eval-when-compile
|
||||
(defmacro screenshot--def-action (name &rest body)
|
||||
"Define action NAME to be performed from the transient interface.
|
||||
BODY is executed after `screenshot-process' is called."
|
||||
`(defun ,(intern (concat "screenshot-" name)) (&optional _args)
|
||||
"Screenshot action to be performed from the transient interface."
|
||||
(interactive
|
||||
(list (transient-args 'screenshot-transient)))
|
||||
(screenshot--process)
|
||||
,@body))
|
||||
|
||||
(screenshot--def-action
|
||||
"save"
|
||||
(rename-file
|
||||
screenshot--tmp-file
|
||||
(concat (file-name-sans-extension
|
||||
(or (buffer-file-name)
|
||||
(expand-file-name "screenshot")))
|
||||
".png")
|
||||
t)
|
||||
(message "Screenshot saved"))
|
||||
|
||||
(screenshot--def-action
|
||||
"save-as"
|
||||
(rename-file
|
||||
screenshot--tmp-file
|
||||
(read-file-name "Save as: " (file-name-directory (or (buffer-file-name) default-directory)))
|
||||
1)
|
||||
(message "Screenshot saved"))
|
||||
|
||||
(screenshot--def-action
|
||||
"copy"
|
||||
(call-process "xclip" nil nil nil
|
||||
"-selection" "clipboard"
|
||||
"-target" "image/png"
|
||||
"-in" screenshot--tmp-file)
|
||||
(delete-file screenshot--tmp-file)
|
||||
(message "Screenshot copied"))
|
||||
|
||||
(defcustom screenshot-upload-fn nil
|
||||
"Function or string which provides a method to upload a file.
|
||||
If a function, it must take a filename and returns a URL to it.
|
||||
If a string, it is formatted with the file name, and run as a shell command.
|
||||
|
||||
Note: you have to define this yourself, there is no default."
|
||||
:type '(choice function string)
|
||||
:group 'screenshot)
|
||||
|
||||
(screenshot--def-action
|
||||
"upload"
|
||||
(if (not screenshot-upload-fn)
|
||||
(error "No upload function defined")
|
||||
(message "Uploading...")
|
||||
(let ((url
|
||||
(pcase screenshot-upload-fn
|
||||
((pred functionp) (funcall screenshot-upload-fn screenshot--tmp-file))
|
||||
((pred stringp) (string-trim-right (shell-command-to-string (format screenshot-upload-fn screenshot--tmp-file))))
|
||||
(_ (error "Upload function is not a function or string!")))))
|
||||
(gui-select-text url)
|
||||
(message "Screenshot uploaded, link copied to clipboard (%s)" url)))
|
||||
(delete-file screenshot--tmp-file)))
|
||||
|
||||
;;; Screenshot transient
|
||||
|
||||
(transient-define-prefix screenshot-transient ()
|
||||
["Code"
|
||||
(screenshot--set-line-numbers-p)
|
||||
(screenshot--set-relative-line-numbers-p)
|
||||
(screenshot--set-text-only-p)
|
||||
(screenshot--set-truncate-lines-p)
|
||||
(screenshot--set-font-family)
|
||||
(screenshot--set-font-size)]
|
||||
["Frame"
|
||||
(screenshot--set-border-width)
|
||||
(screenshot--set-radius)
|
||||
(screenshot--set-min-width)
|
||||
(screenshot--set-max-width)]
|
||||
["Shadow"
|
||||
(screenshot--set-shadow-radius)
|
||||
(screenshot--set-shadow-intensity)
|
||||
(screenshot--set-shadow-color)
|
||||
(screenshot--set-shadow-offset-horizontal)
|
||||
(screenshot--set-shadow-offset-vertical)]
|
||||
["Action"
|
||||
("s" "Save" screenshot-save)
|
||||
("S" "Save as" screenshot-save-as)
|
||||
("c" "Copy" screenshot-copy)
|
||||
("u" "Upload" screenshot-upload)])
|
||||
|
||||
(provide 'screenshot)
|
||||
;;; screenshot.el ends here
|
200
.doom.d/init.el
200
.doom.d/init.el
@ -1,200 +0,0 @@
|
||||
;;; 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 link to Doom's Module Index where all
|
||||
;; of our modules are listed, including 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
|
||||
;;bidi ; (tfel ot) thgir etirw uoy gnipleh
|
||||
;;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 ; 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 ; 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 +icons) ; making dired pretty [functional]
|
||||
electric ; smarter, keyword-based electric-indent
|
||||
(ibuffer +icons) ; 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
|
||||
;;biblio ; Writes a PhD for you (citation needed)
|
||||
debugger ; 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
|
||||
;;tree-sitter ; syntax and parsing, sitting in a tree...
|
||||
;;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
|
||||
;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER)
|
||||
;;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
|
||||
;;(graphql +lsp) ; Give queries a REST
|
||||
;;(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 +lsp) ; 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
|
||||
+hugo
|
||||
+roam2
|
||||
+pandoc) ; 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) ; 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 ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
|
||||
;;scala ; java, but good
|
||||
;;(scheme +guile) ; a fully conniving family of lisps
|
||||
(sh
|
||||
+lsp
|
||||
+powershell
|
||||
+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))
|
@ -1,65 +0,0 @@
|
||||
;; -*- 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! auto-virtualenv)
|
||||
(package! org-bullets)
|
||||
(package! idle-highlight-mode)
|
||||
(package! symbol-overlay)
|
||||
(package! elpy)
|
||||
(package! groovy-mode)
|
||||
(package! org-recur)
|
||||
(package! ox-gfm)
|
||||
(package! ox-clip)
|
||||
(package! dashboard)
|
||||
(package! clipmon)
|
||||
(package! butler)
|
||||
|
||||
;; (package! lsp-python-ms :disable t)
|
@ -1,3 +0,0 @@
|
||||
JENKINS_URL=http://my-jenkins.url
|
||||
JENKINS_USER=myusername
|
||||
JENKINS_PASS=mypassword
|
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import requests
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
JENKINS_URL = os.environ.get("JENKINS_URL")
|
||||
JENKINS_USER = os.environ.get("JENKINS_USER")
|
||||
JENKINS_PASS = os.environ.get("JENKINS_PASS")
|
||||
|
||||
files = {
|
||||
"jenkinsfile": (None, open("Jenkinsfile", "rb")),
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{JENKINS_URL}/pipeline-model-converter/validate",
|
||||
files=files,
|
||||
auth=(JENKINS_USER, JENKINS_PASS))
|
||||
print(response.text)
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("Jenkins can't be found. Is the VPN on?")
|
@ -1 +0,0 @@
|
||||
(icalendar-import-file "/tmp/calendar/personal-calendar.ics" "~/.emacs.d/.local/cache/diary")
|
@ -1,22 +0,0 @@
|
||||
#!/bin/bash
|
||||
# This is a cron that runs every 15 mins and populates my emacs diary file with my calendar items
|
||||
|
||||
# Downloading calendar
|
||||
echo "Downloading Calendar"
|
||||
mkdir -p /tmp/calendar
|
||||
cd /tmp/calendar
|
||||
wget "https://cloud.rogs.me/remote.php/dav/public-calendars/kRMMJ2CArQeCPzRi/?export" -O "personal-calendar.ics" -c
|
||||
wget "https://files-f1.motorsportcalendars.com/f1-calendar_qualifying_sprint_gp.ics" -O "f1.ics" -c
|
||||
|
||||
# Merge the calendars
|
||||
cat f1.ics >> personal-calendar.ics
|
||||
|
||||
#Generating the file
|
||||
|
||||
echo "#Generating the file"
|
||||
rm ~/.emacs.d/.local/cache/diary
|
||||
emacs --batch -l ~/.doom.d/scripts/ics-to-org.el
|
||||
|
||||
echo "#Deleting everything"
|
||||
#Deleting everything
|
||||
rm -r /tmp/calendar
|
Loading…
x
Reference in New Issue
Block a user