├── cnfonts ├── cnfonts.conf └── v4 │ └── profile1.el ├── lsp-bridge └── codeium_api_key.txt ├── config ├── modes │ ├── nsis.el │ ├── elisp.el │ ├── save-place.el │ ├── tts.el │ ├── cmake.el │ ├── cpp.el │ ├── org-page.el │ ├── go.el │ ├── helm.el │ ├── python.el │ ├── org.el │ ├── magit.el │ ├── dired.el │ └── evil.el ├── utils.el ├── modes.el ├── key.el └── basic.el ├── init.el ├── README.md ├── .gitignore ├── myelpa ├── flymake-bridge.el ├── cmake-format.el ├── dired-sort-menu.el └── helm-ag.el └── custom.el /cnfonts/cnfonts.conf: -------------------------------------------------------------------------------- 1 | (("profile1" . 12.5)) -------------------------------------------------------------------------------- /lsp-bridge/codeium_api_key.txt: -------------------------------------------------------------------------------- 1 | 1d545a73-5503-4c28-ac48-f928d701d9e6 -------------------------------------------------------------------------------- /config/modes/nsis.el: -------------------------------------------------------------------------------- 1 | (autoload 'nsis-mode "nsis-mode" "NSIS mode" t) 2 | (setq auto-mode-alist (append '(("\\.\\([Nn][Ss][Hh]\\)$" . 3 | nsis-mode)) auto-mode-alist)) 4 | -------------------------------------------------------------------------------- /config/modes/elisp.el: -------------------------------------------------------------------------------- 1 | (defun elisp-hook() 2 | (company-mode) 3 | (yas-minor-mode) 4 | (add-to-list (make-local-variable 'company-backends) 'company-elisp)) 5 | (add-hook 'emacs-lisp-mode-hook 'elisp-hook) 6 | -------------------------------------------------------------------------------- /config/utils.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (defconst os:windowsp (eq system-type 'windows-nt)) 3 | (defconst os:osxp (eq system-type 'darwin)) 4 | (defconst os:linuxp (eq system-type 'gnu/linux)) 5 | (provide 'utils) 6 | -------------------------------------------------------------------------------- /init.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (add-to-list 'load-path "~/.emacs.d/config") 3 | (package-initialize) 4 | (setq custom-file "~/.emacs.d/custom.el") 5 | (load custom-file :noerror) 6 | (require 'basic) 7 | (require 'modes) 8 | (require 'key) 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | emacs.d 3 | ======== 4 | 5 | my emacs configuration. 6 | Mainly for: 7 | 8 | [C++](http://hyphenlee.github.io/blog/2015/03/11/emacs-configure-for-c++/) 9 | 10 | javascript 11 | 12 | org 13 | 14 | org-page for blog 15 | 16 | python 17 | -------------------------------------------------------------------------------- /config/modes/save-place.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | 3 | ;; save place when file is close and resume next itme 4 | (setq save-place-file "~/.emacs.d/.emacs-places") 5 | (setq-default save-place t) ;; activate it for all buffers 6 | (require 'saveplace) ;; get the package 7 | -------------------------------------------------------------------------------- /config/modes/tts.el: -------------------------------------------------------------------------------- 1 | (add-hook 'load-path "~/.emacs.d/plugins/tts-mode") 2 | (require 'tts) 3 | (tts-voice-macsay) 4 | (defun tts-current-buffer() 5 | (interactive ) 6 | (if (get-process "say") 7 | (kill-process (get-process "say")) 8 | (progn 9 | (if mark-active 10 | (setq start (region-beginning) 11 | end (region-end)) 12 | (setq start (point-min) 13 | end (point-max))) 14 | (tts-say (buffer-substring-no-properties start end))))) 15 | 16 | -------------------------------------------------------------------------------- /config/modes/cmake.el: -------------------------------------------------------------------------------- 1 | ;;(setq load-path (cons (expand-file-name "/opt/emacs_plugins") load-path)) 2 | (require 'cmake-mode) 3 | ;; (require 'cmake-format) 4 | (setq auto-mode-alist 5 | (append '(("CMakeLists\\.txt\\'" . cmake-mode) 6 | ("\\.cmake\\'" . cmake-mode)) 7 | auto-mode-alist)) 8 | (defun cmake-hook() 9 | (company-mode) 10 | (yas-minor-mode) 11 | (set (make-local-variable 'company-backends) 12 | '((company-dabbrev-code company-yasnippet company-abbrev company-cmake))) 13 | ) 14 | (add-hook 'cmake-mode-hook 'cmake-hook) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */.git 2 | auto-save-list/* 3 | .emacs-places 4 | .emacs.desktop 5 | ac-comphist.dat 6 | bookmarks 7 | history 8 | ido.last 9 | places 10 | projectile.cache 11 | recentf 12 | .emacs.desktop.lock 13 | eshell/* 14 | url/* 15 | tramp 16 | backups/* 17 | .smex-items 18 | semanticdb/* 19 | elpa/* 20 | server 21 | .DS_Store 22 | .last-package-update-day 23 | .mc-lists.el 24 | .org-id-locations 25 | eww-bookmarks 26 | smex-items 27 | /eln-cache/ 28 | /transient/ 29 | /.dap-breakpoints 30 | /.extension/ 31 | /.lsp-session-v1 32 | /session.10e5edce8dc94a4922167868812014167700000023120099 33 | /.cache/ 34 | /dap-ui-repl-lldb-vscode-history 35 | session* 36 | /projects 37 | /forge-database.sqlite 38 | -------------------------------------------------------------------------------- /config/modes/cpp.el: -------------------------------------------------------------------------------- 1 | (require 'yasnippet) 2 | (require 'lsp-bridge) 3 | (defun my-c-mode-hook() 4 | (yas-global-mode 1) 5 | (lsp-bridge-mode) 6 | (setq acm-enable-codeium 1) 7 | (local-set-key (kbd "M-q") 'kill-current-buffer) 8 | (local-set-key (kbd "") 'lsp-bridge-find-def) 9 | (local-set-key (kbd "C-") 'lsp-bridge-peek) 10 | (local-set-key (kbd "C-c r") 'lsp-bridge-rename) 11 | (local-set-key (kbd "S-") 'lsp-bridge-find-references) 12 | (local-set-key (kbd "M-") 'lsp-bridge-code-action) 13 | (local-set-key (kbd "C-\\") 'lsp-bridge-code-format) 14 | (local-set-key (kbd "M-") 'lsp-bridge-find-def-return) 15 | (local-set-key (kbd "") 'realgud:gdb) 16 | (local-set-key (kbd "") 'lsp-bridge-find-def-return) 17 | ) 18 | (add-hook 'c++-mode-hook 'my-c-mode-hook) 19 | 20 | (require 'flymake-bridge) 21 | (add-hook 'lsp-bridge-mode-hook #'flymake-bridge-setup) 22 | -------------------------------------------------------------------------------- /config/modes.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (if 3 | os:windowsp 4 | ( mapc (lambda (filename) (load (concat "~/.emacs.d/config/modes/" filename))) 5 | (list 6 | "helm.el" 7 | "cmake.el" 8 | "nsis.el" 9 | "org.el" 10 | "go.el" 11 | "magit.el" 12 | "elisp.el" 13 | "dired.el" 14 | ))) 15 | (if os:osxp 16 | (mapc (lambda (filename) (load (concat "~/.emacs.d/config/modes/" filename))) 17 | (list 18 | "dired.el" 19 | "helm.el" 20 | "org-page.el" 21 | "org.el" 22 | "cmake.el" 23 | "elisp.el" 24 | "go.el" 25 | "magit.el" 26 | ))) 27 | (if os:linuxp 28 | (mapc (lambda (filename) (load (concat "~/.emacs.d/config/modes/" filename))) 29 | (list 30 | "dired.el" 31 | "helm.el" 32 | "org.el" 33 | "cpp.el" 34 | "elisp.el" 35 | "cmake.el" 36 | "magit.el" 37 | ))) 38 | 39 | (provide 'modes) 40 | -------------------------------------------------------------------------------- /config/modes/org-page.el: -------------------------------------------------------------------------------- 1 | ;;; the following is only needed if you install org-page manually 2 | (require 'org-page) 3 | (setq op/repository-directory "git://github.com/hyphenlee/hyphenlee.github.io.git") 4 | (setq op/repository-directory "~/blog") 5 | (setq op/site-domain "https://hyphenlee.github.io") 6 | ;;; for commenting, you can choose either disqus or duoshuo 7 | (setq op/personal-disqus-shortname "hyphensblog") 8 | ;; (setq op/personal-duoshuo-shortname "your_duoshuo_shortname") 9 | ;;; the configuration below are optional 10 | (setq op/personal-google-analytics-id "UA-59619368-1") 11 | (setq op/site-main-title "Let it be") 12 | (setq op/site-sub-title "Don't leave this world without anything left.") 13 | (setq op/personal-github-link "https://github.com/hyphenlee") 14 | ;; (setq op/personal-avatar "/media/img/profile.jpg") 15 | (defun lhf/public-blog () 16 | (interactive) 17 | (let () 18 | (cd op/repository-directory) 19 | (save-some-buffers t nil) 20 | (shell-command "git add -a .") 21 | (shell-command "git commit -am \"post\"") 22 | (op/do-publication t nil nil) 23 | (shell-command "git add -a .") 24 | (shell-command "git commit -am \"post\"") 25 | (shell-command "git push") 26 | (op/git-change-branch op/repository-directory op/repository-org-branch))) 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /config/modes/go.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | ;; (defun auto-complete-for-go () 3 | ;; (auto-complete-mode 1)) 4 | ;; (require 'auto-complete) 5 | ;; (add-hook 'go-mode-hook 'auto-complete-for-go) 6 | ;; (with-eval-after-load 'go-mode 7 | ;; (require 'go-autocomplete)) 8 | (defun my-go-mode-hook () 9 | ; Use goimports instead of go-fmt 10 | (setq gofmt-command "goimports") 11 | ;; (auto-complete-mode) 12 | ; Call Gofmt before saving 13 | (add-hook 'before-save-hook 'gofmt-before-save) 14 | ; Customize compile command to run go build 15 | (if (not (string-match "go" compile-command)) 16 | (set (make-local-variable 'compile-command) 17 | "go build -v && go test -v && go vet")) 18 | ; Godef jump key binding 19 | (local-set-key (kbd "M-.") 'godef-jump) 20 | (local-set-key (kbd "") 'godef-jump) 21 | (local-set-key (kbd "C--") 'pop-tag-mark) 22 | (local-set-key (kbd "") 'pop-tag-mark) 23 | (local-set-key (kbd "") 'godef-jump) 24 | (require 'company) 25 | (require 'company-go) 26 | (yas-minor-mode) 27 | (setq dap-auto-configure-features '(sessions locals controls tooltip)) 28 | (require 'dap-go) 29 | ) 30 | (add-hook 'go-mode-hook 'my-go-mode-hook) 31 | 32 | -------------------------------------------------------------------------------- /config/modes/helm.el: -------------------------------------------------------------------------------- 1 | (require 'helm) 2 | ;; ;; (require 'helm-config) 3 | ;; (setq helm-grep-default-command "grep -a -d recurse %e -n%cH -e %p %f") 4 | 5 | ;; ;; search in folder 6 | ;; (setq helm-grep-ag-command (concat "rg" 7 | ;; " --color=never" 8 | ;; " --smart-case" 9 | ;; " --no-heading" 10 | ;; " --line-number %s %s %s") 11 | ;; helm-grep-file-path-style 'relative) 12 | 13 | (defun mu-helm-rg (directory &optional with-types) 14 | "Search in DIRECTORY with RG. 15 | With WITH-TYPES, ask for file types to search in." 16 | (interactive "P") 17 | (require 'helm-adaptive) 18 | (helm-grep-ag-1 (expand-file-name directory) 19 | (helm-aif (and with-types 20 | (helm-grep-ag-get-types)) 21 | (helm-comp-read 22 | "RG type: " it 23 | :must-match t 24 | :marked-candidates t 25 | :fc-transformer 'helm-adaptive-sort 26 | :buffer "*helm rg types*")))) 27 | (defun mu-helm-file-search (&optional with-types) 28 | "Search in `default-directory' with RG. 29 | With WITH-TYPES, ask for file types to search in." 30 | (interactive "P") 31 | (mu-helm-rg default-directory with-types)) 32 | -------------------------------------------------------------------------------- /config/modes/python.el: -------------------------------------------------------------------------------- 1 | (require 'yasnippet) 2 | (require 'lsp-bridge) 3 | (require 'dap-python) 4 | (setq dap-python-debugger 'debugpy) 5 | 6 | (defun my-python-mode-hook() 7 | (yas-global-mode 1) 8 | (lsp-bridge-mode) 9 | (setq acm-enable-codeium 1) 10 | (local-set-key (kbd "M-q") 'kill-current-buffer) 11 | (local-set-key (kbd "") 'lsp-bridge-find-def) 12 | (local-set-key (kbd "C-") 'lsp-bridge-peek) 13 | (local-set-key (kbd "C-c r") 'lsp-bridge-rename) 14 | (local-set-key (kbd "S-") 'lsp-bridge-find-references) 15 | (local-set-key (kbd "M-") 'lsp-bridge-code-action) 16 | (local-set-key (kbd "C-\ ") 'lsp-bridge-format) 17 | ) 18 | (add-hook 'python-mode-hook 'my-python-mode-hook) 19 | 20 | (leaf dap-mode 21 | :ensure t 22 | :init 23 | (dap-mode 1) 24 | (dap-tooltip-mode 1) 25 | (dap-auto-configure-mode 1) 26 | (dap-ui-controls-mode 1) 27 | :require t dap-lldb 28 | :bind 29 | (:dap-mode-map 30 | ([f5] . dap-debug) 31 | ([f11] . dap-step-in) 32 | ("S-" . dap-step-out) 33 | ("S-" . dap-disconnect) 34 | ("" . dap-next) 35 | ("" . dap-continue) 36 | ("" . dap-breakpoint-toggle)) 37 | :config 38 | (leaf dap-ui 39 | :ensure nil 40 | :require t 41 | :config 42 | (dap-ui-mode 1)) 43 | :custom 44 | (dap-auto-configure-features . '(sessions locals breakpoints expressions repl controls tooltip)) 45 | (dap-lldb-debug-program . `(,(expand-file-name "/usr/bin/lldb-vscode"))) 46 | ) 47 | -------------------------------------------------------------------------------- /config/modes/org.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (require 'utils) 3 | ;; org-mode setting 4 | 5 | ;; (require 'org-install) 6 | (add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode)) 7 | 8 | (global-set-key "\C-ca" 'org-agenda) 9 | (setq org-agenda-files 10 | (list "~/note/gtd.org" "~/note/note.org" "~/note/work.org")) 11 | (setq org-insert-mode-line-in-empty-file t) 12 | (setq org-hierarchical-todo-statistics t) 13 | 14 | (defun wttr/org-mode-setup () 15 | (define-key org-mode-map (kbd "C-c i") #'(lambda () 16 | (interactive) 17 | (org-time-stamp 4))) 18 | (require 'org-bullets) 19 | (org-bullets-mode) 20 | ) 21 | (add-hook 'org-mode-hook #'wttr/org-mode-setup) 22 | (setq org-src-preserve-indentation t) 23 | (setq org-src-ask-before-returning-to-edit-buffer nil) 24 | (setq org-src-fontify-natively t) 25 | 26 | ;; startup settings i like 27 | (setq org-startup-folded 'content) 28 | (setq org-startup-indented t) 29 | ; (setq org-startup-align-all-tables t) 30 | (setq org-startup-with-inline-images t) 31 | (setq org-hide-leading-stars t) 32 | 33 | 34 | ;; export settings 35 | ;;;; prevent the _ to become a sub title 36 | ;; (setq org-export-with-sub-superscripts nil) 37 | (setq org-todo-keywords 38 | '((sequence "TODO(t!)" "WORKING(w!)" "|" "DONE(d!)" "ABORT(a!)") 39 | )) 40 | (setq org-default-notes-file "~/note/note.org") 41 | (define-key global-map "\C-cc" 'org-capture) 42 | (setq org-capture-templates 43 | '(("t" "Todo" entry (file "~/note/gtd.org") 44 | "* TODO %?\n %i\n" :clock-in t :clock-resume t) 45 | ("n" "Note" entry (file "~/note/note.org") 46 | "* %?\n %i\n") 47 | ("w" "Work" entry (file "~/note/work.org") 48 | "* %?\n %i\n ") 49 | )) 50 | 51 | ;;org-journal 52 | ;; (setq org-journal-enable-encryption 1) 53 | (setq org-tag-alist '(("crypt" . ?e) ("laptop" . ?l))) 54 | (setq org-journal-dir "~/note/journal") 55 | ;;org-babel 56 | (org-babel-do-load-languages 57 | 'org-babel-load-languages 58 | '((emacs-lisp . t) 59 | (C . t) 60 | (org . t) 61 | (python . t) 62 | (shell . t) 63 | (ruby . t) 64 | )) 65 | (org-display-inline-images t t) 66 | (setq org-confirm-babel-evaluate nil) 67 | -------------------------------------------------------------------------------- /config/modes/magit.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (autoload 'magit-status "magit" "magit" t) 3 | (setq magit-refresh-verbose t) 4 | (use-package magit 5 | :config 6 | 7 | (transient-define-suffix magit-submodule-update-all () 8 | "Update all submodules" 9 | :description "Update All (git submodule update --init --recursive)" 10 | (interactive) 11 | (magit-with-toplevel 12 | (magit-run-git-async "submodule" "update" "--init" "--recursive"))) 13 | 14 | (transient-define-suffix magit-submodule-update-latest () 15 | "Update all submodules to latest" 16 | :description "Update All (git submodule foreach git pull origin main)" 17 | (interactive) 18 | (magit-with-toplevel 19 | (magit-run-git-async "submodule" "foreach" "--recursive" "git " "pull" "origin" "main"))) 20 | (transient-define-suffix magit-submodule-reset-all () 21 | "Reset all submodules to latest" 22 | :description "Reset All (git submodule foreach git reset --hard --fd)" 23 | (interactive) 24 | (magit-with-toplevel 25 | (magit-run-git-async "submodule" "foreach" "--recursive" "git " "reset" "--hard" "origin/main") 26 | (magit-run-git-async "submodule" "foreach" "--recursive" "git " "clean" "-fd" ) 27 | )) 28 | (transient-append-suffix 'magit-submodule "u" 29 | '("U" magit-submodule-update-all)) 30 | (transient-append-suffix 'magit-submodule "u" 31 | '("P" magit-submodule-update-latest)) 32 | (transient-append-suffix 'magit-submodule "u" 33 | '("R" magit-submodule-reset-all)) 34 | ) 35 | ;; (require 'forge) 36 | ;; (add-to-list 'ghub-insecure-hosts "192.168.1.124/api/v4") 37 | ;; (add-to-list 'ghub-insecure-hosts "192.168.1.124") 38 | 39 | ;; (add-to-list 'forge-alist 40 | ;; '("192.168.1.124" "192.168.1.124/api/v4" 41 | ;; "192.168.1.124" forge-gitlab-repository)) 42 | ;; ediff merge: keep both with d 43 | (defun ediff-copy-both-to-C () 44 | (interactive) 45 | (ediff-copy-diff ediff-current-difference nil 'C nil 46 | (concat 47 | (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer) 48 | (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer)))) 49 | (defun add-d-to-ediff-mode-map () (define-key ediff-mode-map "d" 'ediff-copy-both-to-C)) 50 | (add-hook 'ediff-keymap-setup-hook 'add-d-to-ediff-mode-map) 51 | 52 | (setq magit-no-confirm 't) 53 | 54 | -------------------------------------------------------------------------------- /config/modes/dired.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (require 'utils) 3 | 4 | ;; recrusive delete and copy directory 5 | (setq dired-recursive-deletes t) 6 | (setq dired-recursive-copies t) 7 | 8 | ;; prevent the warning message 9 | (put 'dired-find-alternate-file 'disabled nil) 10 | 11 | ;; normal we use the Recycle bin 12 | (setq delete-by-moving-to-trash t) 13 | 14 | ;; use the isearch only for filename 15 | (setq dired-isearch-filenames t) 16 | 17 | ;; we use extra function 18 | (require 'dired-x) 19 | ;; use wdired 20 | (require 'wdired) 21 | (setq wdired-allow-to-change-permissions 'advanced) 22 | (define-key dired-mode-map (kbd "r") 'wdired-change-to-wdired-mode) 23 | (add-hook 'dired-mode-hook 24 | (lambda () 25 | (define-key dired-mode-map (kbd "u") 26 | (lambda () (interactive) (find-alternate-file ".."))) 27 | )) 28 | (setq dired-omit-mode t) 29 | (setq-default dired-omit-files-p t) ; Buffer-local variable 30 | (setq dired-omit-files (concat dired-omit-files "\\|^\\..+$")) 31 | ;;(setq dired-omit-extensions (".rbo" ".rbc" ".o" "~" ".bin" ".lbin" ".so" ".a" ".ln" ".blg" ".bbl" ".elc" ".lof" ".glo" ".idx" ".lot" ".svn/" ".hg/" ".git/" ".bzr/" "CVS/" "_darcs/" "_MTN/" ".fmt" ".tfm" ".class" ".fas" ".lib" ".mem" ".x86f" ".sparcf" ".dfsl" ".pfsl" ".d64fsl" ".p64fsl" ".lx64fsl" ".lx32fsl" ".dx64fsl" ".dx32fsl" ".fx64fsl" ".fx32fsl" ".sx64fsl" ".sx32fsl" ".wx64fsl" ".wx32fsl" ".fasl" ".ufsl" ".fsl" ".dxl" ".lo" ".la" ".gmo" ".mo" ".toc" ".aux" ".cp" ".fn" ".ky" ".pg" ".tp" ".vr" ".cps" ".fns" ".kys" ".pgs" ".tps" ".vrs" ".pyc" ".pyo" ".idx" ".lof" ".lot" ".glo" ".blg" ".bbl" ".cp" ".cps" ".fn" ".fns" ".ky" ".kys" ".pg" ".pgs" ".tp" ".tps" ".vr" ".vrs")) 32 | (setq dired-omit-extensions nil) 33 | 34 | (defun open-in-external-app () 35 | "Open the current file or dired marked files in external app." 36 | (interactive) 37 | (let ( doIt 38 | (myFileList 39 | (cond 40 | ((string-equal major-mode "dired-mode") (dired-get-marked-files)) 41 | (t (list (buffer-file-name))) ) ) ) 42 | 43 | (setq doIt (if (<= (length myFileList) 5) 44 | t 45 | (y-or-n-p "Open more than 5 files?") ) ) 46 | 47 | (when doIt 48 | (cond 49 | ((string-equal system-type "windows-nt") 50 | (mapc (lambda (fPath) (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" fPath t t)) ) myFileList)) 51 | ((string-equal system-type "darwin") 52 | (mapc (lambda (fPath) (shell-command (format "open \"%s\"" fPath)) ) myFileList) ) 53 | ((string-equal system-type "gnu/linux") 54 | (mapc (lambda (fPath) (let ((process-connection-type nil)) (start-process "" nil "xdg-open" fPath)) ) myFileList) ) ) ) 55 | (local-set-key (kbd "C-c C-o") 'open-in-external-app)) ) 56 | (add-hook 'dired-mode-hook 'open-in-external-app) 57 | (put 'dired-find-alternate-file 'disabled nil) 58 | -------------------------------------------------------------------------------- /config/key.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (require 'utils) 3 | (defun kid-switch-to-shell () 4 | (interactive) 5 | (when (null (cdr (window-list))) 6 | (split-window-vertically)) 7 | (let ((file buffer-file-name)) 8 | (other-window 1) 9 | (shell) 10 | (when file 11 | (end-of-buffer) 12 | (when (looking-back shell-prompt-pattern) 13 | (insert "cd " (file-name-directory file)) 14 | (call-interactively 'comint-send-input))))) 15 | ;; open file in current buffer from outer explorer 16 | (defun w32-open-current-file-in-explorer () 17 | "open the current buffer file in windows explorer" 18 | (interactive) 19 | (let ((file buffer-file-name)) 20 | (when file 21 | (w32-shell-execute 22 | nil 23 | "explorer.exe" 24 | (concat "/e,/select," (replace-regexp-in-string "/" "\\\\" file) ))))) 25 | ; open external shell from current file directory 26 | (defun w32-open-shell-from-current-file-directory () 27 | "open cmd from current file directory" 28 | (interactive) 29 | (let ((file buffer-file-name)) 30 | (when (and file (file-name-directory file)) 31 | (w32-shell-execute 32 | nil 33 | "cmd.exe" 34 | (concat "/k cd /d" (file-name-directory file)))))) 35 | (defun wttr/w32:copy-current-file-name (&optional prefix) 36 | (interactive "p") 37 | (cond 38 | ((equal prefix 1) 39 | (kill-new (buffer-name))) 40 | ((equal prefix 4) 41 | (kill-new (or (buffer-file-name) 42 | (buffer-name)))))) 43 | 44 | (global-set-key (kbd "") 'wttr/w32:copy-current-file-name) 45 | (global-set-key "\C-c\C-y" 'copy-line) 46 | (global-set-key "\C-c\C-v" 'view-mode) 47 | (global-set-key (kbd "M-1") 'helm-bookmarks) 48 | (global-set-key (kbd "M-2") #'helm-mini) 49 | (global-set-key (kbd "M-3") #'helm-find-files) 50 | (global-set-key (kbd "M-4") 'magit-status) 51 | (global-set-key (kbd "M-0") 'delete-window) 52 | (global-set-key (kbd "M-q") 'kill-current-buffer) 53 | (global-set-key (kbd "") 'kid-switch-to-shell) 54 | (global-set-key (kbd "") 'w32-open-current-file-in-explorer) 55 | (global-set-key (kbd "") 'w32-open-shell-from-current-file-directory) 56 | (global-set-key (kbd "C-x o") 'switch-window) 57 | (global-set-key (kbd "M-y") 'helm-show-kill-ring) 58 | (global-set-key (kbd "M-m") 'helm-semantic-or-imenu) 59 | (global-set-key (kbd "C-x C-b") #'helm-buffers-list) 60 | (global-set-key (kbd "M-x") #'helm-M-x) 61 | (global-set-key (kbd "C-c M-x") #'execute-extended-command) 62 | (global-set-key (kbd "C-x C-f") #'helm-find-files) 63 | (global-set-key (kbd "C-x C-r") #'helm-recentf) 64 | (global-set-key (kbd "C-x r l") #'helm-bookmarks) 65 | (global-set-key (kbd "C-c s") 'helm-do-ag-project-root) 66 | (global-set-key (kbd "C-c f") 'mu-helm-file-search) 67 | (global-set-key (kbd "C-c ") 'c-hungry-delete-backwards) 68 | (global-set-key (kbd "M-[") 'avy-goto-word-1) 69 | (global-set-key (kbd "C-c t") 'git-timemachine) 70 | ;;magit 71 | (global-set-key [remap forward-word] 'forward-symbol) 72 | (global-set-key [remap backward-word] 'backward-symbol) 73 | 74 | 75 | ;;magit 76 | (with-eval-after-load 'magit 77 | (define-key magit-status-mode-map (kbd "M-1") nil) 78 | (define-key magit-log-mode-map (kbd "M-1") nil) 79 | (define-key magit-status-mode-map (kbd "M-2") nil) 80 | (define-key magit-log-mode-map (kbd "M-2") nil) 81 | (define-key magit-status-mode-map (kbd "M-3") nil) 82 | (define-key magit-log-mode-map (kbd "M-3") nil) 83 | (define-key magit-status-mode-map (kbd "M-4") nil) 84 | (define-key magit-log-mode-map (kbd "M-4") nil) 85 | ) 86 | (global-set-key (kbd "C-c p") 'project-find-file) 87 | (global-set-key (kbd "M-o") 'helm-projectile-find-other-file) 88 | (global-set-key (kbd "C-") 'switch-to-next-buffer) 89 | (global-set-key (kbd "C-S-") 'switch-to-prev-buffer) 90 | (global-set-key (kbd "C-;") 'ace-jump-mode) 91 | 92 | (provide 'key) 93 | -------------------------------------------------------------------------------- /myelpa/flymake-bridge.el: -------------------------------------------------------------------------------- 1 | ;;; flymake-bridge.el --- A lsp-bridge Flymake backend -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 liuyinz 4 | 5 | ;; Author: liuyinz 6 | ;; Maintainer: liuyinz 7 | ;; Version: 0.1.0 8 | ;; Package-Requires: ((emacs "26.1") (lsp-bridge)) 9 | ;; Keywords: tools 10 | ;; Homepage: https://github.com/liuyinz/flymake-bridge 11 | 12 | ;; This file is not a part of GNU Emacs. 13 | 14 | ;; This program is free software; you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;; This file is not a part of GNU Emacs. 28 | 29 | ;;; Commentary: 30 | 31 | ;; A lsp-bridge Flymake backend for server diagnostics. 32 | ;; Enable it by calling `flymake-bridge-setup' from a 33 | ;; file-visiting buffer. Or to add it to hooks such as: 34 | 35 | ;; (add-hook 'lsp-bridge-mode-hook #'flymake-bridge-setup) 36 | 37 | ;;; Code: 38 | 39 | (require 'cl-lib) 40 | (require 'flymake) 41 | (require 'lsp-bridge) 42 | 43 | (declare-function acm-backend-lsp-position-to-point "acm-backend-lsp") 44 | 45 | (defface flymake-bridge-server 46 | '((t (:foreground "violet" :bold t))) 47 | "Server of current lsp-bridge as shown in the diagnostic message.") 48 | 49 | (defun flymake-bridge (report-fn &rest _args) 50 | "A flymake backend for `lsp-bridge-diagnostic'. 51 | Add this to `flymake-diagnostic-functions' to enable it. 52 | Calls REPORT-FN directly." 53 | (cl-loop for diag in (seq-remove 54 | (lambda (x) 55 | (member (plist-get x :severity) 56 | lsp-bridge-diagnostic-hide-severities)) 57 | lsp-bridge-diagnostic-records) 58 | collect 59 | (flymake-make-diagnostic 60 | (current-buffer) 61 | (acm-backend-lsp-position-to-point 62 | (plist-get (plist-get diag :range) :start)) 63 | (acm-backend-lsp-position-to-point 64 | (plist-get (plist-get diag :range) :end)) 65 | (cl-case (plist-get diag :severity) 66 | (1 :error) 67 | (2 :warning) 68 | ((3 4) :note)) 69 | (concat (when-let ((code (plist-get diag :code))) 70 | (concat "[" (or (and (stringp code) code) 71 | (prin1-to-string code)) 72 | "] ")) 73 | (plist-get diag :message) 74 | (when-let ((server (plist-get diag :server-name))) 75 | (concat " (" (propertize server 'face 76 | 'flymake-bridge-server) 77 | ")")))) 78 | into diags 79 | finally (funcall report-fn diags))) 80 | 81 | (defun flymake-bridge-ensure () 82 | "Add `flymake-bridge' to diagnositc functions if not yet." 83 | (unless (memq 'flymake-bridge flymake-diagnostic-functions) 84 | (setq lsp-bridge-diagnostic-enable-overlays nil) 85 | (add-hook 'flymake-diagnostic-functions #'flymake-bridge nil t) 86 | (add-hook 'lsp-bridge-diagnostic-update-hook #'flymake-start nil t) 87 | (flymake-mode 1)) 88 | (remove-hook 'lsp-bridge-diagnostic-update-hook #'flymake-bridge-ensure t)) 89 | 90 | ;;;###autoload 91 | (defun flymake-bridge-setup () 92 | "Setup lsp-bridge-diagnostic integration with Flymake." 93 | (interactive) 94 | (if (< emacs-major-version 26) 95 | (error "Flymake-bridge requires Emacs 26 or later") 96 | (add-hook 'lsp-bridge-diagnostic-update-hook #'flymake-bridge-ensure nil t))) 97 | 98 | (provide 'flymake-bridge) 99 | ;;; flymake-bridge.el ends here 100 | -------------------------------------------------------------------------------- /config/modes/evil.el: -------------------------------------------------------------------------------- 1 | ;; -*- coding: utf-8 -*- 2 | (require 'utils) 3 | 4 | ;;;; pre setting 5 | (setq-default evil-auto-indent t) 6 | (setq evil-shift-width 4) 7 | (setq evil-repeat-move-cursor t) 8 | (setq evil-find-skip-newlines nil) 9 | (setq evil-move-cursor-back t) 10 | (setq evil-want-fine-undo nil) 11 | (setq evil-regexp-search t) 12 | (setq evil-search-wrap t) 13 | (setq evil-flash-delay 3) 14 | (setq evil-want-C-i-jump nil) 15 | (setq evil-want-C-u-scroll nil) 16 | ;; load 17 | (require 'evil) 18 | ;(global-set-key "\C-z" 'evil-mode) 19 | ;;(evil-mode 1) 20 | ;;(global-set-key "\C-z" 'evil-mode) 21 | ;;;; cursor appearance 22 | ;evil-default-cursor [Variable] 23 | ;The default cursor. 24 | ;evil-normal-state-cursor [Variable] 25 | ;The cursor for Normal state. 26 | ;evil-insert-state-cursor [Variable] 27 | ;The cursor for Insert state. 28 | ;evil-visual-state-cursor [Variable] 29 | ;The cursor for Visual state. 30 | ;evil-replace-state-cursor [Variable] 31 | ;The cursor for Replace state. 32 | ;evil-operator-state-cursor [Variable] 33 | ;The cursor for Operator-Pending state. 34 | ;evil-motion-state-cursor [Variable] 35 | ;The cursor for Motion state. 36 | ;evil-emacs-state-cursor [Variable] 37 | ;The cursor for Emacs state. 38 | 39 | ;;;; initial state 40 | ;evil-set-initial-state 41 | 42 | ;;;; key map 43 | ;evil-normal-state-map [Variable] 44 | ;The global keymap for Normal state. 45 | (define-key evil-normal-state-map (kbd "C-s") 'save-buffer) 46 | 47 | ;; some auto load 48 | (autoload 'dired-jump "dired" "dired-jump" t) 49 | (autoload 'dired-jump-other-window "dired" "dired-jump" t) 50 | ;; add ";" sub map 51 | (define-prefix-command 'wttr/my-evil-normal-map) 52 | (define-key evil-normal-state-map (kbd ";") 'wttr/my-evil-normal-map) 53 | (defun kill-current-buffer() 54 | (interactive) 55 | (kill-buffer (current-buffer))) 56 | (mapc (lambda (info) 57 | (define-key wttr/my-evil-normal-map 58 | (read-kbd-macro (car info)) 59 | (cdr info))) 60 | '( 61 | ("d" . ido-dired) 62 | ("E" . eshell) 63 | ("e" . kid-switch-to-shell) 64 | ("f" . ido-find-file) 65 | ("r" . revert-buffer-with-coding-system) 66 | ("4" . wttr/kill-buffer-may-have-clients) 67 | ("k" . kill-current-buffer) 68 | ("o" . other-window) 69 | ("w" . ido-write-file) 70 | ("b" . ido-switch-buffer) 71 | ("B" . ido-switch-buffer-other-window) 72 | ("n" . next-buffer) 73 | ("p" . previous-buffer) 74 | ("s" . save-buffer) 75 | ("g" . wttr/customized:rgrep) 76 | ("a" . wttr/customized:rgrep-using-ack) 77 | ("l" . ibuffer) 78 | ("j" . dired-jump ) 79 | ("J" . dired-jump-other-window ) 80 | ("i" . ispell-buffer) 81 | ("m" . magit-status) 82 | )) 83 | 84 | (define-key evil-visual-state-map (kbd "SPC") 'scroll-up) 85 | (define-key evil-normal-state-map (kbd "SPC") 'scroll-up) 86 | ;; opposite to C-o : evil-jump-backward 87 | (define-key evil-normal-state-map (kbd "TAB") 'evil-jump-forward) 88 | ;; opposite to u : undo-tree-undo 89 | (define-key evil-normal-state-map (kbd "C-r") 'undo-tree-redo) 90 | 91 | ;; replace the to move-end-of-line 92 | (define-key evil-insert-state-map (kbd "C-e") 'move-end-of-line) 93 | 94 | ;; we do not need c-n and c-p to evil-complete 95 | (define-key evil-insert-state-map (kbd "C-n") 'next-line) 96 | (define-key evil-insert-state-map (kbd "C-p") 'previous-line) 97 | 98 | ;; recover the c-k, do not trigger special char input as vim 99 | (define-key evil-insert-state-map (kbd "C-k") 'kill-line) 100 | ;; recover the c-y, yank 101 | (define-key evil-insert-state-map (kbd "C-y") 'yank) 102 | 103 | (define-key evil-visual-state-map "C-q" 'evil-change-to-previous-state) 104 | (define-key evil-insert-state-map "C-q" 'evil-normal-state) 105 | (define-key evil-replace-state-map "C-q" 'evil-normal-state) 106 | (require 'key-chord) 107 | (auto-complete-mode) 108 | (setq key-chord-two-keys-delay 0.5) 109 | (key-chord-mode 1) 110 | (key-chord-define evil-insert-state-map "jk" 'evil-normal-state) 111 | ;; some mode that should use emacs state 112 | ;; (dolist (mode '(dired-mode 113 | ;; eassist-mode 114 | ;; gtags-select-mode 115 | ;; magit-status-mode 116 | ;; magit-log-mode 117 | ;; magit-commit-mode 118 | ;; magit-diff-mode 119 | ;; org-mode 120 | ;; fundamental-mode 121 | ;; text-mode)) 122 | ;; (add-to-list 'evil-emacs-state-modes mode)) 123 | 124 | (setq evil-default-state 'emacs) 125 | -------------------------------------------------------------------------------- /cnfonts/v4/profile1.el: -------------------------------------------------------------------------------- 1 | ;; `cnfonts--custom-set-fontsnames' 结构与 `cnfonts--fontnames-fallback' 相同。 2 | (setq cnfonts--custom-set-fontnames 3 | '( 4 | ("Consolas" "Monaco" "DejaVu Sans Mono" "Droid Sans Mono" "PragmataPro" "Courier" "Courier New" "Ubuntu Mono" "Liberation Mono" "MonacoB" "MonacoB2" "MonacoBSemi" "Droid Sans Mono Pro" "Inconsolata" "Source Code Pro" "Lucida Console" "Envy Code R" "Andale Mono" "Lucida Sans Typewriter" "monoOne" "Lucida Typewriter" "Panic Sans" "Hack" "Bitstream Vera Sans Mono" "HyperFont" "PT Mono" "Ti92Pluspc" "Excalibur Monospace" "Menlof" "Cousine" "Fira Mono" "Lekton" "M+ 1mn" "BPmono" "Free Mono" "Anonymous Pro" "ProFont" "ProFontWindows" "Latin Modern Mono" "Code 2002" "ProggyCleanTT" "ProggyTinyTT" "Iosevka Term" "Inconsolata-dz" "American Typewriter" "Menlo" "Anka/Coder Condensed" "Fantasque Sans Mono" "M+ 1m" "CamingoCode" "Office Code Pro" "Roboto Mono" "Input Mono" "Courier Prime Code" "NanumGothicCoding" "Monoid" "Edlo" "Iosevka" "Mononoki" "Robot Mono" "Fantasque" "Fira Code" "Go Mono" "Noto Sans Mono CJK" "InputMonoCompressed" "Hasklig" "Terminus" "FantasqueSansMono" "AnonymousPro" "3270" "Arimo" "D2Coding" "Inconsolata-g" "ProFont for Powerline" "Meslo" "Meslo Dotted" "Noto Mono" "Symbol Neu" "Tinos" "Space Mono" "SFMono Nerd Font" "PragmataPro Mono") 5 | ("华文楷体" "华文行楷" "Microsoft Yahei" "方正姚体" "宋体" "微软雅黑" "Noto Sans S Chinese Regular" "Microsoft YaHei Mono" "Microsoft_Yahei" "Ubuntu Mono" "文泉驿等宽微米黑" "文泉驿等宽正黑" "黑体" "Source Han Sans SC" "Source Han Serif SC" "思源黑体 CN Regular" "思源黑体 CN Medium" "思源黑体 CN Normal" "思源宋体 CN" "思源宋体 CN Medium" "思源宋体 CN SemiBold" "Hiragino Sans GB" "文泉驿正黑" "文泉驿点阵正黑" "SimHei" "SimSun" "NSimSun" "FangSong" "KaiTi" "FangSong_GB2312" "KaiTi_GB2312" "LiSu" "YouYuan" "新宋体" "楷体_GB2312" "仿宋_GB2312" "幼圆" "隶书" "STXihei" "STKaiti" "STSong" "STZhongsong" "STFangsong" "FZShuTi" "FZYaoti" "STCaiyun" "STHupo" "STLiti" "STXingkai" "STXinwei" "方正舒体" "方正粗圆_GBK" "华文仿宋" "华文中宋" "华文彩云" "华文隶书" "华文细黑" "华文琥珀" "华文新魏" "微软简标宋" "微软简粗黑" "微软简老宋" "微软简魏碑" "微软简楷体" "微软简行楷" "微软简隶书" "微软简中圆" "微软简仿宋" "方正大黑_GBK" "方正大标宋_GBK" "方正报宋_GBK" "方正彩云_GBK" "方正综艺_GBK" "方正稚艺_GBK" "方正幼线_GBK" "方正准圆_GBK" "方正中等线_GBK" "方正中倩_GBK" "方正行楷_GBK" "方正细珊瑚_GBK" "方正细圆_GBK" "方正细倩_GBK" "方正新舒体_GBK" "方正新报宋_GBK" "方正小标宋_GBK" "方正姚体_GBK" "方正魏碑_GBK" "方正舒体__GBK" "方正细黑一_GBK" "方正细等线_GBK" "方正水柱_GBK" "方正宋黑_GBK" "方正宋三_GBK" "方正宋一_GBK" "方正隶二_GBK" "方正隶书_GBK" "方正胖娃_GBK" "方正美黑_GBK" "方正瘦金书_GBK" "方正平和_GBK" "方正少儿_GBK" "方正书宋_GBK" "方正黑体_GBK" "方正黄草_GBK" "方正隶变_GBK" "方正琥珀_GBK" "方正楷体_GBK" "方正康体_GBK" "方正华隶_GBK" "方正仿宋_GBK" "方正超粗黑_GBK" "方正粗宋_GBK" "方正粗倩_GBK" "方正徐静蕾字体" "方正黑体简体" "方正黄草简体" "方正隶二简体" "方正隶书简体" "方正铁筋隶书简体" "方正超粗黑简体" "方正行楷简体" "方正艺黑简体" "方正胖娃简体" "方正胖头鱼简体" "方正美黑简体" "方正综艺简体" "方正细黑一简体" "方正细等线简体" "方正细珊瑚简体" "方正细圆简体" "方正细倩简体" "方正粗活意简体" "方正粗宋简体" "方正粗圆简体" "方正粗倩简体" "方正稚艺简体" "方正祥隶简体" "方正硬笔行书简体" "方正硬笔楷书简体" "方正瘦金书简体" "方正琥珀简体" "方正流行体简体" "方正水黑简体" "方正水柱简体" "方正北魏楷书简体" "方正华隶简体" "方正卡通简体" "方正古隶简体" "方正启体简体" "方正大标宋简体" "方正大黑简体" "方正姚体简体" "方正宋一简体" "方正宋黑简体" "方正小标宋简体" "方正少儿简体" "方正平和简体" "方正幼线简体" "方正康体简体" "方正彩云简体" "方正报宋简体" "方正新报宋简体" "方正新舒体简体" "方正楷体简体" "方正毡笔黑简体" "方正剪纸简体" "方正准圆简体" "方正仿宋简体" "方正书宋简体" "方正中等线简体" "方正中倩简体" "方正黄草简" "方正魏碑简体" "方正隶变简体" "汉仪彩云体简" "汉仪彩蝶体简" "汉仪报宋简" "汉仪柏青体简" "汉仪白棋体简" "汉仪长宋简" "汉仪长美黑简" "汉仪长艺体简" "汉仪粗仿宋简" "汉仪粗圆简" "汉仪粗宋简" "汉仪粗黑简" "汉仪超粗黑简" "汉仪超粗宋简" "汉仪超粗圆简" "汉仪陈频破体简" "汉仪嘟嘟体简" "汉仪大宋简" "汉仪大隶书简" "汉仪大黑简" "汉仪方叠体简" "汉仪方隶简" "汉仪蝶语体简" "汉仪黛玉体简" "汉仪仿宋简" "汉仪哈哈体简" "汉仪橄榄体简" "汉仪海韵体简" "汉仪琥珀体简" "汉仪花蝶体简" "汉仪黑咪体简" "汉仪黑棋体简" "汉仪凌波体简" "汉仪家书简" "汉仪楷体简" "汉仪漫步体简" "汉仪火柴体简" "汉仪立黑简" "汉仪菱心体简" "汉仪萝卜体简" "汉仪书宋一简" "汉仪书宋二简" "汉仪书魂体简" "汉仪南宫体简" "汉仪咪咪体简" "汉仪清韵体简" "汉仪瘦金书简" "汉仪神工体简" "汉仪双线体简" "汉仪太极体简" "汉仪娃娃篆简" "汉仪水波体简" "汉仪水滴体简" "汉仪特细等线简" "汉仪舒同体简" "汉仪魏碑简" "汉仪小隶书简" "汉仪秀英体简" "汉仪细中圆简" "汉仪细圆简" "汉仪细等线简" "汉仪细行楷简" "汉仪行楷简" "汉仪醒示体简" "汉仪丫丫体简" "汉仪中楷简" "汉仪中等线简" "汉仪中黑简" "汉仪圆叠体简" "汉仪雁翎体简" "汉仪雪君体简" "汉仪雪峰体简" "汉仪中圆简" "汉仪中宋简" "汉仪中隶书简" "汉仪竹节体简" "汉仪字典宋简" "经典行楷简" "经典魏碑简" "经典空叠圆简" "经典仿宋简" "经典细标宋简" "经典细等线简" "经典中宋简" "经典标宋简" "经典粗仿黑" "经典粗黑简" "经典超圆简" "经典长宋简" "经典叠圆体简" "经典等线简" "经典粗圆简" "经典粗宋简" "经典楷体简" "经典空叠黑" "经典黑体简" "经典平黑简" "经典空趣体简" "经典美黑简" "经典隶变简" "经典图案字" "经典宋体简" "经典特宋简" "经典特黑简" "经典舒同体简" "经典趣体简" "经典圆叠黑" "经典细空艺" "经典细空黑" "经典行书简" "经典细宋简" "经典细隶书简" "经典中圆简" "经典圆体简" "经典综艺体简" "经典细圆简" "长城中圆体" "长城黑宋体" "长城黑体" "长城细圆体" "长城长宋体" "长城行楷体" "长城行书体" "长城美黑体" "长城细仿宋体" "长城粗魏碑体" "长城粗隶书体" "长城粗行楷体" "长城粗圆体" "长城特粗黑" "长城特粗宋体" "长城特粗圆体" "长城特圆体" "长城楷体" "长城新魏碑体" "长城新艺体" "长城报宋体" "长城小标宋体" "长城小姚体" "长城宋体" "长城大黑体" "长城大标宋体" "长城仿宋体" "长城中隶体" "长城中行书体" "Noto Sans Mono CJK SC" "Noto Sans Mono CJK TC" "Noto Sans CJK SC" "Noto Sans CJK TC" "Source Han Serif CN" "Source Han Sans CN" "思源黑体 CN") 6 | ("MingLiU-ExtB" "SimSun-ExtB" "HanaMinB" "PMingLiU-ExtB" "MingLiU_HKSCS-ExtB" "Hanazono Mincho" "Hanazono Mincho A" "Hanazono Mincho B" "Hanazono Mincho C" "Hanazono Mincho Ex" "Hanazono Mincho Ex A1" "Hanazono Mincho Ex A2" "Hanazono Mincho Ex B" "Hanazono Mincho Ex C" "Hanazono Mincho I") 7 | ("Segoe UI Symbol" "Symbola" "Standard Symbols L") 8 | ("NanumGothic" "Arial Unicode MS" "MS Gothic" "Lucida Sans Unicode") 9 | )) 10 | 11 | ;; `cnfonts--custom-set-fontsizes' 结构与 `cnfonts--fontsizes-fallback' 相同。 12 | (setq cnfonts--custom-set-fontsizes 13 | '( 14 | (6 7 7 6 6 ) 15 | (7 8 8 7 7 ) 16 | (8 9 9 8 8 ) 17 | (9 10.5 10.5 9 9 ) 18 | (10 12.0 12.5 10 10 ) 19 | (11 13.0 13.0 11 11 ) 20 | (11.5 13.5 13.5 11.5 11.5) 21 | (12 14.0 14.0 12 12 ) 22 | (12.5 15.0 15.0 12.5 12.5) 23 | (13 15.5 15.5 13 13 ) 24 | (13.5 16.0 16.0 13.5 13.5) 25 | (14 16.5 16.5 14 14 ) 26 | (14.5 17.0 17.0 14.5 14.5) 27 | (15 18.0 18.0 15 15 ) 28 | (16 19.5 19.5 16 16 ) 29 | (18 21.0 21.0 18 18 ) 30 | (20 24.0 24.0 20 20 ) 31 | (22 25.5 25.5 22 22 ) 32 | (24 28.5 28.5 24 24 ) 33 | (26 31.5 31.5 26 26 ) 34 | (28 33.0 33.0 28 28 ) 35 | (30 36.0 36.0 30 30 ) 36 | (32 39.0 39.0 32 32 ) 37 | )) 38 | -------------------------------------------------------------------------------- /config/basic.el: -------------------------------------------------------------------------------- 1 | (require 'package) 2 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) 3 | (add-to-list 'load-path "~/.emacs.d/myelpa") 4 | ;;(exec-path-from-shell-initialize) 5 | (require 'utils) 6 | ;; set user information 7 | (menu-bar-mode 'nil) 8 | 9 | (setq create-lockfiles nil) 10 | (modify-syntax-entry ?_ "w") 11 | (auto-fill-mode -1) 12 | 13 | ;;==================================== 14 | ;; UI 15 | ;;==================================== 16 | ;; close startup message 17 | (setq inhibit-startup-message t) 18 | ;; turn on syntax hilight 19 | (global-font-lock-mode t) 20 | ;; remove toolbar/menu bar/scroll bar 21 | (tool-bar-mode 0) 22 | (set-scroll-bar-mode nil) 23 | ;; show clock at statusline 24 | (display-time-mode t) 25 | (setq display-time-24hr-format t) 26 | (setq display-time-day-and-date t) 27 | (setq display-time-use-mail-icon t) 28 | (setq display-time-interval 10) 29 | 30 | ;; show column number in mode line 31 | (setq column-number-mode t) 32 | 33 | ;; show parent 34 | (show-paren-mode t) 35 | (setq show-paren-style 'parenthesis) 36 | 37 | ;; hilight mark area 38 | (transient-mark-mode t) 39 | 40 | ;; make the title infomation more useful 41 | (setq frame-title-format 42 | (list "GNU Emacs " emacs-version "@" system-name " - " '(buffer-file-name "%f" "%b"))) 43 | ;; don't ring at error 44 | (setq ring-bell-function 'ignore) 45 | 46 | ;; can use the narrow to region 47 | (put 'narrow-to-region 'disabled nil) 48 | 49 | ;; use mouse to copy thing automatically 50 | (setq mouse-drag-copy-region t) 51 | 52 | ;; move mouse when cursor is close to it 53 | ;(mouse-avoidance-mode 'animate) 54 | (mouse-avoidance-mode 'none) 55 | 56 | ;; no backup file, and auto save 57 | (setq 58 | backup-by-copying t ; don't clobber symlinks 59 | backup-directory-alist 60 | '(("." . "~/.saves")) ; don't litter my fs tree 61 | delete-old-versions t 62 | kept-new-versions 6 63 | kept-old-versions 2 64 | version-control t) ; use versioned backups 65 | (setq auto-save-default nil) 66 | 67 | ;; use y --> yes 68 | (fset 'yes-or-no-p 'y-or-n-p) 69 | 70 | ;; setup for newline auto-appending support 71 | (setq next-line-add-newline t) 72 | 73 | ;;encoding 74 | (set-language-environment 'UTF-8) 75 | ;; (set-language-environment 'Chinese-GB) 76 | ;; setup up a big kill-ring, so i will never miss anything:-) 77 | (setq kill-ring-max 1000) 78 | 79 | (setq extended-command-history-max 500) 80 | (setq query-replace-history-max 500) 81 | (setq replace-string-history-max 500) 82 | (setq file-name-history-max 500) 83 | (setq replace-regex-history-max 500) 84 | (setq minibuffer-history-max 1000) 85 | (setq shell-command-history-max 1000) 86 | (setq find-file-history-max 1000) 87 | ;; we need to paste something from another program, but sometimes we 88 | ;; do real paste after some kill action, that will erase the 89 | ;; clipboard,so we need to save it to kill ring, here is the setting 90 | ;; used to control that 91 | (setq save-interprogram-paste-before-kill t) 92 | (setq x-select-enable-clipboard t) 93 | 94 | ;; scroll one line at a time (less "jumpy" than defaults) 95 | (setq mouse-wheel-scroll-amount '(1 ((shift) . 3))) ;; one line at a time 96 | (setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling 97 | (setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse 98 | (setq scroll-step 1) ;; keyboard scroll one line at a time 99 | (setq scroll-margin 0) ;; stop autoscroll when mouse is close to margin 100 | 101 | ;; not use tab, use space to indent 102 | (setq-default indent-tabs-mode nil) 103 | (setq-default tab-width 4) 104 | 105 | ;; always split window vertically 106 | (setq split-width-threshold nil) 107 | 108 | ;;copy line 109 | (defun copy-line () 110 | (interactive) 111 | (kill-ring-save (save-excursion 112 | (back-to-indentation) 113 | (point)) 114 | (line-end-position)) 115 | (message "line copied")) 116 | 117 | (defun indent-buffer() 118 | (interactive) 119 | (indent-region (buffer-end 0) (buffer-end 1))) 120 | 121 | 122 | (defun xah-syntax-color-hex () 123 | "Syntax color hex color spec such as 「#ff1100」 in current buffer." 124 | (interactive) 125 | (font-lock-add-keywords 126 | nil 127 | '(("#[abcdef[:digit:]]\\{6\\}" 128 | (0 (put-text-property 129 | (match-beginning 0) 130 | (match-end 0) 131 | 'face (list :background (match-string-no-properties 0))))))) 132 | (font-lock-fontify-buffer) 133 | ) 134 | 135 | (defun lhf-log-color () 136 | "Syntax color hex color spec such as 「#ff1100」 in current buffer." 137 | (interactive) 138 | (font-lock-add-keywords nil '( 139 | ("\\<\\(ERROR\\)" 1 '(:foreground "red") t) 140 | ("\\<\\(NOTICE\\)" 1 '(:foreground "green") t) 141 | ("\\<\\(WARN\\)" 1 '(:foreground "yellow") t) 142 | ("\\<\\(DEBUG\\)" 1 '(:foreground "yellow") t) 143 | )) 144 | ) 145 | ;; (server-start) 146 | (defun backward-symbol (&optional arg) 147 | "Move backward until encountering the beginning of a symbol. 148 | With argument, do this that many times." 149 | (interactive "p") 150 | (forward-symbol (- (or arg 1)))) 151 | 152 | (defun backward-same-syntax (&optional arg) 153 | "Move backward until encountering the beginning of a same-syntax. 154 | With argument, do this thato many times." 155 | (interactive "p") 156 | (forward-same-syntax (- (or arg 1)))) 157 | (global-auto-revert-mode 1) 158 | (setq auto-revert-interval 1) 159 | 160 | (global-display-line-numbers-mode) 161 | 162 | (defun my-flymd-browser-function (url) 163 | (let ((process-environment (browse-url-process-environment))) 164 | (apply 'start-process 165 | (concat "chrome " url) nil 166 | "chrome" 167 | (list "--new-window" "--allow-file-access-from-files" url)))) 168 | (setq flymd-browser-open-function 'my-flymd-browser-function) 169 | (setq helm-ag-base-command "rg -i --line-number --no-heading --color never") 170 | (setq helm-ag-success-exit-status '(0 2)) 171 | 172 | (load-theme 'dracula) 173 | 174 | (when (eq system-type 'darwin) 175 | (setq mac-option-key-is-meta nil 176 | mac-command-key-is-meta t 177 | mac-command-modifier 'meta 178 | mac-option-modifier 'super)) 179 | (require 'cnfonts) 180 | (cnfonts-enable) 181 | 182 | (helm-mode 1) 183 | (pixel-scroll-precision-mode 1) 184 | (which-key-mode) 185 | 186 | ;;all-the-icons 187 | 188 | ;; (add-hook 'dired-mode-hook 'all-the-icons-dired-mode) 189 | (defun my-dired-init () 190 | "to be run as hook for `dired-mode'." 191 | (dired-hide-details-mode 1)) 192 | (require 'dired-sort-menu) 193 | (add-hook 'dired-mode-hook 'my-dired-init) 194 | (require 'helm-ag) 195 | (provide 'basic) 196 | 197 | -------------------------------------------------------------------------------- /custom.el: -------------------------------------------------------------------------------- 1 | (custom-set-variables 2 | ;; custom-set-variables was added by Custom. 3 | ;; If you edit it by hand, you could mess it up, so be careful. 4 | ;; Your init file should contain only one such instance. 5 | ;; If there is more than one, they won't work right. 6 | '(auto-revert-interval 1) 7 | '(connection-local-criteria-alist 8 | '(((:application tramp :protocol "kubernetes") 9 | tramp-kubernetes-connection-local-default-profile) 10 | ((:application eshell) eshell-connection-default-profile) 11 | ((:application tramp :protocol "flatpak") 12 | tramp-container-connection-local-default-flatpak-profile 13 | tramp-flatpak-connection-local-default-profile) 14 | ((:application tramp) 15 | tramp-connection-local-default-system-profile 16 | tramp-connection-local-default-shell-profile))) 17 | '(connection-local-profile-alist 18 | '((tramp-flatpak-connection-local-default-profile 19 | (tramp-remote-path "/app/bin" tramp-default-remote-path "/bin" 20 | "/usr/bin" "/sbin" "/usr/sbin" 21 | "/usr/local/bin" "/usr/local/sbin" 22 | "/local/bin" "/local/freeware/bin" 23 | "/local/gnu/bin" "/usr/freeware/bin" 24 | "/usr/pkg/bin" "/usr/contrib/bin" "/opt/bin" 25 | "/opt/sbin" "/opt/local/bin")) 26 | (tramp-kubernetes-connection-local-default-profile 27 | (tramp-config-check . tramp-kubernetes--current-context-data) 28 | (tramp-extra-expand-args 97 29 | (tramp-kubernetes--container 30 | (car tramp-current-connection)) 31 | 104 32 | (tramp-kubernetes--pod 33 | (car tramp-current-connection)) 34 | 120 35 | (tramp-kubernetes--context-namespace 36 | (car tramp-current-connection)))) 37 | (eshell-connection-default-profile (eshell-path-env-list)) 38 | (tramp-container-connection-local-default-flatpak-profile 39 | (tramp-remote-path "/app/bin" tramp-default-remote-path "/bin" 40 | "/usr/bin" "/sbin" "/usr/sbin" 41 | "/usr/local/bin" "/usr/local/sbin" 42 | "/local/bin" "/local/freeware/bin" 43 | "/local/gnu/bin" "/usr/freeware/bin" 44 | "/usr/pkg/bin" "/usr/contrib/bin" "/opt/bin" 45 | "/opt/sbin" "/opt/local/bin")) 46 | (tramp-connection-local-darwin-ps-profile 47 | (tramp-process-attributes-ps-args "-acxww" "-o" 48 | "pid,uid,user,gid,comm=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 49 | "-o" "state=abcde" "-o" 50 | "ppid,pgid,sess,tty,tpgid,minflt,majflt,time,pri,nice,vsz,rss,etime,pcpu,pmem,args") 51 | (tramp-process-attributes-ps-format (pid . number) 52 | (euid . number) 53 | (user . string) 54 | (egid . number) (comm . 52) 55 | (state . 5) (ppid . number) 56 | (pgrp . number) 57 | (sess . number) 58 | (ttname . string) 59 | (tpgid . number) 60 | (minflt . number) 61 | (majflt . number) 62 | (time . tramp-ps-time) 63 | (pri . number) 64 | (nice . number) 65 | (vsize . number) 66 | (rss . number) 67 | (etime . tramp-ps-time) 68 | (pcpu . number) 69 | (pmem . number) (args))) 70 | (tramp-connection-local-busybox-ps-profile 71 | (tramp-process-attributes-ps-args "-o" 72 | "pid,user,group,comm=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 73 | "-o" "stat=abcde" "-o" 74 | "ppid,pgid,tty,time,nice,etime,args") 75 | (tramp-process-attributes-ps-format (pid . number) 76 | (user . string) 77 | (group . string) (comm . 52) 78 | (state . 5) (ppid . number) 79 | (pgrp . number) 80 | (ttname . string) 81 | (time . tramp-ps-time) 82 | (nice . number) 83 | (etime . tramp-ps-time) 84 | (args))) 85 | (tramp-connection-local-bsd-ps-profile 86 | (tramp-process-attributes-ps-args "-acxww" "-o" 87 | "pid,euid,user,egid,egroup,comm=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 88 | "-o" 89 | "state,ppid,pgid,sid,tty,tpgid,minflt,majflt,time,pri,nice,vsz,rss,etimes,pcpu,pmem,args") 90 | (tramp-process-attributes-ps-format (pid . number) 91 | (euid . number) 92 | (user . string) 93 | (egid . number) 94 | (group . string) (comm . 52) 95 | (state . string) 96 | (ppid . number) 97 | (pgrp . number) 98 | (sess . number) 99 | (ttname . string) 100 | (tpgid . number) 101 | (minflt . number) 102 | (majflt . number) 103 | (time . tramp-ps-time) 104 | (pri . number) 105 | (nice . number) 106 | (vsize . number) 107 | (rss . number) 108 | (etime . number) 109 | (pcpu . number) 110 | (pmem . number) (args))) 111 | (tramp-connection-local-default-shell-profile 112 | (shell-file-name . "/bin/sh") (shell-command-switch . "-c")) 113 | (tramp-connection-local-default-system-profile 114 | (path-separator . ":") (null-device . "/dev/null")))) 115 | '(custom-safe-themes 116 | '("2d74de1cc32d00b20b347f2d0037b945a4158004f99877630afc034a674e3ab7" 117 | "a8e721dc14961b37d513b53f5f688c39f5d20b86a2e1f6142eb6c730f2ddd86f" 118 | "c650a74280e8ce4ae4b50835b7a3bc62aeffa202ffea82260e529f0a69027696" 119 | "dcb1cc804b9adca583e4e65755895ba0a66ef82d29464cf89a78b88ddac6ca53" 120 | "603a831e0f2e466480cdc633ba37a0b1ae3c3e9a4e90183833bc4def3421a961" 121 | "f681100b27d783fefc3b62f44f84eb7fa0ce73ec183ebea5903df506eb314077" 122 | "b54bf2fa7c33a63a009f249958312c73ec5b368b1094e18e5953adb95ad2ec3a" 123 | "05626f77b0c8c197c7e4a31d9783c4ec6e351d9624aa28bc15e7f6d6a6ebd926" 124 | "c1284dd4c650d6d74cfaf0106b8ae42270cab6c58f78efc5b7c825b6a4580417" 125 | "4aee8551b53a43a883cb0b7f3255d6859d766b6c5e14bcb01bed572fcbef4328" 126 | default)) 127 | '(package-selected-packages 128 | '(0blayout aas ac-c-headers ac-clang ace-isearch ace-jump-mode 129 | all-the-icons-dired ample-regexps anaconda-mode 130 | auto-package-update avy call-graph cmake-font-lock 131 | cnfonts color-theme-modern company cov direx 132 | dracula-theme editorconfig faceup forge git-timemachine 133 | helm-ag helm-projectile helm-xref highlight-indentation 134 | magit nsis-mode nyan-mode org-bullets powershell 135 | protobuf-mode qml-mode switch-window which-key)) 136 | '(package-vc-selected-packages 137 | '((lsp-bridge :url "https://github.com/manateelazycat/lsp-bridge.git" 138 | :vc-backend Git))) 139 | '(realgud-safe-mode nil) 140 | '(safe-local-variable-values '((highlight-80+-columns . 100))) 141 | '(scheme-program-name "scheme")) 142 | (custom-set-faces 143 | ;; custom-set-faces was added by Custom. 144 | ;; If you edit it by hand, you could mess it up, so be careful. 145 | ;; Your init file should contain only one such instance. 146 | ;; If there is more than one, they won't work right. 147 | ) 148 | -------------------------------------------------------------------------------- /myelpa/cmake-format.el: -------------------------------------------------------------------------------- 1 | ;;; cmake-format.el --- Format CMake scripts using cmake-format -*- lexical-binding: t; -*- 2 | 3 | ;;; Commentary: 4 | 5 | ;; This file is an adaption of code from go-mode.el 6 | ;; see https://github.com/dominikh/go-mode.el 7 | ;; Copyright 2019 Simon Reiser 8 | ;; Copyright 2013 the go-mode Authors. All rights reserved. 9 | ;; Use of this source code is governed by a BSD-style 10 | ;; license that can be found in the LICENSE file. 11 | 12 | ;; Author: Simon Reiser, the go-mode Authors 13 | ;; Version: 0.1.0 14 | ;; Keywords: languages CMake cmake-format 15 | ;; Package-Requires: ((emacs "25.1")) 16 | ;; URL: https://github.com/simonfxr/cmake-format.el 17 | ;; 18 | ;; This file is not part of GNU Emacs. 19 | 20 | ;;; Code: 21 | (require 'cl) 22 | (defcustom cmake-format-command "cmake-format" 23 | "The 'cmake-format' command." 24 | :type 'string 25 | :group 'cmake-format) 26 | 27 | (defcustom cmake-format-args nil 28 | "Additional arguments to pass to cmake-format." 29 | :type '(repeat string) 30 | :group 'cmake-format) 31 | 32 | (defcustom cmake-format-show-errors 'buffer 33 | "Where to display cmake-format error output. 34 | It can either be displayed in its own buffer, in the echo area, or not at all. 35 | Please note that Emacs outputs to the echo area when writing 36 | files and will overwrite the formatter's echo output if used from inside 37 | a `before-save-hook'." 38 | :type '(choice 39 | (const :tag "Own buffer" buffer) 40 | (const :tag "Echo area" echo) 41 | (const :tag "None" nil)) 42 | :group 'cmake-format) 43 | 44 | (defun cmake-format--apply-rcs-patch (patch-buffer) 45 | "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." 46 | (let ((target-buffer (current-buffer)) 47 | ;; Relative offset between buffer line numbers and line numbers 48 | ;; in patch. 49 | ;; 50 | ;; Line numbers in the patch are based on the source file, so 51 | ;; we have to keep an offset when making changes to the 52 | ;; buffer. 53 | ;; 54 | ;; Appending lines decrements the offset (possibly making it 55 | ;; negative), deleting lines increments it. This order 56 | ;; simplifies the forward-line invocations. 57 | (line-offset 0) 58 | (column (current-column))) 59 | (save-excursion 60 | (with-current-buffer patch-buffer 61 | (goto-char (point-min)) 62 | (while (not (eobp)) 63 | (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") 64 | (error "Invalid rcs patch or internal error in cmake-format--apply-rcs-patch")) 65 | (forward-line) 66 | (let ((action (match-string 1)) 67 | (from (string-to-number (match-string 2))) 68 | (len (string-to-number (match-string 3)))) 69 | (cond 70 | ((equal action "a") 71 | (let ((start (point))) 72 | (forward-line len) 73 | (let ((text (buffer-substring start (point)))) 74 | (with-current-buffer target-buffer 75 | (decf line-offset len) 76 | (goto-char (point-min)) 77 | (forward-line (- from len line-offset)) 78 | (insert text))))) 79 | ((equal action "d") 80 | (with-current-buffer target-buffer 81 | (cmake-format--goto-line (- from line-offset)) 82 | (incf line-offset len) 83 | (cmake-format--delete-whole-line len))) 84 | (t 85 | (error "Invalid rcs patch or internal error in cmake-format--apply-rcs-patch"))))))) 86 | (move-to-column column))) 87 | 88 | ;;;###autoload 89 | (defun cmake-format-buffer () 90 | "Format the current buffer using cmake-format." 91 | (interactive) 92 | (let ((tmpfile (let ((temporary-file-directory (expand-file-name "."))) 93 | (make-nearby-temp-file "cmake-format" nil ".cmake.tmp"))) 94 | (patchbuf (get-buffer-create "*cmake-format patch*")) 95 | (err-buf (if cmake-format-show-errors (get-buffer-create "*cmake-format Errors*"))) 96 | (coding-system-for-read 'utf-8) 97 | (coding-system-for-write 'utf-8) 98 | (our-fmt-args cmake-format-args)) 99 | (unwind-protect 100 | (save-restriction 101 | (widen) 102 | (if err-buf 103 | (with-current-buffer err-buf 104 | (setq buffer-read-only nil) 105 | (erase-buffer))) 106 | (with-current-buffer patchbuf 107 | (erase-buffer)) 108 | 109 | (write-region nil nil tmpfile) 110 | 111 | (setq our-fmt-args 112 | (append our-fmt-args 113 | (list "-i" (file-local-name tmpfile)))) 114 | (message "Calling cmake-format: %s %s" cmake-format-command our-fmt-args) 115 | ;; We're using err-buf for the mixed stdout and stderr output. This 116 | ;; is not an issue because gofmt -w does not produce any stdout 117 | ;; output in case of success. 118 | (if (zerop (apply #'process-file cmake-format-command nil err-buf nil our-fmt-args)) 119 | (progn 120 | ;; There is no remote variant of ‘call-process-region’, but we 121 | ;; can invoke diff locally, and the results should be the same. 122 | (if (zerop (let ((local-copy (file-local-copy tmpfile))) 123 | (unwind-protect 124 | (call-process-region 125 | (point-min) (point-max) "diff" nil patchbuf 126 | nil "-n" "-" (or local-copy tmpfile)) 127 | (when local-copy (delete-file local-copy))))) 128 | (message "Buffer is already formatted") 129 | (cmake-format--apply-rcs-patch patchbuf) 130 | (message "Applied cmake-format")) 131 | (if err-buf (cmake-format--kill-error-buffer err-buf))) 132 | (message "Running cmake-format failed") 133 | (if err-buf (cmake-format--process-errors (buffer-file-name) tmpfile err-buf)))) 134 | 135 | (kill-buffer patchbuf) 136 | (delete-file tmpfile)))) 137 | 138 | (defun cmake-format--process-errors (filename tmpfile err-buf) 139 | "Write errors from running cmake-format TMPFILE into ERR-BUF. FILENAME specifies the original `buffer-name'." 140 | (with-current-buffer err-buf 141 | (if (eq cmake-format-show-errors 'echo) 142 | (progn 143 | (message "%s" (buffer-string)) 144 | (cmake-format--kill-error-buffer err-buf)) 145 | ;; Convert the gofmt stderr to something understood by the compilation mode. 146 | (goto-char (point-min)) 147 | (insert "cmake-format errors:\n") 148 | (let ((truefile tmpfile)) 149 | (while (search-forward-regexp 150 | (concat "^\\(" (regexp-quote (file-local-name truefile)) 151 | "\\):") 152 | nil t) 153 | (replace-match (file-name-nondirectory filename) t t nil 1))) 154 | (compilation-mode) 155 | (display-buffer err-buf)))) 156 | 157 | (defun cmake-format--kill-error-buffer (err-buf) 158 | "Hide Window showing ERR-BUF and kill the buffer." 159 | (let ((win (get-buffer-window err-buf))) 160 | (if win 161 | (quit-window t win) 162 | (kill-buffer err-buf)))) 163 | 164 | (defun cmake-format--goto-line (line) 165 | "Like (goto-line LINE) but zero based." 166 | (goto-char (point-min)) 167 | (forward-line (1- line))) 168 | 169 | (defun cmake-format--delete-whole-line (&optional arg) 170 | "Delete the current line without putting it in the `kill-ring'. 171 | Derived from function `kill-whole-line'. ARG is defined as for that 172 | function." 173 | (setq arg (or arg 1)) 174 | (if (and (> arg 0) 175 | (eobp) 176 | (save-excursion (forward-visible-line 0) (eobp))) 177 | (signal 'end-of-buffer nil)) 178 | (if (and (< arg 0) 179 | (bobp) 180 | (save-excursion (end-of-visible-line) (bobp))) 181 | (signal 'beginning-of-buffer nil)) 182 | (cond ((zerop arg) 183 | (delete-region (progn (forward-visible-line 0) (point)) 184 | (progn (end-of-visible-line) (point)))) 185 | ((< arg 0) 186 | (delete-region (progn (end-of-visible-line) (point)) 187 | (progn (forward-visible-line (1+ arg)) 188 | (unless (bobp) 189 | (backward-char)) 190 | (point)))) 191 | (t 192 | (delete-region (progn (forward-visible-line 0) (point)) 193 | (progn (forward-visible-line arg) (point)))))) 194 | 195 | (defun cmake-format--before-save () 196 | "Hook run from `before-save-hook'." 197 | (if cmake-format-mode 198 | (cmake-format-buffer))) 199 | 200 | ;;;###autoload 201 | (define-minor-mode cmake-format-mode 202 | "Automatically run cmake-format before saving." 203 | :lighter "cmake-format" 204 | (if cmake-format-mode 205 | (add-hook 'before-save-hook #'cmake-format--before-save))) 206 | 207 | (provide 'cmake-format) 208 | 209 | ;;; cmake-format.el ends here 210 | -------------------------------------------------------------------------------- /myelpa/dired-sort-menu.el: -------------------------------------------------------------------------------- 1 | ;;; dired-sort-menu.el --- provide menu/dialogue for dired sort options 2 | 3 | ;; Copyright (C) 2000, 2001 Francis J. Wright 4 | 5 | ;; Author: Francis J. Wright 6 | ;; Maintainer: Francis J. Wright 7 | ;; Time-stamp: <26 July 2001> 8 | ;; Version: 2001.07.26 9 | ;; Package-Requires: () 10 | ;; URL: http://centaur.maths.qmw.ac.uk/Emacs/ 11 | ;; URL: https://sites.google.com/site/fjwcentaur/emacs/files/dired-sort-menu.el 12 | ;; Keywords: dired, sort, menu, dialogue 13 | 14 | ;; $Id: dired-sort-menu.el,v 1.26 2001-07-26 21:22:48+01 fjw Exp $ 15 | 16 | ;; This file is not part of GNU Emacs. 17 | 18 | ;; This package is free software; you can redistribute it and/or modify 19 | ;; it under the terms of the GNU General Public License as published by 20 | ;; the Free Software Foundation; either version 2, or (at your option) 21 | ;; any later version. 22 | 23 | ;; This package is distributed in the hope that it will be useful, 24 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | ;; GNU General Public License for more details. 27 | 28 | ;; You should have received a copy of the GNU General Public License 29 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 30 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 31 | ;; Boston, MA 02111-1307, USA. 32 | 33 | ;;; Commentary: 34 | 35 | ;; This package, which is intended for use with GNU Emacs 20/21, 36 | ;; implements a menu and a dialogue that consist of a set of radio 37 | ;; buttons and toggles to control sort order in dired mode. The menu 38 | ;; is added as a submenu to the Immediate menu, and bound to S-mouse-2 39 | ;; as a popup menu. The dialogue can be accessed via the menu or the 40 | ;; dired key binding `C-d' or the command `dired-sort-dialogue', which 41 | ;; *must* be run in a dired buffer! The dialogue uses the Emacs 42 | ;; widget library, which does not require GUI support but uses it if 43 | ;; available. The dialogue can be used with a character-based 44 | ;; display; the main keys to use are and if a mouse is not 45 | ;; available or does not work. With a GUI the dialogue pops up its 46 | ;; own frame by default; otherwise it uses part of the dired window. 47 | ;; This is controlled by the customizable user option 48 | ;; `dired-sort-dialogue-own-frame'. 49 | 50 | ;; The menu and dialogue support the `ls' sort switches [tSXUucrR]. 51 | ;; These are all implemented by GNU `ls'; the subset [tSucr] are 52 | ;; implemented by the standard Emacs 20 `ls-lisp' emulation and all 53 | ;; are implemented by the Emacs 21 `ls-lisp' (see above URL). 54 | ;; Changing a main sort option turns off reverse sorting; toggling 55 | ;; reverse sorting does not change the main sort option. Setting any 56 | ;; prefix argument before selecting a sort order reverses it 57 | ;; (e.g. press `C-u' or `C-number' or `M-number' BEFORE accessing the 58 | ;; menu bar). The menu also supports two Emacs 21 `ls-lisp' switches: 59 | ;; `ls-lisp-ignore-case' ignores case in alphanumeric sorts and 60 | ;; `ls-lisp-dirs-first' lists all directories first. (These latter 61 | ;; two switches improve compatibility with Microsoft Windows 62 | ;; Explorer.) 63 | 64 | ;; The toggles for reverse sorting, `ls-lisp-ignore-case' and 65 | ;; `ls-lisp-dirs-first', are bound respectively to the keys "r", "c" 66 | ;; and "b" in the dired map. 67 | 68 | ;; A `Configuration' submenu allows a dired sort configuration to be 69 | ;; saved or restored, the current and saved configurations to be 70 | ;; swaped (bound to "T" for "toggle"), or the default to be restored. 71 | ;; The saved configuration is saved as a customization, which can also 72 | ;; be changed directly. This submenu also allows 73 | ;; `dired-sort-dialogue-own-frame' to be toggled. 74 | 75 | ;; If a `dired-sort-menu' option causes an error (but not if it is 76 | ;; just ignored) then it is automatically disabled via the 77 | ;; customization facility. This is useful because not all `ls' and 78 | ;; `ftp' programs support the same sort options. 79 | 80 | ;; The dialogue allows the setting of arbitrary combinations of dired 81 | ;; sort options together, without requiring multiple menu accesses. 82 | ;; Each dired buffer has its own dialogue. This dialogue can be left 83 | ;; displayed, in which case it is automatically updated to reflect any 84 | ;; changes to its dired buffer that are made by other means. For 85 | ;; tidiness, a dialogue buffer is automatically killed whenever it or 86 | ;; its dired buffer becomes invisible. 87 | 88 | ;; To do: 89 | ;; test `dired-sort-menu-invalid-options' facility; 90 | ;; per-host `dired-sort-menu-invalid-options-remote'; 91 | ;; cascade multiple dialogue frames? 92 | 93 | ;;; Installation: 94 | 95 | ;; Put this file somewhere where Emacs can find it (i.e. in one of the 96 | ;; directories in your `load-path' such as `site-lisp'), optionally 97 | ;; byte-compile it (recommended), and put this in your .emacs: 98 | ;; 99 | ;; (add-hook 'dired-load-hook 100 | ;; (lambda () (require 'dired-sort-menu))) 101 | 102 | 103 | ;;; History: 104 | 105 | ;; Code for alternative ways to find files split off into 106 | ;; `dired-explore'. 107 | 108 | ;;; Code: 109 | 110 | (require 'dired) 111 | (require 'easymenu) 112 | ;; Silence compiler: 113 | (eval-when-compile 114 | (defvar ls-lisp-ignore-case) ; not in current standard version 115 | (defvar ls-lisp-dirs-first) ; not in current standard version 116 | (require 'ange-ftp)) 117 | 118 | (defun dired-sort-menu-remote-p () 119 | "Return the host name for a remote ange-ftp directory or nil if local." 120 | ;; Use the actual host name later! 121 | ;; The default extended filename syntax is '/user@host:name', where the 122 | ;; 'user@' part may be omitted. This syntax can be customised to a certain 123 | ;; extent by changing ange-ftp-name-format. There are limitations. 124 | (and (string-match "\\`/\\([^@:/]+@\\)?\\([^:/]+\\):" default-directory) 125 | (match-string 2))) 126 | 127 | ;;;###autoload 128 | (defgroup dired-sort-menu nil 129 | "Menu and dialogue to control `dired' sort options." 130 | :group 'dired) 131 | 132 | (defcustom dired-sort-menu-saved-config nil 133 | "*Alist of saved `dired' sort options. Defaults to None (nil). 134 | Can be customized directly or by saving from Dired sort menu. If the 135 | AList option is chosen in a customization buffer then it defaults to 136 | the default options, which can be set via the Dired and Ls-Lisp 137 | customization buffers." 138 | :type `(choice 139 | (const :tag "None" nil) 140 | (list :tag "AList" 141 | :value 142 | ((dired-actual-switches 143 | . ,dired-listing-switches) 144 | (ls-lisp-ignore-case 145 | . ,(let ((v (get 'ls-lisp-ignore-case 'standard-value))) 146 | (and v (apply 'eval v)))) 147 | (ls-lisp-dirs-first 148 | . ,(let ((v (get 'ls-lisp-dirs-first 'standard-value))) 149 | (and v (apply 'eval v))))) 150 | (cons :tag "Any Ls" 151 | (const :tag "Switches" dired-actual-switches) 152 | string) 153 | (cons :tag "Ls-Lisp Only" 154 | (const :tag "Ignore Case" ls-lisp-ignore-case) 155 | boolean) 156 | (cons :tag "Ls-Lisp Only" 157 | (const :tag "Dirs First" ls-lisp-dirs-first) 158 | boolean))) 159 | :group 'dired-sort-menu) 160 | 161 | (defcustom dired-sort-menu-invalid-options nil 162 | "*List of sort option strings that are invalid for the local ls program. 163 | The corresponding menu/dialogue items are inactive/unavailable. 164 | This variable is automatically updated when a sort option causes an 165 | error. It can also be set or reset explicitly, and you should reset 166 | it (usually to nil) if you change the ls program used by `dired'. 167 | Ignored when using `ls-lisp'." 168 | :type '(repeat string) 169 | :group 'dired-sort-menu) 170 | 171 | (defcustom dired-sort-menu-invalid-options-remote nil 172 | ;; Add option for one such variable per remote host later or some 173 | ;; kind of alist. 174 | "*List of sort option strings that are invalid for the remote ls program. 175 | The corresponding menu/dialogue items are inactive/unavailable. 176 | This variable is automatically updated when a sort option causes an 177 | error. It can also be set or reset explicitly, and you should reset 178 | it if you change the remote host or ls program used by `dired'. 179 | [Note that `ls-lisp' is never used for remote directory listings.]" 180 | :type '(repeat string) 181 | :group 'dired-sort-menu) 182 | 183 | (defconst dired-sort-display-graphic-p ; window-system 184 | (if (fboundp 'display-graphic-p) 185 | (funcall (symbol-function 'display-graphic-p)) ; Emacs 21 186 | (memq window-system '(x w32))) ; Emacs 20 187 | "Non-nil if graphic display is possible.") 188 | 189 | (defcustom dired-sort-dialogue-own-frame dired-sort-display-graphic-p 190 | "*If non-nil then dialogues should pop up in their own frames (if possible)." 191 | :type 'boolean 192 | :group 'dired-sort-menu) 193 | 194 | (defsubst dired-sort-dialogue-own-frame-really () 195 | "Return non-nil if dialogues should and can pop up in their own frames." 196 | (and dired-sort-dialogue-own-frame dired-sort-display-graphic-p)) 197 | 198 | 199 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 200 | 201 | (defun ls-lisp-var-p (var) 202 | "Return non-nil if ls-lisp variable VAR should be used." 203 | (and (boundp var) 204 | (boundp 'ls-lisp-use-insert-directory-program) 205 | (not ls-lisp-use-insert-directory-program) 206 | (not (dired-sort-menu-remote-p)))) 207 | 208 | (defsubst dired-sort-menu-help (help-string r) 209 | "Return HELP-STRING, for reversed sort order if R is non-nil." 210 | (if r (concat help-string " [reversed]") help-string)) 211 | 212 | (defun dired-sort-menu-items (&optional r) 213 | "Return sort-menu item list; reverse sort with optional argument R = \"r\"." 214 | (list 215 | (vector 216 | "Name" 217 | `(dired-sort-menu-set-switches ,(concat "" r)) :style 'radio 218 | :selected (if r 219 | '(and (not (dired-sort-menu-switch-p "[tSXUuc]")) 220 | (dired-sort-menu-switch-p "r")) 221 | '(not (dired-sort-menu-switch-p "[tSXUuc]"))) 222 | :help (dired-sort-menu-help "Sort files by name" r) 223 | :active t) 224 | (vector 225 | "Time Modified" 226 | `(dired-sort-menu-set-switches ,(concat "t" r)) :style 'radio 227 | :selected `(dired-sort-menu-switch-p "t" ,r) 228 | :help (dired-sort-menu-help "Sort files by time last modified" r) 229 | :active t) 230 | ;; extended sort options ... 231 | (vector 232 | "Size" 233 | `(dired-sort-menu-set-switches ,(concat "S" r)) :style 'radio 234 | :selected `(dired-sort-menu-switch-p "S" ,r) 235 | :help (dired-sort-menu-help "Sort files by size" r) 236 | :active '(dired-sort-menu-active-p "S")) 237 | (vector 238 | "Extension" 239 | `(dired-sort-menu-set-switches ,(concat "X" r)) :style 'radio 240 | :selected `(dired-sort-menu-switch-p "X" ,r) 241 | :help (dired-sort-menu-help "Sort files by extension" r) 242 | :active '(dired-sort-menu-active-p "X")) 243 | (vector 244 | "Unsorted" 245 | `(dired-sort-menu-set-switches ,(concat "U" r)) :style 'radio 246 | :selected `(dired-sort-menu-switch-p "U" ,r) 247 | :help (dired-sort-menu-help "Sort files by physical order" r) 248 | :active '(dired-sort-menu-active-p "U")) 249 | (vector 250 | "Time Created" ; Windows 251 | `(dired-sort-menu-set-switches ,(concat "c" r)) :style 'radio 252 | :selected `(dired-sort-menu-switch-p "c" ,r) 253 | :help (dired-sort-menu-help "Sort files by time created" r) 254 | :active '(dired-sort-menu-active-p "c") 255 | :visible (and (eq system-type 'windows-nt) 256 | (not (dired-sort-menu-remote-p)))) 257 | (vector 258 | "Time Changed" ; not Windows 259 | `(dired-sort-menu-set-switches ,(concat "c" r)) :style 'radio 260 | :selected `(dired-sort-menu-switch-p "c" ,r) 261 | :help (dired-sort-menu-help "Sort files by time last changed" r) 262 | :active '(dired-sort-menu-active-p "c") 263 | :visible (or (not (eq system-type 'windows-nt)) 264 | (dired-sort-menu-remote-p))) 265 | (vector 266 | "Time Accessed" ; not useful under Windows 267 | `(dired-sort-menu-set-switches ,(concat "u" r)) :style 'radio 268 | :selected `(dired-sort-menu-switch-p "u" ,r) 269 | :help (dired-sort-menu-help "Sort files by time last accessed" r) 270 | :active '(dired-sort-menu-active-p "u") 271 | :visible (or (not (eq system-type 'windows-nt)) 272 | (dired-sort-menu-remote-p))) 273 | )) 274 | 275 | ;; Build a menu assigned to `dired-sort-menu' as both a variable and a 276 | ;; function, the former to use as a menu-bar sub-menu and the latter 277 | ;; as a popup menu: 278 | (easy-menu-define ; (SYMBOL MAPS DOC MENU) 279 | dired-sort-menu 280 | nil 281 | "\"Sort By\" menu for dired mode." 282 | `("Sort By" 283 | ,@(dired-sort-menu-items) 284 | "--" 285 | ("Reversed Sort By" 286 | ,@(dired-sort-menu-items "r")) 287 | "--" 288 | ["Reverse" dired-sort-menu-toggle-reverse :style toggle 289 | :selected (dired-sort-menu-switch-p "r") 290 | :help "Reverse current sort order" 291 | :active (dired-sort-menu-active-p "r")] 292 | ["Recursive" dired-sort-menu-toggle-recursive :style toggle 293 | :selected (dired-sort-menu-switch-p "R") 294 | :help "Recursively list all subdirectories" 295 | :active (dired-sort-menu-active-p "R")] 296 | ["Ignore Case" dired-sort-menu-toggle-ignore-case :style toggle 297 | :selected ls-lisp-ignore-case :active t 298 | :help "Ignore case in alphanumeric sorting" 299 | ;; supported only by (Emacs 21) ls-lisp library and local dired: 300 | :visible (ls-lisp-var-p 'ls-lisp-ignore-case)] 301 | ["Dirs First" dired-sort-menu-toggle-dirs-first :style toggle 302 | :selected ls-lisp-dirs-first :active t 303 | :help "List subdirectories first [last if reversed]" 304 | ;; supported only by (Emacs 21) ls-lisp library and local dired: 305 | :visible (ls-lisp-var-p 'ls-lisp-dirs-first)] 306 | "--" 307 | ("Configuration" 308 | ["Save Current" dired-sort-menu-save-config 309 | :suffix (dired-sort-menu-current-suffix) 310 | :help "Save current sort configuration as a customization" 311 | :active t] 312 | ["Restore Saved" dired-sort-menu-restore-config 313 | :suffix (dired-sort-menu-restore-suffix) 314 | :help "Restore sort configuration saved as a customization" 315 | :active dired-sort-menu-saved-config] 316 | ["Swap Current/Saved" dired-sort-menu-swap-config 317 | :help "Exchange current and saved sort configurations" 318 | :active dired-sort-menu-saved-config] 319 | "---" 320 | ["Restore Default" dired-sort-menu-restore-default 321 | :suffix (dired-sort-menu-default-suffix) 322 | :help "Restore default sort configuration from customization" 323 | :active t] 324 | ["Customize Saved" (customize-option 'dired-sort-menu-saved-config) 325 | :help "Customize saved sort configuration" 326 | :active t] 327 | "---" 328 | ["Dialogue Own Frame" (setq dired-sort-dialogue-own-frame 329 | (not dired-sort-dialogue-own-frame)) 330 | :style toggle 331 | :selected dired-sort-dialogue-own-frame 332 | :help "Sort dialogues use own frame if checked, else dired frame" 333 | :active t]) 334 | ["Dialogue" dired-sort-dialogue 335 | :help "Open a dialogue box to control this dired's sort options" 336 | :active t] 337 | )) 338 | 339 | ;; Menu bar "Immediate" menu sub-menu: 340 | (easy-menu-add-item ; (map path item &optional before) 341 | dired-mode-map '("menu-bar" "immediate") 342 | dired-sort-menu 343 | 'revert-buffer) 344 | 345 | ;; Popup menu on "Shift Mouse 2": 346 | (define-key ; (keymap key def) 347 | dired-mode-map [S-down-mouse-2] 'dired-sort-menu-popup) 348 | 349 | ;;;###autoload 350 | (defun dired-sort-menu-popup (event) 351 | "Pop up and run \"Sort By\" menu for dired mode *in EVENT window*." 352 | (interactive "@e") 353 | (let ((menu-item (x-popup-menu event dired-sort-menu))) 354 | ;; menu-item is null if pop-up menu is cancelled 355 | (and menu-item 356 | (command-execute 357 | (lookup-key dired-sort-menu (apply 'vector menu-item)))))) 358 | 359 | ;; Add a few key bindings, as suggested by Ed Park . 360 | ;; Mnemonic *r*everse: 361 | (define-key dired-mode-map "r" 'dired-sort-menu-toggle-reverse) 362 | ;; Mnemonic *c*asefold: 363 | (define-key dired-mode-map "c" 'dired-sort-menu-toggle-ignore-case) 364 | ;; Mnemonic Files at *b*ottom: 365 | (define-key dired-mode-map "b" 'dired-sort-menu-toggle-dirs-first) 366 | ;; Mnemonic *T*oggle Current/Saved: 367 | (define-key dired-mode-map "T" 'dired-sort-menu-swap-config) 368 | 369 | 370 | 371 | (defun dired-sort-menu-switch-p (switch &optional r) 372 | "Return true if regexp SWITCH matches (case sensitive) ls switch string. 373 | With optional regexp R this must also match (case sensitive)." 374 | (let (case-fold-search) 375 | (setq switch (string-match switch dired-actual-switches)) 376 | (if r 377 | (and switch (string-match r dired-actual-switches)) 378 | switch 379 | ))) 380 | 381 | (defun dired-sort-menu-active-p (switch) 382 | "Return true if argument SWITCH not known to be invalid for the current ls." 383 | (if (dired-sort-menu-remote-p) 384 | (not (member switch dired-sort-menu-invalid-options-remote)) 385 | (or (and (boundp 'ls-lisp-use-insert-directory-program) 386 | (not ls-lisp-use-insert-directory-program)) ; ls-lisp 387 | (not (member switch dired-sort-menu-invalid-options))))) 388 | 389 | ;;;###autoload 390 | (defun dired-sort-menu-toggle-ignore-case () 391 | "Toggle ls-lisp switch `ls-lisp-ignore-case' and update buffer." 392 | (interactive) 393 | (setq ls-lisp-ignore-case (not ls-lisp-ignore-case)) 394 | (revert-buffer)) 395 | 396 | ;;;###autoload 397 | (defun dired-sort-menu-toggle-dirs-first () 398 | "Toggle ls-lisp switch `ls-lisp-dirs-first' and update buffer." 399 | (interactive) 400 | (setq ls-lisp-dirs-first (not ls-lisp-dirs-first)) 401 | (revert-buffer)) 402 | 403 | (defun dired-sort-menu-set-switches (switch &optional reverse) 404 | "Set one ls sort switch SWITCH and update buffer. 405 | Clears *all* other sort switches. 406 | Reverse sort order if optional argument REVERSE is non-nil or if 407 | called with any prefix argument \(e.g. \\[universal-argument] or 408 | C-number or M-number BEFORE accessing menu bar)." 409 | ;; [FJW: I would like to use modifier keys to reverse the sort, but 410 | ;; that is not possible!] 411 | ;; First delete ALL ls sort switches: 412 | (let (case-fold-search) 413 | (while (string-match "[rtSXUuc]" dired-actual-switches) 414 | (setq dired-actual-switches 415 | (replace-match "" t t dired-actual-switches)))) 416 | (setq dired-actual-switches 417 | (concat dired-actual-switches switch)) 418 | (if (or reverse current-prefix-arg) 419 | (setq dired-actual-switches 420 | (concat dired-actual-switches "r"))) 421 | (dired-sort-menu-revert-buffer switch)) 422 | 423 | (defun dired-sort-menu-revert-buffer (switch) 424 | "Revert buffer carefully, handling invalid sort options gracefully. 425 | If the current sort option SWITCH causes an error then update 426 | `dired-sort-menu-invalid-options' or 427 | `dired-sort-menu-invalid-options-remote' as appropriate." 428 | (condition-case nil 429 | (revert-buffer) 430 | ('file-error 431 | (if (dired-sort-menu-remote-p) 432 | (customize-save-variable 433 | 'dired-sort-menu-invalid-options-remote 434 | (cons switch dired-sort-menu-invalid-options-remote)) 435 | (customize-save-variable 436 | 'dired-sort-menu-invalid-options 437 | (cons switch dired-sort-menu-invalid-options))) 438 | ;; Remove all sort switches: 439 | (dired-sort-menu-set-switches "") 440 | (error 441 | "Invalid sort menu option disabled and directory sorted by name")))) 442 | 443 | ;;;###autoload 444 | (defun dired-sort-menu-toggle-reverse () 445 | "Toggle ls -r switch and update buffer. 446 | Does not affect other sort switches." 447 | (interactive) 448 | (let (case-fold-search) 449 | (setq dired-actual-switches 450 | (if (string-match "r" dired-actual-switches) 451 | (replace-match "" t t dired-actual-switches) 452 | (concat dired-actual-switches "r"))) 453 | (dired-sort-menu-revert-buffer "r"))) 454 | 455 | (defsubst dired-sort-menu-R-check (switches) 456 | "Additional processing of -R in ls option string SWITCHES. 457 | Calls `dired-sort-R-check' only if defined (by `dired-fix' or Emacs 21). 458 | Saves `dired-subdir-alist' when R is set and restores saved value 459 | minus any directories explicitly deleted when R is cleared. 460 | To be called first in body of `dired-sort-other', etc." 461 | (and (fboundp 'dired-sort-R-check) 462 | (funcall (symbol-function 'dired-sort-R-check) switches))) 463 | 464 | ;;;###autoload 465 | (defun dired-sort-menu-toggle-recursive () 466 | "Toggle ls -R switch and update buffer. 467 | Does not affect other sort switches." 468 | (interactive) 469 | (let* (case-fold-search 470 | (switches 471 | (if (string-match "R" dired-actual-switches) 472 | (replace-match "" t t dired-actual-switches) 473 | (concat dired-actual-switches "R")))) 474 | (dired-sort-menu-R-check switches) 475 | (setq dired-actual-switches switches) 476 | (dired-sort-menu-revert-buffer "R"))) 477 | 478 | 479 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 480 | 481 | ;; Code to save and restore dired sort configurations. This has a lot 482 | ;; of possibilities, e.g. saving multiple configurations, which I 483 | ;; could implement if there were any demand. 484 | 485 | (defsubst dired-sort-menu-current-suffix-flag (flag label) 486 | "Construct \"Configuration/Save Current\" menu item suffix for FLAG with LABEL." 487 | (and (boundp flag) 488 | (concat "; [" (if (eval flag) "X" " ") "] " label))) 489 | 490 | (defun dired-sort-menu-current-suffix () 491 | "Construct \"Configuration/Save Current\" menu item suffix." 492 | (concat 493 | "(ls " dired-actual-switches 494 | (dired-sort-menu-current-suffix-flag 495 | 'ls-lisp-ignore-case "Ignore Case") 496 | (dired-sort-menu-current-suffix-flag 497 | 'ls-lisp-dirs-first "Dirs First") 498 | ")" 499 | )) 500 | 501 | (defsubst dired-sort-menu-restore-suffix-flag (flag label) 502 | "Construct \"Configuration/Restore Saved\" menu item suffix for FLAG with LABEL." 503 | (and (setq flag (assq flag dired-sort-menu-saved-config)) 504 | (concat "; [" (if (cdr flag) "X" " ") "] " label))) 505 | 506 | (defun dired-sort-menu-restore-suffix () 507 | "Construct \"Configuration/Restore Saved\" menu item suffix." 508 | (and dired-sort-menu-saved-config 509 | (concat 510 | "(ls " 511 | (cdr (assq 'dired-actual-switches 512 | dired-sort-menu-saved-config)) 513 | (dired-sort-menu-restore-suffix-flag 514 | 'ls-lisp-ignore-case "Ignore Case") 515 | (dired-sort-menu-restore-suffix-flag 516 | 'ls-lisp-dirs-first "Dirs First") 517 | ")" 518 | ))) 519 | 520 | (defsubst dired-sort-menu-default-suffix-flag (flag label) 521 | "Construct \"Configuration/Restore Default\" menu item suffix for FLAG with LABEL." 522 | (and (boundp flag) 523 | (concat "; [" (if (apply 'eval (get flag 'standard-value)) "X" " ") "] " label))) 524 | 525 | (defun dired-sort-menu-default-suffix () 526 | "Construct \"Configuration/Restore Default\" menu item suffix." 527 | (concat 528 | "(ls " dired-listing-switches 529 | (dired-sort-menu-default-suffix-flag 530 | 'ls-lisp-ignore-case "Ignore Case") 531 | (dired-sort-menu-default-suffix-flag 532 | 'ls-lisp-dirs-first "Dirs First") 533 | ")" 534 | )) 535 | 536 | (defun dired-sort-menu-config-alist () 537 | "Return `dired' sort configuration as alist." 538 | (cons 539 | (cons 'dired-actual-switches dired-actual-switches) 540 | (and (boundp 'ls-lisp-use-insert-directory-program) 541 | (not ls-lisp-use-insert-directory-program) 542 | (append 543 | (and (boundp 'ls-lisp-ignore-case) 544 | (list (cons 'ls-lisp-ignore-case ls-lisp-ignore-case))) 545 | (and (boundp 'ls-lisp-dirs-first) 546 | (list (cons 'ls-lisp-dirs-first ls-lisp-dirs-first))))))) 547 | 548 | (defun dired-sort-menu-save-config () 549 | "Save current `dired' sort configuration." 550 | (interactive) 551 | (customize-save-variable 'dired-sort-menu-saved-config 552 | (dired-sort-menu-config-alist))) 553 | 554 | (defun dired-sort-menu-restore-config (&optional swap) 555 | "Restore saved `dired' sort configuration. 556 | If optional (interactive prefix) argument SWAP is non-nil then swap 557 | saved and current configurations." 558 | (interactive "P") 559 | (when dired-sort-menu-saved-config 560 | (setq swap (if swap 561 | (dired-sort-menu-config-alist) 562 | dired-sort-menu-saved-config)) 563 | (while dired-sort-menu-saved-config 564 | (set (caar dired-sort-menu-saved-config) 565 | (cdar dired-sort-menu-saved-config)) 566 | (setq dired-sort-menu-saved-config 567 | (cdr dired-sort-menu-saved-config))) 568 | (revert-buffer) 569 | (setq dired-sort-menu-saved-config swap))) 570 | 571 | ;;;###autoload 572 | (defun dired-sort-menu-swap-config () 573 | "Swap saved and current `dired' sort configuration." 574 | (interactive) 575 | (dired-sort-menu-restore-config t)) 576 | 577 | (defun dired-sort-menu-restore-default () 578 | "Restore default `dired' sort configuration." 579 | (interactive) 580 | (setq dired-actual-switches dired-listing-switches) 581 | (if (boundp 'ls-lisp-ignore-case) 582 | (setq ls-lisp-ignore-case 583 | (apply 'eval (get 'ls-lisp-ignore-case 'standard-value)))) 584 | (if (boundp 'ls-lisp-dirs-first) 585 | (setq ls-lisp-dirs-first 586 | (apply 'eval (get 'ls-lisp-dirs-first 'standard-value)))) 587 | (revert-buffer)) 588 | 589 | 590 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 591 | 592 | ;; Code to provide a customize-style dialogue-box that allows all sort 593 | ;; options to be set at once. Care must be taken to access 594 | ;; `dired-actual-switches' only when the dired buffer is current, 595 | ;; because this variable is buffer-local. There is currently no good 596 | ;; way to show that a widget is inactive, so inactive widgets are 597 | ;; simply not displayed. 598 | 599 | ;; Key binding, mnemonic *D*ialogue (d, D and M-d are used): 600 | (define-key dired-mode-map "\C-d" 'dired-sort-dialogue) 601 | 602 | (require 'wid-edit) 603 | 604 | ;; Buffer-local variables set in dired-sort-dialogue: 605 | (make-variable-buffer-local 606 | (defvar dired-sort-dialogue-dired-buffer)) 607 | (make-variable-buffer-local 608 | (defvar dired-sort-dialogue-radio-widget)) 609 | (make-variable-buffer-local 610 | (defvar dired-sort-dialogue-reverse-widget nil)) 611 | (make-variable-buffer-local 612 | (defvar dired-sort-dialogue-recursive-widget nil)) 613 | (make-variable-buffer-local 614 | (defvar dired-sort-dialogue-ignore-case-widget nil)) 615 | (make-variable-buffer-local 616 | (defvar dired-sort-dialogue-dirs-first-widget nil)) 617 | 618 | (defun dired-sort-dialogue-choice () 619 | "Return string to set Dired sort dialogue radio button choice." 620 | (let (case-fold-search 621 | (start (string-match "[tSXUuc]" dired-actual-switches))) 622 | (if start 623 | (substring dired-actual-switches start (1+ start)) 624 | ""))) 625 | 626 | (defsubst dired-sort-dialogue-buffer-p (buffer-name) 627 | "Return non-nil if BUFFER-NAME corresponds to a dialogue buffer." 628 | (string-match "\\`\\*<\\(.*\\)> Dired Sort Options\\*\\'" buffer-name)) 629 | 630 | (defsubst dired-sort-dialogue-buffer-name (&optional buffer) 631 | "Return name of dialogue buffer corresponding to dired buffer BUFFER. 632 | If BUFFER is nil then current buffer is used." 633 | (concat "*<" (buffer-name buffer) "> Dired Sort Options*")) 634 | 635 | (defconst dired-sort-dialogue-width 23 636 | "Dired sort dialogue width in characters.") 637 | 638 | ;;;###autoload 639 | (defun dired-sort-dialogue () 640 | "A static dialogue version of the Dired sort menu. 641 | This command *must* be run in the Dired buffer!" 642 | (interactive) 643 | (or (eq major-mode 'dired-mode) 644 | (error "This command may only be run in a Dired buffer")) 645 | (let 646 | ;; Must set these variables while still in the dired buffer! 647 | ((radio (dired-sort-dialogue-choice)) 648 | (reverse (dired-sort-menu-switch-p "r")) 649 | (recursive (dired-sort-menu-switch-p "R")) 650 | (dired-buffer (current-buffer)) 651 | ;; Suspend automatic mechanisms: 652 | window-configuration-change-hook 653 | kill-buffer-hook) 654 | 655 | ;; Check whether a dialogue buffer for this dired buffer is 656 | ;; already visible, and if so re-use its window: 657 | (let ((bufname (dired-sort-dialogue-buffer-name)) 658 | (bufs (buffer-list)) buf 659 | (title (concat "<" (buffer-name dired-buffer) ">"))) 660 | (while (and bufs (not (string= bufname 661 | (buffer-name (setq buf (car bufs)))))) 662 | (setq bufs (cdr bufs))) 663 | (if bufs 664 | (progn 665 | (if (dired-sort-dialogue-own-frame-really) 666 | (progn 667 | (select-frame (window-frame (get-buffer-window buf t))) 668 | (raise-frame)) 669 | (select-window (get-buffer-window buf t))) 670 | (set-window-dedicated-p (selected-window) nil) 671 | (kill-buffer buf)) 672 | (if (dired-sort-dialogue-own-frame-really) 673 | ;; If room then put dialogue immediately to the right of 674 | ;; the dired frame, else at right edge of screen. 675 | (let* ((alist (frame-parameters)) 676 | (top (cdr (assq 'top alist))) ; pixels 677 | (left (cdr (assq 'left alist))) ; pixels 678 | ) 679 | ;; Allow form INTEGER or (+ INTEGER): 680 | (or (atom left) (setq left (cadr left))) 681 | ;; Set left of dialogue frame to avoid falling off right 682 | ;; of display: 683 | (setq left (+ left (frame-pixel-width))) 684 | (setq left (if (> (+ left (* dired-sort-dialogue-width 685 | (frame-char-width))) 686 | (x-display-pixel-width)) 687 | -10 688 | ;; (+ left (* 2 (cdr (assq 'border-width alist)))))) 689 | (+ left 10))) 690 | (select-frame (make-frame 691 | `((title . ,title) 692 | (top . ,top) 693 | (left . ,left) 694 | (width . ,dired-sort-dialogue-width) 695 | (height . 22) 696 | (minibuffer . nil) 697 | (vertical-scroll-bars . nil) 698 | (horizontal-scroll-bars . nil) 699 | (unsplittable . nil) 700 | (menu-bar-lines . 0) 701 | )))) 702 | (split-window ; WINDOW SIZE HORIZONTAL 703 | nil (- (window-width) dired-sort-dialogue-width) t) 704 | (select-window (next-window)))) 705 | (switch-to-buffer bufname) 706 | (set-window-dedicated-p (selected-window) t) ; can crash Emacs! 707 | (kill-all-local-variables) 708 | ; (or buffer-display-table 709 | ; (setq buffer-display-table 710 | ; (or standard-display-table (make-display-table)))) 711 | ; (set-display-table-slot buffer-display-table 0 ?_) 712 | (setq truncate-lines t 713 | mode-line-format title)) 714 | 715 | (let ((inhibit-read-only t)) 716 | (erase-buffer)) 717 | ;; Must set this only once in the dialogue buffer! 718 | (setq dired-sort-dialogue-dired-buffer dired-buffer) 719 | 720 | (let ((start (point))) 721 | (widget-insert "Dired Sort Options") 722 | (put-text-property start (point) 'face 'bold)) 723 | (widget-insert " for\n<" 724 | (buffer-name dired-buffer) 725 | ">\n\n(Use any mouse button)\n\n ") 726 | (setq dired-sort-dialogue-radio-widget 727 | (eval `(widget-create 728 | 'radio-button-choice 729 | :indent 1 730 | :value radio 731 | '(item :tag "Name" "") 732 | '(item :tag "Time Modified" "t") 733 | ,@(if (dired-sort-menu-active-p "S") 734 | '('(item :tag "Size" "S"))) 735 | ,@(if (dired-sort-menu-active-p "X") 736 | '('(item :tag "Extension" "X"))) 737 | ,@(if (dired-sort-menu-active-p "U") 738 | '('(item :tag "Unsorted" "U"))) 739 | ,@(if (dired-sort-menu-active-p "c") 740 | `('(item :tag 741 | ,(if (or (not (eq system-type 'windows-nt)) 742 | (dired-sort-menu-remote-p)) 743 | "Time Changed" 744 | "Time Created") "c"))) 745 | ,@(if (and (dired-sort-menu-active-p "u") 746 | (or (not (eq system-type 'windows-nt)) 747 | (dired-sort-menu-remote-p))) 748 | '('(item :tag "Time Accessed" "u"))) 749 | ))) 750 | (widget-insert " _____________________\n\n ") 751 | (when (dired-sort-menu-active-p "r") 752 | (setq dired-sort-dialogue-reverse-widget 753 | (widget-create 'checkbox 754 | :help-echo "Reverse the sort order" 755 | reverse)) 756 | (widget-insert " Reverse\n ")) 757 | (when (dired-sort-menu-active-p "R") 758 | (setq dired-sort-dialogue-recursive-widget 759 | (widget-create 'checkbox 760 | :help-echo "Recursively list all subdirectories" 761 | recursive)) 762 | (widget-insert " Recursive\n ")) 763 | (when (ls-lisp-var-p 'ls-lisp-ignore-case) 764 | (setq dired-sort-dialogue-ignore-case-widget 765 | (widget-create 'checkbox 766 | :help-echo "Ignore case when sorting" 767 | ls-lisp-ignore-case)) 768 | (widget-insert " Ignore Case\n ")) 769 | (when (ls-lisp-var-p 'ls-lisp-dirs-first) 770 | (setq dired-sort-dialogue-dirs-first-widget 771 | (widget-create 'checkbox 772 | :help-echo "Sort directories first" 773 | ls-lisp-dirs-first)) 774 | (widget-insert " Dirs First\n ")) 775 | (widget-insert "_____________________\n\n ") 776 | (widget-create 'push-button 777 | :notify 'dired-sort-dialogue-OK 778 | :help-echo "Apply the settings and close the window" 779 | "OK") 780 | (widget-insert " ") 781 | (widget-create 'push-button 782 | :notify 'dired-sort-dialogue-close 783 | :help-echo "Close the window and ignore the settings" 784 | "Cancel") 785 | (widget-insert " ") 786 | (widget-create 'push-button 787 | :notify 'dired-sort-dialogue-apply 788 | :help-echo "Apply the settings without closing the window" 789 | "Apply") 790 | (widget-setup) 791 | (goto-char (point-min)) 792 | ; (use-local-map widget-keymap) 793 | ; (let ((map (make-sparse-keymap))) 794 | ; (suppress-keymap map) 795 | ; (set-keymap-parent map widget-keymap) 796 | ; (define-key map [down-mouse-1] 'widget-button-click) 797 | ; (define-key map [down-mouse-3] 'widget-button-click) 798 | ; (use-local-map map)) 799 | (let ((map widget-keymap)) 800 | ;; (define-key map [t] 'undefined) 801 | ;; (define-key map [tab] 'widget-forward) 802 | ;; (define-key map [return] 'widget-button-press) 803 | (define-key map [down-mouse-1] 'widget-button-click) 804 | (define-key map [down-mouse-3] 'widget-button-click) 805 | ;; (define-key map [escape] (lambda () (interactive) 806 | ;; (dired-sort-dialogue-close))) 807 | ;; (define-key map "\C-h" 'describe-bindings) 808 | (use-local-map map))) 809 | ;; Set up these hooks here to avoid any possibility of causing 810 | ;; trouble if the dialogue facility is not used: 811 | (add-hook 'kill-buffer-hook 812 | 'dired-sort-dialogue-auto-kill-1) 813 | (add-hook 'window-configuration-change-hook 814 | 'dired-sort-dialogue-auto-kill-2)) 815 | 816 | (defun dired-sort-dialogue-apply (&rest ignore) 817 | "Apply the dired sort dialogue settings (without closing it)." 818 | (let ((radio-widget dired-sort-dialogue-radio-widget) 819 | (reverse-widget dired-sort-dialogue-reverse-widget) 820 | (recursive-widget dired-sort-dialogue-recursive-widget) 821 | (ignore-case-widget dired-sort-dialogue-ignore-case-widget) 822 | (dirs-first-widget dired-sort-dialogue-dirs-first-widget) 823 | window-configuration-change-hook) 824 | (with-current-buffer dired-sort-dialogue-dired-buffer 825 | (if dirs-first-widget 826 | (setq ls-lisp-dirs-first 827 | (widget-value dirs-first-widget))) 828 | (if ignore-case-widget 829 | (setq ls-lisp-ignore-case 830 | (widget-value ignore-case-widget))) 831 | (if recursive-widget 832 | (let* (case-fold-search 833 | (switches 834 | (if (widget-value recursive-widget) 835 | (if (not (string-match "R" dired-actual-switches)) 836 | (concat dired-actual-switches "R") 837 | dired-actual-switches) 838 | (if (string-match "R" dired-actual-switches) 839 | (replace-match "" t t dired-actual-switches) 840 | dired-actual-switches)))) 841 | (dired-sort-menu-R-check switches) 842 | (setq dired-actual-switches switches))) 843 | (dired-sort-menu-set-switches 844 | (widget-value radio-widget) 845 | (and reverse-widget (widget-value reverse-widget)))))) 846 | 847 | (defun dired-sort-dialogue-close (&rest ignore) 848 | "Close the dired sort dialogue (ignoring the settings)." 849 | (let ((dired-buffer dired-sort-dialogue-dired-buffer) 850 | window-configuration-change-hook 851 | kill-buffer-hook) 852 | (set-window-dedicated-p (selected-window) nil) 853 | (kill-buffer (current-buffer)) 854 | (if (dired-sort-dialogue-own-frame-really) 855 | (delete-frame) 856 | (or (one-window-p t) (delete-window))) 857 | (select-window (get-buffer-window dired-buffer)))) 858 | 859 | (defun dired-sort-dialogue-OK (&rest ignore) 860 | "Apply the dired sort dialogue settings and close it." 861 | (dired-sort-dialogue-apply) 862 | (dired-sort-dialogue-close)) 863 | 864 | 865 | ;; Code to keep dialogue buffers tidy. 866 | 867 | (defadvice show-paren-function (around show-paren-function-advice activate) 868 | "Do not show matching parens in a dired sort dialogue buffer." 869 | (or (dired-sort-dialogue-buffer-p (buffer-name)) ad-do-it)) 870 | 871 | (defadvice dired-revert (after dired-revert-advice activate) 872 | "Redisplay and update any related dired sort dialogue buffer." 873 | (if (and (eq major-mode 'dired-mode) 874 | (get-buffer (dired-sort-dialogue-buffer-name))) 875 | (if (dired-sort-dialogue-own-frame-really) 876 | (let ((frame (selected-frame))) 877 | (dired-sort-dialogue) 878 | (raise-frame frame)) 879 | (save-selected-window 880 | (dired-sort-dialogue))))) 881 | 882 | (defun dired-sort-dialogue-auto-kill-1 () 883 | "If dialogue buffer is explicitly killed delete its window/frame. 884 | Also, kill any dialogue buffer related to a killed dired mode buffer. 885 | This function is hung on `kill-buffer-hook'." 886 | (if (dired-sort-dialogue-buffer-p (buffer-name)) 887 | (if (dired-sort-dialogue-own-frame-really) 888 | (delete-frame) 889 | (delete-window)) 890 | (let (buf kill-buffer-hook window-configuration-change-hook) 891 | (when (and (eq major-mode 'dired-mode) 892 | (setq buf 893 | (get-buffer 894 | (dired-sort-dialogue-buffer-name)))) 895 | (or (one-window-p t) (delete-windows-on buf)) 896 | (kill-buffer buf))))) 897 | 898 | (defun dired-sort-dialogue-auto-kill-2 () 899 | "Kill any dialogue buffer that has ceased to be visible. 900 | But do nothing if it was explicitly killed. Also, kill any dialogue 901 | buffer whose dired buffer has ceased to be visible. 902 | This function is hung on `window-configuration-change-hook'." 903 | (or (eq this-command 'kill-buffer) ; both are needed! 904 | (eq last-command 'kill-buffer) ; both are needed! 905 | (let ((bufs (buffer-list)) buf bufname 906 | kill-buffer-hook window-configuration-change-hook) 907 | (while bufs 908 | (setq buf (car bufs) 909 | bufs (cdr bufs) 910 | bufname (buffer-name buf)) 911 | (if (dired-sort-dialogue-buffer-p bufname) 912 | (cond ((null (get-buffer-window buf 'visible)) 913 | ;; dialogue hidden... 914 | (kill-buffer buf)) 915 | ((null (get-buffer-window 916 | (get-buffer (match-string 1 bufname)) 'visible)) 917 | ;; dired hidden... 918 | (or (one-window-p t) 919 | (eq this-command 'mouse-delete-other-windows) 920 | (eq this-command 'delete-other-windows) 921 | (delete-windows-on buf)) 922 | (kill-buffer buf)))))))) 923 | 924 | (defadvice handle-delete-frame 925 | (before handle-delete-frame-advice activate) 926 | "Kill dialogue buffer before killing its frame." 927 | (let* ((frame (posn-window (event-start event))) 928 | (buf (car (buffer-list frame)))) 929 | (when (dired-sort-dialogue-buffer-p (buffer-name buf)) 930 | (set-window-dedicated-p (selected-window) nil) 931 | (kill-buffer buf)))) 932 | 933 | (provide 'dired-sort-menu) 934 | 935 | ;;; dired-sort-menu.el ends here 936 | -------------------------------------------------------------------------------- /myelpa/helm-ag.el: -------------------------------------------------------------------------------- 1 | ;;; helm-ag.el --- The silver searcher with helm interface -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2017 Syohei YOSHIDA 4 | ;; Copyright (C) 2020-2024 Shen, Jen-Chieh 5 | 6 | ;; Author: Syohei YOSHIDA 7 | ;; Maintainer: Jen-Chieh Shen 8 | ;; URL: https://github.com/syohex/emacs-helm-ag 9 | ;; Version: 0.64 10 | ;; Package-Requires: ((emacs "27.1") (helm "2.0")) 11 | 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | 27 | ;; helm-ag provides interfaces of the silver searcher(Other search programs can be used 28 | ;; such as the platinum searcher, ack). And helm-ag provides wgrep like features which 29 | ;; users can edit from searched result. 30 | 31 | ;;; Code: 32 | 33 | (eval-when-compile 34 | (require 'grep) 35 | (defvar helm-help-message)) 36 | 37 | (require 'cl-lib) 38 | (require 'helm) 39 | (require 'helm-grep) 40 | (require 'helm-occur) 41 | (require 'helm-utils) 42 | (require 'compile) 43 | (require 'subr-x) 44 | 45 | (declare-function helm-read-file-name "helm-mode") 46 | (declare-function helm-grep-get-file-extensions "helm-grep") 47 | (declare-function helm-help "helm-help") 48 | 49 | (defgroup helm-ag nil 50 | "The silver searcher with helm interface." 51 | :group 'helm) 52 | 53 | (defsubst helm-ag--windows-p () 54 | "Check `ag' available window." 55 | (memq system-type '(ms-dos windows-nt))) 56 | 57 | (defcustom helm-ag-base-command 58 | (if (helm-ag--windows-p) 59 | "ag --vimgrep" 60 | "ag --nocolor --nogroup") 61 | "Base command of `ag'." 62 | :type 'string 63 | :group 'helm-ag) 64 | 65 | (defcustom helm-ag-command-option nil 66 | "Command line option of `ag'. This is appended after `helm-ag-base-command'." 67 | :type 'string 68 | :group 'helm-ag) 69 | 70 | (defcustom helm-ag-success-exit-status nil 71 | "Allows specifying the return code or codes of `helm-ag-base-command' that \ 72 | will be treated as successful." 73 | :type '(choice integer 74 | (list integer))) 75 | 76 | (defcustom helm-ag-insert-at-point nil 77 | "Insert thing at point as search pattern. 78 | You can set value same as `thing-at-point'" 79 | :type 'symbol 80 | :group 'helm-ag) 81 | 82 | (defcustom helm-ag-ignore-patterns nil 83 | "Ignore patterns for `ag'. This parameters are specified as --ignore." 84 | :type '(repeat string)) 85 | 86 | (defcustom helm-ag-use-grep-ignore-list nil 87 | "Use `grep-find-ignored-files' and `grep-find-ignored-directories' as \ 88 | ignore pattern. 89 | They are specified to `--ignore' options." 90 | :type 'boolean 91 | :group 'helm-ag) 92 | 93 | (defcustom helm-ag-always-set-extra-option nil 94 | "Always set `ag' options of `helm-do-ag'." 95 | :type 'boolean 96 | :group 'helm-ag) 97 | 98 | (defcustom helm-ag-fuzzy-match nil 99 | "Enable fuzzy match." 100 | :type 'boolean 101 | :group 'helm-ag) 102 | 103 | (defcustom helm-ag-edit-save t 104 | "Save buffers you edit at completed." 105 | :type 'boolean 106 | :group 'helm-ag) 107 | 108 | (defcustom helm-ag-use-emacs-lisp-regexp nil 109 | "[Experimental] Use Emacs Lisp regexp instead of PCRE." 110 | :type 'boolean 111 | :group 'helm-ag) 112 | 113 | (defcustom helm-ag-use-agignore nil 114 | "Use .agignore where is at project root if it exists." 115 | :type 'boolean 116 | :group 'helm-ag) 117 | 118 | (defcustom helm-ag-use-temp-buffer nil 119 | "Use temporary buffer for persistent action." 120 | :type 'boolean 121 | :group 'helm-ag) 122 | 123 | (defcustom helm-ag-ignore-buffer-patterns nil 124 | "Use temporary buffer for persistent action." 125 | :type '(repeat regexp) 126 | :group 'helm-ag) 127 | 128 | (defcustom helm-ag-show-status-function 'helm-ag-show-status-default-mode-line 129 | "Function called after that `ag' process is finished after `helm-do-ag'. 130 | Default behaviour shows finish and result in mode-line." 131 | :type 'function 132 | :group 'helm-ag) 133 | 134 | (defface helm-ag-edit-deleted-line 135 | '((t (:inherit font-lock-comment-face :strike-through t))) 136 | "Face of deleted line in edit mode.") 137 | 138 | (defvar helm-ag--command-history '()) 139 | (defvar helm-ag--helm-history '()) 140 | (defvar helm-ag--context-stack nil) 141 | (defvar helm-ag--default-directory nil) 142 | (defvar helm-ag--last-default-directory nil) 143 | (defvar helm-ag--last-query nil) 144 | (defvar helm-ag--last-command nil) 145 | (defvar helm-ag--elisp-regexp-query nil) 146 | (defvar helm-ag--valid-regexp-for-emacs nil) 147 | (defvar helm-ag--extra-options nil) 148 | (defvar helm-ag--extra-options-history nil) 149 | (defvar helm-ag--original-window nil) 150 | (defvar helm-ag--search-this-file-p nil) 151 | (defvar helm-ag--default-target nil) 152 | (defvar helm-ag--buffer-search nil) 153 | (defvar helm-ag--command-features '()) 154 | (defvar helm-ag--ignore-case nil) 155 | (defvar helm-do-ag--extensions nil) 156 | (defvar helm-do-ag--commands nil) 157 | 158 | (defun helm-ag--ignore-case-p (cmds input) 159 | "Determine if search should be case-insensitive based on CMDS and INPUT." 160 | (cl-loop for cmd in cmds 161 | when (member cmd '("-i" "--ignore-case")) 162 | return t 163 | 164 | when (member cmd '("-s" "--case-sensitive")) 165 | return nil 166 | 167 | finally 168 | return (let ((case-fold-search nil)) 169 | (not (string-match-p "[A-Z]" input))))) 170 | 171 | (defun helm-ag--save-current-context () 172 | "Save the current buffer context (file and position) to `helm-ag--context-stack'." 173 | (let ((curpoint (with-helm-current-buffer 174 | (point)))) 175 | (helm-aif (buffer-file-name helm-current-buffer) 176 | (push (list :file it :point curpoint) helm-ag--context-stack) 177 | (push (list :buffer helm-current-buffer :point curpoint) helm-ag--context-stack)))) 178 | 179 | (defun helm-ag--insert-thing-at-point (thing) 180 | "Retrieve the text at the point for a specified THING and insert it." 181 | (helm-aif (thing-at-point thing) 182 | (substring-no-properties it) 183 | "")) 184 | 185 | (defun helm-ag--searched-word () 186 | "Return the word to be searched based on `helm-ag-insert-at-point'." 187 | (if helm-ag-insert-at-point 188 | (helm-ag--insert-thing-at-point helm-ag-insert-at-point) 189 | "")) 190 | 191 | (defun helm-ag--construct-ignore-option (pattern) 192 | "Construct an `ag' ignore option string using PATTERN." 193 | (concat "--ignore=" pattern)) 194 | 195 | (defun helm-ag--grep-ignore-list-to-options () 196 | "Generate a list of `ag' ignore options from `grep' ignored files and directories." 197 | (require 'grep) 198 | (cl-loop for ignore in (append grep-find-ignored-files 199 | grep-find-ignored-directories) 200 | collect (helm-ag--construct-ignore-option ignore))) 201 | 202 | (defun helm-ag--parse-options-and-query (input) 203 | "Parse command-line options and search query from INPUT. 204 | 205 | E.g. Given INPUT = \"--ignore-case --context 5 error\", 206 | returns '((\"--ignore-case\" \"--context\") . \"5 error\")." 207 | (with-temp-buffer 208 | (insert input) 209 | (let (end options) 210 | (goto-char (point-min)) 211 | (when (re-search-forward "\\s-*--\\s-+" nil t) 212 | (setq end (match-end 0))) 213 | (goto-char (point-min)) 214 | (while (re-search-forward "\\(?:\\=\\|\\s-+\\)\\(-\\S-+\\)\\(?:\\s-+\\|$\\)" end t) 215 | (push (match-string-no-properties 1) options) 216 | (when end 217 | (cl-decf end (- (match-end 0) (match-beginning 0)))) 218 | (replace-match "")) 219 | (cons options (buffer-string))))) 220 | 221 | (defun helm-ag--parse-query (input) 222 | "Parse command-line search query and options, applying regexp conversions. 223 | 224 | E.g. Given INPUT = \"--ignore-case error\", 225 | returns '(\"--ignore-case\" \"error\")." 226 | (let* ((parsed (helm-ag--parse-options-and-query input)) 227 | (options (car parsed)) 228 | (query (cdr parsed))) 229 | (when helm-ag-use-emacs-lisp-regexp 230 | (setq query (helm-ag--elisp-regexp-to-pcre query))) 231 | (setq helm-ag--last-query query 232 | helm-ag--elisp-regexp-query (helm-ag--convert-to-elisp-regexp query)) 233 | (setq helm-ag--valid-regexp-for-emacs 234 | (helm-ag--validate-regexp helm-ag--elisp-regexp-query)) 235 | (if (not options) 236 | (list query) 237 | (nconc (nreverse options) (list query))))) 238 | 239 | (defsubst helm-ag--search-buffer-p (bufname) 240 | "Determine if BUFNAME should be searched by checking against ignore patterns." 241 | (cl-loop for regexp in helm-ag-ignore-buffer-patterns 242 | never (string-match-p regexp bufname))) 243 | 244 | (defun helm-ag--file-visited-buffers () 245 | "Retrieve a list of file-visiting buffers, filtered by ignore patterns." 246 | (let ((bufs (cl-loop for buf in (buffer-list) 247 | when (buffer-file-name buf) 248 | collect it))) 249 | (if (not helm-ag-ignore-buffer-patterns) 250 | bufs 251 | (cl-loop for buf in bufs 252 | when (helm-ag--search-buffer-p buf) 253 | collect buf)))) 254 | 255 | (defun helm-ag--construct-targets (targets) 256 | "Convert TARGETS to relative paths based on the default directory." 257 | (let ((default-directory helm-ag--default-directory)) 258 | (cl-loop for target in targets 259 | collect (file-relative-name target)))) 260 | 261 | (defun helm-ag--root-agignore () 262 | "Return the path to the \".agignore\" file at the project root, if it exists." 263 | (let ((root (helm-ag--project-root))) 264 | (when root 265 | (let ((default-directory root)) 266 | (when (file-exists-p ".agignore") 267 | (expand-file-name (concat default-directory ".agignore"))))))) 268 | 269 | (defun helm-ag--construct-command (this-file) 270 | "Construct the `ag' command for search, incorporating options and targets. 271 | 272 | THIS-FILE is an optional file path to add as a specific target in the search 273 | command. The function builds the command by combining `helm-ag-base-command', 274 | user options, and file patterns to ignore. 275 | 276 | This function returns a cons cell where the car is the `ag' command and the 277 | cdr is a list of all arguments." 278 | (let* ((commands (split-string helm-ag-base-command nil t)) 279 | (command (car commands)) 280 | (args (cdr commands))) 281 | (when helm-ag-command-option 282 | (let ((ag-options (split-string helm-ag-command-option nil t))) 283 | (setq args (append args ag-options)))) 284 | (when helm-ag-use-agignore 285 | (helm-aif (helm-ag--root-agignore) 286 | (setq args (append args (list "-p" it))))) 287 | (when helm-ag-ignore-patterns 288 | (setq args (append args (mapcar 'helm-ag--construct-ignore-option 289 | helm-ag-ignore-patterns)))) 290 | (when helm-ag-use-grep-ignore-list 291 | (setq args (append args (helm-ag--grep-ignore-list-to-options)))) 292 | (setq args (append args (helm-ag--parse-query helm-ag--last-query))) 293 | (when this-file 294 | (setq args (append args (list this-file)))) 295 | (when helm-ag--buffer-search 296 | (setq args (append args (helm-ag--file-visited-buffers)))) 297 | (when helm-ag--default-target 298 | (setq args (append args (helm-ag--construct-targets helm-ag--default-target)))) 299 | (cons command args))) 300 | 301 | (defun helm-ag--remove-carrige-returns () 302 | "Remove carriage return characters from the current buffer." 303 | (when (helm-ag--windows-p) 304 | (save-excursion 305 | (goto-char (point-min)) 306 | (while (re-search-forward "\xd" nil t) 307 | (replace-match ""))))) 308 | 309 | (defun helm-ag--abbreviate-file-name () 310 | "Abbreviate file paths in the current buffer." 311 | (unless (helm-ag--windows-p) 312 | (save-excursion 313 | (goto-char (point-min)) 314 | (forward-line 1) 315 | (while (re-search-forward "^\\([^:]+\\)" nil t) 316 | (replace-match (abbreviate-file-name (match-string-no-properties 1))))))) 317 | 318 | (defun helm-ag--command-succeeded-p (exit-status) 319 | "Determine if a command succeeded based on EXIT-STATUS" 320 | (cond ((integerp helm-ag-success-exit-status) (= exit-status helm-ag-success-exit-status)) 321 | ((consp helm-ag-success-exit-status) (member exit-status helm-ag-success-exit-status)) 322 | (t (zerop exit-status)))) 323 | 324 | (defun helm-ag--init () 325 | "Initialize helm-ag search buffer, execute searching, and handle results." 326 | (let ((buf-coding buffer-file-coding-system)) 327 | (helm-attrset 'recenter t) 328 | (with-current-buffer (helm-candidate-buffer 'global) 329 | (let* ((default-directory (or helm-ag--default-directory 330 | default-directory)) 331 | (cmds (helm-ag--construct-command (helm-attr 'search-this-file))) 332 | (coding-system-for-read buf-coding) 333 | (coding-system-for-write buf-coding)) 334 | (setq helm-ag--ignore-case (helm-ag--ignore-case-p cmds helm-ag--last-query) 335 | helm-ag--last-command cmds) 336 | (let ((ret (apply #'process-file (car cmds) nil t nil (cdr cmds)))) 337 | (if (zerop (length (buffer-string))) 338 | (error "No ag output: '%s'" helm-ag--last-query) 339 | (unless (helm-ag--command-succeeded-p ret) 340 | (unless (executable-find (car cmds)) 341 | (error "'%s' is not installed" (car cmds))) 342 | (error "Failed: '%s'" helm-ag--last-query)))) 343 | (when helm-ag--buffer-search 344 | (helm-ag--abbreviate-file-name)) 345 | (helm-ag--remove-carrige-returns) 346 | (helm-ag--save-current-context))))) 347 | 348 | (add-to-list 'debug-ignored-errors "^No ag output: ") 349 | 350 | (defun helm-ag--search-only-one-file-p () 351 | "Check if helm-ag is set to search only a single file." 352 | (when (and helm-ag--default-target (= (length helm-ag--default-target) 1)) 353 | (let ((target (car helm-ag--default-target))) 354 | (unless (file-directory-p target) 355 | target)))) 356 | 357 | (defun helm-ag--find-file-action (candidate find-func this-file &optional persistent) 358 | "Open a file and navigate to a specific line based on search results. 359 | 360 | CANDIDATE is a string representing the search result line, which includes 361 | the file path and line number to open. FIND-FUNC is a function used to 362 | open the file. THIS-FILE, if provided, specifies the file to open directly, 363 | bypassing the path in CANDIDATE. PERSISTENT, if non-nil, indicates that 364 | this is a temporary preview action." 365 | (when (memq 'pt helm-ag--command-features) 366 | ;; 'pt' always show filename if matched file is only one. 367 | (setq this-file nil)) 368 | (let* ((file-line (helm-grep-split-line candidate)) 369 | (filename (or this-file (cl-first file-line) candidate)) 370 | (line (if this-file 371 | (cl-first (split-string candidate ":")) 372 | (cl-second file-line))) 373 | (default-directory (or helm-ag--default-directory 374 | helm-ag--last-default-directory 375 | default-directory))) 376 | (unless persistent 377 | (setq helm-ag--last-default-directory default-directory)) 378 | (funcall find-func filename) 379 | (goto-char (point-min)) 380 | (when line 381 | (forward-line (1- (string-to-number line)))) 382 | (ignore-errors 383 | (and (re-search-forward helm-ag--last-query (line-end-position) t) 384 | ;; `helm-goto-char' expands folded headings/outlines if needed 385 | (helm-goto-char (match-beginning 0)))))) 386 | 387 | (defun helm-ag--open-file-with-temp-buffer (filename) 388 | "Open FILENAME in a temporary buffer." 389 | (let ((search-directory default-directory)) 390 | (switch-to-buffer (get-buffer-create " *helm-ag persistent*")) 391 | (setq default-directory search-directory 392 | buffer-read-only nil) 393 | (fundamental-mode) 394 | (erase-buffer) 395 | (insert-file-contents filename) 396 | (let ((buffer-file-name filename)) 397 | (set-auto-mode) 398 | (font-lock-fontify-region (point-min) (point-max))))) 399 | 400 | (defsubst helm-ag--vimgrep-option () 401 | "Check if helm-ag use Vim-style grep options." 402 | (member "--vimgrep" helm-ag--last-command)) 403 | 404 | (defun helm-ag--search-this-file-p () 405 | "Check if the current search is restricted to a single file" 406 | (unless (helm-ag--vimgrep-option) 407 | (if (eq (helm-get-current-source) 'helm-source-do-ag) 408 | (helm-ag--search-only-one-file-p) 409 | (helm-attr 'search-this-file)))) 410 | 411 | (defun helm-ag--persistent-action (candidate) 412 | "Display CANDIDATE in a buffer temporarily for preview." 413 | (let ((find-func (if helm-ag-use-temp-buffer 414 | #'helm-ag--open-file-with-temp-buffer 415 | #'find-file)) 416 | (helm-ag-p (assoc-default 'real-to-display (helm-get-current-source)))) 417 | (helm-ag--find-file-action candidate find-func (helm-ag--search-this-file-p) t) 418 | (let ((helm-input (if helm-ag-p 419 | (concat helm-ag--last-query " " helm-input) 420 | helm-input))) 421 | (helm-highlight-current-line)))) 422 | 423 | (defun helm-ag--validate-regexp (regexp) 424 | "Check if REGEXP is a valid regular expression." 425 | (condition-case nil 426 | (progn 427 | (string-match-p regexp "") 428 | t) 429 | (invalid-regexp nil))) 430 | 431 | (defun helm-ag--convert-to-elisp-regexp (regexp) 432 | "Convert REGEXP to Elisp-compatible regular expression." 433 | ;; This is very simple conversion 434 | (with-temp-buffer 435 | (insert regexp) 436 | (goto-char (point-min)) 437 | ;; convert (, ), {, }, | 438 | (while (re-search-forward "[(){}|]" nil t) 439 | (backward-char 1) 440 | (cond ((looking-back "\\\\\\\\" nil)) 441 | ((looking-back "\\\\" nil) 442 | (delete-char -1)) 443 | (t 444 | (insert "\\"))) 445 | (forward-char 1)) 446 | ;; convert \s and \S -> \s- \S- 447 | (goto-char (point-min)) 448 | (while (re-search-forward "\\(\\\\s\\)" nil t) 449 | (unless (looking-back "\\\\\\\\s" nil) 450 | (insert "-"))) 451 | (buffer-string))) 452 | 453 | (defun helm-ag--elisp-regexp-to-pcre (regexp) 454 | "Convert an Elisp-compatible REGEXP to PCRE format." 455 | (with-temp-buffer 456 | (insert regexp) 457 | (goto-char (point-min)) 458 | (while (re-search-forward "[(){}|]" nil t) 459 | (backward-char 1) 460 | (cond ((looking-back "\\\\\\\\" nil)) 461 | ((looking-back "\\\\" nil) 462 | (delete-char -1)) 463 | (t 464 | (insert "\\"))) 465 | (forward-char 1)) 466 | (buffer-string))) 467 | 468 | (defun helm-ag--highlight-candidate (candidate) 469 | "Highlight matching parts in CANDIDATE based on the current query." 470 | (let ((limit (1- (length candidate))) 471 | (last-pos 0) 472 | (case-fold-search helm-ag--ignore-case)) 473 | (when helm-ag--valid-regexp-for-emacs 474 | (while (and (< last-pos limit) 475 | (string-match helm-ag--elisp-regexp-query candidate last-pos)) 476 | (let ((start (match-beginning 0)) 477 | (end (match-end 0))) 478 | (if (= start end) 479 | (cl-incf last-pos) 480 | (put-text-property start end 'face 'helm-match candidate) 481 | (setq last-pos (1+ (match-end 0))))))) 482 | candidate)) 483 | 484 | (defun helm-ag--candidate-transform-for-this-file (candidate) 485 | "Format CANDIDATE to display line number and highlighted content for a single file." 486 | (when (string-match "\\`\\([^:]+\\):\\(.*\\)" candidate) 487 | (format "%s:%s" 488 | (propertize (match-string 1 candidate) 'face 'helm-grep-lineno) 489 | (helm-ag--highlight-candidate (match-string 2 candidate))))) 490 | 491 | (defun helm-ag--candidate-transform-for-files (candidate) 492 | "Format CANDIDATE to display line number and highlighted content for files." 493 | (helm-aif (helm-grep-split-line candidate) 494 | (format "%s:%s:%s" 495 | (propertize (cl-first it) 'face 'helm-moccur-buffer) 496 | (propertize (cl-second it) 'face 'helm-grep-lineno) 497 | (helm-ag--highlight-candidate (cl-third it))))) 498 | 499 | (defun helm-ag--candidate-transformer (candidate) 500 | "Transform CANDIDATE for display based on search context." 501 | (or (if (helm-attr 'search-this-file) 502 | (helm-ag--candidate-transform-for-this-file candidate) 503 | (helm-ag--candidate-transform-for-files candidate)) 504 | candidate)) 505 | 506 | (defun helm-ag--action-find-file (candidate) 507 | "Open CANDIDATE in the current window." 508 | (helm-ag--find-file-action candidate 'find-file (helm-ag--search-this-file-p))) 509 | 510 | (defun helm-ag--action-find-file-other-window (candidate) 511 | "Open CANDIDATE in another window." 512 | (helm-ag--find-file-action candidate 'find-file-other-window (helm-ag--search-this-file-p))) 513 | 514 | (defvar helm-ag--actions 515 | (helm-make-actions 516 | "Open file" #'helm-ag--action-find-file 517 | "Open file other window" #'helm-ag--action-find-file-other-window 518 | "Save results in buffer" #'helm-ag--action-save-buffer 519 | "Edit search results" #'helm-ag--edit) 520 | "Actions for `helm-ag'.") 521 | 522 | (defvar helm-ag-map 523 | (let ((map (make-sparse-keymap))) 524 | (set-keymap-parent map helm-map) 525 | (define-key map (kbd "C-c o") 'helm-ag--run-other-window-action) 526 | (define-key map (kbd "C-l") 'helm-ag--up-one-level) 527 | (define-key map (kbd "C-c C-e") 'helm-ag-edit) 528 | (define-key map (kbd "C-x C-s") 'helm-ag--run-save-buffer) 529 | (define-key map (kbd "C-c ?") 'helm-ag-help) 530 | (define-key map (kbd "C-c >") 'helm-ag--next-file) 531 | (define-key map (kbd "") 'helm-ag--next-file) 532 | (define-key map (kbd "C-c <") 'helm-ag--previous-file) 533 | (define-key map (kbd "") 'helm-ag--previous-file) 534 | map) 535 | "Keymap for `helm-ag'.") 536 | 537 | (defvar helm-ag-source 538 | (helm-build-in-buffer-source "The Silver Searcher" 539 | :init 'helm-ag--init 540 | :real-to-display 'helm-ag--candidate-transformer 541 | :persistent-action 'helm-ag--persistent-action 542 | :fuzzy-match helm-ag-fuzzy-match 543 | :action helm-ag--actions 544 | :candidate-number-limit 9999 545 | :keymap helm-ag-map 546 | :follow (and helm-follow-mode-persistent 1)) 547 | "Helm source definition for integrating with The Silver Searcher.") 548 | 549 | ;;;###autoload 550 | (defun helm-ag-pop-stack () 551 | "Restore the previous search context from the `helm-ag--context-stack'." 552 | (interactive) 553 | (let ((context (pop helm-ag--context-stack))) 554 | (unless context 555 | (error "Context stack is empty !")) 556 | (helm-aif (plist-get context :file) 557 | (find-file it) 558 | (let ((buf (plist-get context :buffer))) 559 | (if (buffer-live-p buf) 560 | (switch-to-buffer buf) 561 | (error "The buffer is already killed")))) 562 | (goto-char (plist-get context :point)))) 563 | 564 | ;;;###autoload 565 | (defun helm-ag-clear-stack () 566 | "Clear all entries in the `helm-ag--context-stack'." 567 | (interactive) 568 | (setq helm-ag--context-stack nil)) 569 | 570 | (defun helm-ag--marked-input (escape) 571 | "Return the selected region as search input, optionally escaping spaces." 572 | (when (use-region-p) 573 | (let ((input (buffer-substring-no-properties (region-beginning) (region-end)))) 574 | (deactivate-mark) 575 | (if (not escape) 576 | input 577 | (replace-regexp-in-string " " "\\\\ " input))))) 578 | 579 | (defun helm-ag--query (&optional query) 580 | "Display prompt for a search QUERY, using the marked region or the word at point." 581 | (let* ((searched-word (helm-ag--searched-word)) 582 | (marked-word (helm-ag--marked-input nil)) 583 | (query (or query 584 | (read-from-minibuffer "Pattern: " 585 | (or marked-word searched-word) 586 | nil 587 | nil 588 | 'helm-ag--command-history 589 | (helm-aif (symbol-at-point) 590 | (symbol-name it)))))) 591 | (when (string-empty-p query) 592 | (error "Input is empty!!")) 593 | (setq helm-ag--last-query query))) 594 | 595 | (defsubst helm-ag--init-state () 596 | "Initialize helm-ag search state variables." 597 | (setq helm-ag--original-window (selected-window) 598 | helm-ag--last-default-directory nil)) 599 | 600 | (defun helm-ag--get-default-directory () 601 | "Determine the default search directory for `helm-ag'" 602 | (let ((prefix-val (and current-prefix-arg (abs (prefix-numeric-value current-prefix-arg))))) 603 | (cond ((not prefix-val) default-directory) 604 | ((= prefix-val 4) 605 | (file-name-as-directory 606 | (read-directory-name "Search directory: " nil nil t))) 607 | ((= prefix-val 16) 608 | (let ((dirs (list (read-directory-name "Search directory: " nil nil t)))) 609 | (while (y-or-n-p "More directories ? ") 610 | (push (read-directory-name "Search directory: " nil nil t) dirs)) 611 | (reverse dirs)))))) 612 | 613 | (defsubst helm-ag--helm-header (dir) 614 | "Generate a header string for helm-ag search" 615 | (if helm-ag--buffer-search 616 | "Search Buffers" 617 | (concat "Search at " (abbreviate-file-name dir)))) 618 | 619 | (defun helm-ag--run-other-window-action () 620 | "Execute helm-ag action to open a search result in another window." 621 | (interactive) 622 | (with-helm-alive-p 623 | (helm-exit-and-execute-action #'helm-ag--action-find-file-other-window))) 624 | 625 | (defun helm-ag--exit-from-edit-mode () 626 | "Exit helm-ag's edit mode, restoring the original window." 627 | (when (window-live-p helm-ag--original-window) 628 | (select-window helm-ag--original-window)) 629 | (kill-buffer (get-buffer "*helm-ag-edit*"))) 630 | 631 | (defun helm-ag--match-line-regexp () 632 | "Generate a regular expression to match lines in search results." 633 | ;; $1: file name 634 | ;; $2: line 635 | ;; $3: match body 636 | ;; $4: file attributes part(filename, line, column) 637 | (cond ((helm-ag--vimgrep-option) 638 | "^\\(?4:\\(?1:[^:]+\\):\\(?2:[1-9][0-9]*\\):[^:]+:\\)\\(?3:.*\\)$") 639 | (helm-ag--search-this-file-p 640 | "^\\(?4:\\(?2:[1-9][0-9]*\\)[:-]\\)\\(?3:.*\\)$") 641 | (t 642 | "^\\(?4:\\(?1:[^:]+\\):\\(?2:[1-9][0-9]*\\)[:-]\\)\\(?3:.*\\)$"))) 643 | 644 | (defun helm-ag--edit-commit () 645 | "Apply edited changes from `*helm-ag-edit*' buffer to files." 646 | (interactive) 647 | (goto-char (point-min)) 648 | (let ((read-only-files 0) 649 | (files-to-lines (make-hash-table :test #'equal)) 650 | (regexp (helm-ag--match-line-regexp)) 651 | (line-deletes (make-hash-table :test #'equal))) 652 | ;; Group changes by file 653 | (while (re-search-forward regexp nil t) 654 | (let* ((file (or (match-string-no-properties 1) helm-ag--search-this-file-p)) 655 | (line (string-to-number (match-string-no-properties 2))) 656 | (body (match-string-no-properties 3)) 657 | (ovs (overlays-at (line-beginning-position))) 658 | (lines-list (gethash file files-to-lines))) 659 | (if (not (file-writable-p file)) 660 | (cl-incf read-only-files) 661 | (if lines-list 662 | (progn 663 | (push (list line body ovs) lines-list) 664 | (puthash file lines-list files-to-lines)) 665 | (puthash file (list (list line body ovs)) files-to-lines))))) 666 | ;; Batch edits by file 667 | (maphash 668 | (lambda (curr-file lines-data) 669 | (with-temp-buffer 670 | (insert-file-contents curr-file) 671 | (dolist (curr-line-data (reverse lines-data)) 672 | (cl-destructuring-bind 673 | (line body ovs) curr-line-data 674 | (goto-char (point-min)) 675 | (let ((deleted-lines (gethash curr-file line-deletes 0)) 676 | (deleted (and ovs (overlay-get (car ovs) 'helm-ag-deleted)))) 677 | (forward-line (- line 1 deleted-lines)) 678 | (delete-region (line-beginning-position) (line-end-position)) 679 | (if (not deleted) 680 | (insert body) 681 | (let ((beg (point))) 682 | (forward-line 1) 683 | (delete-region beg (point)) 684 | (puthash curr-file (1+ deleted-lines) line-deletes)))))) 685 | (when helm-ag-edit-save 686 | (write-region (point-min) (point-max) curr-file)))) 687 | files-to-lines) 688 | ;; Finish 689 | (helm-ag--exit-from-edit-mode) 690 | (if (not (zerop read-only-files)) 691 | (message "%d files are read-only and not editable." read-only-files) 692 | (message "Success update")))) 693 | 694 | (defun helm-ag--edit-abort () 695 | "Abort the current helm-ag edit session and discard unsaved changes." 696 | (interactive) 697 | (when (y-or-n-p "Discard changes ? ") 698 | (helm-ag--exit-from-edit-mode) 699 | (message "Abort edit"))) 700 | 701 | (defun helm-ag--mark-line-deleted () 702 | "Mark the current line as deleted in helm-ag's edit mode." 703 | (interactive) 704 | (let* ((beg (line-beginning-position)) 705 | (end (line-end-position)) 706 | (ov (make-overlay beg end))) 707 | (overlay-put ov 'face 'helm-ag-edit-deleted-line) 708 | (overlay-put ov 'helm-ag-deleted t))) 709 | 710 | (defun helm-ag--unmark () 711 | "Remove the deletion mark from the current line in helm-ag's edit mode." 712 | (interactive) 713 | (dolist (ov (overlays-in (line-beginning-position) (line-end-position))) 714 | (when (overlay-get ov 'helm-ag-deleted) 715 | (delete-overlay ov)))) 716 | 717 | (defvar helm-ag-edit-map 718 | (let ((map (make-sparse-keymap))) 719 | (define-key map (kbd "C-c C-c") 'helm-ag--edit-commit) 720 | (define-key map (kbd "C-c C-k") 'helm-ag--edit-abort) 721 | (define-key map (kbd "C-c C-d") 'helm-ag--mark-line-deleted) 722 | (define-key map (kbd "C-c C-u") 'helm-ag--unmark) 723 | map) 724 | "Editing keymap for `helm-ag'") 725 | 726 | (defsubst helm-ag--edit-func-to-keys (func) 727 | "Return the key binding for FUNC in helm-ag's edit mode." 728 | (key-description (car-safe (where-is-internal func helm-ag-edit-map)))) 729 | 730 | (defun helm-ag--edit (_candidate) 731 | "Initialize helm-ag's edit buffer for interactive editing of search results. 732 | 733 | This function sets up an editable buffer `*helm-ag-edit*' populated with 734 | search results from the `*helm-ag*' buffer, allowing users to make batch 735 | edits directly to lines matched by the search: 736 | - Sets the working directory to match the Helm-Ag buffer's default directory. 737 | - Populates `*helm-ag-edit*' with marked lines if any are marked; otherwise, 738 | it includes all search results. 739 | - Adds text properties to the buffer to prevent accidental modifications 740 | to file line numbers. 741 | - Displays a header line indicating keybindings for committing and aborting edits." 742 | (let* ((helm-buf-dir (or helm-ag--default-directory 743 | helm-ag--last-default-directory 744 | default-directory)) 745 | (default-directory helm-buf-dir)) 746 | (with-current-buffer (get-buffer-create "*helm-ag-edit*") 747 | (let ((inhibit-read-only t)) 748 | (erase-buffer)) 749 | (setq-local helm-ag--default-directory helm-buf-dir) 750 | (unless (helm-ag--vimgrep-option) 751 | (setq-local helm-ag--search-this-file-p 752 | (assoc-default 'search-this-file (helm-get-current-source)))) 753 | (let (buf-content) 754 | (with-current-buffer (get-buffer "*helm-ag*") 755 | (goto-char (point-min)) 756 | (forward-line 1) 757 | (let* ((body-start (point)) 758 | (marked-lines (cl-loop for ov in (overlays-in body-start (point-max)) 759 | when (eq 'helm-visible-mark (overlay-get ov 'face)) 760 | return (helm-marked-candidates)))) 761 | (if (not marked-lines) 762 | (setq buf-content (buffer-substring-no-properties 763 | body-start (point-max))) 764 | (setq buf-content (concat (string-join marked-lines "\n") "\n"))))) 765 | (insert buf-content) 766 | (add-text-properties (point-min) (point-max) 767 | '(read-only t rear-nonsticky t front-sticky t)) 768 | (let ((inhibit-read-only t) 769 | (regexp (helm-ag--match-line-regexp))) 770 | (setq header-line-format 771 | (format "[%s] %s: Commit, %s: Abort" 772 | (abbreviate-file-name helm-ag--default-directory) 773 | (helm-ag--edit-func-to-keys #'helm-ag--edit-commit) 774 | (helm-ag--edit-func-to-keys #'helm-ag--edit-abort))) 775 | (goto-char (point-min)) 776 | (while (re-search-forward regexp nil t) 777 | (let ((file-line-begin (match-beginning 4)) 778 | (file-line-end (match-end 4)) 779 | (body-begin (match-beginning 3)) 780 | (body-end (match-end 3))) 781 | (add-text-properties file-line-begin file-line-end 782 | '(face font-lock-function-name-face 783 | intangible t)) 784 | (remove-text-properties body-begin body-end '(read-only t)) 785 | (set-text-properties body-end (1+ body-end) 786 | '(read-only t rear-nonsticky t)))))))) 787 | (other-window 1) 788 | (switch-to-buffer (get-buffer "*helm-ag-edit*")) 789 | (goto-char (point-min)) 790 | (setq next-error-function 'compilation-next-error-function) 791 | (setq-local compilation-locs (make-hash-table :test 'equal :weakness 'value)) 792 | (use-local-map helm-ag-edit-map)) 793 | 794 | (defun helm-ag-edit () 795 | "Trigger helm-ag's interactive edit mode for modifying search results." 796 | (interactive) 797 | (helm-exit-and-execute-action 'helm-ag--edit)) 798 | 799 | (defconst helm-ag--help-message 800 | "\n* Helm Ag\n 801 | 802 | \n** Specific commands for Helm Ag:\n 803 | \\ 804 | \\[helm-ag--run-other-window-action]\t\t-> Open result in other buffer 805 | \\[helm-ag--up-one-level]\t\t-> Search in parent directory. 806 | \\[helm-ag-edit]\t\t-> Edit search results. 807 | \\[helm-ag-help]\t\t-> Show this help. 808 | \n** Helm Ag Map\n 809 | \\{helm-map}" 810 | "Help message for `helm-ag'.") 811 | 812 | (defun helm-ag-help () 813 | "Display help message for `helm-ag'." 814 | (interactive) 815 | (let ((helm-help-message helm-ag--help-message)) 816 | (helm-help))) 817 | 818 | (defun helm-ag-mode-jump () 819 | "Jump to the file and line of the search result under the cursor." 820 | (interactive) 821 | (let ((line (helm-current-line-contents))) 822 | (helm-ag--find-file-action line 'find-file helm-ag--search-this-file-p))) 823 | 824 | (defun helm-ag-mode-jump-other-window () 825 | "Jump to the file and line of the search result under the cursor in another window." 826 | (interactive) 827 | (let ((line (helm-current-line-contents))) 828 | (helm-ag--find-file-action line 'find-file-other-window helm-ag--search-this-file-p))) 829 | 830 | (defvar helm-ag-mode-map 831 | (let ((map (make-sparse-keymap))) 832 | (define-key map (kbd "RET") 'helm-ag-mode-jump) 833 | (define-key map (kbd "C-o") 'helm-ag-mode-jump-other-window) 834 | (define-key map (kbd "g") 'helm-ag--update-save-results) 835 | map) 836 | "Mode keymap for `helm-ag'") 837 | 838 | (define-derived-mode helm-ag-mode special-mode "helm-ag" 839 | "Major mode to provide actions in helm grep saved buffer. 840 | 841 | Special commands: 842 | \\{helm-ag-mode-map}") 843 | 844 | (defun helm-ag--put-result-in-save-buffer (result search-this-file-p) 845 | "Insert search RESULT into a buffer and set up `helm-ag-mode'" 846 | (setq buffer-read-only t) 847 | (let ((inhibit-read-only t)) 848 | (erase-buffer) 849 | (insert "-*- mode: helm-ag -*-\n\n" 850 | (format "Ag Results for `%s':\n\n" helm-ag--last-query)) 851 | (save-excursion 852 | (insert result))) 853 | (helm-ag-mode) 854 | (unless (helm-ag--vimgrep-option) 855 | (setq-local helm-ag--search-this-file-p search-this-file-p)) 856 | (setq-local helm-ag--default-directory default-directory)) 857 | 858 | (defun helm-ag--save-results (use-other-buf) 859 | "Save the results of the last helm-ag search into a buffer." 860 | (let* ((search-this-file-p nil) 861 | (result (with-current-buffer helm-buffer 862 | (goto-char (point-min)) 863 | (forward-line 1) 864 | (buffer-substring (point) (point-max)))) 865 | (default-directory helm-ag--default-directory) 866 | (buf (if use-other-buf 867 | (read-string "Results buffer name: " 868 | (format "*helm ag results for '%s'*" helm-ag--last-query)) 869 | "*helm ag results*"))) 870 | (when (buffer-live-p (get-buffer buf)) 871 | (kill-buffer buf)) 872 | (with-current-buffer (get-buffer-create buf) 873 | (helm-ag--put-result-in-save-buffer result search-this-file-p) 874 | (pop-to-buffer buf) 875 | (message "Helm Ag Results saved in `%s' buffer" buf)))) 876 | 877 | (defun helm-ag--update-save-results () 878 | "Update the saved search results using the last helm-ag command." 879 | (interactive) 880 | (let* ((default-directory helm-ag--default-directory) 881 | (result (with-temp-buffer 882 | (apply #'process-file (car helm-ag--last-command) nil t nil 883 | (cdr helm-ag--last-command)) 884 | (helm-ag--remove-carrige-returns) 885 | (when helm-ag--buffer-search 886 | (helm-ag--abbreviate-file-name)) 887 | (helm-ag--propertize-candidates helm-ag--last-query) 888 | (buffer-string)))) 889 | (helm-ag--put-result-in-save-buffer result helm-ag--search-this-file-p) 890 | (message "Update Results"))) 891 | 892 | (defun helm-ag--action-save-buffer (_arg) 893 | "Save the results of the last helm-ag search into the default results buffer." 894 | (helm-ag--save-results nil)) 895 | 896 | (defun helm-ag--run-save-buffer () 897 | "Run the action to save helm-ag search results, respecting current prefix argument." 898 | (interactive) 899 | (let ((use-other-buf-p current-prefix-arg)) 900 | (with-helm-alive-p 901 | (helm-exit-and-execute-action 902 | (lambda (_arg) 903 | (helm-ag--save-results use-other-buf-p)))))) 904 | 905 | (defun helm-ag--file-of-current-file () 906 | "Extract the file name from the current line in the helm buffer." 907 | (let ((line (helm-current-line-contents))) 908 | (when (string-match helm-grep-split-line-regexp line) 909 | (match-string-no-properties 1 line)))) 910 | 911 | (defun helm-ag--move-file-common (pred move-fn wrap-fn) 912 | "Navigate between files in Helm Ag results using a common pattern. 913 | 914 | PRED is a predicate function to determine stopping conditions. 915 | MOVE-FN is a function to move the cursor to the next or previous line. 916 | WRAP-FN is a function to wrap around the buffer when the end or beginning is reached." 917 | (with-helm-window 918 | (let ((file (helm-ag--file-of-current-file))) 919 | (funcall move-fn) 920 | (while (and (not (funcall pred)) (string= file (helm-ag--file-of-current-file))) 921 | (funcall move-fn)) 922 | (when (funcall pred) 923 | (funcall wrap-fn))))) 924 | 925 | (defun helm-ag--previous-file () 926 | "Move the selection to the previous file in the helm-ag results." 927 | (interactive) 928 | (helm-ag--move-file-common 929 | #'helm-beginning-of-source-p #'helm-previous-line #'helm-end-of-buffer)) 930 | 931 | (defun helm-ag--next-file () 932 | "Move the selection to the next file in the helm-ag results." 933 | (interactive) 934 | (helm-ag--move-file-common 935 | #'helm-end-of-source-p #'helm-next-line #'helm-beginning-of-buffer)) 936 | 937 | (defsubst helm-ag--root-directory-p () 938 | "Check if the current directory contains a vcs root." 939 | (cl-loop for dir in '(".git/" ".hg/") 940 | thereis (file-directory-p dir))) 941 | 942 | (defun helm-ag--up-one-level () 943 | "Move up one directory level for Helm Ag search, unless at the project root." 944 | (interactive) 945 | (if (or (not (helm-ag--root-directory-p)) 946 | (y-or-n-p "Current directory might be the project root. \ 947 | Continue searching the parent directory? ")) 948 | (let ((parent (file-name-directory (directory-file-name default-directory)))) 949 | (helm-run-after-exit 950 | (lambda () 951 | (let* ((default-directory parent) 952 | (helm-ag--default-directory parent)) 953 | (setq helm-ag--last-default-directory default-directory) 954 | (helm-attrset 'name (helm-ag--helm-header default-directory) helm-ag-source) 955 | (helm :sources '(helm-ag-source) :buffer "*helm-ag*" :keymap helm-ag-map 956 | :history 'helm-ag--helm-history))))) 957 | (message nil))) 958 | 959 | ;;;###autoload 960 | (defun helm-ag-this-file (&optional query) 961 | "Do ag with in this file with QUERY." 962 | (interactive) 963 | (helm-ag--init-state) 964 | (let ((filename (file-name-nondirectory (buffer-file-name))) 965 | (helm-ag--default-directory default-directory)) 966 | (helm-ag--query query) 967 | (helm-ag--set-command-features) 968 | (helm-attrset 'search-this-file (file-relative-name (buffer-file-name)) 969 | helm-ag-source) 970 | (helm-attrset 'name (format "Search at %s" filename) helm-ag-source) 971 | (helm :sources '(helm-ag-source) :buffer "*helm-ag*" :keymap helm-ag-map 972 | :history 'helm-ag--helm-history))) 973 | 974 | ;;;###autoload 975 | (defun helm-ag (&optional basedir query) 976 | "Do ag with in BASEDIR and with QUERY." 977 | (interactive) 978 | (helm-ag--init-state) 979 | (let ((dir (helm-ag--get-default-directory)) 980 | targets) 981 | (when (listp dir) 982 | (setq basedir default-directory 983 | targets dir)) 984 | (let ((helm-ag--default-directory (or basedir dir)) 985 | (helm-ag--default-target targets)) 986 | (helm-ag--query query) 987 | (helm-attrset 'search-this-file nil helm-ag-source) 988 | (helm-attrset 'name (helm-ag--helm-header helm-ag--default-directory) helm-ag-source) 989 | (helm :sources '(helm-ag-source) :buffer "*helm-ag*" :keymap helm-ag-map 990 | :history 'helm-ag--helm-history)))) 991 | 992 | (defun helm-ag--split-string (str) 993 | "Split STR into a list of substrings separated by spaces, handling escaped spaces." 994 | (with-temp-buffer 995 | (insert str) 996 | (goto-char (point-min)) 997 | (let ((prev (point)) 998 | patterns) 999 | (while (search-forward " " nil 'move) 1000 | (cond ((looking-back "\\\\\\\\ " nil) 1001 | (push (buffer-substring-no-properties prev (1- (point))) patterns) 1002 | (skip-chars-forward " ") 1003 | (setq prev (point))) 1004 | ((looking-back "\\\\ " nil) 1005 | (replace-match " ")) 1006 | (t (push (buffer-substring-no-properties prev (1- (point))) patterns) 1007 | (skip-chars-forward " ") 1008 | (setq prev (point))))) 1009 | (push (buffer-substring-no-properties prev (point)) patterns) 1010 | (reverse (cl-loop for p in patterns unless (string= p "") collect p))))) 1011 | 1012 | (defsubst helm-ag--convert-invert-pattern (pattern) 1013 | "Convert PATTERN to a PCRE negative lookahead assertion if it starts with '!' and is supported." 1014 | (when (and (memq 'pcre helm-ag--command-features) 1015 | (string-prefix-p "!" pattern) (> (length pattern) 1)) 1016 | (concat "^(?!.*" (substring pattern 1) ").+$"))) 1017 | 1018 | (defun helm-ag--join-patterns (input) 1019 | "Join multiple search patterns from INPUT into a single regex pattern." 1020 | (let ((patterns (helm-ag--split-string input))) 1021 | (if (= (length patterns) 1) 1022 | (or (helm-ag--convert-invert-pattern (car patterns)) 1023 | (car patterns)) 1024 | (cond ((memq 'pcre helm-ag--command-features) 1025 | (cl-loop for s in patterns 1026 | if (helm-ag--convert-invert-pattern s) 1027 | concat (concat "(?=" it ")") 1028 | else 1029 | concat (concat "(?=.*" s ".*)"))) 1030 | ((memq 're2 helm-ag--command-features) 1031 | (string-join patterns ".*")) 1032 | ;; we don't know anything about this pattern 1033 | (t input))))) 1034 | 1035 | (defun helm-ag--do-ag-highlight-patterns (input) 1036 | "Process INPUT to generate a list of valid regex patterns for highlighting." 1037 | (if (or (memq 'pcre helm-ag--command-features) 1038 | (memq 're2 helm-ag--command-features)) 1039 | (cl-loop with regexp = (helm-ag--convert-to-elisp-regexp input) 1040 | for pattern in (helm-ag--split-string regexp) 1041 | when (helm-ag--validate-regexp pattern) 1042 | collect pattern) 1043 | (list (helm-ag--join-patterns input)))) 1044 | 1045 | (defun helm-ag--propertize-candidates (input) 1046 | "Apply text properties to search result candidates in the current buffer based on INPUT." 1047 | (save-excursion 1048 | (goto-char (point-min)) 1049 | (forward-line 1) 1050 | (let ((patterns (helm-ag--do-ag-highlight-patterns input))) 1051 | (cl-loop with one-file-p = (and (not (helm-ag--vimgrep-option)) 1052 | (helm-ag--search-only-one-file-p)) 1053 | while (not (eobp)) 1054 | for num = 1 then (1+ num) 1055 | do 1056 | (progn 1057 | (let ((start (point)) 1058 | (bound (line-end-position))) 1059 | (if (and one-file-p (search-forward ":" bound t)) 1060 | (set-text-properties (line-beginning-position) (1- (point)) 1061 | '(face helm-grep-lineno)) 1062 | (when (re-search-forward helm-grep-split-line-regexp bound t) 1063 | (set-text-properties (match-beginning 1) (match-end 1) '(face helm-moccur-buffer)) 1064 | (set-text-properties (match-beginning 2) (match-end 2) '(face helm-grep-lineno)) 1065 | (goto-char (match-beginning 3)))) 1066 | (let ((curpoint (point)) 1067 | (case-fold-search helm-ag--ignore-case)) 1068 | (dolist (pattern patterns) 1069 | (let ((last-point (point))) 1070 | (while (and (< (point) bound) 1071 | (re-search-forward pattern bound t)) 1072 | (set-text-properties (match-beginning 0) (match-end 0) 1073 | '(face helm-match)) 1074 | (when (= last-point (point)) 1075 | (forward-char 1)) 1076 | (setq last-point (point))) 1077 | (goto-char curpoint)))) 1078 | (put-text-property start bound 'helm-cand-num num)) 1079 | (forward-line 1)))))) 1080 | 1081 | (defun helm-ag-show-status-default-mode-line () 1082 | "Update the mode line to show the status of the helm-ag process." 1083 | (setq mode-line-format 1084 | '(" " mode-line-buffer-identification " " 1085 | (:eval (propertize 1086 | (format 1087 | "[AG process finished - (%s results)] " 1088 | (helm-get-candidate-number)) 1089 | 'face 'helm-grep-finish))))) 1090 | 1091 | (defun helm-ag--do-ag-propertize (input) 1092 | "Apply text properties and update the helm window based on INPUT." 1093 | (with-helm-window 1094 | (helm-ag--remove-carrige-returns) 1095 | (when helm-ag--buffer-search 1096 | (helm-ag--abbreviate-file-name)) 1097 | (helm-ag--propertize-candidates input) 1098 | (when helm-ag-show-status-function 1099 | (funcall helm-ag-show-status-function) 1100 | (force-mode-line-update)))) 1101 | 1102 | (defun helm-ag--construct-extension-options () 1103 | "Construct file extension options for the `ag' command based on `helm-do-ag--extensions'." 1104 | (cl-loop for ext in helm-do-ag--extensions 1105 | unless (string= ext "*") 1106 | collect 1107 | (concat "-G" (replace-regexp-in-string 1108 | "\\*" "" 1109 | (replace-regexp-in-string "\\." "\\\\." ext))))) 1110 | 1111 | (defun helm-ag--show-result-p (options has-query) 1112 | "Check if search results should be shown based on OPTIONS and HAS-QUERY." 1113 | (or has-query 1114 | (cl-loop for opt in options 1115 | thereis (string-prefix-p "-g" opt)))) 1116 | 1117 | (defun helm-ag--construct-do-ag-command (pattern) 1118 | "Construct the command line for executing ag based on PATTERN." 1119 | (let* ((opt-query (helm-ag--parse-options-and-query pattern)) 1120 | (options (car opt-query)) 1121 | (query (cdr opt-query)) 1122 | (has-query (not (string= query "")))) 1123 | (when helm-ag-use-emacs-lisp-regexp 1124 | (setq query (helm-ag--elisp-regexp-to-pcre query))) 1125 | (when (helm-ag--show-result-p options has-query) 1126 | (append (car helm-do-ag--commands) 1127 | options 1128 | (and has-query (list (helm-ag--join-patterns query))) 1129 | (cdr helm-do-ag--commands))))) 1130 | 1131 | (defun helm-ag--do-ag-set-command () 1132 | "Construct the command to be executed for `helm-do-ag'." 1133 | (let ((cmd-opts (split-string helm-ag-base-command nil t))) 1134 | (when helm-ag-command-option 1135 | (setq cmd-opts (append cmd-opts (split-string helm-ag-command-option nil t)))) 1136 | (when helm-ag--extra-options 1137 | (setq cmd-opts (append cmd-opts (split-string helm-ag--extra-options)))) 1138 | (when helm-ag-ignore-patterns 1139 | (setq cmd-opts 1140 | (append cmd-opts 1141 | (mapcar #'helm-ag--construct-ignore-option 1142 | helm-ag-ignore-patterns)))) 1143 | (when helm-ag-use-agignore 1144 | (helm-aif (helm-ag--root-agignore) 1145 | (setq cmd-opts (append cmd-opts (list "-p" it))))) 1146 | (when helm-do-ag--extensions 1147 | (setq cmd-opts (append cmd-opts (helm-ag--construct-extension-options)))) 1148 | (when helm-ag-use-grep-ignore-list 1149 | (setq cmd-opts (append cmd-opts (helm-ag--grep-ignore-list-to-options)))) 1150 | (let (targets) 1151 | (when helm-ag--buffer-search 1152 | (setq targets (helm-ag--file-visited-buffers))) 1153 | (setq helm-do-ag--commands 1154 | (cons cmd-opts 1155 | (if helm-ag--default-target 1156 | (append targets (helm-ag--construct-targets helm-ag--default-target)) 1157 | targets)))))) 1158 | 1159 | (defun helm-ag--do-ag-candidate-process (dir) 1160 | "Execute `helm-do-ag' in the specified directory DIR." 1161 | (let* ((non-essential nil) 1162 | (default-directory dir) 1163 | (cmd-args (helm-ag--construct-do-ag-command helm-pattern))) 1164 | (when cmd-args 1165 | (let ((proc (apply #'start-file-process "helm-do-ag" nil cmd-args))) 1166 | (setq helm-ag--last-query helm-pattern 1167 | helm-ag--last-command cmd-args 1168 | helm-ag--ignore-case (helm-ag--ignore-case-p cmd-args helm-pattern) 1169 | helm-ag--last-default-directory default-directory) 1170 | (prog1 proc 1171 | (set-process-sentinel 1172 | proc 1173 | (lambda (process event) 1174 | (helm-process-deferred-sentinel-hook 1175 | process event (helm-default-directory)) 1176 | (when (string= event "finished\n") 1177 | (helm-ag--do-ag-propertize helm-input))))))))) 1178 | 1179 | (defconst helm-do-ag--help-message 1180 | "\n* Helm Do Ag\n 1181 | 1182 | \n** Specific commands for Helm Ag:\n 1183 | \\ 1184 | \\[helm-ag--run-other-window-action]\t\t-> Open result in other buffer 1185 | \\[helm-ag--do-ag-up-one-level]\t\t-> Search in parent directory. 1186 | \\[helm-ag-edit]\t\t-> Edit search results. 1187 | \\[helm-ag--do-ag-help]\t\t-> Show this help. 1188 | \n** Helm Ag Map\n 1189 | \\{helm-map}" 1190 | "Help message for `helm-do-ag'") 1191 | 1192 | (defun helm-ag--do-ag-help () 1193 | "Display help for `helm-do-ag'" 1194 | (interactive) 1195 | (let ((helm-help-message helm-do-ag--help-message)) 1196 | (helm-help))) 1197 | 1198 | (defvar helm-do-ag-map 1199 | (let ((map (make-sparse-keymap))) 1200 | (set-keymap-parent map helm-ag-map) 1201 | (define-key map (kbd "C-l") 'helm-ag--do-ag-up-one-level) 1202 | (define-key map (kbd "C-c ?") 'helm-ag--do-ag-help) 1203 | map) 1204 | "Keymap for `helm-do-ag'.") 1205 | 1206 | (defun helm-ag--highlight-string-matched (str patterns) 1207 | "Highlight occurrences of PATTERNS in the given STR." 1208 | (with-temp-buffer 1209 | (insert str) 1210 | (goto-char (point-min)) 1211 | (dolist (pattern patterns) 1212 | (let ((last-point (point))) 1213 | (while (and (not (eobp)) (re-search-forward pattern nil t)) 1214 | (set-text-properties (match-beginning 0) (match-end 0) 1215 | '(face helm-match)) 1216 | (when (= last-point (point)) 1217 | (forward-char 1)) 1218 | (setq last-point (point))))) 1219 | (buffer-string))) 1220 | 1221 | (defun helm-ag--filter-one (candidate input) 1222 | "Filter a single CANDIDATE based on the INPUT." 1223 | (let ((patterns (helm-ag--do-ag-highlight-patterns input)) 1224 | (one-file-p (and (not (helm-ag--vimgrep-option)) 1225 | (helm-ag--search-only-one-file-p)))) 1226 | (if one-file-p 1227 | (if (string-match "^\\([^:]+\\):\\(.*\\)$" candidate) 1228 | (cons (concat (propertize (match-string-no-properties 1 candidate) 1229 | 'face 'helm-grep-lineno) 1230 | ":" 1231 | (helm-ag--highlight-string-matched 1232 | (match-string-no-properties 2 candidate) patterns)) 1233 | candidate) 1234 | candidate) 1235 | (let* ((split (helm-grep-split-line candidate)) 1236 | (file (nth 0 split)) 1237 | (lineno (nth 1 split)) 1238 | (str (nth 2 split))) 1239 | (if (and lineno str) 1240 | (cons (concat (propertize file 'face 'helm-moccur-buffer) 1241 | ":" 1242 | (propertize lineno 'face 'helm-grep-lineno) 1243 | ":" 1244 | (helm-ag--highlight-string-matched str patterns)) 1245 | candidate) 1246 | candidate))))) 1247 | 1248 | (defun helm-do-ag--filter-one-by-one (candidate) 1249 | "Filter a single CANDIDATE one by one." 1250 | (save-excursion 1251 | (if (consp candidate) 1252 | candidate 1253 | (when (stringp candidate) 1254 | (helm-ag--filter-one candidate helm-input))))) 1255 | 1256 | (defclass helm-do-ag-class (helm-source-async) 1257 | ((nohighlight :initform t) 1258 | (keymap :initform helm-do-ag-map) 1259 | (history :initform 'helm-ag--helm-history) 1260 | (filter-one-by-one :initform 'helm-do-ag--filter-one-by-one) 1261 | (candidate-number-limit :initform 99999) 1262 | (requires-pattern :initform 3) 1263 | (persistent-action :initform 'helm-ag--persistent-action) 1264 | (nomark :initform nil) 1265 | (action :initform 'helm-ag--actions)) 1266 | "helm source class for `helm-do-ag'") 1267 | 1268 | (defvar helm-source-do-ag nil) 1269 | 1270 | (defun helm-ag--do-ag-set-source (dir &optional search-dir) 1271 | "Set up the helm source for helm-ag search in DIR." 1272 | (let ((search-dir (or search-dir dir))) 1273 | (setq helm-source-do-ag 1274 | (helm-make-source "AG" 'helm-do-ag-class 1275 | :candidates-process 1276 | (lambda () 1277 | (helm-ag--do-ag-set-command) 1278 | (helm-ag--do-ag-candidate-process dir)) 1279 | :header-name 1280 | (lambda (_name) (helm-ag--helm-header search-dir)) 1281 | :follow (and helm-follow-mode-persistent 1))))) 1282 | 1283 | (defun helm-ag--do-ag-up-one-level () 1284 | "Execute a helm-ag search in the parent directory." 1285 | (interactive) 1286 | (if (or (not (helm-ag--root-directory-p)) 1287 | (y-or-n-p "Current directory might be the project root. \ 1288 | Continue searching the parent directory? ")) 1289 | (let ((parent (file-name-directory (directory-file-name default-directory))) 1290 | (initial-input helm-input)) 1291 | (helm-run-after-exit 1292 | (lambda () 1293 | (let ((default-directory parent) 1294 | (helm-ag--default-directory parent)) 1295 | (setq helm-ag--last-default-directory default-directory) 1296 | (helm-ag--do-ag-set-source default-directory) 1297 | (helm :sources 'helm-source-do-ag :buffer "*helm-ag*" 1298 | :keymap helm-do-ag-map :input initial-input 1299 | :history 'helm-ag--helm-history))))) 1300 | (message nil))) 1301 | 1302 | (defun helm-ag--set-do-ag-option () 1303 | "Set extra options for helm-ag search." 1304 | (if (or (< (prefix-numeric-value current-prefix-arg) 0) 1305 | helm-ag-always-set-extra-option) 1306 | (let ((option (read-string "Extra options: " (or helm-ag--extra-options "") 1307 | 'helm-ag--extra-options-history))) 1308 | (setq helm-ag--extra-options option)) 1309 | (setq helm-ag--extra-options nil))) 1310 | 1311 | (defun helm-ag--set-command-features () 1312 | "Set the features of the command used for searching." 1313 | (let ((cmd (intern (car (split-string helm-ag-base-command))))) 1314 | (setq helm-ag--command-features (list cmd)) 1315 | (cl-case cmd 1316 | (ack (add-to-list 'helm-ag--command-features 1317 | (if (string-match-p "-\\(?:Q\\|-literal\\)\\>" helm-ag-base-command) 1318 | 'fixed 1319 | 'pcre))) 1320 | (ag (add-to-list 'helm-ag--command-features 1321 | (if (string-match-p "-\\(?:[QF]\\|-literal\\|-fixed-strings\\)\\>" helm-ag-base-command) 1322 | 'fixed 1323 | 'pcre))) 1324 | (pt (add-to-list 'helm-ag--command-features 1325 | (if (string-match-p "-e\\>" helm-ag-base-command) 1326 | 're2 1327 | 'fixed))) 1328 | (rg (add-to-list 'helm-ag--command-features 1329 | (if (string-match-p "-\\(?:F\\|-fixed-strings\\)\\>" helm-ag-base-command) 1330 | 'fixed 1331 | (if (string-match-p "--pcre2\\>" helm-ag-base-command) 1332 | 'pcre 1333 | 're2))))))) 1334 | 1335 | (defun helm-ag--do-ag-searched-extensions () 1336 | "Retrieve file extensions to search based on the prefix argument." 1337 | (when (and current-prefix-arg (= (abs (prefix-numeric-value current-prefix-arg)) 4)) 1338 | (helm-grep-get-file-extensions helm-ag--default-target))) 1339 | 1340 | (defsubst helm-do-ag--target-one-directory-p (targets) 1341 | "Check if TARGETS contains exactly one directory." 1342 | (and (listp targets) (= (length targets) 1) (file-directory-p (car targets)))) 1343 | 1344 | (defun helm-do-ag--helm (default-input search-this-file) 1345 | "Start a helm session for searching with `helm-ag'." 1346 | (let ((search-dir (if (not (helm-ag--windows-p)) 1347 | helm-ag--default-directory 1348 | (if (helm-do-ag--target-one-directory-p helm-ag--default-target) 1349 | (car helm-ag--default-target)))) 1350 | (dir (or helm-ag--default-directory 1351 | helm-ag--last-default-directory 1352 | default-directory))) 1353 | (helm-ag--do-ag-set-source dir search-dir) 1354 | (helm-attrset 'search-this-file search-this-file helm-source-do-ag) 1355 | (helm :sources 'helm-source-do-ag :buffer "*helm-ag*" :keymap helm-do-ag-map 1356 | :input (or default-input (helm-ag--marked-input t) 1357 | (helm-ag--insert-thing-at-point helm-ag-insert-at-point)) 1358 | :history 'helm-ag--helm-history))) 1359 | 1360 | ;;;###autoload 1361 | (defun helm-do-ag-this-file (&optional query) 1362 | "Execute `helm-do-ag' Search in the current file." 1363 | (interactive) 1364 | (helm-aif (buffer-file-name) 1365 | (helm-do-ag default-directory (list it) query) 1366 | (error "Error: This buffer is not visited file"))) 1367 | 1368 | ;;;###autoload 1369 | (defun helm-do-ag (&optional basedir targets default-input) 1370 | "Execute a `helm-do-ag' search in the specified directory or files. 1371 | 1372 | BASEDIR: An optional directory in which to perform the search. If not 1373 | specified, the current directory is used. 1374 | TARGETS: A list of files or directories to search. If not provided, 1375 | the user will be prompted to select files. 1376 | DEFAULT-INPUT: An optional string that serves as the initial input for 1377 | the search. 1378 | 1379 | The function initializes the helm state, determines the default directory 1380 | and target, and sets up the helm source for searching. It handles Windows 1381 | specific behavior for path arguments and saves the current context for 1382 | subsequent searches." 1383 | (interactive) 1384 | (require 'helm-mode) 1385 | (helm-ag--init-state) 1386 | (let* ((helm-ag--default-directory (or basedir default-directory)) 1387 | (helm-ag--default-target (cond (targets targets) 1388 | ((and (helm-ag--windows-p) basedir) (list basedir)) 1389 | (t 1390 | (when (and (not basedir) (not helm-ag--buffer-search)) 1391 | (helm-read-file-name 1392 | "Search in file(s): " 1393 | :default default-directory 1394 | :marked-candidates t :must-match t))))) 1395 | (helm-do-ag--extensions (when helm-ag--default-target 1396 | (helm-ag--do-ag-searched-extensions))) 1397 | (one-directory-p (helm-do-ag--target-one-directory-p 1398 | helm-ag--default-target)) 1399 | (search-this-file (and (= (length helm-ag--default-target) 1) 1400 | (not (file-directory-p (car helm-ag--default-target))) 1401 | (car helm-ag--default-target)))) 1402 | (helm-ag--set-do-ag-option) 1403 | (helm-ag--set-command-features) 1404 | (helm-ag--save-current-context) 1405 | (if (or (helm-ag--windows-p) (not one-directory-p)) ;; Path argument must be specified on Windows 1406 | (helm-do-ag--helm default-input search-this-file) 1407 | (let* ((helm-ag--default-directory 1408 | (file-name-as-directory (car helm-ag--default-target))) 1409 | (helm-ag--default-target nil)) 1410 | (helm-do-ag--helm default-input search-this-file))))) 1411 | 1412 | (defun helm-ag--project-root () 1413 | "Find the root directory of the current project." 1414 | (cl-loop for dir in '(".git/" ".hg/" ".svn/" ".git") 1415 | when (locate-dominating-file default-directory dir) 1416 | return it)) 1417 | 1418 | ;;;###autoload 1419 | (defun helm-ag-project-root (&optional query) 1420 | "Execute `helm-ag' in the project directory with optional default input QUERY." 1421 | (interactive) 1422 | (let ((rootdir (helm-ag--project-root))) 1423 | (unless rootdir 1424 | (error "Could not find the project root. Create a git, hg, or svn repository there first")) 1425 | (helm-ag rootdir query))) 1426 | 1427 | ;;;###autoload 1428 | (defun helm-do-ag-project-root (&optional query) 1429 | "Execute `helm-do-ag' in the project directory with optional default input QUERY.." 1430 | (interactive) 1431 | (let ((rootdir (helm-ag--project-root))) 1432 | (unless rootdir 1433 | (error "Could not find the project root. Create a git, hg, or svn repository there first")) 1434 | (helm-do-ag rootdir nil query))) 1435 | 1436 | ;;;###autoload 1437 | (defun helm-ag-buffers (&optional query) 1438 | "Execute `helm-ag' in all buffers with optional default input QUERY." 1439 | (interactive) 1440 | (let ((helm-ag--buffer-search t)) 1441 | (helm-ag nil query))) 1442 | 1443 | ;;;###autoload 1444 | (defun helm-do-ag-buffers (&optional query) 1445 | "Execute `helm-do-ag' in all buffers with optional default input QUERY." 1446 | (interactive) 1447 | (let ((helm-ag--buffer-search t)) 1448 | (helm-do-ag nil nil query))) 1449 | 1450 | (provide 'helm-ag) 1451 | 1452 | ;;; helm-ag.el ends here 1453 | --------------------------------------------------------------------------------