move stuff around
This commit is contained in:
19
custom.el
19
custom.el
@@ -5,17 +5,22 @@
|
||||
;; Your init file should contain only one such instance.
|
||||
;; If there is more than one, they won't work right.
|
||||
'(package-selected-packages
|
||||
'(aider browse-kill-ring caddyfile-mode cape cider company corfu
|
||||
dockerfile-mode dumb-jump dumber-jump emacs-lldb emmet
|
||||
emmet-mode exec-path-from-shell flycheck fsharp-mode
|
||||
haskell-mode haskell-ts-mode helm htmlize idris-mode
|
||||
'(aider browse-kill-ring caddyfile-mode cape cider cmake-mode company
|
||||
corfu dart-mode dockerfile-mode dumb-jump dumber-jump
|
||||
emmet-mode exec-path-from-shell expand-region flutter
|
||||
flycheck fsharp-mode gptel haskell-mode haskell-ts-mode
|
||||
helm htmlize idris-mode llm-tool-collection llm-tools
|
||||
marginalia markdown-ts-mode move-text multiple-cursors
|
||||
odin-mode orderless org-mode parinfer-rust-mode
|
||||
realgud-lldb rg sly terraform-mode tree-sitter-langs
|
||||
ocaml-ts-mode orderless parinfer-rust-mode realgud-lldb
|
||||
reason-mode rg sly terraform-mode tree-sitter-langs tuareg
|
||||
vertico visual-fill-column yasnippet-snippets zig-mode))
|
||||
'(package-vc-selected-packages
|
||||
'((llm-tool-collection :url
|
||||
"https://github.com/skissue/llm-tool-collection")))
|
||||
'(safe-local-variable-directories '("/Users/grant/programming/project/edit/"))
|
||||
'(safe-local-variable-values
|
||||
'((Package . CL-USER) (Syntax . ANSI-Common-Lisp) (Base . 10)
|
||||
'((checkdoc-allow-quoting-nil-and-t . t) (Package . CL-USER)
|
||||
(Syntax . ANSI-Common-Lisp) (Base . 10)
|
||||
(flycheck-clang-includes . "SDL3-3.2.26/include"))))
|
||||
(custom-set-faces
|
||||
;; custom-set-faces was added by Custom.
|
||||
|
||||
257
init.el
257
init.el
@@ -1,5 +1,7 @@
|
||||
;;; -*- lexical-binding: t; -*-
|
||||
|
||||
(setq gc-cons-threshold (* 800000 100))
|
||||
|
||||
(require 'package)
|
||||
(add-to-list 'package-archives
|
||||
'("melpa" . "https://melpa.org/packages/") t)
|
||||
@@ -10,10 +12,13 @@
|
||||
|
||||
;; If startup times are slow
|
||||
;; (setq use-package-verbose t)
|
||||
;; (setq use-package-compute-statistics t)
|
||||
(setq use-package-compute-statistics t)
|
||||
|
||||
(add-to-list 'load-path (concat user-emacs-directory "local-lisp/"))
|
||||
(add-to-list 'load-path (concat user-emacs-directory "user-lisp/"))
|
||||
(add-to-list 'load-path "/opt/homebrew/Cellar/mu/1.12.14/share/emacs/site-lisp/mu/mu4e")
|
||||
|
||||
(global-auto-revert-mode 1)
|
||||
(recentf-mode 1)
|
||||
(setq tab-bar-show nil)
|
||||
(setq inhibit-startup-message t)
|
||||
(setq initial-scratch-message nil)
|
||||
@@ -39,6 +44,13 @@
|
||||
(pixel-scroll-precision-mode 1)
|
||||
(kill-ring-deindent-mode 1)
|
||||
|
||||
(setopt auto-revert-avoid-polling t)
|
||||
(setopt auto-revert-interval 5)
|
||||
(setopt auto-revert-check-vc-info t)
|
||||
(global-auto-revert-mode 1)
|
||||
(setq global-auto-revert-non-file-buffers t)
|
||||
(setq-default grep-command "rg")
|
||||
|
||||
(setq use-package-always-ensure t)
|
||||
(setq mac-command-modifier 'control)
|
||||
(setq is-mac (string= system-type "darwin"))
|
||||
@@ -51,13 +63,14 @@
|
||||
(defun hgh/enable-cursor-blink ()
|
||||
(blink-cursor-mode 1))
|
||||
|
||||
(add-hook 'activate-mark-hook 'hgh/disable-cursor-blink)
|
||||
(add-hook 'activate-mark-hook 'hgh/disable-cursor-blink)
|
||||
(add-hook 'deactivate-mark-hook 'hgh/enable-cursor-blink)
|
||||
|
||||
(when is-mac
|
||||
(setq dired-use-ls-dired t
|
||||
insert-directory-program "gls"
|
||||
dired-listing-switches "-aBhl --group-directories-first"))
|
||||
dired-listing-switches "-aBhl --group-directories-first")
|
||||
(setq-default manual-program "gman"))
|
||||
|
||||
;; Add .asdf to exec-path
|
||||
(when (file-exists-p (file-truename "~/.asdf"))
|
||||
@@ -74,6 +87,11 @@
|
||||
(when exec-path
|
||||
(setenv "PATH" (string-join exec-path ":")))
|
||||
|
||||
(defun hgh/duplicate-dwim ()
|
||||
(interactive)
|
||||
(duplicate-dwim)
|
||||
(next-line))
|
||||
|
||||
(defun hgh/visit-init-file ()
|
||||
(interactive)
|
||||
(find-file user-init-file))
|
||||
@@ -110,7 +128,11 @@
|
||||
(keymap-global-set "M-1" #'delete-other-windows)
|
||||
(keymap-global-set "M-2" #'split-window-below)
|
||||
(keymap-global-set "M-3" #'split-window-right)
|
||||
(keymap-global-set "C-," #'duplicate-dwim)
|
||||
(keymap-global-set "C-," #'hgh/duplicate-dwim)
|
||||
|
||||
(keymap-global-set "C-c f n" #'flymake-goto-next-error)
|
||||
(keymap-global-set "C-c f p" #'flymake-goto-prev-error)
|
||||
(keymap-global-set "C-c f b" #'flymake-show-buffer-diagnostics)
|
||||
|
||||
(keymap-set completion-list-mode-map "M-n" #'minibuffer-next-completion)
|
||||
(keymap-set completion-list-mode-map "M-p" #'minibuffer-previous-completion)
|
||||
@@ -123,34 +145,18 @@
|
||||
|
||||
(load-theme 'modus-vivendi t)
|
||||
|
||||
;; Let's prefer completion-preview for now if it's available
|
||||
(if (version<= "30.1" emacs-version)
|
||||
(use-package completion-preview
|
||||
:ensure nil
|
||||
:demand t
|
||||
:bind
|
||||
(:map completion-preview-active-mode-map
|
||||
("M-n" . completion-preview-next-candidate)
|
||||
("M-p" . completion-preview-preview-candidate))
|
||||
:config
|
||||
(global-completion-preview-mode t))
|
||||
(use-package corfu
|
||||
:custom
|
||||
(corfu-auto t)
|
||||
(corfu-cycle t)
|
||||
:config
|
||||
(global-corfu-mode 1)))
|
||||
(use-package corfu
|
||||
:custom
|
||||
(corfu-auto t)
|
||||
(corfu-cycle t)
|
||||
:config
|
||||
(global-corfu-mode 1))
|
||||
|
||||
(use-package exec-path-from-shell
|
||||
:when is-mac
|
||||
:config
|
||||
(exec-path-from-shell-initialize))
|
||||
|
||||
(use-package man
|
||||
:when is-mac
|
||||
:custom
|
||||
(manual-program "gman"))
|
||||
|
||||
(use-package magit
|
||||
:defer t)
|
||||
|
||||
@@ -164,7 +170,7 @@
|
||||
("\\.jsx\\'" . tsx-ts-mode)
|
||||
("\\.json\\'" . json-ts-mode))
|
||||
:custom
|
||||
(treesit-extra-load-path '("~/repos/tree-sitter-module/dist")))
|
||||
(treesit-extra-load-path `("~/repos/tree-sitter-module/dist" ,(concat user-emacs-directory "/tree-sitter"))))
|
||||
|
||||
(use-package tree-sitter-langs
|
||||
:defer t)
|
||||
@@ -175,6 +181,7 @@
|
||||
(use-package company)
|
||||
|
||||
(defun enable-parinfer ()
|
||||
(require 's)
|
||||
(let ((buf (or (buffer-file-name) (buffer-name) "")))
|
||||
(when (and
|
||||
(not (s-contains? "sbcl" buf))
|
||||
@@ -191,13 +198,46 @@
|
||||
(parinfer-rust-disable-troublesome-modes t))
|
||||
|
||||
(use-package sly
|
||||
:mode "\\.lisp\\'"
|
||||
:commands (sly)
|
||||
:custom
|
||||
(inferior-lisp-program "/opt/homebrew/bin/sbcl"))
|
||||
|
||||
(defun hgh/--project-execute (command name)
|
||||
(require 'project)
|
||||
(when (not command)
|
||||
(user-error (concat "No command specified for project-" name)))
|
||||
(let ((default-directory (project-root (project-current t))))
|
||||
(compilation-start command
|
||||
nil
|
||||
(lambda (mode-name) (concat "*" (project-name (project-current)) " " name "*")))))
|
||||
|
||||
|
||||
|
||||
(defmacro hgh/define-project-command (name)
|
||||
"Define a project command with NAME.
|
||||
Creates a variable `hgh/project-NAME-command' and function `hgh/project-NAME'."
|
||||
(let ((var-sym (intern (format "hgh/project-%s-command" name)))
|
||||
(fn-sym (intern (format "hgh/project-%s" name))))
|
||||
`(progn
|
||||
(defvar ,var-sym nil
|
||||
,(format "Command to %s the project." name))
|
||||
(put ',var-sym 'safe-local-variable #'stringp)
|
||||
(defun ,fn-sym ()
|
||||
,(format "Execute the project %s command." name)
|
||||
(interactive)
|
||||
(hgh/--project-execute ,var-sym ,name)))))
|
||||
|
||||
|
||||
(hgh/define-project-command "run")
|
||||
(hgh/define-project-command "test")
|
||||
(hgh/define-project-command "lint")
|
||||
|
||||
(use-package project
|
||||
:bind (("C-x p s" . hgh/project-ripgrep)
|
||||
("C-x p S" . project-shell)))
|
||||
("C-x p S" . project-shell)
|
||||
("C-x p t" . hgh/project-test)
|
||||
("C-x p r" . hgh/project-run)
|
||||
("C-x p l" . hgh/project-lint)))
|
||||
|
||||
(use-package vertico
|
||||
:custom
|
||||
@@ -222,9 +262,6 @@
|
||||
:init
|
||||
(add-to-list 'completion-at-point-functions #'cape-dabbrev))
|
||||
|
||||
(use-package browse-kill-ring
|
||||
:bind (("C-c y" . browse-kill-ring)))
|
||||
|
||||
(defun hgh/org-mode-visual-fill ()
|
||||
(setq visual-fill-column-width 120
|
||||
visual-fill-column-center-text t)
|
||||
@@ -245,7 +282,10 @@
|
||||
(svelte-mode . eglot-ensure)
|
||||
(haskell-mode . eglot-ensure)
|
||||
(terraform-mode . eglot-ensure)
|
||||
(odin-mode . eglot-ensure))
|
||||
(odin-mode . eglot-ensure)
|
||||
(tuareg-mode . eglot-ensure)
|
||||
(ruby-mode . eglot-ensure)
|
||||
(dart-mode . eglot-ensure))
|
||||
:bind
|
||||
(:map eglot-mode-map
|
||||
("C-c r" . eglot-rename)
|
||||
@@ -257,21 +297,39 @@
|
||||
:config
|
||||
;; Set up using clippy with rust analyzer
|
||||
(setf eglot-server-programs
|
||||
(cl-remove-if (lambda (c) (equal (car c) 'rust-mode))
|
||||
(cl-remove-if (lambda (c) (equal (car c) (or 'rust-mode 'tuareg-mode '(dart-mode dart-ts-mode))))
|
||||
eglot-server-programs))
|
||||
|
||||
(setf eglot-server-programs
|
||||
(append (list
|
||||
(list '(rust-ts-mode rust-mode) "rust-analyzer" :initializationOptions '(:checkOnSave (:command "clippy")))
|
||||
'(svelte-mode "svelteserver" "--stdio")
|
||||
'(haskell-mode "haskell-language-server-wrapper" "--lsp"))
|
||||
eglot-server-programs)))
|
||||
'(haskell-mode "haskell-language-server-wrapper" "--lsp")
|
||||
'(tuareg-mode "dune" "exec" "ocamllsp")
|
||||
'(reason-mode "dune" "exec" "ocamllsp")
|
||||
'(ruby-mode "solargraph" "socket" "--port" :autoport)
|
||||
'(dart-mode "dart" "language-server" "--client-id" "emacs.eglot-dart")
|
||||
eglot-server-programs))))
|
||||
|
||||
(setenv "DOTNET_ROOT" "~/.local/share/mise/installs/dotnet/9")
|
||||
(push '("\\.csproj$" . xml-mode) auto-mode-alist)
|
||||
(push '("\\.vs$" . c-mode) auto-mode-alist)
|
||||
(push '("\\.fs$" . c-mode) auto-mode-alist)
|
||||
(push '("\\.sbclrc" . lisp-mode) auto-mode-alist)
|
||||
(push '("\\.csproj$" . xml-mode) auto-mode-alist)
|
||||
(push '("\\.vs$" . c-mode) auto-mode-alist)
|
||||
(push '("\\.fs$" . c-mode) auto-mode-alist)
|
||||
(push '("\\.sbclrc" . lisp-mode) auto-mode-alist)
|
||||
(push '("dune-project\\'" . lisp-mode) auto-mode-alist)
|
||||
(push '("dune\\'" . lisp-mode) auto-mode-alist)
|
||||
(push '("\\.ml\\'" . tuareg-mode) auto-mode-alist)
|
||||
(setf auto-mode-alist
|
||||
(cl-remove-if
|
||||
(lambda (tup)
|
||||
(or
|
||||
(and
|
||||
(string-equal (car tup) "\\.ml\\'")
|
||||
(eq (cdr tup) 'ocaml-ts-mode))
|
||||
(and
|
||||
(string-equal (car tup) "\\.ml\\'")
|
||||
(eq (cdr tup) 'lisp-mode))))
|
||||
auto-mode-alist))
|
||||
|
||||
;; paredit
|
||||
|
||||
@@ -285,16 +343,10 @@
|
||||
(define-key paredit-mode-map (kbd "M-:") #'paredit-backward-barf-sexp)
|
||||
(enable-paredit-mode))
|
||||
|
||||
(defvar lisp-mode-hooks nil)
|
||||
(setf lisp-mode-hooks '((emacs-lisp-mode-hook emacs-lisp-mode)
|
||||
(clojure-mode-hook clojure-mode)
|
||||
(lisp-mode-hook lisp-mode)
|
||||
(sly-mode-hook sly-mode)))
|
||||
|
||||
(defun hgh/rg (search)
|
||||
(interactive "sSearch: ")
|
||||
(compilation-start
|
||||
(concat "rg --no-heading '" search "'")
|
||||
(concat "rg -S --no-heading '" search "'")
|
||||
'compilation-mode
|
||||
(lambda (s)
|
||||
(concat
|
||||
@@ -317,7 +369,6 @@
|
||||
(advice-add 'mc/mark-next-like-this :before #'hgh/set-cursor-type-box)
|
||||
(advice-add 'mc/mark-previous-like-this :before #'hgh/set-cursor-type-box))
|
||||
|
||||
|
||||
(use-package yasnippet
|
||||
:diminish
|
||||
:defer t
|
||||
@@ -365,26 +416,27 @@
|
||||
(org-html-head-include-default-style nil)
|
||||
(org-html-head-include-scripts nil)
|
||||
(org-html-head "<link rel=\"stylesheet\" href=\"https://cdn.simplecss.org/simple.min.css\" />")
|
||||
|
||||
(org-capture-templates
|
||||
'(("t" "Task" entry (file+headline "~/org/inbox.org" "Tasks")
|
||||
"* TODO %?\n%U\n%i\n%a")
|
||||
("n" "Note" entry (file+headline "~/org/inbox.org" "Notes")
|
||||
"* %?\n%U\n%i\n%a")
|
||||
("i" "Idea" entry (file+headline "~/org/inbox.org" "Ideas")
|
||||
"* %?\n%U\n%i\n%a")))
|
||||
'(("t" "Task" entry (file+headline "~/org/inbox.org" "Tasks")
|
||||
"* TODO %?\n%U\n%i\n%a")
|
||||
("n" "Note" entry (file+headline "~/org/inbox.org" "Notes")
|
||||
"* %?\n%U\n%i\n%a")
|
||||
("i" "Idea" entry (file+headline "~/org/inbox.org" "Ideas")
|
||||
"* %?\n%U\n%i\n%a")))
|
||||
|
||||
(org-publish-use-timestamps-flag nil)
|
||||
(org-publish-project-alist
|
||||
(list
|
||||
(list "writings"
|
||||
:base-directory "~/Documents/writings/content"
|
||||
:publishing-directory "~/Documents/writings/public"
|
||||
:exclude "~/Documents/writings/notes"
|
||||
:recursive t
|
||||
:time-stamp-file nil
|
||||
:section-numbers nil
|
||||
:with-creator t
|
||||
:with-author nil)))
|
||||
(list
|
||||
(list "writings"
|
||||
:base-directory "~/Documents/writings/content"
|
||||
:publishing-directory "~/Documents/writings/public"
|
||||
:exclude "~/Documents/writings/notes"
|
||||
:recursive t
|
||||
:time-stamp-file nil
|
||||
:section-numbers nil
|
||||
:with-creator t
|
||||
:with-author nil)))
|
||||
|
||||
:config
|
||||
(require 'ox-publish)
|
||||
@@ -394,10 +446,12 @@
|
||||
'((shell . t))))
|
||||
|
||||
(use-package dumber-jump
|
||||
:defer t
|
||||
:init
|
||||
(add-hook 'xref-backend-functions #'dumber-jump-xref-activate))
|
||||
|
||||
(use-package idris-mode)
|
||||
(use-package idris-mode
|
||||
:mode "\\.idr\\'")
|
||||
|
||||
(use-package htmlize)
|
||||
|
||||
@@ -444,3 +498,74 @@
|
||||
js-mode
|
||||
;; Need to add this
|
||||
tsx-ts-mode)))
|
||||
|
||||
(use-package mu4e
|
||||
:ensure nil
|
||||
:commands (mu4e)
|
||||
:custom
|
||||
(mu4e-maildir "~/Mail")
|
||||
(mu4e-get-mail-command "mbsync -a")
|
||||
(mu4e-update-interval 300)
|
||||
(mu4e-change-filenames-when-moving t)
|
||||
(mu4e-context-policy 'pick-first)
|
||||
(mu4e-compose-context-policy 'ask-if-none)
|
||||
(mu4e-contexts
|
||||
(list
|
||||
(make-mu4e-context
|
||||
:name "Grant"
|
||||
:match-func
|
||||
(lambda (msg)
|
||||
(when msg
|
||||
(string-prefix-p "/grant" (mu4e-message-field msg :maildir))))
|
||||
:vars '((user-mail-address . "grant@granthorner.dev")
|
||||
(user-full-name . "Grant Horner")
|
||||
(mu4e-drafts-folder . "/grant/Drafts")
|
||||
(mu4e-sent-folder . "/grant/Sent Mail")
|
||||
(mu4e-trash-folder . "/grant/Trash")
|
||||
(mu4e-rstrash-folder . "/grant/Trash")))
|
||||
(make-mu4e-context
|
||||
:name "Gmail"
|
||||
:match-func
|
||||
(lambda (msg)
|
||||
(when msg
|
||||
(string-prefix-p "/gmail" (mu4e-message-field msg :maildir))))
|
||||
:vars '((user-mail-address . "gmail@granthorner.dev")
|
||||
(user-full-name . "Grant Horner")
|
||||
(mu4e-drafts-folder . "/gmail/Drafts")
|
||||
(mu4e-sent-folder . "/gmail/Sent Mail")
|
||||
(mu4e-trash-folder . "/gmail/Trash")
|
||||
(mu4e-rstrash-folder . "/gmail/Trash"))))))
|
||||
|
||||
(use-package tuareg
|
||||
:mode "\\.ml\\'")
|
||||
|
||||
(defun hgh/eglot-restart ()
|
||||
(interactive)
|
||||
(require 'eglot)
|
||||
(when eglot--managed-mode
|
||||
(message "Shutting down eglot servers...")
|
||||
(eglot-shutdown-all)
|
||||
(message "Restarting eglot for current buffer...")
|
||||
(eglot)))
|
||||
|
||||
(use-package reason-mode
|
||||
:mode "\\.re\\'")
|
||||
|
||||
(use-package expand-region
|
||||
:bind ("C-=" . er/expand-region))
|
||||
|
||||
(use-package ediff
|
||||
:defer t
|
||||
:custom
|
||||
(ediff-window-setup-function 'ediff-setup-windows-plain)
|
||||
(ediff-split-window-function 'split-window-horizontally)
|
||||
(ediff-diff-options "-w")
|
||||
:config
|
||||
(set-face-attribute 'ediff-odd-diff-A nil :background "#75383b")
|
||||
(set-face-attribute 'ediff-even-diff-A nil :background "#75383b")
|
||||
(set-face-attribute 'ediff-odd-diff-B nil :background "#265441")
|
||||
(set-face-attribute 'ediff-even-diff-B nil :background "#265441"))
|
||||
|
||||
(use-package dart-mode)
|
||||
|
||||
(use-package flutter)
|
||||
|
||||
@@ -83,7 +83,7 @@ http_headers = {
|
||||
:group 'codex)
|
||||
|
||||
(defcustom codex-sessions-file (concat user-emacs-directory "codex-sessions")
|
||||
"What the default name should be fore the codex chat buffer."
|
||||
"What the default name should be for the codex chat buffer."
|
||||
:type 'string
|
||||
:group 'codex)
|
||||
|
||||
346
user-lisp/llm-tools.el
Normal file
346
user-lisp/llm-tools.el
Normal file
@@ -0,0 +1,346 @@
|
||||
;;; llm-tools.el --- Emacs tools for GPTel models -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2024
|
||||
|
||||
;; Author: Your Name
|
||||
;; Keywords: tools, convenience, gptel
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This file provides GPTel tools that allow LLM models to look up
|
||||
;; information in Emacs, including function names and documentation,
|
||||
;; variable names and documentation, key bindings, and more.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'gptel)
|
||||
|
||||
;; ============================================================================
|
||||
;; Documentation Lookup Tools
|
||||
;; ============================================================================
|
||||
|
||||
(gptel-make-tool
|
||||
:name "get_function_doc"
|
||||
:category "emacs"
|
||||
:description "Get the documentation string for an Emacs Lisp function. Returns the function's docstring, argument list, and source file location."
|
||||
:function (lambda (function-name)
|
||||
(let ((sym (intern-soft function-name)))
|
||||
(if (and sym (fboundp sym))
|
||||
(let* ((doc (documentation sym t))
|
||||
(arglist (help-function-arglist sym))
|
||||
(file (find-lisp-object-file-name sym 'defun)))
|
||||
(format "Function: %s\nArguments: %s\nFile: %s\n\nDocumentation:\n%s"
|
||||
sym
|
||||
(if arglist (prin1-to-string arglist) "(unknown)")
|
||||
(or file "built-in/unknown")
|
||||
(or doc "No documentation available")))
|
||||
(format "Error: Function '%s' not found" function-name))))
|
||||
:args '((:name "function-name"
|
||||
:type string
|
||||
:description "The name of the function (symbol) to look up"
|
||||
:optional nil)))
|
||||
|
||||
(gptel-make-tool
|
||||
:name "get_variable_doc"
|
||||
:category "emacs"
|
||||
:description "Get the documentation string for an Emacs Lisp variable. Returns the variable's docstring, current value (if safe to display), and source file location."
|
||||
:function (lambda (variable-name)
|
||||
(let ((sym (intern-soft variable-name)))
|
||||
(if (and sym (boundp sym))
|
||||
(let* ((doc (documentation-property sym 'variable-documentation))
|
||||
(file (find-lisp-object-file-name sym 'defvar))
|
||||
(value (bound-and-true-p sym))
|
||||
(value-str (if (and value (or (numberp value)
|
||||
(stringp value)
|
||||
(symbolp value)
|
||||
(and (listp value)
|
||||
(< (length (format "%s" value)) 200))))
|
||||
(format "%S" value)
|
||||
"(complex value omitted)")))
|
||||
(format "Variable: %s\nCurrent Value: %s\nFile: %s\n\nDocumentation:\n%s"
|
||||
sym
|
||||
value-str
|
||||
(or file "built-in/unknown")
|
||||
(or doc "No documentation available")))
|
||||
(format "Error: Variable '%s' not found" variable-name))))
|
||||
:args '((:name "variable-name"
|
||||
:type string
|
||||
:description "The name of the variable (symbol) to look up"
|
||||
:optional nil)))
|
||||
|
||||
(gptel-make-tool
|
||||
:name "get_face_doc"
|
||||
:category "emacs"
|
||||
:description "Get information about an Emacs face including its documentation and attributes."
|
||||
:function (lambda (face-name)
|
||||
(let ((sym (intern-soft face-name)))
|
||||
(if (and sym (facep sym))
|
||||
(let* ((doc (documentation-property sym 'face-documentation))
|
||||
(attrs (face-all-attributes sym (selected-frame))))
|
||||
(format "Face: %s\n\nDocumentation:\n%s\n\nAttributes:\n%s"
|
||||
sym
|
||||
(or doc "No documentation available")
|
||||
(mapconcat (lambda (attr)
|
||||
(format " %s: %S" (car attr) (cdr attr)))
|
||||
attrs
|
||||
"\n")))
|
||||
(format "Error: Face '%s' not found" face-name))))
|
||||
:args '((:name "face-name"
|
||||
:type string
|
||||
:description "The name of the face to look up"
|
||||
:optional nil)))
|
||||
|
||||
(gptel-make-tool
|
||||
:name "describe_key"
|
||||
:category "emacs"
|
||||
:description "Get the command bound to a specific key sequence in Emacs."
|
||||
:function (lambda (key-sequence)
|
||||
(condition-case err
|
||||
(let* ((keys (read-kbd-macro key-sequence))
|
||||
(binding (key-binding keys))
|
||||
(cmd (if (arrayp binding)
|
||||
(format "Keymap: %S" binding)
|
||||
binding)))
|
||||
(if (and cmd (not (eq cmd 'nil)))
|
||||
(format "Key: %s\nBound to: %s\n\nDocumentation:\n%s"
|
||||
key-sequence
|
||||
(if (symbolp cmd) cmd (format "%S" cmd))
|
||||
(if (symbolp cmd)
|
||||
(or (documentation cmd t) "No documentation")
|
||||
"Not a command"))
|
||||
(format "Key '%s' is not bound to any command" key-sequence)))
|
||||
(error (format "Error parsing key sequence '%s': %s"
|
||||
key-sequence
|
||||
(error-message-string err)))))
|
||||
:args '((:name "key-sequence"
|
||||
:type string
|
||||
:description "The key sequence as a string (e.g., 'C-x C-f')"
|
||||
:optional nil)))
|
||||
|
||||
(gptel-make-tool
|
||||
:name "apropos_lookup"
|
||||
:category "emacs"
|
||||
:description "Look up Emacs symbols using apropos. Searches for symbols matching a pattern and returns their names, types, and documentation snippets."
|
||||
:function (lambda (pattern type)
|
||||
(let* ((symbols (apropos-internal pattern (lambda (s) (fboundp s))))
|
||||
(vars (apropos-internal pattern (lambda (s) (boundp s))))
|
||||
(faces (apropos-internal pattern (lambda (s) (facep s))))
|
||||
(filter-fn (cond
|
||||
((string= type "functions") (lambda (s) (memq s symbols)))
|
||||
((string= type "variables") (lambda (s) (memq s vars)))
|
||||
((string= type "faces") (lambda (s) (memq s faces)))
|
||||
(t (lambda (s) t))))
|
||||
(all-symbols (delete-dups (append symbols vars faces)))
|
||||
(filtered (seq-filter filter-fn all-symbols)))
|
||||
(if filtered
|
||||
(format "Found %d symbols matching '%s':\n\n%s"
|
||||
(length filtered)
|
||||
pattern
|
||||
(mapconcat (lambda (sym)
|
||||
(format "%s (%s)\n %s"
|
||||
sym
|
||||
(cond ((fboundp sym) "function")
|
||||
((boundp sym) "variable")
|
||||
((facep sym) "face")
|
||||
(t "symbol"))
|
||||
(let ((doc (or (documentation sym t)
|
||||
(documentation-property sym 'variable-documentation)
|
||||
(documentation-property sym 'face-documentation)
|
||||
"No documentation")))
|
||||
(if (> (length doc) 100)
|
||||
(concat (substring doc 0 100) "...")
|
||||
doc))))
|
||||
filtered
|
||||
"\n\n"))
|
||||
(format "No symbols matching pattern '%s' found" pattern))))
|
||||
:args '((:name "pattern"
|
||||
:type string
|
||||
:description "The regex pattern to search for in symbol names"
|
||||
:optional nil)
|
||||
(:name "type"
|
||||
:type string
|
||||
:description "Filter by type: 'functions', 'variables', 'faces', or 'all' (default 'all')"
|
||||
:optional t
|
||||
:default "all")))
|
||||
|
||||
;; ============================================================================
|
||||
;; Buffer/List Tools
|
||||
;; ============================================================================
|
||||
|
||||
(gptel-make-tool
|
||||
:name "list_modes"
|
||||
:category "emacs"
|
||||
:description "List major and minor modes available in Emacs."
|
||||
:function (lambda (mode-type pattern)
|
||||
(let ((major-modes (apropos-internal "-mode\\'" (lambda (s)
|
||||
(and (boundp s)
|
||||
(get s 'derived-mode-parent)))))
|
||||
(minor-modes (apropos-internal "-mode\\'" (lambda (s)
|
||||
(and (boundp s)
|
||||
(get s 'minor-mode-function)))))
|
||||
(filter (when pattern
|
||||
(lambda (s) (string-match-p pattern (symbol-name s))))))
|
||||
(when filter
|
||||
(setq major-modes (seq-filter filter major-modes))
|
||||
(setq minor-modes (seq-filter filter minor-modes)))
|
||||
(let ((results
|
||||
(cond
|
||||
((string= mode-type "major")
|
||||
(cons "Major Modes" major-modes))
|
||||
((string= mode-type "minor")
|
||||
(cons "Minor Modes" minor-modes))
|
||||
(t
|
||||
`(("Major Modes" . ,major-modes)
|
||||
("Minor Modes" . ,minor-modes))))))
|
||||
(if (consp (car results))
|
||||
(format "%s\n\n%s"
|
||||
(format "%s (%d):\n%s"
|
||||
(caar results)
|
||||
(length (cdar results))
|
||||
(mapconcat (lambda (m) (format " - %s" m))
|
||||
(cdar results)
|
||||
"\n"))
|
||||
(format "%s (%d):\n%s"
|
||||
(cadr results)
|
||||
(length (cddr results))
|
||||
(mapconcat (lambda (m) (format " - %s" m))
|
||||
(cddr results)
|
||||
"\n")))
|
||||
(format "%s (%d):\n%s"
|
||||
(car results)
|
||||
(length (cdr results))
|
||||
(mapconcat (lambda (m) (format " - %s" m))
|
||||
(cdr results)
|
||||
"\n"))))))
|
||||
:args '((:name "mode-type"
|
||||
:type string
|
||||
:description "Type of mode: 'major', 'minor', or 'all' (default 'all')"
|
||||
:optional t
|
||||
:default "all")
|
||||
(:name "pattern"
|
||||
:type string
|
||||
:description "Optional regex pattern to filter mode names"
|
||||
:optional t)))
|
||||
|
||||
;; ============================================================================
|
||||
;; Source Location Tools
|
||||
;; ============================================================================
|
||||
|
||||
(gptel-make-tool
|
||||
:name "find_function_source"
|
||||
:category "emacs"
|
||||
:description "Find the file where an Emacs Lisp function is defined. Returns the file path and line number if available."
|
||||
:function (lambda (function-name)
|
||||
(let ((sym (intern-soft function-name)))
|
||||
(if (and sym (fboundp sym))
|
||||
(let* ((file (find-lisp-object-file-name sym 'defun))
|
||||
(source (when file
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (format "^(defun\\s +%s" (regexp-quote function-name)) nil t)
|
||||
(when (re-search-backward "^(defun\\s +" nil t)
|
||||
(cons file (line-number-at-pos)))))))
|
||||
(if source
|
||||
(format "Function '%s' is defined in:\n %s:%d"
|
||||
function-name (car source) (cdr source))
|
||||
(format "Function '%s' source location: %s"
|
||||
function-name (or file "built-in/unknown"))))
|
||||
(format "Error: Function '%s' not found" function-name))))
|
||||
:args '((:name "function-name"
|
||||
:type string
|
||||
:description "The name of the function to locate"
|
||||
:optional nil)))
|
||||
|
||||
(gptel-make-tool
|
||||
:name "find_variable_source"
|
||||
:category "emacs"
|
||||
:description "Find the file where an Emacs Lisp variable is defined. Returns the file path if available."
|
||||
:function (lambda (variable-name)
|
||||
(let ((sym (intern-soft variable-name)))
|
||||
(if (and sym (boundp sym))
|
||||
(let ((file (or (find-lisp-object-file-name sym 'defvar)
|
||||
(find-lisp-object-file-name sym 'defcustom)
|
||||
(find-lisp-object-file-name sym 'defconst))))
|
||||
(format "Variable '%s' is defined in: %s"
|
||||
variable-name (or file "built-in/unknown")))
|
||||
(format "Error: Variable '%s' not found" variable-name))))
|
||||
:args '((:name "variable-name"
|
||||
:type string
|
||||
:description "The name of the variable to locate"
|
||||
:optional nil)))
|
||||
|
||||
;; ============================================================================
|
||||
;; Current Context Tools
|
||||
;; ============================================================================
|
||||
|
||||
(gptel-make-tool
|
||||
:name "get_buffer_context"
|
||||
:category "emacs"
|
||||
:description "Get information about the current buffer including its name, major mode, minor modes, and file path if applicable."
|
||||
:function (lambda ()
|
||||
(format "Buffer Context:\n Name: %s\n File: %s\n Major Mode: %s\n Minor Modes: %s\n Read-only: %s\n Modified: %s"
|
||||
(buffer-name (current-buffer))
|
||||
(or (buffer-file-name (current-buffer)) "N/A")
|
||||
major-mode
|
||||
(mapconcat (lambda (m) (format "%s" m))
|
||||
(seq-filter (lambda (m) (and (boundp m) (symbol-value m)))
|
||||
minor-mode-list)
|
||||
", ")
|
||||
(if buffer-read-only "yes" "no")
|
||||
(if (buffer-modified-p) "yes" "no")))
|
||||
:args nil)
|
||||
|
||||
(gptel-make-tool
|
||||
:name "get_active_features"
|
||||
:category "emacs"
|
||||
:description "Get information about active Emacs features and packages."
|
||||
:function (lambda ()
|
||||
(format "Active Features (loaded packages/modules):\n%s"
|
||||
(mapconcat (lambda (f) (format " - %s" f))
|
||||
features
|
||||
"\n")))
|
||||
:args nil)
|
||||
|
||||
;; ============================================================================
|
||||
;; Tool Registry for Easy Enable/Disable
|
||||
;; ============================================================================
|
||||
|
||||
(defconst llm-tools-tool-list
|
||||
'("get_function_doc"
|
||||
"get_variable_doc"
|
||||
"get_face_doc"
|
||||
"describe_key"
|
||||
"apropos_lookup"
|
||||
"list_modes"
|
||||
"find_function_source"
|
||||
"find_variable_source"
|
||||
"get_buffer_context"
|
||||
"get_active_features")
|
||||
"List of all LLM tool symbols defined in this file.")
|
||||
|
||||
;;;###autoload
|
||||
(defun llm-tools-enable-all ()
|
||||
"Enable all LLM Emacs tools for GPTel."
|
||||
(interactive)
|
||||
(dolist (tool llm-tools-tool-list)
|
||||
(when (and tool (gptel-get-tool tool))
|
||||
(add-to-list 'gptel-tools (gptel-get-tool tool))))
|
||||
(message "LLM Emacs tools enabled for GPTel (%d tools)"
|
||||
(length gptel-tools)))
|
||||
|
||||
;;;###autoload
|
||||
(defun llm-tools-disable-all ()
|
||||
"Disable all LLM Emacs tools from GPTel."
|
||||
(interactive)
|
||||
(dolist (tool llm-tools-tool-list)
|
||||
(let ((tool-obj (gptel-get-tool tool)))
|
||||
(when tool-obj
|
||||
(setq gptel-tools (delete tool-obj gptel-tools)))))
|
||||
(message "LLM Emacs tools disabled from GPTel"))
|
||||
|
||||
(provide 'llm-tools)
|
||||
|
||||
;;; llm-tools.el ends here
|
||||
115
user-lisp/magit-ediff-all.el
Normal file
115
user-lisp/magit-ediff-all.el
Normal file
@@ -0,0 +1,115 @@
|
||||
;;; magit-ediff-all.el --- Compare all changed files between revisions using Ediff -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2025 The Magit Project Contributors
|
||||
|
||||
;; Author: [Your Name]
|
||||
;; Maintainer: [Your Name]
|
||||
|
||||
;; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This library extends Magit's ediff support by adding a command to
|
||||
;; compare all changed files between two revisions using an ediff
|
||||
;; session group.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'magit)
|
||||
(require 'magit-ediff)
|
||||
(require 'ediff)
|
||||
(require 'cl-lib)
|
||||
|
||||
;; Define the new suffix key and description
|
||||
(defvar magit-ediff-all-key "A"
|
||||
"Key binding for the ediff all command in the magit-ediff transient.")
|
||||
|
||||
(defvar magit-ediff-all-description "All changed files (between revisions)"
|
||||
"Description for the ediff all command in the magit-ediff transient.")
|
||||
|
||||
(defun magit-ediff-all--write-file (temp-files temp-buffers rev dir file)
|
||||
(let* ((rel-path (if (file-name-absolute-p file)
|
||||
(file-relative-name file (magit-toplevel))
|
||||
file))
|
||||
(rel-file (expand-file-name rel-path dir))
|
||||
;; Handle files that are added or removed - use empty buffer
|
||||
(buf (or (magit-find-file-noselect rev file)
|
||||
(generate-new-buffer " *empty*"))))
|
||||
;; Create necessary subdirectories
|
||||
(let ((dir-part (file-name-directory rel-file)))
|
||||
(when dir-part
|
||||
(make-directory dir-part t)))
|
||||
(push rel-file temp-files)
|
||||
(push buf temp-buffers)
|
||||
(with-current-buffer buf
|
||||
(write-region (point-min) (point-max) rel-file nil 'quiet)
|
||||
(add-hook 'ediff-prepare-buffer-hook
|
||||
(lambda ()
|
||||
(rename-buffer (format "%s~%s~" rel-file rev)))
|
||||
0 t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-all-files (revA revB)
|
||||
"Compare all changed files between REVA and REVB using Ediff.
|
||||
|
||||
This creates an Ediff session group that allows you to navigate
|
||||
through all changed files between the two revisions and compare
|
||||
them one at a time."
|
||||
(interactive
|
||||
(pcase-let ((`(,a ,b) (magit-ediff-compare--read-revisions nil)))
|
||||
(list a b)))
|
||||
|
||||
(when (or (not revA) (not revB))
|
||||
(user-error "Both revisions must be specified"))
|
||||
|
||||
(magit-with-toplevel
|
||||
(let* ((changed-files (magit-changed-files revA revB))
|
||||
(dirA (make-temp-file (format "magit-ediff-%s-" revA) t))
|
||||
(dirB (make-temp-file (format "magit-ediff-%s-" revB) t))
|
||||
temp-files
|
||||
temp-buffers)
|
||||
|
||||
(message "Preparing %d files for comparison..." (length changed-files))
|
||||
|
||||
;; Create temporary files for each revision
|
||||
(dolist (file changed-files)
|
||||
(magit-ediff-all--write-file temp-files temp-buffers revA dirA file)
|
||||
(magit-ediff-all--write-file temp-files temp-buffers revB dirB file))
|
||||
|
||||
|
||||
;; Define cleanup function
|
||||
(cl-labels
|
||||
((magit-ediff-all--cleanup ()
|
||||
(dolist (file temp-files)
|
||||
(when (file-exists-p file)
|
||||
(delete-file file)))
|
||||
;; Kill any buffers visiting files in the temp directories
|
||||
(dolist (buf (buffer-list))
|
||||
(when-let ((filename (buffer-file-name buf)))
|
||||
(when (or (string-prefix-p (expand-file-name dirA) filename)
|
||||
(string-prefix-p (expand-file-name dirB) filename))
|
||||
(when (buffer-live-p buf)
|
||||
(kill-buffer buf)))))
|
||||
(when (file-exists-p dirA)
|
||||
(delete-directory dirA t))
|
||||
(when (file-exists-p dirB)
|
||||
(delete-directory dirB t))
|
||||
(dolist (buf temp-buffers)
|
||||
(when (buffer-live-p buf)
|
||||
(kill-buffer buf)))
|
||||
(remove-hook 'ediff-quit-session-group-hook #'magit-ediff-all--cleanup)))
|
||||
|
||||
;; Register hooks
|
||||
(add-hook 'ediff-quit-session-group-hook #'magit-ediff-all--cleanup)
|
||||
|
||||
;; Open ediff on the two directories
|
||||
(message "Opening Ediff session group...")
|
||||
(ediff-directories dirA dirB nil)))))
|
||||
|
||||
;; Add the command to the magit-ediff transient
|
||||
(with-eval-after-load 'magit-ediff
|
||||
(transient-append-suffix 'magit-ediff "r"
|
||||
`(,magit-ediff-all-key ,magit-ediff-all-description magit-ediff-all-files)))
|
||||
|
||||
(provide 'magit-ediff-all)
|
||||
;;; magit-ediff-all.el ends here
|
||||
Reference in New Issue
Block a user