aboutsummaryrefslogtreecommitdiff
path: root/emacs/.emacs.d/lisp
diff options
context:
space:
mode:
authorYuchen Pei <id@ypei.org>2023-06-19 11:19:19 +1000
committerYuchen Pei <id@ypei.org>2023-06-19 22:41:03 +1000
commit06914aff2b0011d41bc50447965a8d7c6ef52c9b (patch)
tree278047a8c1c02347d5cf7a0119ac4cc373e3e191 /emacs/.emacs.d/lisp
parentf06c1c12456a598b7007a6ef08306051d0d49064 (diff)
precision fixes
- Adding org-jira - Also fixed getting builders in buildbot - Adding gdb-mi and gud - Adding gdb and shell configs - Also mariadb dev related stuff - A few more modes - some more updates
Diffstat (limited to 'emacs/.emacs.d/lisp')
-rw-r--r--emacs/.emacs.d/lisp/my/my-magit.el11
-rw-r--r--emacs/.emacs.d/lisp/my/my-mariadb.el126
-rw-r--r--emacs/.emacs.d/lisp/my/my-org-jira.el184
-rw-r--r--emacs/.emacs.d/lisp/my/my-ox-jira.el45
-rw-r--r--emacs/.emacs.d/lisp/my/my-prog.el279
m---------emacs/.emacs.d/lisp/org-jira0
m---------emacs/.emacs.d/lisp/ox-jira.el0
7 files changed, 645 insertions, 0 deletions
diff --git a/emacs/.emacs.d/lisp/my/my-magit.el b/emacs/.emacs.d/lisp/my/my-magit.el
index 779c7c7..cf6749b 100644
--- a/emacs/.emacs.d/lisp/my/my-magit.el
+++ b/emacs/.emacs.d/lisp/my/my-magit.el
@@ -55,5 +55,16 @@
(interactive)
(magit-status (my-project-read-project-root)))
+;; override `magit-status' to allow build dir and src dir distinction
+;; FIXME: there ought to be a better solution to do project things
+;; from the builddir
+(defun my-magit-status ()
+ (interactive)
+ (if (magit-toplevel)
+ (magit-status)
+ (magit-status
+ (replace-regexp-in-string "/build\\>.*" "/src"
+ default-directory))))
+
(provide 'my-magit)
;;; my-magit.el ends here
diff --git a/emacs/.emacs.d/lisp/my/my-mariadb.el b/emacs/.emacs.d/lisp/my/my-mariadb.el
new file mode 100644
index 0000000..5ffd6a0
--- /dev/null
+++ b/emacs/.emacs.d/lisp/my/my-mariadb.el
@@ -0,0 +1,126 @@
+;;; my-mariadb.el -- Customization for mariadb development-branch -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation.
+
+;; Author: Yuchen Pei <id@ypei.org>
+;; Package-Requires: ((emacs "28.2"))
+
+;; This file is part of dotted.
+
+;; dotted is free software: you can redistribute it and/or modify it under
+;; the terms of the GNU Affero General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; dotted 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 Affero General
+;; Public License for more details.
+
+;; You should have received a copy of the GNU Affero General Public
+;; License along with dotted. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Customization for mariadb development-branch.
+
+;;; Code:
+
+(require 'my-prog)
+
+(defun my-sql-maybe-mtrr ()
+ (interactive)
+ (if (equal (file-name-extension (buffer-file-name))
+ "test")
+ (call-interactively 'project-compile)
+ (sql-send-buffer)))
+
+(defun my-gdb-maria ()
+ (require 'ycp-gdb)
+ (interactive)
+ (when (and (buffer-live-p gud-comint-buffer)
+ (get-buffer-process gud-comint-buffer))
+ (my-gdb-quit))
+ (sleep-for 1)
+ (my-gdb
+ (format "rr replay %s -d ~/bin/gdb-mi.sh"
+ (expand-file-name
+ (replace-regexp-in-string
+ "/src"
+ "/build/mysql-test/var/log/mysqld.1.rr/latest-trace"
+ (project-root (project-current t)))))))
+
+(defun my-gdb-maria-spider ()
+ (require 'ycp-gdb)
+ (interactive)
+ (when (and (buffer-live-p gud-comint-buffer)
+ (get-buffer-process gud-comint-buffer))
+ (my-gdb-quit))
+ (sleep-for 1)
+ (my-gdb
+ (format "rr replay %s -d /home/ycp/source/mariadb-tools/gdb-mi.sh"
+ (expand-file-name
+ (replace-regexp-in-string
+ "/src"
+ "/build/mysql-test/var/log/mysqld.1.1.rr/latest-trace"
+ (project-root (project-current t)))))))
+
+(defun my-maria-style ()
+ (setq comment-start "/* "
+ comment-end " */"
+ comment-continue " "
+ comment-style 'extra-line))
+
+(setq my-c-style-maria-spider
+ '("maria-spider"
+ (c-basic-offset . 2)
+ (c-offsets-alist
+ . (;; no indentation when opening an brace
+ (substatement-open . 0)
+ ;; no extra space when aligning continued lines of block comments
+ (c . 0)
+ (arglist-intro . +)
+ (arglist-close . 0)
+ (arglist-cont-nonempty c-lineup-gcc-asm-reg c-lineup-arglist)
+ (arglist-cont . 0)
+ (statement-block-intro . +)
+ ))
+ ;; empty comment start on block comment continuation refill
+ (c-block-comment-prefix . "")
+))
+
+(add-to-list 'c-style-alist my-c-style-maria-spider)
+
+(defun my-mdev-22979 (beg end)
+ (interactive "r")
+ (save-excursion
+ (goto-char end)
+ (insert " )},
+")
+ (goto-char beg)
+ (insert " {C_STRING_WITH_LEN(
+")))
+
+(defun my-gdb-mysql-parse-frame ()
+ (interactive)
+ (let ((old-frame) (level))
+ (with-current-buffer (gdb-stack-buffer-name)
+ (save-excursion
+ (beginning-of-buffer)
+ (setq old-frame (point))
+ (text-property-search-forward
+ 'gdb-frame
+ "mysql_parse"
+ (lambda (val prop) (equal (alist-get 'func prop) val)))
+ (beginning-of-line)
+ (setq level (alist-get 'level (get-text-property (point) 'gdb-frame)))))
+ (with-current-buffer gud-comint-buffer
+ (insert "f " level)
+ (comint-send-input))
+ (with-current-buffer (gdb-stack-buffer-name)
+ (save-excursion
+ (goto-char old-frame)
+ (gdb-select-frame)))))
+
+(provide 'my-mariadb)
+;;; my-mariadb.el ends here
diff --git a/emacs/.emacs.d/lisp/my/my-org-jira.el b/emacs/.emacs.d/lisp/my/my-org-jira.el
new file mode 100644
index 0000000..7ff7738
--- /dev/null
+++ b/emacs/.emacs.d/lisp/my/my-org-jira.el
@@ -0,0 +1,184 @@
+;;; my-org-jira.el -- Extensions for org-jira -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation.
+
+;; Author: Yuchen Pei <id@ypei.org>
+;; Package-Requires: ((emacs "28.2"))
+
+;; This file is part of dotted.
+
+;; dotted is free software: you can redistribute it and/or modify it under
+;; the terms of the GNU Affero General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; dotted 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 Affero General
+;; Public License for more details.
+
+;; You should have received a copy of the GNU Affero General Public
+;; License along with dotted. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Extensions for org-jira.
+
+;;; Code:
+
+(require 'org-jira)
+
+;; Override `org-jira--render-issue'
+;; include issue-id in the headline
+(defun my-org-jira--render-issue (Issue)
+ "Render single ISSUE."
+ ;; (org-jira-log "Rendering issue from issue list")
+ ;; (org-jira-log (org-jira-sdk-dump Issue))
+ (with-slots (filename proj-key issue-id summary status priority headline id) Issue
+ (let (p)
+ (with-current-buffer (org-jira--get-project-buffer Issue)
+ (org-jira-freeze-ui
+ (org-jira-maybe-activate-mode)
+ (org-jira--maybe-render-top-heading proj-key)
+ (setq p (org-find-entry-with-id issue-id))
+ (save-restriction
+ (if (and p (>= p (point-min))
+ (<= p (point-max)))
+ (progn
+ (goto-char p)
+ (forward-thing 'whitespace)
+ (org-jira-kill-line))
+ (goto-char (point-max))
+ (unless (looking-at "^")
+ (insert "\n"))
+ (insert "** "))
+ (org-jira-insert
+ (concat (org-jira-get-org-keyword-from-status status)
+ " "
+ (org-jira-get-org-priority-cookie-from-issue priority)
+ issue-id " " headline))
+ (save-excursion
+ (unless (search-forward "\n" (point-max) 1)
+ (insert "\n")))
+ (org-narrow-to-subtree)
+ (save-excursion
+ (org-back-to-heading t)
+ (org-set-tags-to (replace-regexp-in-string "-" "_" issue-id)))
+ (mapc (lambda (entry)
+ (let ((val (slot-value Issue entry)))
+ (when (or (and val (not (string= val "")))
+ (eq entry 'assignee)) ;; Always show assignee
+ (org-jira-entry-put (point) (symbol-name entry) val))))
+ '(assignee filename reporter type type-id priority labels resolution status components created updated sprint))
+
+ (org-jira-entry-put (point) "ID" issue-id)
+ (org-jira-entry-put (point) "CUSTOM_ID" issue-id)
+
+ ;; Insert the duedate as a deadline if it exists
+ (when org-jira-deadline-duedate-sync-p
+ (let ((duedate (oref Issue duedate)))
+ (when (> (length duedate) 0)
+ (org-deadline nil duedate))))
+
+ (mapc
+ (lambda (heading-entry)
+ (ensure-on-issue-id-with-filename issue-id filename
+ (let* ((entry-heading
+ (concat (symbol-name heading-entry)
+ (format ": [[%s][%s]]"
+ (concat jiralib-url "/browse/" issue-id) issue-id))))
+ (setq p (org-find-exact-headline-in-buffer entry-heading))
+ (if (and p (>= p (point-min))
+ (<= p (point-max)))
+ (progn
+ (goto-char p)
+ (org-narrow-to-subtree)
+ (goto-char (point-min))
+ (forward-line 1)
+ (delete-region (point) (point-max)))
+ (if (org-goto-first-child)
+ (org-insert-heading)
+ (goto-char (point-max))
+ (org-insert-subheading t))
+ (org-jira-insert entry-heading "\n"))
+
+ ;; Insert 2 spaces of indentation so Jira markup won't cause org-markup
+ (org-jira-insert
+ (replace-regexp-in-string
+ "^" " "
+ (format "%s" (slot-value Issue heading-entry)))))))
+ '(description))
+
+ (when org-jira-download-comments
+ (org-jira-update-comments-for-issue Issue)
+
+ ;; FIXME: Re-enable when attachments are not erroring.
+ ;;(org-jira-update-attachments-for-current-issue)
+ )
+
+ ;; only sync worklog clocks when the user sets it to be so.
+ (when org-jira-worklog-sync-p
+ (org-jira-update-worklogs-for-issue issue-id filename))))))))
+
+;; Overload `org-jira-update-worklogs-from-org-clocks'.
+(defun my-org-jira-update-worklogs-from-org-clocks ()
+ "Update or add a worklog based on the org clocks."
+ (interactive)
+ (let* ((issue-id (org-jira-get-from-org 'issue 'key))
+ (filename (org-jira-filename))
+ ;; Fetch all workflogs for this issue
+ (jira-worklogs-ht (org-jira-worklog-to-hashtable issue-id)))
+ (org-jira-log (format "About to sync worklog for issue: %s in file: %s"
+ issue-id filename))
+ (ensure-on-issue-id-with-filename
+ issue-id filename
+ (search-forward (format ":%s:" (or (org-clock-drawer-name) "LOGBOOK")) nil 1 1)
+ (org-beginning-of-line)
+ ;; (org-cycle 1)
+ (while (search-forward "CLOCK: " nil 1 1)
+ (let ((org-time (buffer-substring-no-properties (point) (point-at-eol))))
+ (forward-line)
+ ;; See where the stuff ends (what point)
+ (let (next-clock-point)
+ (save-excursion
+ (search-forward-regexp "\\(CLOCK\\|:END\\):" nil 1 1)
+ (setq next-clock-point (point)))
+ (let ((clock-content
+ (buffer-substring-no-properties (point) next-clock-point)))
+ ;; Update via jiralib call
+ (let* ((worklog (org-jira-org-clock-to-jira-worklog org-time clock-content))
+ (comment-text (cdr (assoc 'comment worklog)))
+ (comment-text (if (string= (org-trim comment-text) "") nil comment-text)))
+ (unless (cdr (assoc 'worklog-id worklog))
+ (jiralib-add-worklog
+ issue-id
+ (cdr (assoc 'started worklog))
+ (cdr (assoc 'time-spent-seconds worklog))
+ comment-text
+ nil) ; no callback - synchronous
+ )
+ )))))
+ (org-jira-log (format "Updating worklog from org-jira-update-worklogs-from-org-clocks call"))
+ (org-jira-update-worklogs-for-issue issue-id filename)
+ )))
+
+(defun my-org-jira-comment-url (issue-id comment-id)
+ (format
+ "%s/browse/%s?focusedCommentId=%s&page=com.atlassian.jira.plugin.system.issuetabpanels%%3Acomment-tabpanel#comment-%s"
+ jiralib-url issue-id comment-id comment-id))
+
+(defun my-org-jira-comment-url-at-point ()
+ (my-org-jira-comment-url
+ (org-entry-get
+ (save-excursion
+ (outline-up-heading 1)
+ (point))
+ "ID")
+ (org-entry-get (point) "ID")))
+
+(defun my-org-jira-kill-comment-url-at-point ()
+ (interactive)
+ (kill-new (my-org-jira-comment-url-at-point)))
+
+(provide 'my-org-jira)
+;;; my-org-jira.el ends here
diff --git a/emacs/.emacs.d/lisp/my/my-ox-jira.el b/emacs/.emacs.d/lisp/my/my-ox-jira.el
new file mode 100644
index 0000000..ac581bf
--- /dev/null
+++ b/emacs/.emacs.d/lisp/my/my-ox-jira.el
@@ -0,0 +1,45 @@
+;;; my-ox-jira.el -- Extensions to ox-jira -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation.
+
+;; Author: Yuchen Pei <id@ypei.org>
+;; Package-Requires: ((emacs "28.2"))
+
+;; This file is part of dotted.
+
+;; dotted is free software: you can redistribute it and/or modify it under
+;; the terms of the GNU Affero General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; dotted 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 Affero General
+;; Public License for more details.
+
+;; You should have received a copy of the GNU Affero General Public
+;; License along with dotted. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Extensions to ox-jira.
+
+;;; Code:
+
+(require 'ox-jira)
+(require 'my-prog)
+(defun my-org-backtrace-to-github-jira (beg end)
+ (interactive "r")
+ (let ((bt (buffer-substring-no-properties beg end))
+ (revision (when current-prefix-arg
+ (read-string "Rrevision: ")))
+ )
+ (with-temp-buffer
+ (insert "#+options: ^:nil
+")
+ (goto-char (point-max))
+ (insert (my-org-backtrace-to-github bt revision))
+ (ox-jira-export-as-jira))))
+
+(provide 'my-ox-jira)
+;;; my-ox-jira.el ends here
diff --git a/emacs/.emacs.d/lisp/my/my-prog.el b/emacs/.emacs.d/lisp/my/my-prog.el
index 210d7ef..309df2e 100644
--- a/emacs/.emacs.d/lisp/my/my-prog.el
+++ b/emacs/.emacs.d/lisp/my/my-prog.el
@@ -86,6 +86,26 @@
"-ansi -pedantic -Wall -g")
file)))))
+(defun my-c-switch-between-header-and-source ()
+ "Switch between a c/c++ header (.h) and its corresponding source (.c/.cpp/.cc)."
+ (interactive)
+ (let ((bse (file-name-sans-extension buffer-file-name))
+ (ext (downcase (file-name-extension buffer-file-name)))
+ (new-file))
+ (cond
+ ;; first condition - the extension is "h"
+ ((equal ext "h")
+ (cond ((file-exists-p (setq new-file (concat bse ".c")))
+ (find-file new-file))
+ ((file-exists-p (setq new-file (concat bse ".cpp")))
+ (find-file new-file))
+ ((file-exists-p (setq new-file (concat bse ".cc")))
+ (find-file new-file))))
+ ;; second condition - the extension is "c" or "cpp"
+ ((member ext '("c" "cpp" "cc"))
+ (when (file-exists-p (setq new-file (concat bse ".h")))
+ (find-file new-file))))))
+
;;; To override `xref-query-replace-in-results'.
(defun my-xref-query-replace-in-results (from to)
"Perform interactive replacement of FROM with TO in all displayed xrefs.
@@ -155,5 +175,264 @@ create a shell buffer using `my-shell-with-directory'"
(call-interactively 'my-shell-with-directory)
(my-buffer-quick-major-mode 'shell-mode)))
+;;; gdb
+(require 'gdb-mi)
+(require 'gud)
+(require 'org)
+(defun my-org-backtrace-region (beg end)
+ "Convert selected backtrace to org links and copy the result to kill ring.
+
+With a prefix arg, convert from bottom to top."
+ (let ((bt (buffer-substring-no-properties beg end))
+ (case-fold-search nil)
+ (results) (func-name))
+ (with-temp-buffer
+ (insert bt)
+ (goto-char (point-min))
+ ;; remove paging lines
+ (flush-lines "^--.*--$")
+ (goto-char (point-min))
+ (while (re-search-forward "^#[0-9]+\\ +\\(?:[0-9a-fx]+ in \\)?\\([^ ]+\\) .*$" end t)
+ (setq func-name (match-string-no-properties 1))
+ (let ((point-from) (file-location-from)
+ (point-at) (file-location-at)
+ (file-location))
+ (save-excursion
+ (when (re-search-forward " from \\(/[^ ]+\\)$" end t)
+ (setq file-location-from (match-string-no-properties 1)
+ point-from (point))))
+ (save-excursion
+ ;; a weak check of file:lineno
+ (when (re-search-forward " at \\(/[^ ]+:.*\\)$" end t)
+ (setq file-location-at (match-string-no-properties 1)
+ point-at (point))))
+ (setq file-location
+ (cond ((not file-location-from) file-location-at)
+ ((not file-location-at) file-location-from)
+ ((< point-at point-from) file-location-at)
+ (t file-location-from)))
+ (when (and func-name file-location)
+ (push (concat "[[" file-location "][" func-name "]]") results))))
+ (unless current-prefix-arg (setq results (reverse results)))
+ (kill-new (string-join results (if current-prefix-arg " < " " > "))))))
+
+(defun my-stack-frame-to-org (rev)
+ "Convert stack frame in gdb frames buffer to org.
+
+Conversion is in a similar fashion to `my-org-backtrace-region'."
+ (let ((results))
+ (with-current-buffer (gdb-stack-buffer-name)
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward "^[0-9]+\\ +in \\(.*\\) of \\(.*\\)$" nil t)
+ (setq func-name (match-string-no-properties 1)
+ file-location (match-string-no-properties 2))
+ (push (concat "[[" file-location "][" func-name "]]") results))))
+ (unless rev (setq results (reverse results)))
+ (string-join results (if current-prefix-arg " < " " > "))))
+
+(defun my-insert-stack-frame-to-org (rev)
+ (interactive "P")
+ (insert (my-stack-frame-to-org rev)))
+
+(defun my-kill-stack-frame-to-org (rev)
+ (interactive "P")
+ (kill-new (my-stack-frame-to-org rev)))
+
+(defun my-org-backtrace ()
+ (interactive)
+ (if (region-active-p)
+ (my-org-backtrace-region (region-beginning) (region-end))
+ (my-org-backtrace-region
+ (point)
+ (save-excursion (comint-next-prompt 1) (point)))))
+
+(defun my-gdb (&optional command)
+ "lock gud command buffer and restore middle and right windows.
+
+Assuming a three window horizontal split to start with. Assuming
+the default configuration of gdb windows is all of them on the
+left and the source buffer on the right.
+"
+ (interactive)
+ ;; save windows
+ (let* ((old-window (selected-window))
+ (right-buffer
+ (window-buffer
+ (progn
+ (while (window-in-direction 'right)
+ (select-window (window-in-direction 'right)))
+ (selected-window))))
+ (middle-buffer
+ (window-buffer
+ (window-in-direction 'left))))
+ (select-window old-window)
+ ;; call gdb
+ (if (called-interactively-p)
+ (call-interactively 'gdb)
+ (gdb command))
+ (sleep-for 6)
+ ;; lock gud-comint-buffer
+ (my-toggle-lock-window-to-buffer (get-buffer-window gud-comint-buffer))
+ ;; restore windows
+ (select-window (car gdb-source-window-list))
+ (switch-to-buffer middle-buffer)
+ (split-window-horizontally)
+ (select-window (window-next-sibling))
+ (switch-to-buffer right-buffer)
+ (balance-windows)
+ (when (window-live-p old-window) (select-window old-window))))
+
+(defun my-gdb-restart ()
+ (interactive)
+ (let ((old-window (selected-window)))
+ (select-window (get-buffer-window gud-comint-buffer))
+ (gdb-delchar-or-quit 0)
+ (call-interactively 'my-gdb)
+ (when (window-live-p old-window) (select-window old-window))))
+
+(defun my-gdb-kill ()
+ (interactive)
+ (let ((kill-buffer-query-functions nil))
+ (kill-buffer "*gud-replay*")))
+
+(defun my-gdb-quit ()
+ (interactive)
+ (let ((old-window (selected-window)))
+ (select-window (get-buffer-window gud-comint-buffer))
+ (goto-char (point-max))
+ (gdb-delchar-or-quit 0)
+ (my-toggle-lock-current-window-to-buffer)
+ (when (window-live-p old-window) (select-window old-window))))
+
+(defun my-gdb-frames-add-breakpoint ()
+ (interactive)
+ (gdb-select-frame)
+ (with-current-buffer (gdb-get-source-buffer)
+ (call-interactively 'gud-break)))
+
+(defun my-gdb-frames-remove-breakpoint ()
+ (interactive)
+ (gdb-select-frame)
+ (with-current-buffer (gdb-get-source-buffer)
+ (call-interactively 'gud-remove)))
+
+(defun my-gdb-frames-select-next (n)
+ (interactive "p")
+ (next-line n)
+ (gdb-select-frame))
+(defun my-gdb-frames-select-previous (n)
+ (interactive "p")
+ (next-line (- n))
+ (gdb-select-frame))
+(defun my-gdb-switch-to-source-buffer ()
+ (interactive)
+ (select-window (display-buffer (gdb-get-source-buffer))))
+
+(defun my-gud-comint-set-prompt-regexp ()
+ (setq comint-prompt-regexp "\\((rr)|(gdb)\\) "))
+
+(defun my-file-loc-to-github (file-loc &optional revision)
+ "Convert a file location to a github url."
+ (pcase-let* ((`(,file ,line-no) (split-string file-loc ":"))
+ (revision (or revision (vc-working-revision file)))
+ (repo-url (vc-git-repository-url file))
+ (repo-root (vc-git-root file))
+ (path (file-relative-name file repo-root)))
+ (format "%s/blob/%s/%s#L%s" repo-url revision path line-no)))
+
+(defun my-org-backtrace-to-github (bt &optional revision)
+ (string-join
+ (mapcar
+ (lambda (link)
+ (string-match "\\[\\[\\(.*\\)\\]\\[\\(.*\\)\\]\\]" link)
+ (let ((target (match-string 1 link))
+ (label (match-string 2 link)))
+ (format "[[%s][%s]]"
+ (my-file-loc-to-github target revision)
+ label)))
+ (split-string bt " > "))
+ " > "))
+
+(defun my-org-backtrace-to-github-region (beg end)
+ (interactive "r")
+ (kill-new
+ (my-org-backtrace-to-github (buffer-substring-no-properties beg end))))
+
+(defun my-org-backtrace-to-github-slack (beg end)
+ (interactive "r")
+ (let ((bt (buffer-substring-no-properties beg end))
+ (revision (when current-prefix-arg
+ (read-string "Rrevision: ")))
+ )
+ (with-temp-buffer
+ (insert "#+options: ^:nil
+")
+ (goto-char (point-max))
+ (insert (my-org-backtrace-to-github bt revision))
+ (org-md-export-as-markdown))))
+
+;;; which-func
+(defun my-copy-which-func ()
+ (interactive)
+ (kill-new (which-function))
+ )
+
+(defun my-set-header-line-to-which-func ()
+ (setq header-line-format
+ '((which-func-mode
+ ("" which-func-format " ")
+ ))))
+
+;; override bookmark-make-record for easier default bookmark name.
+(defun my-bookmark-make-record ()
+ "Return a new bookmark record (NAME . ALIST) for the current location."
+ (let ((record (funcall bookmark-make-record-function)))
+ ;; Set up default name if the function does not provide one.
+ (unless (stringp (car record))
+ (if (car record) (push nil record))
+ (setcar record (or bookmark-current-bookmark (bookmark-buffer-name))))
+ ;; Set up defaults.
+ (bookmark-prop-set
+ record 'defaults
+ (delq nil (delete-dups (append (bookmark-prop-get record 'defaults)
+ (list (which-function)
+ bookmark-current-bookmark
+ (car record)
+ (bookmark-buffer-name))))))
+ record))
+
+;;; bison-mode
+(require 'bison-mode)
+(defun my-bison-imenu-create-index ()
+ (let ((end))
+ (beginning-of-buffer)
+ (re-search-forward "^%%")
+ (forward-line 1)
+ (setq end (save-excursion
+ (when (re-search-forward "^%%" nil t) (point))))
+ (loop while (re-search-forward "^\\([a-z].*?\\)\\s-*\n?\\s-*:" end t)
+ collect (cons (match-string 1) (point)))))
+(defun my-bison-set-imenu-create-index-function ()
+ (setq imenu-create-index-function
+ 'my-bison-imenu-create-index))
+
+;;; json-mode
+(require 'json-mode)
+(defun my-json-mode-path ()
+ (string-join
+ (mapcar 'prin1-to-string (plist-get (json-path-to-position (point)) :path))
+ "/"))
+
+(defun my-json-set-header-line-to-path ()
+ (setq header-line-format
+ '((:eval (my-json-mode-path)))))
+
+;;; eglot
+(defun my-eglot-format-buffer-when-managed ()
+ (when (eglot-managed-p)
+ (unless (derived-mode-p 'haskell-mode 'c-mode 'c++-mode)
+ (eglot-format-buffer))))
+
(provide 'my-prog)
;;; my-prog.el ends here
diff --git a/emacs/.emacs.d/lisp/org-jira b/emacs/.emacs.d/lisp/org-jira
new file mode 160000
+Subproject 63a74d4458f512f0825e9f55491baad3ee4e9c4
diff --git a/emacs/.emacs.d/lisp/ox-jira.el b/emacs/.emacs.d/lisp/ox-jira.el
new file mode 160000
+Subproject 00184f8fdef02a3a359a253712e8769cbfbea3b