aboutsummaryrefslogblamecommitdiff
path: root/emacs/.emacs.d/lisp/my/my-help.el
blob: 27c23ce0d39bf8d5a817f273b33bc739f85bdc87 (plain) (tree)








































































































































                                                                                          
;;; my-help.el -- Help related extensions for emacs core -*- lexical-binding: t -*-

;; Copyright (C) 2023 Free Software Foundation.

;; Author: Yuchen Pei <id@ypei.org>
;; Package-Requires: ((emacs "28.2"))

;; This file is part of dotfiles.

;; dotfiles is free software: you can redistribute it and/or modify it under
;; the terms of the GNU Affero General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; dotfiles is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
;; Public License for more details.

;; You should have received a copy of the GNU Affero General Public
;; License along with dotfiles.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Emacs help related extensions.

;;; Code:


;; find source of external command
(defun my-external-command-open-source (command)
  (interactive
   (list (completing-read
          "Open source of command: "
          (my-external-command-collection))))
  (let ((subject
         (cadr
          (split-string (my-shell-command-output
                         (format "type %s" command))
                        " is "))))
    (pcase subject
      ("a shell builtin" (error "%s is %s" command subject))
      ("a shell keyword" (error "%s is %s" command subject))
      ((guard (string-prefix-p "is aliased to " subject))
       (substring subject (error "%s is %s" command subject)))
      (_
       (pcase-let
           ((`(,path ,type)
             (split-string 
              (my-shell-command-output (format "file -Li %s" subject))
              ": ")))
         (if (string-prefix-p "text/" type)
             (progn
               (message "Opening %s" path)
               (find-file path))
           (error "%s (%s) is not plaintext: %s" command path type)))))))

(defun my-external-command-collection ()
  (mapcan
   (lambda (dir)
     (mapcar
      (lambda (file)
        (string-match "\\(?:.*\\)/\\(.*\\)" file)
        (match-string 1 file))
      (seq-filter
       (lambda (file)
         (and (not (file-directory-p file))
              (file-executable-p file)))
       (directory-files dir t "[^~#]$"))))
   (seq-filter
    'file-accessible-directory-p
    (exec-path))))

(defun my-woman-man (arg)
  (interactive "P")
  (if arg (call-interactively 'man)
    (call-interactively 'woman)))

(defun my-help-goto-symbol (symbol)
  (interactive
   ;; copied from prompt code of help-describe-symbol
   (let* ((v-or-f (symbol-at-point))
          (found (if v-or-f (cl-some (lambda (x) (funcall (nth 1 x) v-or-f))
                                     describe-symbol-backends)))
          (v-or-f (if found v-or-f (function-called-at-point)))
          (found (or found v-or-f))
          (enable-recursive-minibuffers t)
          (val (completing-read (format-prompt "Describe symbol"
                                               (and found v-or-f))
				                        #'help--symbol-completion-table
				                        (lambda (vv)
                                  (cl-some (lambda (x) (funcall (nth 1 x) vv))
                                           describe-symbol-backends))
				                        t nil nil
				                        (if found (symbol-name v-or-f)))))
     (list (if (equal val "")
	             (or v-or-f "") (intern val)))))
  (help-do-xref nil #'describe-symbol (list symbol)))

(defun my-describe-local-variable (variable &optional buffer frame)
  (interactive
   (let ((v (variable-at-point))
	       (enable-recursive-minibuffers t)
         (orig-buffer (current-buffer))
	       val)
     (setq val (completing-read
                (format-prompt "Describe variable" (and (symbolp v) v))
                #'help--symbol-completion-table
                (lambda (vv)
                  (and (local-variable-p vv)
                       (or (get vv 'variable-documentation)
                           (and (not (keywordp vv))
                                ;; Since the variable may only exist in the
                                ;; original buffer, we have to look for it
                                ;; there.
                                (buffer-local-boundp vv orig-buffer)))))
                t nil nil
                (if (symbolp v) (symbol-name v))))
     (list (if (equal val "")
	             v (intern val)))))
  (describe-variable variable buffer frame))

(defun my-info-display-manual ()
  (interactive)
  (call-interactively 'info-display-manual)
  (when (derived-mode-p 'Info-mode)
    (rename-buffer
     (generate-new-buffer-name
      (format "*info %s*"
              (file-name-sans-extension
		           (file-name-nondirectory Info-current-file)))))))

(defun my-describe-symbol-at-point ()
  (interactive)
  (describe-symbol (symbol-at-point)))

(provide 'my-help)
;;; my-help.el ends here