diff --git a/config.org b/config.org index 5efad46..d49c205 100644 --- a/config.org +++ b/config.org @@ -8,6 +8,9 @@ - [[#evil-mode][Evil Mode]] - [[#recent-files][Recent Files]] - [[#general-keybindings][General Keybindings]] +- [[#startup-time][Startup Time]] +- [[#app-launcher][App Launcher]] + - [[#standalone-run][Standalone run]] - [[#fonts][Fonts]] - [[#zooming-inout][Zooming In/Out]] - [[#graphical-user-interface-tweaks][GRAPHICAL USER INTERFACE TWEAKS]] @@ -119,14 +122,18 @@ "t l" '(display-line-numbers-mode :wk "Toggle line numbers") "t t" '(visual-line-mode :wk "Toggle truncated lines") "t v" '(vterm-toggle :wk "Toggle vterm")) + (kylekrein/leader-keys + "o" '(:ignore t :wk "Open") + "o a" '(org-agenda :wk "Agenda") + ) (kylekrein/leader-keys "n" '(:ignore t :wk "Notes") "n r" '(:ignore t :wk "Roam") "n r f" '(org-roam-node-find :wk "Find and open") "n r i" '(org-roam-node-insert :wk "Insert link") "n r l" '(org-roam-buffer-toggle :wk "Show backlinks") - "n r d" '(:ignore t :wk "Dailies") - "n r d m" '(org-roam-dailies-map :wk "Show dailies map") + "n r m" '(org-roam-dailies-map :wk "Dailies") + "n r s" '(org-roam-db-sync :wk "Sync Notes DB (After changes on another device)") ) (kylekrein/leader-keys @@ -150,6 +157,203 @@ #+end_src +* Startup Time +#+begin_src emacs-lisp +;; Startup time +(defun efs/display-startup-time () + (message + "Emacs loaded in %s with %d garbage collections." + (format + "%.2f seconds" + (float-time + (time-subtract after-init-time before-init-time))) + gcs-done)) + +(add-hook 'emacs-startup-hook #'efs/display-startup-time) +#+end_src +* App Launcher +This code creates a menu to launch linux apps, that have Desktop entry. +Code was taken from [[https://github.com/SebastienWae/app-launcher/blob/main/app-launcher.el][this awesome repo]] +#+begin_src emacs-lisp +(require 'xdg) +(require 'cl-seq) + +(defcustom app-launcher-apps-directories + (mapcar (lambda (dir) (expand-file-name "applications" dir)) + (cons (xdg-data-home) + (xdg-data-dirs))) + "Directories in which to search for applications (.desktop files)." + :type '(repeat directory)) + +(defcustom app-launcher--annotation-function #'app-launcher--annotation-function-default + "Define the function that genereate the annotation for each completion choices." + :type 'function) + +(defcustom app-launcher--action-function #'app-launcher--action-function-default + "Define the function that is used to run the selected application." + :type 'function) + +(defvar app-launcher--cache nil + "Cache of desktop files data.") + +(defvar app-launcher--cache-timestamp nil + "Time when we last updated the cached application list.") + +(defvar app-launcher--cached-files nil + "List of cached desktop files.") + +(defun app-launcher-list-desktop-files () + "Return an alist of all Linux applications. +Each list entry is a pair of (desktop-name . desktop-file). +This function always returns its elements in a stable order." + (let ((hash (make-hash-table :test #'equal)) + result) + (dolist (dir app-launcher-apps-directories) + (when (file-exists-p dir) + (let ((dir (file-name-as-directory dir))) + (dolist (file (directory-files-recursively dir ".*\\.desktop$")) + (let ((id (subst-char-in-string ?/ ?- (file-relative-name file dir)))) + (when (and (not (gethash id hash)) (file-readable-p file)) + (push (cons id file) result) + (puthash id file hash))))))) + result)) + +(defun app-launcher-parse-files (files) + "Parse the .desktop files to return usable informations." + (let ((hash (make-hash-table :test #'equal))) + (dolist (entry files hash) + (let ((file (cdr entry))) + (with-temp-buffer + (insert-file-contents file) + (goto-char (point-min)) + (let ((start (re-search-forward "^\\[Desktop Entry\\] *$" nil t)) + (end (re-search-forward "^\\[" nil t)) + (visible t) + name comment exec) + (catch 'break + (unless start + (message "Warning: File %s has no [Desktop Entry] group" file) + (throw 'break nil)) + + (goto-char start) + (when (re-search-forward "^\\(Hidden\\|NoDisplay\\) *= *\\(1\\|true\\) *$" end t) + (setq visible nil)) + (setq name (match-string 1)) + + (goto-char start) + (unless (re-search-forward "^Type *= *Application *$" end t) + (throw 'break nil)) + (setq name (match-string 1)) + + (goto-char start) + (unless (re-search-forward "^Name *= *\\(.+\\)$" end t) + (push file counsel-linux-apps-faulty) + (message "Warning: File %s has no Name" file) + (throw 'break nil)) + (setq name (match-string 1)) + + (goto-char start) + (when (re-search-forward "^Comment *= *\\(.+\\)$" end t) + (setq comment (match-string 1))) + + (goto-char start) + (unless (re-search-forward "^Exec *= *\\(.+\\)$" end t) + ;; Don't warn because this can technically be a valid desktop file. + (throw 'break nil)) + (setq exec (match-string 1)) + + (goto-char start) + (when (re-search-forward "^TryExec *= *\\(.+\\)$" end t) + (let ((try-exec (match-string 1))) + (unless (locate-file try-exec exec-path nil #'file-executable-p) + (throw 'break nil)))) + + (puthash name + (list (cons 'file file) + (cons 'exec exec) + (cons 'comment comment) + (cons 'visible visible)) + hash)))))))) + +(defun app-launcher-list-apps () + "Return list of all Linux .desktop applications." + (let* ((new-desktop-alist (app-launcher-list-desktop-files)) + (new-files (mapcar 'cdr new-desktop-alist))) + (unless (and (equal new-files app-launcher--cached-files) + (null (cl-find-if + (lambda (file) + (time-less-p + app-launcher--cache-timestamp + (nth 5 (file-attributes file)))) + new-files))) + (setq app-launcher--cache (app-launcher-parse-files new-desktop-alist)) + (setq app-launcher--cache-timestamp (current-time)) + (setq app-launcher--cached-files new-files))) + app-launcher--cache) + +(defun app-launcher--annotation-function-default (choice) + "Default function to annotate the completion choices." + (let ((str (cdr (assq 'comment (gethash choice app-launcher--cache))))) + (when str (concat " - " (propertize str 'face 'completions-annotations))))) + +(defun app-launcher--action-function-default (selected) + "Default function used to run the selected application." + (let* ((exec (cdr (assq 'exec (gethash selected app-launcher--cache)))) + (command (let (result) + (dolist (chunk (split-string exec " ") result) + (unless (or (equal chunk "%U") + (equal chunk "%F") + (equal chunk "%u") + (equal chunk "%f")) + (setq result (concat result chunk " "))))))) + (call-process-shell-command command nil 0 nil))) + +;;;###autoload +(defun app-launcher-run-app (&optional arg) + "Launch an application installed on your machine. +When ARG is non-nil, ignore NoDisplay property in *.desktop files." + (interactive) + (let* ((candidates (app-launcher-list-apps)) + (result (completing-read + "Run app: " + (lambda (str pred flag) + (if (eq flag 'metadata) + '(metadata + (annotation-function . (lambda (choice) + (funcall + app-launcher--annotation-function + choice)))) + (complete-with-action flag candidates str pred))) + (lambda (x y) + (if arg + t + (cdr (assq 'visible y)))) + t nil 'app-launcher nil nil))) + (funcall app-launcher--action-function result))) +#+end_src +** Standalone run +This code snippet runs app launcher without emacs frame +To use it, create a global keyboard shortcut with the following code +~emacsclient -cF "((visibility . nil))" -e "(emacs-run-launcher)~ +#+begin_src emacs-lisp +(defun emacs-run-launcher () + "Create and select a frame called emacs-run-launcher which consists only of a minibuffer and has specific dimensions. Runs app-launcher-run-app on that frame, which is an emacs command that prompts you to select an app and open it in a dmenu like behaviour. Delete the frame after that command has exited" + (interactive) + (with-selected-frame + (make-frame '((name . "emacs-run-launcher") + (minibuffer . only) + (fullscreen . 0) ; no fullscreen + (undecorated . t) ; remove title bar + ;;(auto-raise . t) ; focus on this frame + ;;(tool-bar-lines . 0) + ;;(menu-bar-lines . 0) + (internal-border-width . 10) + (width . 80) + (height . 11))) + (unwind-protect + (app-launcher-run-app) + (delete-frame)))) +#+end_src * Fonts Defining the various fonts that Emacs will use. #+begin_src emacs-lisp