316 lines
11 KiB
EmacsLisp
316 lines
11 KiB
EmacsLisp
|
;;; helm-sys.el --- System related functions for helm. -*- lexical-binding: t -*-
|
|||
|
|
|||
|
;; Copyright (C) 2012 ~ 2016 Thierry Volpiatto <thierry.volpiatto@gmail.com>
|
|||
|
|
|||
|
;; This program is free software; you can redistribute it and/or modify
|
|||
|
;; it under the terms of the GNU General Public License as published by
|
|||
|
;; the Free Software Foundation, either version 3 of the License, or
|
|||
|
;; (at your option) any later version.
|
|||
|
|
|||
|
;; This program 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 General Public License for more details.
|
|||
|
|
|||
|
;; You should have received a copy of the GNU General Public License
|
|||
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|
|||
|
;;; Code:
|
|||
|
|
|||
|
(require 'cl-lib)
|
|||
|
(require 'helm)
|
|||
|
(require 'helm-help)
|
|||
|
(require 'helm-utils)
|
|||
|
|
|||
|
|
|||
|
(defgroup helm-sys nil
|
|||
|
"System related helm library."
|
|||
|
:group 'helm)
|
|||
|
|
|||
|
(defface helm-top-columns
|
|||
|
'((t :inherit helm-header))
|
|||
|
"Face for helm help string in minibuffer."
|
|||
|
:group 'helm-sys)
|
|||
|
|
|||
|
|
|||
|
(defun helm-top-command-set-fn (var _value)
|
|||
|
(set var
|
|||
|
(cl-case system-type
|
|||
|
(darwin "env COLUMNS=%s ps -axo pid,user,pri,nice,ucomm,tty,start,vsz,%%cpu,%%mem,etime,command")
|
|||
|
(t "env COLUMNS=%s top -b -n 1"))))
|
|||
|
|
|||
|
(defcustom helm-top-command "env COLUMNS=%s top -b -n 1"
|
|||
|
"Top command used to display output of top.
|
|||
|
To use top command, a version supporting batch mode (-b option) is needed.
|
|||
|
On Mac OSX top command doesn't support this, so ps command
|
|||
|
is used by default instead.
|
|||
|
If you modify this the number and order of elements displayed
|
|||
|
should be the same as top command to have the sort commands
|
|||
|
working properly, that is 12 elements with the 2 first being
|
|||
|
PID and USER and the last 4 being %CPU, %MEM, TIME and COMMAND.
|
|||
|
A format string where %s will be replaced with `frame-width'."
|
|||
|
:group 'helm-sys
|
|||
|
:type 'string
|
|||
|
:set 'helm-top-command-set-fn)
|
|||
|
|
|||
|
|
|||
|
;;; Top (process)
|
|||
|
;;
|
|||
|
;;
|
|||
|
(defvar helm-top-sort-fn nil)
|
|||
|
(defvar helm-top-map
|
|||
|
(let ((map (make-sparse-keymap)))
|
|||
|
(set-keymap-parent map helm-map)
|
|||
|
(define-key map (kbd "M-P") 'helm-top-run-sort-by-cpu)
|
|||
|
(define-key map (kbd "M-C") 'helm-top-run-sort-by-com)
|
|||
|
(define-key map (kbd "M-M") 'helm-top-run-sort-by-mem)
|
|||
|
(define-key map (kbd "M-U") 'helm-top-run-sort-by-user)
|
|||
|
map))
|
|||
|
|
|||
|
(defvar helm-source-top
|
|||
|
(helm-build-in-buffer-source "Top"
|
|||
|
:header-name (lambda (name) (concat name " (Press C-c C-u to refresh)"))
|
|||
|
:init #'helm-top-init
|
|||
|
:nomark t
|
|||
|
:display-to-real #'helm-top-display-to-real
|
|||
|
:persistent-action #'helm-top-sh-persistent-action
|
|||
|
:persistent-help "SIGTERM"
|
|||
|
:help-message 'helm-top-help-message
|
|||
|
:follow 'never
|
|||
|
:keymap helm-top-map
|
|||
|
:filtered-candidate-transformer #'helm-top-sort-transformer
|
|||
|
:action-transformer #'helm-top-action-transformer))
|
|||
|
|
|||
|
(defvar helm-top--line nil)
|
|||
|
(defun helm-top-transformer (candidates _source)
|
|||
|
"Transformer for `helm-top'.
|
|||
|
Return empty string for non--valid candidates."
|
|||
|
(cl-loop for disp in candidates collect
|
|||
|
(cond ((string-match "^ *[0-9]+" disp) disp)
|
|||
|
((string-match "^ *PID" disp)
|
|||
|
(setq helm-top--line (cons (propertize disp 'face 'helm-top-columns) "")))
|
|||
|
(t (cons disp "")))
|
|||
|
into lst
|
|||
|
finally return (or (member helm-top--line lst)
|
|||
|
(cons helm-top--line lst))))
|
|||
|
|
|||
|
(defun helm-top--skip-top-line ()
|
|||
|
(let ((src-name (assoc-default 'name (helm-get-current-source))))
|
|||
|
(helm-aif (and (stringp src-name)
|
|||
|
(string= src-name "Top")
|
|||
|
(helm-get-selection nil t))
|
|||
|
(when (string-match-p "^ *PID" it)
|
|||
|
(helm-next-line)))))
|
|||
|
|
|||
|
(defun helm-top-action-transformer (actions _candidate)
|
|||
|
"Action transformer for `top'.
|
|||
|
Show actions only on line starting by a PID."
|
|||
|
(let ((disp (helm-get-selection nil t)))
|
|||
|
(cond ((string-match "^ *[0-9]+" disp)
|
|||
|
(list '("kill (SIGTERM)" . (lambda (pid) (helm-top-sh "TERM" pid)))
|
|||
|
'("kill (SIGKILL)" . (lambda (pid) (helm-top-sh "KILL" pid)))
|
|||
|
'("kill (SIGINT)" . (lambda (pid) (helm-top-sh "INT" pid)))
|
|||
|
'("kill (Choose signal)"
|
|||
|
. (lambda (pid)
|
|||
|
(helm-top-sh
|
|||
|
(helm-comp-read (format "Kill [%s] with signal: " pid)
|
|||
|
'("ALRM" "HUP" "INT" "KILL" "PIPE" "POLL"
|
|||
|
"PROF" "TERM" "USR1" "USR2" "VTALRM"
|
|||
|
"STKFLT" "PWR" "WINCH" "CHLD" "URG"
|
|||
|
"TSTP" "TTIN" "TTOU" "STOP" "CONT"
|
|||
|
"ABRT" "FPE" "ILL" "QUIT" "SEGV"
|
|||
|
"TRAP" "SYS" "EMT" "BUS" "XCPU" "XFSZ")
|
|||
|
:must-match t)
|
|||
|
pid)))))
|
|||
|
(t actions))))
|
|||
|
|
|||
|
(defun helm-top-sh (sig pid)
|
|||
|
"Run kill shell command with signal SIG on PID for `helm-top'."
|
|||
|
(let ((cmd (format "kill -%s %s" sig pid)))
|
|||
|
(message "Executed %s\n%s" cmd (shell-command-to-string cmd))))
|
|||
|
|
|||
|
(defun helm-top-sh-persistent-action (pid)
|
|||
|
(delete-other-windows)
|
|||
|
(helm-top-sh "TERM" pid)
|
|||
|
(helm-force-update))
|
|||
|
|
|||
|
(defun helm-top-init ()
|
|||
|
"Insert output of top command in candidate buffer."
|
|||
|
(unless helm-top-sort-fn (helm-top-set-mode-line "CPU"))
|
|||
|
(with-current-buffer (helm-candidate-buffer 'global)
|
|||
|
(call-process-shell-command
|
|||
|
(format helm-top-command (frame-width))
|
|||
|
nil (current-buffer))))
|
|||
|
|
|||
|
(defun helm-top-display-to-real (line)
|
|||
|
"Return pid only from LINE."
|
|||
|
(car (split-string line)))
|
|||
|
|
|||
|
;; Sort top command
|
|||
|
|
|||
|
(defun helm-top-set-mode-line (str)
|
|||
|
(if (string-match "Sort:\\[\\(.*\\)\\] " helm-top-mode-line)
|
|||
|
(setq helm-top-mode-line (replace-match str nil nil helm-top-mode-line 1))
|
|||
|
(setq helm-top-mode-line (concat (format "Sort:[%s] " str) helm-top-mode-line))))
|
|||
|
|
|||
|
(defun helm-top-sort-transformer (candidates source)
|
|||
|
(helm-top-transformer
|
|||
|
(if helm-top-sort-fn
|
|||
|
(cl-loop for c in candidates
|
|||
|
if (string-match "^ *[0-9]+" c)
|
|||
|
collect c into pid-cands
|
|||
|
else collect c into header-cands
|
|||
|
finally return (append
|
|||
|
header-cands
|
|||
|
(sort pid-cands helm-top-sort-fn)))
|
|||
|
candidates)
|
|||
|
source))
|
|||
|
|
|||
|
(defun helm-top-sort-by-com (s1 s2)
|
|||
|
(let* ((split-1 (split-string s1))
|
|||
|
(split-2 (split-string s2))
|
|||
|
(com-1 (nth 11 split-1))
|
|||
|
(com-2 (nth 11 split-2)))
|
|||
|
(string< com-1 com-2)))
|
|||
|
|
|||
|
(defun helm-top-sort-by-mem (s1 s2)
|
|||
|
(let* ((split-1 (split-string s1))
|
|||
|
(split-2 (split-string s2))
|
|||
|
(mem-1 (string-to-number (nth 9 split-1)))
|
|||
|
(mem-2 (string-to-number (nth 9 split-2))))
|
|||
|
(> mem-1 mem-2)))
|
|||
|
|
|||
|
(defun helm-top-sort-by-user (s1 s2)
|
|||
|
(let* ((split-1 (split-string s1))
|
|||
|
(split-2 (split-string s2))
|
|||
|
(user-1 (nth 1 split-1))
|
|||
|
(user-2 (nth 1 split-2)))
|
|||
|
(string< user-1 user-2)))
|
|||
|
|
|||
|
(defun helm-top-run-sort-by-com ()
|
|||
|
(interactive)
|
|||
|
(helm-top-set-mode-line "COM")
|
|||
|
(setq helm-top-sort-fn 'helm-top-sort-by-com)
|
|||
|
(helm-force-update))
|
|||
|
|
|||
|
(defun helm-top-run-sort-by-cpu ()
|
|||
|
(interactive)
|
|||
|
(helm-top-set-mode-line "CPU")
|
|||
|
(setq helm-top-sort-fn nil)
|
|||
|
(helm-force-update))
|
|||
|
|
|||
|
(defun helm-top-run-sort-by-mem ()
|
|||
|
(interactive)
|
|||
|
(helm-top-set-mode-line "MEM")
|
|||
|
(setq helm-top-sort-fn 'helm-top-sort-by-mem)
|
|||
|
(helm-force-update))
|
|||
|
|
|||
|
(defun helm-top-run-sort-by-user ()
|
|||
|
(interactive)
|
|||
|
(helm-top-set-mode-line "USER")
|
|||
|
(setq helm-top-sort-fn 'helm-top-sort-by-user)
|
|||
|
(helm-force-update))
|
|||
|
|
|||
|
|
|||
|
;;; X RandR resolution change
|
|||
|
;;
|
|||
|
;;
|
|||
|
;;; FIXME I do not care multi-display.
|
|||
|
|
|||
|
(defun helm-xrandr-info ()
|
|||
|
"Return a pair with current X screen number and current X display name."
|
|||
|
(with-temp-buffer
|
|||
|
(call-process "xrandr" nil (current-buffer) nil
|
|||
|
"--current")
|
|||
|
(let (screen output)
|
|||
|
(goto-char (point-min))
|
|||
|
(save-excursion
|
|||
|
(when (re-search-forward "\\(^Screen \\)\\([0-9]\\):" nil t)
|
|||
|
(setq screen (match-string 2))))
|
|||
|
(when (re-search-forward "^\\(.*\\) connected" nil t)
|
|||
|
(setq output (match-string 1)))
|
|||
|
(list screen output))))
|
|||
|
|
|||
|
(defun helm-xrandr-screen ()
|
|||
|
"Return current X screen number."
|
|||
|
(car (helm-xrandr-info)))
|
|||
|
|
|||
|
(defun helm-xrandr-output ()
|
|||
|
"Return current X display name."
|
|||
|
(cadr (helm-xrandr-info)))
|
|||
|
|
|||
|
(defvar helm-source-xrandr-change-resolution
|
|||
|
'((name . "Change Resolution")
|
|||
|
(candidates
|
|||
|
. (lambda ()
|
|||
|
(with-temp-buffer
|
|||
|
(call-process "xrandr" nil (current-buffer) nil
|
|||
|
"--screen" (helm-xrandr-screen) "-q")
|
|||
|
(goto-char 1)
|
|||
|
(cl-loop with modes = nil
|
|||
|
while (re-search-forward " \\([0-9]+x[0-9]+\\)" nil t)
|
|||
|
for mode = (match-string 1)
|
|||
|
unless (member mode modes)
|
|||
|
collect mode into modes
|
|||
|
finally return modes))))
|
|||
|
(action
|
|||
|
("Change Resolution"
|
|||
|
. (lambda (mode)
|
|||
|
(call-process "xrandr" nil nil nil
|
|||
|
"--screen" (helm-xrandr-screen)
|
|||
|
"--output" (helm-xrandr-output)
|
|||
|
"--mode" mode))))))
|
|||
|
|
|||
|
|
|||
|
;;; Emacs process
|
|||
|
;;
|
|||
|
;;
|
|||
|
(defvar helm-source-emacs-process
|
|||
|
'((name . "Emacs Process")
|
|||
|
(init . (lambda () (list-processes--refresh)))
|
|||
|
(candidates . (lambda () (mapcar #'process-name (process-list))))
|
|||
|
(persistent-action . (lambda (elm)
|
|||
|
(delete-process (get-process elm))
|
|||
|
(helm-delete-current-selection)))
|
|||
|
(persistent-help . "Kill Process")
|
|||
|
(action ("Kill Process" . (lambda (elm)
|
|||
|
(delete-process (get-process elm)))))))
|
|||
|
|
|||
|
|
|||
|
;;;###autoload
|
|||
|
(defun helm-top ()
|
|||
|
"Preconfigured `helm' for top command."
|
|||
|
(interactive)
|
|||
|
(add-hook 'helm-after-update-hook 'helm-top--skip-top-line)
|
|||
|
(save-window-excursion
|
|||
|
(unless helm-alive-p (delete-other-windows))
|
|||
|
(unwind-protect
|
|||
|
(helm :sources 'helm-source-top
|
|||
|
:buffer "*helm top*" :full-frame t
|
|||
|
:candidate-number-limit 9999
|
|||
|
:preselect "^\\s-*[0-9]+")
|
|||
|
(remove-hook 'helm-after-update-hook 'helm-top--skip-top-line))))
|
|||
|
|
|||
|
;;;###autoload
|
|||
|
(defun helm-list-emacs-process ()
|
|||
|
"Preconfigured `helm' for emacs process."
|
|||
|
(interactive)
|
|||
|
(helm-other-buffer 'helm-source-emacs-process "*helm process*"))
|
|||
|
|
|||
|
;;;###autoload
|
|||
|
(defun helm-xrandr-set ()
|
|||
|
"Preconfigured helm for xrandr."
|
|||
|
(interactive)
|
|||
|
(helm :sources 'helm-source-xrandr-change-resolution
|
|||
|
:buffer "*helm xrandr*"))
|
|||
|
|
|||
|
(provide 'helm-sys)
|
|||
|
|
|||
|
;; Local Variables:
|
|||
|
;; byte-compile-warnings: (not cl-functions obsolete)
|
|||
|
;; coding: utf-8
|
|||
|
;; indent-tabs-mode: nil
|
|||
|
;; End:
|
|||
|
|
|||
|
;;; helm-sys.el ends here
|