nixos-dotfiles
nixos-dotfiles
https://git.tonybtw.com/nixos-dotfiles.git
git://git.tonybtw.com/nixos-dotfiles.git
Remove config/temacs for subtree re-add
Diff
diff --git a/config/temacs/.gitignore b/config/temacs/.gitignore
deleted file mode 100644
index 6ea7464..0000000
--- a/config/temacs/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-elpa/
-eln-cache/
-auto-save-list/
-tramp
-transient/
-recentf
-custom.el
-*.elc
-*~
-\#*\#
diff --git a/config/temacs/config.el b/config/temacs/config.el
deleted file mode 100644
index 3944508..0000000
--- a/config/temacs/config.el
+++ /dev/null
@@ -1,187 +0,0 @@
-;;; config.el --- Configuration -*- lexical-binding: t -*-
-
-(when (file-directory-p "/run/current-system/sw/lib")
- (add-to-list 'treesit-extra-load-path "/run/current-system/sw/lib"))
-
-(setq epg-pinentry-mode 'loopback)
-
-(add-hook 'org-mode-hook 'visual-line-mode)
-
-(defun rc/get-default-font ()
- (cond
- ((eq system-type 'windows-nt) "Consolas-13")
- ((eq system-type 'gnu/linux) "Iosevka Nerd Font-24")))
-
-(add-to-list 'default-frame-alist `(font . ,(rc/get-default-font)))
-
-(tool-bar-mode 0)
-(menu-bar-mode 0)
-(scroll-bar-mode 0)
-(column-number-mode 1)
-(show-paren-mode 1)
-(global-display-line-numbers-mode 1)
-
-(setq-default c-basic-offset 4
- c-default-style '((java-mode . "java")
- (awk-mode . "awk")
- (other . "bsd")))
-
-(add-hook 'c-mode-hook (lambda ()
- (interactive)
- (c-toggle-comment-style -1)))
-
-(add-hook 'rust-mode-hook
- (lambda ()
- (setq-local eglot-workspace-configuration
- '(:rust-analyzer
- (:cargo (:allFeatures t)
- :rustfmt (:extraArgs ["--edition" "2021"]))))
- (add-hook 'before-save-hook 'eglot-format-buffer nil t)))
-
-(add-hook 'before-save-hook 'delete-trailing-whitespace)
-
-(require 'dired-x)
-(require 'dired-aux)
-(setq dired-omit-files
- (concat dired-omit-files "\\|^\\..+$"))
-(setq-default dired-dwim-target t)
-(setq dired-listing-switches "-alh")
-
-(setq-default indent-tabs-mode nil)
-(setq-default tab-width 4)
-(setq make-backup-files nil)
-(setq auto-save-default nil)
-
-(defvar my/base-dir "/home/tony")
-
-(defun my/set-base-dir ()
- "Set the root directory for searches."
- (interactive)
- (setq my/base-dir (read-directory-name "Set base directory: " my/base-dir))
- (message "Search root set to: %s" my/base-dir))
-
-(defun my/consult-fd ()
- "Find files from base dir."
- (interactive)
- (let ((default-directory my/base-dir))
- (consult-fd)))
-
-(defun my/fzf-find-file ()
- "Find files from base dir using fzf."
- (interactive)
- (let ((default-directory my/base-dir))
- (fzf-find-file)))
-
-(defun my/affe-find ()
- "Find files from base dir using affe (async fuzzy)."
- (interactive)
- (affe-find my/base-dir))
-
-(load (expand-file-name "telescope.el" user-emacs-directory))
-
-(defun my/telescope-find-files ()
- "Find files from base dir using telescope."
- (interactive)
- (telescope-find-files my/base-dir))
-
-(defun my/consult-ripgrep ()
- "Ripgrep from base dir."
- (interactive)
- (consult-ripgrep my/base-dir))
-
-(defun my/consult-ripgrep-symbol ()
- "Ripgrep symbol at point from base dir."
- (interactive)
- (consult-ripgrep my/base-dir (thing-at-point 'symbol t)))
-
-(defun my/find-emacs-config ()
- "Find files in emacs config dir."
- (interactive)
- (let ((default-directory "/home/tony/.emacs.d/"))
- (consult-fd)))
-
-(defun my/switch-project ()
- "Pick a project from ~/repos, set base-dir, open dired."
- (interactive)
- (let* ((repos-dir "~/repos/")
- (dirs (seq-filter
- (lambda (f) (file-directory-p (expand-file-name f repos-dir)))
- (directory-files repos-dir nil "^[^.]")))
- (chosen (completing-read "Project: " dirs nil t)))
- (when chosen
- (let ((project-dir (expand-file-name chosen repos-dir)))
- (setq my/base-dir project-dir)
- (dired project-dir)
- (message "Base dir: %s" project-dir)))))
-
-(defun my/vterm-here ()
- "Open vterm in current window."
- (interactive)
- (let ((default-directory (or (and buffer-file-name (file-name-directory buffer-file-name))
- default-directory))
- (display-buffer-alist nil)) ; bypass all display rules
- (pop-to-buffer-same-window (vterm "*vterm*"))))
-
-(setq display-buffer-alist
- '(("\\*xref\\*\\|\\*compilation\\*\\|\\*grep\\*"
- (display-buffer-reuse-window display-buffer-below-selected)
- (window-height . 0.35))))
-
-(defun my/close-popup-window ()
- "Close windows showing xref, compilation, grep, or help buffers."
- (interactive)
- (dolist (win (window-list))
- (when (string-match-p "\\*xref\\*\\|\\*compilation\\*\\|\\*grep\\*\\|\\*Help\\*"
- (buffer-name (window-buffer win)))
- (delete-window win))))
-
-(defun my/reformat-parenthesized-content ()
- "Reformat comma-separated content inside parentheses to multiple lines."
- (interactive)
- (let* ((line (thing-at-point 'line t))
- (inside (and (string-match "(\\([^)]+\\))" line)
- (match-string 1 line))))
- (if (not inside)
- (message "No content found inside parentheses")
- (let* ((prefix (and (string-match "^\\(.*?\\)(" line)
- (match-string 1 line)))
- (suffix (and (string-match ")\\(.*\\)$" line)
- (match-string 1 line)))
- (parts (split-string inside "," t "[ \t]*"))
- (new-lines (list (concat prefix "("))))
- (dotimes (i (length parts))
- (let ((part (nth i parts)))
- (if (< i (1- (length parts)))
- (push (concat " " part ",") new-lines)
- (push (concat " " part) new-lines))))
- (push (concat " )" (string-trim-right suffix)) new-lines)
- (setq new-lines (nreverse new-lines))
- (beginning-of-line)
- (kill-line 1)
- (insert (mapconcat 'identity new-lines "\n") "\n")))))
-
-(add-hook 'prog-mode-hook
- (lambda ()
- (modify-syntax-entry ?- "w")
- (modify-syntax-entry ?_ "w")))
-
-(require 'erc)
-(require 'erc-services)
-(erc-services-mode 1)
-
-(setq erc-nick "tonybtw"
- erc-user-full-name "tony"
- erc-prompt-for-nickserv-password nil
- erc-nickserv-identify-mode 'autodetect
- erc-use-auth-source-for-nickserv-password t)
-
-(setq erc-autojoin-channels-alist
- '(("libera.chat" "#technicalrenaissance")))
-
-(defun my/erc-connect-libera ()
- "Connect to Libera.Chat via ZNC bouncer."
- (interactive)
- (erc-tls :server "znc.tonybtw.com"
- :port 6697
- :nick "tonybtw"
- :user "tonybtw/libera"))
diff --git a/config/temacs/init.el b/config/temacs/init.el
deleted file mode 100644
index 928b114..0000000
--- a/config/temacs/init.el
+++ /dev/null
@@ -1,40 +0,0 @@
-;;; init.el --- Main entry point -*- lexical-binding: t -*-
-
-(setq byte-compile-warnings '(not obsolete))
-(setq warning-suppress-log-types '((comp) (bytecomp)))
-(setq native-comp-async-report-warnings-errors 'silent)
-
-(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
-(package-initialize)
-
-(add-to-list 'package-archives
- '("melpa" . "https://melpa.org/packages/") t)
-
-(defvar rc/package-contents-refreshed nil)
-
-(defun rc/package-refresh-contents-once ()
- (when (not rc/package-contents-refreshed)
- (setq rc/package-contents-refreshed t)
- (package-refresh-contents)))
-
-(defun rc/require-one-package (package)
- (when (not (package-installed-p package))
- (rc/package-refresh-contents-once)
- (package-install package)))
-
-(defun rc/require (&rest packages)
- (dolist (package packages)
- (rc/require-one-package package)))
-
-(defun rc/require-theme (theme)
- (let ((theme-package (intern (concat (symbol-name theme) "-theme"))))
- (rc/require theme-package)
- (load-theme theme t)))
-
-; Load config files
-(load (expand-file-name "packages.el" user-emacs-directory))
-(load (expand-file-name "config.el" user-emacs-directory))
-(load (expand-file-name "keybinds.el" user-emacs-directory))
-
-(when (file-exists-p custom-file)
- (load-file custom-file))
diff --git a/config/temacs/keybinds.el b/config/temacs/keybinds.el
deleted file mode 100644
index d4f8d69..0000000
--- a/config/temacs/keybinds.el
+++ /dev/null
@@ -1,144 +0,0 @@
-;;; keybinds.el --- All keybindings -*- lexical-binding: t -*-
-
-;; C-c to escape insert mode
-(define-key evil-insert-state-map (kbd "C-c") 'evil-normal-state)
-
-;;; Global keys
-(global-set-key (kbd "M-x") 'execute-extended-command)
-(global-set-key (kbd "C-x b") 'consult-buffer)
-(global-set-key (kbd "C-c m s") 'magit-status)
-(global-set-key (kbd "C-c m l") 'magit-log)
-
-;;; Multiple cursors
-(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
-(global-set-key (kbd "C->") 'mc/mark-next-like-this)
-(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
-(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
-(global-set-key (kbd "C-\"") 'mc/skip-to-next-like-this)
-(global-set-key (kbd "C-:") 'mc/skip-to-previous-like-this)
-
-;;; Move text
-(global-set-key (kbd "M-p") 'move-text-up)
-(global-set-key (kbd "M-n") 'move-text-down)
-
-;;; Dired evil bindings
-(with-eval-after-load 'dired
- (evil-define-key 'normal dired-mode-map
- "." 'dired-create-empty-file
- "h" 'dired-up-directory
- "l" 'dired-find-file
- "n" 'evil-search-next
- "N" 'evil-search-previous))
-
-;;; Org mode evil bindings
-(defun my/org-heading-has-checkbox-p ()
- "Check if current heading has [ ] or [X] checkbox."
- (save-excursion
- (beginning-of-line)
- (looking-at "^\\*+\\s-+\\[[ X-]\\]")))
-
-(defun my/org-update-parent-cookie ()
- "Update parent heading's [/] or [n/m] cookie by counting child heading checkboxes."
- (save-excursion
- (when (org-up-heading-safe)
- (let ((start (point))
- (end (save-excursion (org-end-of-subtree t) (point)))
- (level (org-current-level))
- (checked 0)
- (total 0))
- ;; Count direct child headings with checkboxes
- (save-excursion
- (while (re-search-forward (format "^\\*\\{%d\\}\\s-+\\[\\([ X-]\\)\\]" (1+ level)) end t)
- (setq total (1+ total))
- (when (string= (match-string 1) "X")
- (setq checked (1+ checked)))))
- ;; Update the cookie in parent heading
- (beginning-of-line)
- (when (re-search-forward "\\[\\([0-9]*/[0-9]*\\|/\\)\\]" (line-end-position) t)
- (replace-match (format "[%d/%d]" checked total) t t))))))
-
-(defun my/org-toggle-heading-checkbox ()
- "Toggle [ ] <-> [X] in current heading and update parent cookie."
- (save-excursion
- (beginning-of-line)
- (when (re-search-forward "\\[\\([ X-]\\)\\]" (line-end-position) t)
- (replace-match (if (string= (match-string 1) " ") "[X]" "[ ]") t t)))
- (my/org-update-parent-cookie))
-
-(defun my/org-dwim-at-point ()
- "Do-what-I-mean at point: toggle checkbox, follow link, or cycle TODO."
- (interactive)
- (cond
- ;; Heading with [ ] or [X]
- ((my/org-heading-has-checkbox-p)
- (my/org-toggle-heading-checkbox))
- ;; List checkbox
- ((org-at-item-checkbox-p)
- (org-toggle-checkbox))
- ;; Link
- ((org-in-regexp org-link-any-re)
- (org-open-at-point))
- ;; Regular TODO heading
- ((org-at-heading-p)
- (org-todo))
- (t (org-return))))
-
-(with-eval-after-load 'org
- (evil-define-key 'normal org-mode-map
- (kbd "RET") 'my/org-dwim-at-point
- (kbd "<return>") 'my/org-dwim-at-point
- "t" 'org-todo))
-
-;;; LSP keybindings
-(with-eval-after-load 'eglot
- (evil-define-key 'normal eglot-mode-map
- "K" 'eldoc-box-help-at-point
- "gd" 'xref-find-definitions
- "gr" 'xref-find-references))
-
-;;; Leader keybindings (SPC)
-(evil-leader/set-key
- ;; files
- "ff" 'my/telescope-find-files
- "fF" 'my/fzf-find-file
- "fg" 'my/consult-ripgrep
- "fs" 'my/consult-ripgrep-symbol
- "fo" 'consult-recent-file
- "fl" 'consult-line
- "fi" 'my/find-emacs-config
- "fp" 'my/switch-project
- ;; buffers
- "bb" 'consult-buffer
- "fb" 'consult-buffer
- "bp" 'previous-buffer
- "bn" 'next-buffer
- "bd" 'kill-current-buffer
- "bm" 'ibuffer
- ;; custom
- "cr" 'my/set-base-dir
- "cd" (lambda () (interactive) (dired (file-name-directory (or buffer-file-name default-directory))))
- "cl" 'my/close-popup-window
- "cn" 'next-error
- "cp" 'previous-error
- "cc" 'compile
- "cm" 'recompile
- "cb" (lambda () (interactive) (compile "bear -- make"))
- ;; magit
- "ms" 'magit-status
- "ml" 'magit-log
- ;; ssh/servers
- ; "st" 'my/connect-tonydev
- ;; terminal
- "to" 'my/vterm-here
- ;; window
- "wv" 'split-window-right
- "ws" 'split-window-below
- "wd" 'delete-window
- "wh" 'evil-window-left
- "wj" 'evil-window-down
- "wk" 'evil-window-up
- "wl" 'evil-window-right
- ;; reformat
- "qq" 'my/reformat-parenthesized-content
- ;; ERC
- "ec" 'my/erc-connect-libera)
diff --git a/config/temacs/packages.el b/config/temacs/packages.el
deleted file mode 100644
index cc182c1..0000000
--- a/config/temacs/packages.el
+++ /dev/null
@@ -1,221 +0,0 @@
-;;; packages.el --- Package configuration -*- lexical-binding: t -*-
-
-;;; evil mode (vim bindings)
-(setq evil-want-keybinding nil) ; required before loading evil-collection
-(setq evil-search-module 'evil-search) ; required for cgn
-(setq evil-undo-system 'undo-redo) ; use emacs 28+ native undo-redo
-(rc/require 'evil 'evil-leader 'evil-collection 'evil-commentary)
-(global-evil-leader-mode)
-(evil-leader/set-leader "<SPC>")
-(evil-mode 1)
-(evil-collection-init)
-(evil-commentary-mode 1)
-
-;;; Vertico + Consult + Orderless (telescope-like fuzzy finding)
-(rc/require 'vertico 'consult 'orderless 'marginalia 'vertico-posframe 'fzf 'affe)
-(vertico-mode 1)
-(vertico-posframe-mode 1)
-(marginalia-mode 1)
-(recentf-mode 1)
-
-(setq vertico-posframe-parameters
- '((left-fringe . 8)
- (right-fringe . 8)))
-(setq vertico-posframe-poshandler #'posframe-poshandler-frame-center)
-
-(setq completion-styles '(orderless basic)
- completion-category-defaults nil
- completion-category-overrides '((file (styles . (partial-completion)))))
-
-;; Flex matching (fzf-style: characters in sequence)
-(setq orderless-matching-styles '(orderless-literal orderless-flex))
-
-;; Affe (async fuzzy finder using orderless)
-(setq affe-find-command "fd --color=never -t f")
-
-(setq consult-fd-args '("fd" "--color=never" "--type" "f" "--hidden" "--follow" "--exclude" ".git"))
-
-;; Live preview as you navigate
-(setq consult-preview-key 'any)
-
-;;; magit
-(rc/require 'magit)
-(setq magit-auto-revert-mode nil)
-
-;;; multiple cursors
-(rc/require 'multiple-cursors)
-
-;;; Move Text
-(rc/require 'move-text)
-
-;;; Company (autocompletion)
-(rc/require 'company)
-(global-company-mode)
-
-;;; Language modes
-(rc/require 'nix-mode 'zig-mode 'rust-mode 'php-mode 'web-mode 'go-mode 'typescript-mode)
-
-
-;;; Tree-sitter text objects (vif, vaf, vic, vac, etc.)
-(rc/require 'tree-sitter 'tree-sitter-langs 'evil-textobj-tree-sitter)
-(global-tree-sitter-mode)
-(add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode)
-(define-key evil-outer-text-objects-map "f" (evil-textobj-tree-sitter-get-textobj "function.outer"))
-(define-key evil-inner-text-objects-map "f" (evil-textobj-tree-sitter-get-textobj "function.inner"))
-(define-key evil-outer-text-objects-map "c" (evil-textobj-tree-sitter-get-textobj "class.outer"))
-(define-key evil-inner-text-objects-map "c" (evil-textobj-tree-sitter-get-textobj "class.inner"))
-
-;;; CSS color preview
-(rc/require 'rainbow-mode)
-(add-hook 'css-mode-hook 'rainbow-mode)
-(add-hook 'php-mode-hook 'rainbow-mode)
-(add-hook 'html-mode-hook 'rainbow-mode)
-(add-hook 'js-mode-hook 'rainbow-mode)
-(add-hook 'web-mode-hook 'rainbow-mode)
-(add-hook 'scss-mode-hook 'rainbow-mode)
-(add-hook 'conf-mode-hook 'rainbow-mode)
-(add-hook 'toml-mode-hook 'rainbow-mode)
-(add-hook 'yaml-mode-hook 'rainbow-mode)
-(add-hook 'conf-toml-mode-hook 'rainbow-mode)
-
-;;; Treesitter context (sticky function header)
-(rc/require 'topsy)
-(add-hook 'prog-mode-hook 'topsy-mode)
-
-;;; Org mode
-(rc/require 'org-superstar 'org-fancy-priorities)
-
-(setq org-directory "~/org/")
-(setq org-agenda-files '("~/repos/agendas/private.org"))
-
-;; Pretty bullets
-(add-hook 'org-mode-hook #'org-superstar-mode)
-(setq org-superstar-headline-bullets-list '("◉" "●" "○" "◆" "●" "○" "◆"))
-
-;; Priority icons
-(add-hook 'org-mode-hook #'org-fancy-priorities-mode)
-(setq org-fancy-priorities-list '("⚑" "▲" "»"))
-
-;; Syntax highlighting in code blocks
-(setq org-src-fontify-natively t
- org-src-tab-acts-natively t
- org-hide-block-startup nil
- org-src-preserve-indentation nil
- org-edit-src-content-indentation 0)
-
-;; Hide emphasis markers (*bold*, /italic/, etc.)
-(setq org-hide-emphasis-markers t)
-
-;; Visual tweaks
-(setq org-ellipsis " ▾") ; nicer fold indicator
-(setq org-startup-folded 'content) ; show headings on open
-(add-hook 'org-mode-hook #'org-indent-mode) ; clean indentation
-
-;; Make RET follow links and toggle checkboxes
-(setq org-return-follows-link t)
-
-;;; Org Present (presentation mode)
-(rc/require 'org-present 'visual-fill-column)
-
-(defun my/org-present-start ()
- ;; Smaller, more readable font scaling
- (setq-local face-remapping-alist
- '((default (:height 1.3) default)
- (header-line (:height 2.0) variable-pitch)
- (org-document-title (:height 1.5) org-document-title)
- (org-level-1 (:height 1.3) org-level-1)
- (org-level-2 (:height 1.2) org-level-2)
- (org-level-3 (:height 1.1) org-level-3)
- (org-code (:height 1.0) org-code)
- (org-block (:height 1.0) org-block)))
- ;; Center content
- (setq visual-fill-column-width 80)
- (setq visual-fill-column-center-text t)
- (visual-fill-column-mode 1)
- ;; Word wrap
- (visual-line-mode 1)
- ;; Hide UI
- (setq header-line-format " ")
- (display-line-numbers-mode 0)
- (org-display-inline-images))
-
-(defun my/org-present-end ()
- (setq-local face-remapping-alist nil)
- (setq header-line-format nil)
- (visual-fill-column-mode 0)
- (visual-line-mode 0)
- (display-line-numbers-mode 1)
- (org-remove-inline-images))
-
-(add-hook 'org-present-mode-hook #'my/org-present-start)
-(add-hook 'org-present-mode-quit-hook #'my/org-present-end)
-
-;;; LSP (eglot is built-in to Emacs 29+)
-(require 'eglot)
-(rc/require 'eldoc-box)
-
-;; Auto-start LSP for these modes
-(add-hook 'zig-mode-hook 'eglot-ensure)
-(add-hook 'nix-mode-hook 'eglot-ensure)
-(add-hook 'rust-mode-hook 'eglot-ensure)
-(add-hook 'c-mode-hook 'eglot-ensure)
-(add-hook 'php-mode-hook 'eglot-ensure)
-(add-hook 'go-mode-hook 'eglot-ensure)
-(add-hook 'typescript-mode-hook 'eglot-ensure)
-(add-hook 'tsx-ts-mode-hook 'eglot-ensure)
-
-;; LSP server configurations
-(with-eval-after-load 'eglot
- ;; PHP
- (add-to-list 'eglot-server-programs
- '(php-mode . ("intelephense" "--stdio")))
- (add-to-list 'eglot-server-programs
- '(web-mode . ("intelephense" "--stdio")))
- ;; TypeScript/TSX (typescript-language-server)
- (add-to-list 'eglot-server-programs
- '(typescript-mode . ("typescript-language-server" "--stdio")))
- (add-to-list 'eglot-server-programs
- '(tsx-ts-mode . ("typescript-language-server" "--stdio"))))
-
-;; File associations for TypeScript React
-(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))
-(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode))
-
-;; Go format on save
-(add-hook 'go-mode-hook
- (lambda ()
- (add-hook 'before-save-hook 'eglot-format-buffer nil t)))
-
-(add-hook 'web-mode-hook 'eglot-ensure)
-
-
-;;; Direnv integration (loads devshell environment)
-(rc/require 'envrc)
-(envrc-global-mode)
-
-;;; vterm (terminal emulator)
-(rc/require 'vterm)
-(defun rc/find-shell ()
- "Find a suitable shell, checking common locations."
- (or (getenv "SHELL")
- (seq-find #'file-executable-p
- '("/bin/bash" ; FHS standard
- "/usr/bin/bash" ; Some distros
- "/run/current-system/sw/bin/bash" ; NixOS
- "/bin/sh")) ; Ultimate fallback
- "/bin/sh"))
-(setq vterm-shell (rc/find-shell))
-(setq vterm-kill-buffer-on-exit t)
-
-;;; Theme
-;; (rc/require-theme 'gruber-darker)
-(rc/require 'doom-themes)
-(load-theme 'doom-palenight t)
-
-;;; Clean up modeline (hide minor modes)
-(setq eldoc-minor-mode-string nil)
-(setq company-lighter nil)
-(setq-default abbrev-mode nil)
-(with-eval-after-load 'flymake (setq flymake-mode-line-format nil))
-(with-eval-after-load 'envrc (setq envrc-lighter nil))
-(with-eval-after-load 'evil-commentary (setq evil-commentary-mode-lighter nil))
diff --git a/config/temacs/telescope.el b/config/temacs/telescope.el
deleted file mode 100644
index a91ea57..0000000
--- a/config/temacs/telescope.el
+++ /dev/null
@@ -1,320 +0,0 @@
-;;; telescope.el --- Telescope-like fuzzy finder -*- lexical-binding: t -*-
-
-(require 'posframe)
-
-(defgroup telescope nil
- "Telescope-like fuzzy finder."
- :group 'convenience)
-
-(defcustom telescope-fd-command "fd -t f --color=never --hidden --exclude .git"
- "Command to list files."
- :type 'string
- :group 'telescope)
-
-(defcustom telescope-results-width-pct 0.35
- "Width of results window as percentage of frame."
- :type 'float
- :group 'telescope)
-
-(defcustom telescope-preview-width-pct 0.50
- "Width of preview window as percentage of frame."
- :type 'float
- :group 'telescope)
-
-(defcustom telescope-height-pct 0.70
- "Height of telescope as percentage of frame."
- :type 'float
- :group 'telescope)
-
-(defcustom telescope-gap 2
- "Gap between results and preview panes in characters."
- :type 'integer
- :group 'telescope)
-
-(defcustom telescope-padding 2
- "Internal padding within panes in characters."
- :type 'integer
- :group 'telescope)
-
-;; Internal state
-(defvar telescope--input "")
-(defvar telescope--files nil)
-(defvar telescope--filtered nil)
-(defvar telescope--selected 0)
-(defvar telescope--base-dir nil)
-(defvar telescope--results-buffer " *telescope-results*")
-(defvar telescope--preview-buffer " *telescope-preview*")
-
-(defun telescope--get-files (dir)
- "Get all files in DIR using fd."
- (let ((default-directory dir))
- (split-string
- (shell-command-to-string (concat telescope-fd-command " ."))
- "\n" t)))
-
-(defun telescope--fzf-filter (files query)
- "Filter FILES with QUERY using fzf --filter."
- (if (or (null files) (string-empty-p query))
- (seq-take files 100)
- (with-temp-buffer
- (insert (string-join files "\n"))
- (call-process-region (point-min) (point-max) "fzf" t t nil "--filter" query)
- (split-string (buffer-string) "\n" t))))
-
-(defun telescope--render-results ()
- "Render the results buffer."
- (with-current-buffer (get-buffer-create telescope--results-buffer)
- (let* ((inhibit-read-only t)
- (pad (make-string telescope-padding ?\s))
- (max-results (- telescope--height 5)) ; leave room for prompt/footer/padding
- (content-width (- telescope--results-width (* telescope-padding 2) 2))
- (separator (make-string (min content-width 80) ?─)))
- (erase-buffer)
- ;; Top padding
- (insert "\n")
- ;; Prompt
- (insert pad)
- (insert (propertize "> " 'face '(:foreground "#87afff" :weight bold)))
- (insert telescope--input)
- (insert (propertize "█" 'face 'cursor))
- (insert "\n")
- (insert pad)
- (insert (propertize separator 'face '(:foreground "#444444")))
- (insert "\n")
- ;; Results
- (if (null telescope--filtered)
- (progn
- (insert pad)
- (insert (propertize " No matches\n" 'face '(:foreground "#666666" :slant italic))))
- (let ((idx 0))
- (dolist (file (seq-take telescope--filtered max-results))
- (let* ((max-len (- content-width 4))
- (display (if (> (length file) max-len)
- (concat "..." (substring file (- 3 max-len)))
- file))
- (selected (= idx telescope--selected))
- (face (if selected
- '(:background "#3a3a3a" :weight bold)
- nil))
- (prefix (if selected "→ " " ")))
- (insert pad)
- (insert (propertize (concat prefix display "\n") 'face face)))
- (cl-incf idx))))
- ;; Footer
- (insert pad)
- (insert (propertize separator 'face '(:foreground "#444444")))
- (insert "\n")
- (insert pad)
- (insert (propertize (format "%d/%d"
- (min (1+ telescope--selected) (length telescope--filtered))
- (length telescope--filtered))
- 'face '(:foreground "#666666"))))))
-
-(defun telescope--update-preview ()
- "Update preview buffer with selected file content."
- (with-current-buffer (get-buffer-create telescope--preview-buffer)
- (let ((inhibit-read-only t)
- (pad (make-string telescope-padding ?\s)))
- (erase-buffer)
- (if-let* ((selected (nth telescope--selected telescope--filtered))
- (path (expand-file-name selected telescope--base-dir))
- (_ (and (file-exists-p path)
- (file-regular-p path)
- (not (file-directory-p path)))))
- (condition-case nil
- (progn
- ;; Top padding
- (insert "\n")
- ;; File name header
- (insert pad)
- (insert (propertize (file-name-nondirectory path) 'face '(:foreground "#87afff" :weight bold)))
- (insert "\n")
- (insert pad)
- (insert (propertize (make-string (min 60 (- telescope--preview-width 4)) ?─) 'face '(:foreground "#444444")))
- (insert "\n")
- ;; Content with left padding
- (let ((start (point)))
- (insert-file-contents path nil 0 10000)
- (goto-char start)
- ;; Add padding to each line
- (while (not (eobp))
- (insert pad)
- (forward-line 1)))
- (goto-char (point-min))
- ;; Apply syntax highlighting
- (when-let ((mode (assoc-default path auto-mode-alist 'string-match)))
- (delay-mode-hooks (funcall mode))
- (font-lock-ensure)))
- (error
- (erase-buffer)
- (insert "\n" pad)
- (insert (propertize "Cannot preview file" 'face '(:foreground "#666666")))))
- (insert "\n" pad)
- (insert (propertize "No preview available" 'face '(:foreground "#666666")))))))
-
-(defvar telescope--results-x 0)
-(defvar telescope--results-y 0)
-(defvar telescope--preview-x 0)
-(defvar telescope--results-width 55)
-(defvar telescope--preview-width 75)
-(defvar telescope--height 25)
-
-(defun telescope--calc-dimensions ()
- "Calculate frame dimensions based on percentages."
- (let ((frame-cols (frame-width))
- (frame-rows (frame-height)))
- (setq telescope--results-width (max 30 (floor (* frame-cols telescope-results-width-pct))))
- (setq telescope--preview-width (max 40 (floor (* frame-cols telescope-preview-width-pct))))
- (setq telescope--height (max 15 (floor (* frame-rows telescope-height-pct))))))
-
-(defun telescope--calc-positions ()
- "Calculate frame positions."
- (telescope--calc-dimensions)
- (let* ((char-width (frame-char-width))
- (char-height (frame-char-height))
- (border-px 4)
- (gap-px (* telescope-gap char-width))
- (total-pixel-width (+ (* telescope--results-width char-width)
- (* telescope--preview-width char-width)
- (* 2 border-px)
- gap-px))
- (total-pixel-height (* telescope--height char-height))
- (start-x (max 0 (/ (- (frame-pixel-width) total-pixel-width) 2)))
- (start-y (max 0 (/ (- (frame-pixel-height) total-pixel-height) 2))))
- (setq telescope--results-x start-x)
- (setq telescope--results-y start-y)
- (setq telescope--preview-x (+ start-x (* telescope--results-width char-width) border-px gap-px))))
-
-(defun telescope--results-poshandler (_info)
- "Position handler for results frame."
- (cons telescope--results-x telescope--results-y))
-
-(defun telescope--preview-poshandler (_info)
- "Position handler for preview frame."
- (cons telescope--preview-x telescope--results-y))
-
-(defun telescope--show-frames ()
- "Display the telescope posframes."
- (telescope--calc-positions)
- ;; Results frame (left)
- (posframe-show telescope--results-buffer
- :position (point-min)
- :poshandler #'telescope--results-poshandler
- :width telescope--results-width
- :height telescope--height
- :border-width 2
- :border-color "#5f5fff"
- :background-color "#1c1c1c"
- :foreground-color "#d0d0d0"
- :override-parameters '((cursor-type . nil)))
- ;; Preview frame (right)
- (posframe-show telescope--preview-buffer
- :position (point-min)
- :poshandler #'telescope--preview-poshandler
- :width telescope--preview-width
- :height telescope--height
- :border-width 2
- :border-color "#5f87af"
- :background-color "#1c1c1c"
- :foreground-color "#d0d0d0"
- :override-parameters '((cursor-type . nil))))
-
-(defun telescope--hide-frames ()
- "Hide telescope posframes."
- (posframe-hide telescope--results-buffer)
- (posframe-hide telescope--preview-buffer))
-
-(defun telescope--refresh ()
- "Refresh filtered results and display."
- (setq telescope--filtered (telescope--fzf-filter telescope--files telescope--input))
- (setq telescope--selected (min telescope--selected
- (max 0 (1- (length telescope--filtered)))))
- (telescope--render-results)
- (telescope--update-preview)
- (telescope--show-frames))
-
-(defun telescope--select-next ()
- "Select next item."
- (when telescope--filtered
- (let ((max-visible (- telescope--height 5)))
- (setq telescope--selected
- (min (1+ telescope--selected)
- (1- (min (length telescope--filtered) max-visible)))))
- (telescope--render-results)
- (telescope--update-preview)
- (telescope--show-frames)))
-
-(defun telescope--select-prev ()
- "Select previous item."
- (setq telescope--selected (max 0 (1- telescope--selected)))
- (telescope--render-results)
- (telescope--update-preview)
- (telescope--show-frames))
-
-(defun telescope--backspace ()
- "Delete last character from input."
- (when (> (length telescope--input) 0)
- (setq telescope--input (substring telescope--input 0 -1))
- (telescope--refresh)))
-
-(defun telescope--insert-char (char)
- "Insert CHAR into input."
- (setq telescope--input (concat telescope--input (char-to-string char)))
- (telescope--refresh))
-
-;;;###autoload
-(defun telescope-find-files (&optional dir)
- "Find files in DIR using telescope interface."
- (interactive)
- (let ((dir (expand-file-name (or dir default-directory))))
- (setq telescope--base-dir dir)
- (setq telescope--input "")
- (setq telescope--selected 0)
- (setq telescope--files (telescope--get-files dir))
- (setq telescope--filtered (seq-take telescope--files 100))
- (telescope--render-results)
- (telescope--update-preview)
- (telescope--show-frames)
- (let ((result nil))
- (unwind-protect
- (while (null result)
- (let ((key (read-key (propertize " " 'face '(:height 0.1)))))
- (cond
- ;; Quit
- ((memq key '(?\e ?\C-g ?\C-c))
- (setq result 'cancel))
- ;; Confirm selection
- ((memq key '(?\r ?\C-m))
- (if telescope--filtered
- (setq result (expand-file-name
- (nth telescope--selected telescope--filtered)
- telescope--base-dir))
- (setq result 'cancel)))
- ;; Navigation
- ((or (memq key '(?\C-n ?\C-j)) (equal key 'down))
- (telescope--select-next))
- ((or (memq key '(?\C-p ?\C-k)) (equal key 'up))
- (telescope--select-prev))
- ;; Scroll
- ((memq key '(?\C-d))
- (dotimes (_ 5) (telescope--select-next)))
- ((memq key '(?\C-u))
- (dotimes (_ 5) (telescope--select-prev)))
- ;; Delete
- ((memq key '(?\C-h ?\C-? 127 backspace))
- (telescope--backspace))
- ;; Clear input
- ((memq key '(?\C-w))
- (setq telescope--input "")
- (telescope--refresh))
- ;; Regular character input
- ((and (characterp key) (>= key 32) (<= key 126))
- (telescope--insert-char key)))))
- ;; Cleanup
- (telescope--hide-frames))
- ;; Handle result
- (unless (eq result 'cancel)
- (find-file result)))))
-
-(provide 'telescope)
-;;; telescope.el ends here