Fix CUA mode, selection keybindings, and replace Corfu with Company
Major improvements to Emacs configuration: 1. Fixed CUA mode and C-Shift-Arrow selection issues - Properly configured CUA mode for copy/paste (C-c, C-v, C-x) - Fixed C-Shift-Arrow word selection that was being intercepted - Added mode-specific CUA disabling for special modes 2. Replaced Corfu with Company mode - Removed problematic Corfu configuration causing errors - Installed and configured Company for stable auto-completion - Set up proper completion triggers and navigation 3. Integrated standalone fix files into existing configuration - Merged keybinding-fix.el into relevant config files - Added diagnostic functions for debugging keybinding issues - Cleaner organization with fixes in their respective modules 4. Enhanced diagnostics - Added diagnose-cua-selection for CUA/selection issues - Added diagnose-key-conflicts for debugging key bindings - Emergency editing restoration functions preserved All changes tested and verified working. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
46
INTEGRATION-SUMMARY.md
Normal file
46
INTEGRATION-SUMMARY.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Configuration Integration Summary
|
||||
|
||||
## Successfully Integrated Fix Files
|
||||
|
||||
### 1. keybinding-fix.el → Integrated into multiple files
|
||||
- **elfeed-config.el**: Added CUA mode disabling hooks and keybinding setup for elfeed modes
|
||||
- **portfolio-tracker-v2.el**: Added CUA mode disabling in mode definition
|
||||
- **init-editor.el**: Added `diagnose-key-conflicts` function for debugging
|
||||
- **init-ui.el**: Already contains CUA mode configuration with mode-specific disabling
|
||||
|
||||
### 2. elfeed-debug.el → Partially integrated
|
||||
- Diagnostic functionality merged into `diagnose-key-conflicts` in init-editor.el
|
||||
- Elfeed-specific fixes integrated into elfeed-config.el
|
||||
|
||||
### 3. Removed Files
|
||||
- `/Users/jens/.emacs.d/keybinding-fix.el` - Fully integrated
|
||||
- `/Users/jens/.emacs.d/elfeed-debug.el` - Functionality integrated
|
||||
- Temporary test and documentation files removed
|
||||
|
||||
## Retained Fix Files (Properly Organized)
|
||||
|
||||
### In lisp/ directory:
|
||||
1. **init-emergency-fix.el** - Emergency editing restoration functions
|
||||
2. **init-eslint-fix.el** - ESLint configuration handling
|
||||
3. **init-seq-fix.el** - Seq library compatibility fixes
|
||||
|
||||
These are legitimate fix modules that should remain as separate files.
|
||||
|
||||
## Key Improvements
|
||||
|
||||
1. **Cleaner Organization**: Fix code integrated into relevant configuration files
|
||||
2. **No Duplicate Loading**: Removed redundant fix file loading from init.el
|
||||
3. **Better Maintainability**: Related fixes are now with their respective modules
|
||||
|
||||
## Verification
|
||||
✓ Configuration loads successfully
|
||||
✓ CUA mode enabled with proper settings
|
||||
✓ Company mode installed and available
|
||||
✓ Emergency fixes accessible
|
||||
✓ Diagnostic functions available
|
||||
|
||||
## Diagnostic Commands
|
||||
- `M-x diagnose-cua-selection` - Check CUA and selection settings
|
||||
- `M-x diagnose-key-conflicts` - Debug key binding conflicts
|
||||
- `M-x diagnose-editing-issue` - Check why editing might be disabled
|
||||
- `M-x fix-editing-now` - Emergency fix for editing issues
|
||||
14
init.el
14
init.el
@@ -89,15 +89,11 @@
|
||||
(load-file portfolio-tracker)
|
||||
(message "Portfolio tracker with live prices loaded."))))
|
||||
|
||||
;; Keybinding fixes for special modes
|
||||
(let ((keybinding-fix (expand-file-name "lisp/keybinding-fix.el" user-emacs-directory)))
|
||||
(when (file-exists-p keybinding-fix)
|
||||
(load-file keybinding-fix)
|
||||
;; Automatically apply fixes for special modes
|
||||
(fix-elfeed-keybindings)
|
||||
(fix-portfolio-tracker-keybindings)
|
||||
(disable-cua-in-special-modes)
|
||||
(message "Keybinding fixes loaded and applied.")))
|
||||
;; Keybinding fixes are now integrated into the respective configuration files:
|
||||
;; - Elfeed fixes in lisp/elfeed-config.el
|
||||
;; - Portfolio tracker fixes in portfolio-tracker-v2.el
|
||||
;; - CUA mode handling in lisp/init-ui.el
|
||||
;; - Diagnostic functions in lisp/init-editor.el
|
||||
|
||||
;;; Custom Settings (preserved from original)
|
||||
;;; These are managed by Emacs Custom system - do not edit manually
|
||||
|
||||
@@ -181,12 +181,28 @@
|
||||
|
||||
;; Keybindings for elfeed
|
||||
(with-eval-after-load 'elfeed
|
||||
;; Disable CUA mode in elfeed buffers to allow single-key commands
|
||||
(add-hook 'elfeed-search-mode-hook
|
||||
(lambda ()
|
||||
(setq-local cua-mode nil)
|
||||
(setq-local cua-enable-cua-keys nil)))
|
||||
|
||||
(add-hook 'elfeed-show-mode-hook
|
||||
(lambda ()
|
||||
(setq-local cua-mode nil)
|
||||
(setq-local cua-enable-cua-keys nil)))
|
||||
|
||||
;; Define keybindings
|
||||
(define-key elfeed-search-mode-map (kbd "j") 'next-line)
|
||||
(define-key elfeed-search-mode-map (kbd "k") 'previous-line)
|
||||
(define-key elfeed-search-mode-map (kbd "m") 'elfeed-search-toggle-all-star)
|
||||
(define-key elfeed-search-mode-map (kbd "u") 'elfeed-search-toggle-all-unread)
|
||||
(define-key elfeed-search-mode-map (kbd "U") 'elfeed-update-async)
|
||||
(define-key elfeed-search-mode-map (kbd "f") 'elfeed-search-live-filter))
|
||||
(define-key elfeed-search-mode-map (kbd "f") 'elfeed-search-live-filter)
|
||||
(define-key elfeed-search-mode-map (kbd "g") 'elfeed-search-update--force)
|
||||
(define-key elfeed-search-mode-map (kbd "G") 'elfeed-search-fetch)
|
||||
(define-key elfeed-search-mode-map (kbd "r") 'elfeed-search-untag-all-unread)
|
||||
(define-key elfeed-search-mode-map (kbd "s") 'elfeed-search-live-filter))
|
||||
|
||||
;; Function to reload elfeed-org configuration
|
||||
(defun elfeed-org-reload ()
|
||||
|
||||
@@ -151,73 +151,62 @@
|
||||
:hook
|
||||
(embark-collect-mode . consult-preview-at-point-mode))
|
||||
|
||||
;;; Corfu - In-buffer completion popup
|
||||
(use-package corfu
|
||||
;;; Company - In-buffer completion framework
|
||||
(use-package company
|
||||
:ensure t
|
||||
:diminish company-mode
|
||||
:hook (after-init . global-company-mode)
|
||||
:custom
|
||||
(corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
|
||||
(corfu-auto t) ;; Enable auto completion
|
||||
(corfu-auto-delay 0.2)
|
||||
(corfu-auto-prefix 2)
|
||||
(corfu-separator ?\s) ;; Orderless field separator
|
||||
(corfu-quit-at-boundary nil) ;; Never quit at completion boundary
|
||||
(corfu-quit-no-match nil) ;; Never quit, even if there is no match
|
||||
(corfu-preview-current nil) ;; Disable current candidate preview
|
||||
(corfu-preselect 'prompt) ;; Preselect the prompt
|
||||
(corfu-on-exact-match nil) ;; Configure handling of exact matches
|
||||
(corfu-scroll-margin 5) ;; Use scroll margin
|
||||
(company-idle-delay 0.2)
|
||||
(company-minimum-prefix-length 2)
|
||||
(company-show-numbers t)
|
||||
(company-tooltip-align-annotations t)
|
||||
(company-tooltip-flip-when-above t)
|
||||
(company-require-match nil)
|
||||
(company-dabbrev-downcase nil)
|
||||
(company-dabbrev-ignore-case nil)
|
||||
(company-selection-wrap-around t)
|
||||
(company-transformers '(company-sort-by-occurrence))
|
||||
|
||||
:config
|
||||
;; Fix font issues with Corfu child frames
|
||||
(defun corfu--fix-child-frame-font (frame)
|
||||
"Ensure child frames don't inherit problematic font specs."
|
||||
frame)
|
||||
;; Use Tab and Shift-Tab to navigate completions
|
||||
(define-key company-active-map (kbd "TAB") 'company-complete-selection)
|
||||
(define-key company-active-map (kbd "<tab>") 'company-complete-selection)
|
||||
(define-key company-active-map (kbd "S-TAB") 'company-select-previous)
|
||||
(define-key company-active-map (kbd "<backtab>") 'company-select-previous)
|
||||
|
||||
;; Override the frame parameters to avoid font issues
|
||||
(setq corfu--frame-parameters
|
||||
'((no-accept-focus . t)
|
||||
(no-focus-on-map . t)
|
||||
(min-width . t)
|
||||
(min-height . t)
|
||||
(border-width . 0)
|
||||
(outer-border-width . 0)
|
||||
(internal-border-width . 1)
|
||||
(child-frame-border-width . 1)
|
||||
(left-fringe . 7)
|
||||
(right-fringe . 7)
|
||||
(vertical-scroll-bars)
|
||||
(horizontal-scroll-bars)
|
||||
(menu-bar-lines . 0)
|
||||
(tool-bar-lines . 0)
|
||||
(tab-bar-lines . 0)
|
||||
(tab-bar-lines-keep-state . t)
|
||||
(no-other-frame . t)
|
||||
(unsplittable . t)
|
||||
(undecorated . t)
|
||||
(cursor-type)
|
||||
(no-special-glyphs . t)
|
||||
(desktop-dont-save . t)
|
||||
(inhibit-double-buffering . t)))
|
||||
;; Use C-n and C-p for navigation as well
|
||||
(define-key company-active-map (kbd "C-n") 'company-select-next)
|
||||
(define-key company-active-map (kbd "C-p") 'company-select-previous)
|
||||
|
||||
:init
|
||||
(global-corfu-mode))
|
||||
;; Disable conflicting bindings
|
||||
(define-key company-active-map (kbd "RET") nil)
|
||||
(define-key company-active-map (kbd "<return>") nil)
|
||||
|
||||
;;; Cape - Completion extensions for Corfu
|
||||
(use-package cape
|
||||
:ensure t
|
||||
:init
|
||||
;; Add `completion-at-point-functions', used by `completion-at-point'.
|
||||
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
|
||||
(add-to-list 'completion-at-point-functions #'cape-file)
|
||||
(add-to-list 'completion-at-point-functions #'cape-keyword)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-tex)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-sgml)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-rfc1345)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-dict)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-symbol)
|
||||
;;(add-to-list 'completion-at-point-functions #'cape-line)
|
||||
)
|
||||
;; Complete with Enter
|
||||
(define-key company-active-map (kbd "RET") 'company-complete-selection)
|
||||
(define-key company-active-map (kbd "<return>") 'company-complete-selection)
|
||||
|
||||
;; Configure backends
|
||||
(setq company-backends
|
||||
'((company-capf company-files)
|
||||
(company-dabbrev-code company-keywords)
|
||||
company-dabbrev)))
|
||||
|
||||
;;; Company-box - Better UI for Company (optional, may not be available)
|
||||
;; Commented out as company-box is not always available in package repos
|
||||
;; Uncomment if you want to try installing it manually
|
||||
;; (use-package company-box
|
||||
;; :ensure t
|
||||
;; :hook (company-mode . company-box-mode)
|
||||
;; :custom
|
||||
;; (company-box-show-single-candidate t)
|
||||
;; (company-box-backends-colors nil)
|
||||
;; (company-box-max-candidates 50)
|
||||
;; (company-box-icons-alist 'company-box-icons-all-the-icons)
|
||||
;; :config
|
||||
;; ;; Workaround for font/display issues
|
||||
;; (setq company-box-doc-enable nil)) ;; Disable doc popup to avoid display issues
|
||||
|
||||
;;; Additional Consult commands for enhanced functionality
|
||||
(defun consult-ripgrep-project-root ()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(global-auto-revert-mode t)
|
||||
;; Electric-pair-mode is replaced by smartparens in init-qol.el
|
||||
|
||||
;;; Shift-Selection Configuration
|
||||
;; Enable shift-select mode for selecting text with Shift+Arrow keys
|
||||
(setq shift-select-mode t)
|
||||
(transient-mark-mode t)
|
||||
@@ -16,107 +17,141 @@
|
||||
(global-set-key (kbd "C-<left>") 'left-word)
|
||||
(global-set-key (kbd "C-<right>") 'right-word)
|
||||
|
||||
;; Custom functions for shift-selection with word movement
|
||||
(defun left-word-select ()
|
||||
"Move left by words, extending selection."
|
||||
(interactive "^")
|
||||
(left-word))
|
||||
;; Fix for C-Shift-Arrow word selection with CUA mode
|
||||
(defun backward-word-select (&optional arg)
|
||||
"Move backward by words, extending selection with shift."
|
||||
(interactive "^p")
|
||||
(backward-word (or arg 1)))
|
||||
|
||||
(defun right-word-select ()
|
||||
"Move right by words, extending selection."
|
||||
(interactive "^")
|
||||
(right-word))
|
||||
(defun forward-word-select (&optional arg)
|
||||
"Move forward by words, extending selection with shift."
|
||||
(interactive "^p")
|
||||
(forward-word (or arg 1)))
|
||||
|
||||
;; Word selection with C-Shift-left/right
|
||||
(global-set-key (kbd "C-S-<left>") 'left-word-select)
|
||||
(global-set-key (kbd "C-S-<right>") 'right-word-select)
|
||||
;; Bind C-Shift-Arrow keys for word selection
|
||||
(global-set-key (kbd "C-S-<left>") 'backward-word-select)
|
||||
(global-set-key (kbd "C-S-<right>") 'forward-word-select)
|
||||
|
||||
;; Mark these functions as shift-selectable
|
||||
(put 'left-word 'shift-selection-mode t)
|
||||
(put 'right-word 'shift-selection-mode t)
|
||||
(put 'left-word-select 'shift-selection-mode t)
|
||||
(put 'right-word-select 'shift-selection-mode t)
|
||||
;; Mark shift-selection functions properly for CUA compatibility
|
||||
(put 'backward-word-select 'CUA 'move)
|
||||
(put 'forward-word-select 'CUA 'move)
|
||||
(put 'backward-word-select 'shift-selection-mode t)
|
||||
(put 'forward-word-select 'shift-selection-mode t)
|
||||
|
||||
;; Additional selection keybindings for consistency
|
||||
;; Shift+Home/End to select to beginning/end of line
|
||||
(global-set-key (kbd "S-<home>") 'beginning-of-line-select)
|
||||
(global-set-key (kbd "S-<end>") 'end-of-line-select)
|
||||
;; Ensure regular shift-arrow selection works
|
||||
(global-set-key (kbd "S-<left>") 'backward-char)
|
||||
(global-set-key (kbd "S-<right>") 'forward-char)
|
||||
(global-set-key (kbd "S-<up>") 'previous-line)
|
||||
(global-set-key (kbd "S-<down>") 'next-line)
|
||||
|
||||
(defun beginning-of-line-select ()
|
||||
"Move to beginning of line, extending selection."
|
||||
(interactive "^")
|
||||
(beginning-of-line))
|
||||
;; Line selection with Shift-Home/End
|
||||
(global-set-key (kbd "S-<home>") 'beginning-of-line)
|
||||
(global-set-key (kbd "S-<end>") 'end-of-line)
|
||||
|
||||
(defun end-of-line-select ()
|
||||
"Move to end of line, extending selection."
|
||||
(interactive "^")
|
||||
(end-of-line))
|
||||
;; Buffer selection with C-Shift-Home/End
|
||||
(global-set-key (kbd "C-S-<home>") 'beginning-of-buffer)
|
||||
(global-set-key (kbd "C-S-<end>") 'end-of-buffer)
|
||||
|
||||
;; Ctrl+Shift+Home/End to select to beginning/end of buffer
|
||||
(global-set-key (kbd "C-S-<home>") 'beginning-of-buffer-select)
|
||||
(global-set-key (kbd "C-S-<end>") 'end-of-buffer-select)
|
||||
|
||||
(defun beginning-of-buffer-select ()
|
||||
"Move to beginning of buffer, extending selection."
|
||||
(interactive "^")
|
||||
(beginning-of-buffer))
|
||||
|
||||
(defun end-of-buffer-select ()
|
||||
"Move to end of buffer, extending selection."
|
||||
(interactive "^")
|
||||
(end-of-buffer))
|
||||
|
||||
;; Ensure shift-arrow keys work for character selection
|
||||
(global-set-key (kbd "S-<left>") 'left-char-select)
|
||||
(global-set-key (kbd "S-<right>") 'right-char-select)
|
||||
(global-set-key (kbd "S-<up>") 'previous-line-select)
|
||||
(global-set-key (kbd "S-<down>") 'next-line-select)
|
||||
|
||||
(defun left-char-select ()
|
||||
"Move left by character, extending selection."
|
||||
(interactive "^")
|
||||
(left-char))
|
||||
|
||||
(defun right-char-select ()
|
||||
"Move right by character, extending selection."
|
||||
(interactive "^")
|
||||
(right-char))
|
||||
|
||||
(defun previous-line-select ()
|
||||
"Move up by line, extending selection."
|
||||
(interactive "^")
|
||||
(previous-line))
|
||||
|
||||
(defun next-line-select ()
|
||||
"Move down by line, extending selection."
|
||||
(interactive "^")
|
||||
(next-line))
|
||||
;; Mark all movement functions as CUA-compatible
|
||||
(put 'backward-char 'CUA 'move)
|
||||
(put 'forward-char 'CUA 'move)
|
||||
(put 'previous-line 'CUA 'move)
|
||||
(put 'next-line 'CUA 'move)
|
||||
(put 'beginning-of-line 'CUA 'move)
|
||||
(put 'end-of-line 'CUA 'move)
|
||||
(put 'beginning-of-buffer 'CUA 'move)
|
||||
(put 'end-of-buffer 'CUA 'move)
|
||||
|
||||
;;; Text manipulation
|
||||
(global-set-key (kbd "C-<return>") 'cua-set-rectangle-mark)
|
||||
|
||||
;; Diagnostic function for selection keybindings
|
||||
(defun diagnose-selection-keys ()
|
||||
"Check if selection keybindings are properly configured."
|
||||
;; Diagnostic function for CUA and selection keybindings
|
||||
(defun diagnose-cua-selection ()
|
||||
"Diagnose CUA and selection keybinding issues."
|
||||
(interactive)
|
||||
(with-output-to-temp-buffer "*Selection Keys Diagnostics*"
|
||||
(princ "=== SELECTION KEYBINDINGS DIAGNOSTICS ===\n\n")
|
||||
(princ (format "Shift-select mode: %s\n" (if shift-select-mode "ENABLED" "DISABLED")))
|
||||
(princ (format "Transient mark mode: %s\n\n" (if transient-mark-mode "ENABLED" "DISABLED")))
|
||||
(princ "Word selection keys:\n")
|
||||
(princ (format " C-S-<left>: %s\n" (key-binding (kbd "C-S-<left>"))))
|
||||
(princ (format " C-S-<right>: %s\n\n" (key-binding (kbd "C-S-<right>"))))
|
||||
(princ "Character selection keys:\n")
|
||||
(princ (format " S-<left>: %s\n" (key-binding (kbd "S-<left>"))))
|
||||
(princ (format " S-<right>: %s\n" (key-binding (kbd "S-<right>"))))
|
||||
(princ (format " S-<up>: %s\n" (key-binding (kbd "S-<up>"))))
|
||||
(princ (format " S-<down>: %s\n\n" (key-binding (kbd "S-<down>"))))
|
||||
(princ "Line selection keys:\n")
|
||||
(princ (format " S-<home>: %s\n" (key-binding (kbd "S-<home>"))))
|
||||
(princ (format " S-<end>: %s\n\n" (key-binding (kbd "S-<end>"))))
|
||||
(princ "If keys are not bound correctly, reload with:\n")
|
||||
(with-output-to-temp-buffer "*CUA Selection Diagnostics*"
|
||||
(princ "=== CUA AND SELECTION DIAGNOSTICS ===\n\n")
|
||||
(princ "CUA Mode Settings:\n")
|
||||
(princ (format " CUA mode: %s\n" (if cua-mode "ENABLED" "DISABLED")))
|
||||
(princ (format " CUA keys: %s\n" (if cua-enable-cua-keys "ENABLED" "DISABLED")))
|
||||
(princ (format " Prefix delay: %s\n" cua-prefix-override-inhibit-delay))
|
||||
(princ (format " Keep region after copy: %s\n" cua-keep-region-after-copy))
|
||||
(princ "\nShift Selection:\n")
|
||||
(princ (format " Shift-select mode: %s\n" (if shift-select-mode "ENABLED" "DISABLED")))
|
||||
(princ (format " Transient mark mode: %s\n" (if transient-mark-mode "ENABLED" "DISABLED")))
|
||||
(princ "\nKey Bindings:\n")
|
||||
(princ " Copy/Paste:\n")
|
||||
(princ (format " C-c: %s\n" (key-binding (kbd "C-c"))))
|
||||
(princ (format " C-v: %s\n" (key-binding (kbd "C-v"))))
|
||||
(princ (format " C-x: %s\n" (key-binding (kbd "C-x"))))
|
||||
(princ " Word Selection:\n")
|
||||
(princ (format " C-S-<left>: %s\n" (key-binding (kbd "C-S-<left>"))))
|
||||
(princ (format " C-S-<right>: %s\n" (key-binding (kbd "C-S-<right>"))))
|
||||
(princ " Character Selection:\n")
|
||||
(princ (format " S-<left>: %s\n" (key-binding (kbd "S-<left>"))))
|
||||
(princ (format " S-<right>: %s\n" (key-binding (kbd "S-<right>"))))
|
||||
(princ "\nTo fix issues:\n")
|
||||
(princ " M-x ensure-cua-after-init\n")
|
||||
(princ " M-x reload-emacs-config\n")))
|
||||
|
||||
;; Global keybinding for diagnostics
|
||||
(global-set-key (kbd "C-c C-d c") 'diagnose-cua-selection)
|
||||
|
||||
;; Additional diagnostic function for key conflicts
|
||||
(defun diagnose-key-conflicts ()
|
||||
"Diagnose what's intercepting single-key bindings in special modes."
|
||||
(interactive)
|
||||
(with-current-buffer (get-buffer-create "*Key Binding Diagnosis*")
|
||||
(erase-buffer)
|
||||
(insert "=== Key Binding Conflict Diagnosis ===\n\n")
|
||||
|
||||
;; Check various mode states
|
||||
(insert (format "1. Current major mode: %s\n" major-mode))
|
||||
(insert (format "2. CUA mode: %s\n" (if cua-mode "ENABLED" "disabled")))
|
||||
(insert (format "3. God mode loaded: %s\n" (if (featurep 'god-mode) "yes" "no")))
|
||||
(when (featurep 'god-mode)
|
||||
(insert (format " God mode active: %s\n" (if (bound-and-true-p god-local-mode) "YES" "no"))))
|
||||
(insert (format "4. Buffer read-only: %s\n" buffer-read-only))
|
||||
(insert (format "5. Overriding keymaps active: %s\n"
|
||||
(or overriding-terminal-local-map
|
||||
overriding-local-map
|
||||
"none")))
|
||||
|
||||
;; Check specific problematic keys
|
||||
(insert "\n6. Key binding lookups for common single keys:\n")
|
||||
(dolist (key '("j" "k" "r" "u" "g" "m" "s" "a" "t"))
|
||||
(let* ((key-seq (kbd key))
|
||||
(global-binding (lookup-key global-map key-seq))
|
||||
(local-binding (lookup-key (current-local-map) key-seq))
|
||||
(minor-binding (minor-mode-key-binding key-seq)))
|
||||
(insert (format " %s: " key))
|
||||
(when local-binding
|
||||
(insert (format "local=%s " local-binding)))
|
||||
(when minor-binding
|
||||
(insert (format "minor=%s " minor-binding)))
|
||||
(when (and global-binding (not (eq global-binding 'self-insert-command)))
|
||||
(insert (format "global=%s" global-binding)))
|
||||
(insert "\n")))
|
||||
|
||||
;; Check active minor modes
|
||||
(insert "\n7. Active minor modes:\n")
|
||||
(dolist (mode minor-mode-list)
|
||||
(when (and (boundp mode) (symbol-value mode))
|
||||
(insert (format " - %s\n" mode))))
|
||||
|
||||
;; Check CUA key behavior
|
||||
(insert "\n8. CUA mode special behavior:\n")
|
||||
(insert (format " CUA keys active: %s\n"
|
||||
(if (and cua-mode mark-active) "YES (selection active)" "no")))
|
||||
(insert (format " CUA rectangle mode: %s\n"
|
||||
(if (bound-and-true-p cua--rectangle) "YES" "no")))
|
||||
|
||||
(display-buffer (current-buffer))
|
||||
(switch-to-buffer (current-buffer))))
|
||||
|
||||
(global-set-key (kbd "C-c C-d k") 'diagnose-key-conflicts)
|
||||
|
||||
;;; Anzu - show match information in mode line
|
||||
(use-package anzu
|
||||
:ensure t
|
||||
|
||||
@@ -22,36 +22,47 @@
|
||||
(setq jit-lock-contextually t)
|
||||
(setq jit-lock-stealth-time 5)
|
||||
|
||||
;;; CUA Mode Configuration
|
||||
;; Enable full CUA mode for standard copy/paste/cut
|
||||
;; This provides C-c (copy), C-v (paste), C-x (cut), C-z (undo)
|
||||
(setq cua-enable-cua-keys t)
|
||||
(setq cua-auto-tabify-rectangles nil)
|
||||
(setq cua-keep-region-after-copy t)
|
||||
;; Make CUA mode work properly with other keybindings
|
||||
|
||||
;; CRITICAL: Set a very short delay to allow C-c to work as prefix when needed
|
||||
;; This allows C-c C-something to work while C-c alone copies
|
||||
(setq cua-prefix-override-inhibit-delay 0.001)
|
||||
(cua-mode t)
|
||||
|
||||
;; Function to ensure CUA bindings work properly
|
||||
(defun ensure-cua-bindings ()
|
||||
"Ensure CUA mode bindings are properly set."
|
||||
(interactive)
|
||||
;; Force CUA mode to be on
|
||||
(cua-mode 1)
|
||||
;; Enable CUA mode
|
||||
(cua-mode 1)
|
||||
|
||||
;; Function to disable CUA in modes where it conflicts
|
||||
(defun disable-cua-in-conflicting-modes ()
|
||||
"Disable CUA mode in modes where it causes conflicts."
|
||||
(dolist (hook '(elfeed-search-mode-hook
|
||||
elfeed-show-mode-hook
|
||||
magit-mode-hook
|
||||
dired-mode-hook
|
||||
help-mode-hook
|
||||
compilation-mode-hook
|
||||
special-mode-hook))
|
||||
(add-hook hook
|
||||
(lambda ()
|
||||
(setq-local cua-mode nil)
|
||||
(setq-local cua-enable-cua-keys nil)))))
|
||||
|
||||
;; Apply mode-specific fixes
|
||||
(disable-cua-in-conflicting-modes)
|
||||
|
||||
;; Ensure CUA works properly after init
|
||||
(defun ensure-cua-after-init ()
|
||||
"Ensure CUA mode is properly configured after initialization."
|
||||
(when (not cua-mode)
|
||||
(cua-mode 1))
|
||||
;; Ensure the keybindings are active
|
||||
(setq cua-enable-cua-keys t)
|
||||
(message "CUA bindings reinforced: C-c (copy), C-v (paste), C-x (cut), C-z (undo)"))
|
||||
(setq cua-enable-cua-keys t))
|
||||
|
||||
;; Run this after all init files are loaded
|
||||
(add-hook 'after-init-hook 'ensure-cua-bindings)
|
||||
|
||||
;; Ensure CUA works in programming modes
|
||||
(add-hook 'prog-mode-hook
|
||||
(lambda ()
|
||||
(when (not cua-mode)
|
||||
(cua-mode 1))
|
||||
(local-set-key (kbd "C-c") nil) ; Clear any local C-c binding
|
||||
(local-set-key (kbd "C-v") nil) ; Clear any local C-v binding
|
||||
))
|
||||
(add-hook 'after-init-hook 'ensure-cua-after-init)
|
||||
|
||||
;; Function to fix syntax highlighting in current buffer
|
||||
(defun fix-syntax-highlighting ()
|
||||
|
||||
451
portfolio-tracker-v2.el
Normal file
451
portfolio-tracker-v2.el
Normal file
@@ -0,0 +1,451 @@
|
||||
;;; portfolio-tracker-v2.el --- Portfolio tracking with transaction history -*- lexical-binding: t -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; Enhanced portfolio tracker with full transaction history and cost basis tracking
|
||||
;; Supports buy/sell transactions, dividends, and multiple lots (FIFO/LIFO)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'tabulated-list)
|
||||
(require 'url)
|
||||
(require 'json)
|
||||
|
||||
(defgroup portfolio-tracker nil
|
||||
"Portfolio tracking with live price updates."
|
||||
:group 'applications)
|
||||
|
||||
(defcustom portfolio-tracker-update-interval 300
|
||||
"Interval in seconds between automatic price updates."
|
||||
:type 'integer
|
||||
:group 'portfolio-tracker)
|
||||
|
||||
(defcustom portfolio-tracker-cost-basis-method 'fifo
|
||||
"Method for calculating cost basis: fifo or lifo."
|
||||
:type '(choice (const :tag "FIFO" fifo)
|
||||
(const :tag "LIFO" lifo))
|
||||
:group 'portfolio-tracker)
|
||||
|
||||
;; Data structures
|
||||
(cl-defstruct portfolio-transaction
|
||||
date
|
||||
type ; buy, sell, dividend, split
|
||||
symbol
|
||||
shares
|
||||
price
|
||||
fees
|
||||
notes)
|
||||
|
||||
(cl-defstruct portfolio-lot
|
||||
symbol
|
||||
shares ; remaining shares in this lot
|
||||
purchase-date
|
||||
purchase-price
|
||||
fees)
|
||||
|
||||
(cl-defstruct portfolio-holding
|
||||
symbol
|
||||
name
|
||||
total-shares
|
||||
lots ; list of portfolio-lot structs
|
||||
average-cost
|
||||
current-price
|
||||
previous-close
|
||||
value
|
||||
unrealized-gain
|
||||
unrealized-gain-percent
|
||||
realized-gain ; from sell transactions
|
||||
dividends ; total dividends received
|
||||
type) ; stock, etf, crypto
|
||||
|
||||
(defvar portfolio-tracker-transactions nil
|
||||
"List of all transactions.")
|
||||
|
||||
(defvar portfolio-tracker-holdings nil
|
||||
"Current holdings calculated from transactions.")
|
||||
|
||||
(defvar portfolio-tracker-prices-cache nil
|
||||
"Cache of current prices.")
|
||||
|
||||
;; Transaction processing
|
||||
(defun portfolio-tracker-process-transactions ()
|
||||
"Process all transactions to calculate current holdings."
|
||||
(let ((holdings-table (make-hash-table :test 'equal))
|
||||
(realized-gains (make-hash-table :test 'equal))
|
||||
(dividends-table (make-hash-table :test 'equal)))
|
||||
|
||||
;; Sort transactions by date
|
||||
(setq portfolio-tracker-transactions
|
||||
(sort portfolio-tracker-transactions
|
||||
(lambda (a b)
|
||||
(string< (portfolio-transaction-date a)
|
||||
(portfolio-transaction-date b)))))
|
||||
|
||||
;; Process each transaction
|
||||
(dolist (txn portfolio-tracker-transactions)
|
||||
(let ((symbol (portfolio-transaction-symbol txn))
|
||||
(type (portfolio-transaction-type txn)))
|
||||
|
||||
(cond
|
||||
;; Buy transaction - add lot
|
||||
((eq type 'buy)
|
||||
(let* ((holding (gethash symbol holdings-table))
|
||||
(new-lot (make-portfolio-lot
|
||||
:symbol symbol
|
||||
:shares (portfolio-transaction-shares txn)
|
||||
:purchase-date (portfolio-transaction-date txn)
|
||||
:purchase-price (portfolio-transaction-price txn)
|
||||
:fees (or (portfolio-transaction-fees txn) 0))))
|
||||
(if holding
|
||||
(push new-lot (portfolio-holding-lots holding))
|
||||
(puthash symbol
|
||||
(make-portfolio-holding
|
||||
:symbol symbol
|
||||
:lots (list new-lot)
|
||||
:realized-gain 0
|
||||
:dividends 0)
|
||||
holdings-table))))
|
||||
|
||||
;; Sell transaction - remove shares using FIFO/LIFO
|
||||
((eq type 'sell)
|
||||
(let ((holding (gethash symbol holdings-table))
|
||||
(shares-to-sell (portfolio-transaction-shares txn))
|
||||
(sell-price (portfolio-transaction-price txn))
|
||||
(realized 0))
|
||||
(when holding
|
||||
(let ((lots (if (eq portfolio-tracker-cost-basis-method 'lifo)
|
||||
(reverse (portfolio-holding-lots holding))
|
||||
(portfolio-holding-lots holding))))
|
||||
(dolist (lot lots)
|
||||
(when (> shares-to-sell 0)
|
||||
(let ((shares-from-lot (min shares-to-sell
|
||||
(portfolio-lot-shares lot))))
|
||||
(setq realized
|
||||
(+ realized
|
||||
(* shares-from-lot
|
||||
(- sell-price (portfolio-lot-purchase-price lot)))))
|
||||
(setf (portfolio-lot-shares lot)
|
||||
(- (portfolio-lot-shares lot) shares-from-lot))
|
||||
(setq shares-to-sell (- shares-to-sell shares-from-lot)))))
|
||||
;; Remove empty lots
|
||||
(setf (portfolio-holding-lots holding)
|
||||
(cl-remove-if (lambda (lot) (<= (portfolio-lot-shares lot) 0))
|
||||
(portfolio-holding-lots holding)))
|
||||
;; Add to realized gains
|
||||
(setf (portfolio-holding-realized-gain holding)
|
||||
(+ (portfolio-holding-realized-gain holding) realized))))))
|
||||
|
||||
;; Dividend transaction
|
||||
((eq type 'dividend)
|
||||
(let ((holding (gethash symbol holdings-table)))
|
||||
(when holding
|
||||
(setf (portfolio-holding-dividends holding)
|
||||
(+ (portfolio-holding-dividends holding)
|
||||
(* (portfolio-transaction-shares txn)
|
||||
(portfolio-transaction-price txn)))))))))
|
||||
|
||||
;; Calculate totals for each holding
|
||||
(maphash (lambda (symbol holding)
|
||||
(let ((total-shares 0)
|
||||
(total-cost 0))
|
||||
(dolist (lot (portfolio-holding-lots holding))
|
||||
(setq total-shares (+ total-shares (portfolio-lot-shares lot)))
|
||||
(setq total-cost (+ total-cost
|
||||
(* (portfolio-lot-shares lot)
|
||||
(portfolio-lot-purchase-price lot)))))
|
||||
(setf (portfolio-holding-total-shares holding) total-shares)
|
||||
(setf (portfolio-holding-average-cost holding)
|
||||
(if (> total-shares 0)
|
||||
(/ total-cost total-shares)
|
||||
0))))
|
||||
holdings-table)
|
||||
|
||||
;; Convert hash table to list
|
||||
(setq portfolio-tracker-holdings nil)
|
||||
(maphash (lambda (symbol holding)
|
||||
(when (> (portfolio-holding-total-shares holding) 0)
|
||||
(push holding portfolio-tracker-holdings)))
|
||||
holdings-table)))
|
||||
|
||||
;; Yahoo Finance integration
|
||||
(defun portfolio-tracker-fetch-price (symbol callback)
|
||||
"Fetch current price for SYMBOL and call CALLBACK with the result."
|
||||
(let ((url (format "https://query1.finance.yahoo.com/v8/finance/chart/%s" symbol)))
|
||||
(url-retrieve url
|
||||
(lambda (status)
|
||||
(if (plist-get status :error)
|
||||
(message "Error fetching price for %s" symbol)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "^$")
|
||||
(let* ((json-object-type 'plist)
|
||||
(json-array-type 'list)
|
||||
(json (json-read))
|
||||
(result (plist-get json :chart))
|
||||
(data (car (plist-get result :result)))
|
||||
(meta (plist-get data :meta))
|
||||
(price (plist-get meta :regularMarketPrice))
|
||||
(prev-close (plist-get meta :previousClose))
|
||||
(name (plist-get meta :longName)))
|
||||
(funcall callback symbol price prev-close name))))
|
||||
nil t)))
|
||||
|
||||
(defun portfolio-tracker-update-prices ()
|
||||
"Update prices for all holdings."
|
||||
(interactive)
|
||||
(message "Updating prices...")
|
||||
(dolist (holding portfolio-tracker-holdings)
|
||||
(portfolio-tracker-fetch-price
|
||||
(portfolio-holding-symbol holding)
|
||||
(lambda (symbol price prev-close name)
|
||||
(let ((h (cl-find symbol portfolio-tracker-holdings
|
||||
:key #'portfolio-holding-symbol
|
||||
:test #'string=)))
|
||||
(when h
|
||||
(setf (portfolio-holding-current-price h) price)
|
||||
(setf (portfolio-holding-previous-close h) prev-close)
|
||||
(when name
|
||||
(setf (portfolio-holding-name h) name))
|
||||
;; Calculate unrealized gains
|
||||
(let ((total-cost 0))
|
||||
(dolist (lot (portfolio-holding-lots h))
|
||||
(setq total-cost (+ total-cost
|
||||
(* (portfolio-lot-shares lot)
|
||||
(portfolio-lot-purchase-price lot)))))
|
||||
(setf (portfolio-holding-value h)
|
||||
(* (portfolio-holding-total-shares h) price))
|
||||
(setf (portfolio-holding-unrealized-gain h)
|
||||
(- (portfolio-holding-value h) total-cost))
|
||||
(setf (portfolio-holding-unrealized-gain-percent h)
|
||||
(if (> total-cost 0)
|
||||
(* 100 (/ (portfolio-holding-unrealized-gain h) total-cost))
|
||||
0))))
|
||||
(portfolio-tracker-refresh-display))))))
|
||||
|
||||
;; Display functions
|
||||
(eval-and-compile
|
||||
(defvar portfolio-tracker-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
;; Use string keys directly for single characters
|
||||
(define-key map "g" 'portfolio-tracker-refresh)
|
||||
(define-key map "a" 'portfolio-tracker-add-transaction)
|
||||
(define-key map "t" 'portfolio-tracker-show-transactions)
|
||||
(define-key map "h" 'portfolio-tracker-refresh-display)
|
||||
(define-key map "s" 'portfolio-tracker-save)
|
||||
(define-key map "l" 'portfolio-tracker-load)
|
||||
(define-key map "r" 'portfolio-tracker-refresh)
|
||||
(define-key map "q" 'quit-window)
|
||||
map)
|
||||
"Keymap for portfolio-tracker-mode.")
|
||||
|
||||
(define-derived-mode portfolio-tracker-mode tabulated-list-mode "Portfolio"
|
||||
"Major mode for tracking investment portfolio.
|
||||
\\{portfolio-tracker-mode-map}"
|
||||
:keymap portfolio-tracker-mode-map
|
||||
;; Disable CUA mode to allow single-key commands
|
||||
(setq-local cua-mode nil)
|
||||
(setq-local cua-enable-cua-keys nil)
|
||||
(setq tabulated-list-format
|
||||
[("Symbol" 8 t)
|
||||
("Name" 20 t)
|
||||
("Shares" 10 t)
|
||||
("Avg Cost" 10 nil)
|
||||
("Current" 10 nil)
|
||||
("Value" 12 nil)
|
||||
("Unrealized" 12 nil)
|
||||
("Realized" 10 nil)
|
||||
("Dividends" 10 nil)
|
||||
("Total %" 8 nil)])
|
||||
(setq tabulated-list-padding 2)
|
||||
(setq tabulated-list-sort-key (cons "Symbol" nil))
|
||||
(tabulated-list-init-header)))
|
||||
|
||||
(define-derived-mode portfolio-transactions-mode tabulated-list-mode "Transactions"
|
||||
"Major mode for viewing portfolio transactions."
|
||||
(setq tabulated-list-format
|
||||
[("Date" 12 t)
|
||||
("Type" 8 t)
|
||||
("Symbol" 8 t)
|
||||
("Shares" 10 nil)
|
||||
("Price" 10 nil)
|
||||
("Total" 12 nil)
|
||||
("Fees" 8 nil)
|
||||
("Notes" 30 nil)])
|
||||
(setq tabulated-list-padding 2)
|
||||
(setq tabulated-list-sort-key (cons "Date" t))
|
||||
(tabulated-list-init-header))
|
||||
|
||||
(defun portfolio-tracker-refresh-display ()
|
||||
"Refresh the holdings display."
|
||||
(interactive)
|
||||
(let ((buf (get-buffer "*Portfolio Holdings*")))
|
||||
(when buf
|
||||
(with-current-buffer buf
|
||||
(let ((inhibit-read-only t))
|
||||
(setq tabulated-list-entries
|
||||
(mapcar (lambda (holding)
|
||||
(list holding
|
||||
(vector
|
||||
(portfolio-holding-symbol holding)
|
||||
(or (portfolio-holding-name holding) "")
|
||||
(format "%.2f" (portfolio-holding-total-shares holding))
|
||||
(format "$%.2f" (portfolio-holding-average-cost holding))
|
||||
(if (portfolio-holding-current-price holding)
|
||||
(format "$%.2f" (portfolio-holding-current-price holding))
|
||||
"...")
|
||||
(format "$%.2f" (or (portfolio-holding-value holding) 0))
|
||||
(propertize (format "$%.2f" (or (portfolio-holding-unrealized-gain holding) 0))
|
||||
'face (if (>= (or (portfolio-holding-unrealized-gain holding) 0) 0)
|
||||
'success 'error))
|
||||
(format "$%.2f" (portfolio-holding-realized-gain holding))
|
||||
(format "$%.2f" (portfolio-holding-dividends holding))
|
||||
(propertize (format "%.1f%%" (or (portfolio-holding-unrealized-gain-percent holding) 0))
|
||||
'face (if (>= (or (portfolio-holding-unrealized-gain-percent holding) 0) 0)
|
||||
'success 'error)))))
|
||||
portfolio-tracker-holdings))
|
||||
(tabulated-list-print t)
|
||||
;; Add summary at the bottom
|
||||
(portfolio-tracker-display-summary)))))))
|
||||
|
||||
(defun portfolio-tracker-display-summary ()
|
||||
"Display portfolio summary with totals at bottom of buffer."
|
||||
(let ((inhibit-read-only t)
|
||||
(total-cost 0)
|
||||
(total-value 0)
|
||||
(total-unrealized 0)
|
||||
(total-realized 0)
|
||||
(total-dividends 0))
|
||||
;; Calculate totals
|
||||
(dolist (holding portfolio-tracker-holdings)
|
||||
(let ((cost (* (portfolio-holding-total-shares holding)
|
||||
(portfolio-holding-average-cost holding))))
|
||||
(setq total-cost (+ total-cost cost))
|
||||
(setq total-value (+ total-value (or (portfolio-holding-value holding) cost)))
|
||||
(setq total-unrealized (+ total-unrealized (or (portfolio-holding-unrealized-gain holding) 0)))
|
||||
(setq total-realized (+ total-realized (portfolio-holding-realized-gain holding)))
|
||||
(setq total-dividends (+ total-dividends (portfolio-holding-dividends holding)))))
|
||||
|
||||
;; Display summary
|
||||
(goto-char (point-max))
|
||||
(insert "\n" (make-string 100 ?-) "\n")
|
||||
(insert (propertize "PORTFOLIO TOTALS\n" 'face 'bold))
|
||||
(insert (format " Total Buy-In (Cost Basis): %s%.2f\n"
|
||||
(propertize "$" 'face 'default)
|
||||
total-cost))
|
||||
(insert (format " Current Market Value: %s%.2f\n"
|
||||
(propertize "$" 'face 'default)
|
||||
total-value))
|
||||
(insert (format " Unrealized Gain/Loss: %s\n"
|
||||
(propertize (format "$%.2f (%.1f%%)"
|
||||
total-unrealized
|
||||
(if (> total-cost 0)
|
||||
(* 100 (/ total-unrealized total-cost))
|
||||
0))
|
||||
'face (if (>= total-unrealized 0) 'success 'error))))
|
||||
(insert (format " Realized Gain/Loss: %s\n"
|
||||
(propertize (format "$%.2f" total-realized)
|
||||
'face (if (>= total-realized 0) 'success 'error))))
|
||||
(insert (format " Total Dividends Received: %s%.2f\n"
|
||||
(propertize "$" 'face 'default)
|
||||
total-dividends))
|
||||
(insert (make-string 100 ?-) "\n")
|
||||
(let ((total-gain (+ total-unrealized total-realized total-dividends))
|
||||
(total-return-pct (if (> total-cost 0)
|
||||
(* 100 (/ (+ total-unrealized total-dividends) total-cost))
|
||||
0)))
|
||||
(insert (format " TOTAL RETURN: %s\n"
|
||||
(propertize (format "$%.2f (%.1f%%)"
|
||||
total-gain
|
||||
total-return-pct)
|
||||
'face (if (>= total-gain 0) 'success 'error))))
|
||||
(insert (make-string 100 ?=) "\n"))
|
||||
(insert "\nKeys: [g] Refresh | [a] Add Transaction | [t] Show Transactions | [s] Save | [l] Load | [q] Quit\n")))
|
||||
|
||||
(defun portfolio-tracker-refresh ()
|
||||
"Refresh prices and redisplay."
|
||||
(interactive)
|
||||
(portfolio-tracker-process-transactions)
|
||||
(portfolio-tracker-update-prices)
|
||||
(portfolio-tracker-refresh-display))
|
||||
|
||||
(defun portfolio-tracker-add-transaction ()
|
||||
"Add a new transaction interactively."
|
||||
(interactive)
|
||||
(let* ((date (read-string "Date (YYYY-MM-DD): " (format-time-string "%Y-%m-%d")))
|
||||
(type (intern (completing-read "Type: " '("buy" "sell" "dividend") nil t)))
|
||||
(symbol (upcase (read-string "Symbol: ")))
|
||||
(shares (read-number "Shares: "))
|
||||
(price (read-number "Price per share: "))
|
||||
(fees (read-number "Fees (0 if none): " 0))
|
||||
(notes (read-string "Notes (optional): ")))
|
||||
(push (make-portfolio-transaction
|
||||
:date date
|
||||
:type type
|
||||
:symbol symbol
|
||||
:shares shares
|
||||
:price price
|
||||
:fees fees
|
||||
:notes notes)
|
||||
portfolio-tracker-transactions)
|
||||
(portfolio-tracker-process-transactions)
|
||||
(portfolio-tracker-update-prices)))
|
||||
|
||||
(defun portfolio-tracker-show-transactions ()
|
||||
"Show all transactions in a separate buffer."
|
||||
(interactive)
|
||||
(let ((buf (get-buffer-create "*Portfolio Transactions*")))
|
||||
(with-current-buffer buf
|
||||
(portfolio-transactions-mode)
|
||||
(setq tabulated-list-entries
|
||||
(mapcar (lambda (txn)
|
||||
(list txn
|
||||
(vector
|
||||
(portfolio-transaction-date txn)
|
||||
(symbol-name (portfolio-transaction-type txn))
|
||||
(portfolio-transaction-symbol txn)
|
||||
(format "%.2f" (portfolio-transaction-shares txn))
|
||||
(format "$%.2f" (portfolio-transaction-price txn))
|
||||
(format "$%.2f" (* (portfolio-transaction-shares txn)
|
||||
(portfolio-transaction-price txn)))
|
||||
(format "$%.2f" (or (portfolio-transaction-fees txn) 0))
|
||||
(or (portfolio-transaction-notes txn) ""))))
|
||||
(reverse portfolio-tracker-transactions)))
|
||||
(tabulated-list-print))
|
||||
(switch-to-buffer buf)))
|
||||
|
||||
(defun portfolio-tracker-save (file)
|
||||
"Save portfolio data to FILE."
|
||||
(interactive "FSave portfolio to: ")
|
||||
(with-temp-file file
|
||||
(insert ";; Portfolio Tracker Data\n")
|
||||
(insert ";; Transaction history and settings\n\n")
|
||||
(insert "(setq portfolio-tracker-transactions\n")
|
||||
(pp portfolio-tracker-transactions (current-buffer))
|
||||
(insert ")\n"))
|
||||
(message "Portfolio saved to %s" file))
|
||||
|
||||
(defun portfolio-tracker-load (file)
|
||||
"Load portfolio data from FILE."
|
||||
(interactive "fLoad portfolio from: ")
|
||||
(load-file file)
|
||||
(portfolio-tracker-process-transactions)
|
||||
(portfolio-tracker-refresh-display)
|
||||
(portfolio-tracker-update-prices)
|
||||
(message "Portfolio loaded from %s" file))
|
||||
|
||||
;;;###autoload
|
||||
(defun portfolio-tracker ()
|
||||
"Start the portfolio tracker."
|
||||
(interactive)
|
||||
(require 'tabulated-list)
|
||||
(require 'cl-lib)
|
||||
(let ((buf (get-buffer-create "*Portfolio Holdings*")))
|
||||
(switch-to-buffer buf)
|
||||
(unless (eq major-mode 'portfolio-tracker-mode)
|
||||
(portfolio-tracker-mode))
|
||||
(when portfolio-tracker-transactions
|
||||
(portfolio-tracker-process-transactions)
|
||||
(portfolio-tracker-refresh-display))
|
||||
(message "Portfolio Tracker: 'a' add transaction | 't' show transactions | 'g' refresh prices | 'l' load portfolio")))
|
||||
|
||||
(provide 'portfolio-tracker-v2)
|
||||
;;; portfolio-tracker-v2.el ends here
|
||||
Reference in New Issue
Block a user