A helm interface to ps

| categories: helm emacs | tags:

Occassionally, I need to find the PID of a process to kill it or do something else. Usually I do the old-school unix thing of piping the output of one command (ps) to another command (grep) to filter out interesting lines. Then, I can do something with that output.

ps aux | grep emacs
jkitchin         4781   3.1  0.8  2639316  70432 s002  S    12:45PM   0:06.68 /usr/local/Cellar/emacs/HEAD/Emacs.app/Contents/MacOS/Emacs
jkitchin         4777   0.0  0.0  2433364    932 s002  S    12:45PM   0:00.00 /bin/bash /usr/local/bin/emacs
jkitchin         4874   0.0  0.0  2432784    604   ??  S    12:46PM   0:00.00 grep emacs

Today, I will explore using helm in emacs to do something like that. The idea is to create a helm command that uses the output of ps as candidates, and then you select the process you want through the helm interface, and then select an action.

It is easy enough to get the output of the ps command in emacs like this. Here, we just get the first three results, and specify the output we want.

(let ((results (split-string
                (shell-command-to-string
                 "ps x -o ruser,pid,command") "\n")))
  (loop for i from 1 to 3
        collect (elt results i)))
("jkitchin   139 /sbin/launchd" "jkitchin   151 /usr/libexec/UserEventAgent (Aqua)" "jkitchin   152 /usr/sbin/distnoted agent")

These will be the candidates for the helm command. We will create a few actions. One will provide details about the pid, and one could in principle kill the pid or send some signal to it. We will just have these actions create message boxes for us to see helm in action. We will make the kill function interactive, so it allows an arbitrary signal to be sent. The other actions are placeholders for future actions, and so we can show off some shortcuts in helm later.

For the candidates, we will construct a list of cons cells where the car is a line from ps, and that is what will show in the helm selection interface, and the cdr will be the pid which we get by parsing the line to get the second element. When you select an entry in helm, the cdr of that entry (if it exists) is passed to the action function selected.

(defun ps-candidates ()
  "return a list of cons cells (line . pid) for the output of ps"
  (loop for line in
        ;; skip the first line which is a header
        (cdr (split-string
              (shell-command-to-string
               "ps ax -o ruser,pid,command") "\n"))
        collect
        (cons
         line
         (elt (split-string line) 1))))

(defun ps-details (pid)
  "give details of PID."
  (message-box "%s" (shell-command-to-string (format "ps ux %s" pid))))

(defun ps-kill (pid)
  "Message box instead of killing PID."
  (let ((SIG (read-string "Kill with signal: ")))
    (message-box "Killing pid %s with signal %s" pid SIG)))

(defun ps-hello (pid)
  (message-box "Silly 3rd action for %s" pid))

(defun ps-bye (pid)
  (message-box "Silly 4th action for %s" pid))

(defun ps-byebye (pid)
  (message-box "Silly 5th action for %s" pid))

;; the source variable for helm
(setq helm-source-ps '((name . "ps output")
                       ;; these are the entries you can select
                       (candidates . ps-candidates)
                       ;; these are the actions available for the
                       ;; selected entry. each function gets the cdr
                       ;; of the entry selected.
                       (action . (("details" . ps-details)
                                  ("kill" . ps-kill)
                                  ("hello" . ps-hello)
                                  ("bye" . ps-bye)
                                  ("byb-bye" . ps-byebye)))))

;; now we run the helm command
(helm :sources '(helm-source-ps))

You can navigate the helm interface with the arrows, or C-n (next/down) C-p (previous/up), or by typing in the pattern you want to match. There are only two actions here. The first one is the default action, which you can run by pressing tab or enter. The subtle difference between them is that tab leaves the helm window open, while enter runs the default action and closes the helm window. You can get it back with C-c h r (or M-x helm-resume).

To get the kill function, you can press C-z to get the action menu, and then press enter. Helm provides a shortcut for this. C-e selects the second action, so when you remember what the second action is and you want it, you can skip the C-z activity. You can access the third action with C-j. There is a command like helm-select-4th-action, but it is not bound to a key, so we have to make one like this.

(define-key helm-map (kbd "C-k") 'helm-select-4th-action)

You can also define a 5th action like this. It does not seem possible to define an arbitrary nth action, because you cannot get an input for n while helm uses the minibuffer.

(defun 5th-action ()
 (interactive)
 (let ((n 5))
   ;; actions start at 0, so the 5th action is actually indexed at 4
   (helm-select-nth-action (- n 1))))

(define-key helm-map (kbd "C-l") '5th-action)

That is the proof of concept in using a helm interface to interact with unix commands. There are other actions you might choose, like renice, or maybe it is possible to suspend a job by pid. The real application for this I had in mind was interaction with the Torque queue system, where you might want to modify, kill jobs in the queue system this way. I could also see applications in user management, where you have some well defined functions to run, e.g. checking quotas, changing passwords, etc… Clearly the utility of this approach rests heavily on there being a set of actions you do regularly enough to justify coding them into functions, and often enough you would remember to use your helm command! It is an interesting approach as an alternative to writing shell scripts to do this though.

This post might make more sense if you watch this video of the helm interface in action: http://www.youtube.com/watch?v=3FImB6OwHI0

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

The loop macro in emacs lisp

| categories: emacs_lisp | tags:

I was reading The Land Of Lisp chapter on the loop macro in Common Lisp. I am not too familiar with it, or the implementation in emacs-lisp, so in this post we explore what it can do. Here I will explore some uses of the loop macro to do things I used to do in Python all the time.

Here is a simple example to generate a list of numbers with the loop macro..

(loop for i
      below 5
      collect i)
0 1 2 3 4

Evidently, i starts at 0, and increments by one. We can specify a different value like this. Here we use the to token, which also includes the last value.

(loop for i from 2 to 10
  collect i)
2 3 4 5 6 7 8 9 10

IF you want to go backwards:

(loop for i downfrom 10 to 2 collect i)
10 9 8 7 6 5 4 3 2

And if you want an (de)increment different than one, use the by token.

(loop for i downfrom 10 to 2 by 3 collect i)
10 7 4

We can use this to iterate over a list too. Let us collect the square of each element in a simple list. This is similar to the mapcar function.

(loop for i in '(1 2 3 4)
  collect (* i i))
1 4 9 16

You can combine the ideas to get something similar to the enumerate function in python.

(loop for i
      from 0
      for month
      in '(january february march april may june july august september
                   october november december)
      collect (cons i month))
((0 . january)
 (1 . february)
 (2 . march)
 (3 . april)
 (4 . may)
 (5 . june)
 (6 . july)
 (7 . august)
 (8 . september)
 (9 . october)
 (10 . november)
 (11 . december))

The loop stops because we run out of months to iterate over. Here is a variation like the zip function in python.

(loop for element1 in '(a b c d)
      for element2 in '(1 2 3 4)
      collect (list element1 element2))
a 1
b 2
c 3
d 4

We can sum in the loop:

(loop for i in '(100 200 300) sum i)
600

We can conditionally sum things in the loop, e.g. sum only the odd numbers.

(loop for i in '(1 2 3 4 5)
  when (oddp i)
  sum i)
9

We can find the minima and maxima in a list

(loop for i in '(-1 0 1)
  minimize i)
-1
(loop for i in '(-1 0 1)
  maximize i)
1

You may want to do some action in the loop. Say we want to print even numbers from a list.

(loop for i in '(1 2 3 4)
      when (evenp i)
      do (print i))
2

4

There are some ways to break out of a loop using return like this.

(loop for i upto 10
      when (= i 3)
      return 'done
      do (print i))
0

1

2

Alternatively, you can use while/until.

(loop for i downfrom 10
      do (print i)
      until (= i 6))
10

9

8

7

6

Or the while variation:

(loop for i downfrom 10
      do (print i)
      while (> i 6))
10

9

8

7

6

1 Summary

This is not everything the loop macro can do! Here is what the help for that function says.

loop is an alias for `cl-loop' in `cl.el'.

(loop CLAUSE...)

The Common Lisp `loop' macro.
Valid clauses include:
  For clauses:
    for VAR from/upfrom/downfrom EXPR1 to/upto/downto/above/below EXPR2 by EXPR3
    for VAR = EXPR1 then EXPR2
    for VAR in/on/in-ref LIST by FUNC
    for VAR across/across-ref ARRAY
    for VAR being:
      the elements of/of-ref SEQUENCE [using (index VAR2)]
      the symbols [of OBARRAY]
      the hash-keys/hash-values of HASH-TABLE [using (hash-values/hash-keys V2)]
      the key-codes/key-bindings/key-seqs of KEYMAP [using (key-bindings VAR2)]
      the overlays/intervals [of BUFFER] [from POS1] [to POS2]
      the frames/buffers
      the windows [of FRAME]
  Iteration clauses:
    repeat INTEGER
    while/until/always/never/thereis CONDITION
  Accumulation clauses:
    collect/append/nconc/concat/vconcat/count/sum/maximize/minimize FORM
      [into VAR]
  Miscellaneous clauses:
    with VAR = INIT
    if/when/unless COND CLAUSE [and CLAUSE]... else CLAUSE [and CLAUSE...]
    named NAME
    initially/finally [do] EXPRS...
    do EXPRS...
    [finally] return EXPR

For more details, see Info node `(cl)Loop Facility'.

It is obviously quite powerful, although the syntax seems quite different than the usual lisp code I have been writing. It is not clear when this is superior to something like mapcar/mapconcat, or the dolist/dotimes functions.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

A hint system for problems in org-mode

| categories: org | tags:

I use org-mode to write problems for classes that I teach. Sometimes it is helpful to be able to provide hints about aspects of the problem. I have used drawers for that before. Here I will look at another approach. The idea is to store a unique id (org-id) in the problem headline. We will make hints somewhere else, and each hint will store the id they refer to in some property. We will run a command in the problem that finds the hints, and offers a menu you can select from.

In the next sections, we define a Problem statement, a section with hints in it, and finally code that defines a hint function.

1 Problem statement

What is the seventh number in the Fibonacci series?

2 Hints

Hints will be stored in headings somewhere. Here we put them in this file, but they could be stored in another file too. We give each hint a HINT property, with the problem id as the value. Here are three hints. In real example, the hints would not be this easy to find in the document. You might store them somewhere else, in another file for example.

2.1 What is the Fibonacci series?

The $ith number in the Fibonacci series is equal to the sum of the previous two numbers in the series.

2.2 What does the series start with?

The Fibonacci series starts with 1.

2.3 Example of the series.

The Fibonacci series goes as 1, 1, 2, 3, 5, 8, …

3 The hint code

We want to get the id from the problem the point is in, and then find hints for the problem. Then, we construct a menu and prompt the user to select a hint. I use a number to select the hint because it was easier to generate the menu that way. I like characters better, because you just have to press a key. With numbers you type the number and press enter. We open a new buffer with the contents of the hint in it. You can close the buffer by pressing q.

(defun hint ()
  "Present a menu of hints for the problem at point"
  (interactive)
  (let ((id (org-entry-get (point) "ID"))
        (entries '())
        (menu "")
        choice)

    (unless id
      (error "No problem ID found"))

    (org-map-entries
     (lambda ()
       (save-restriction
         (org-narrow-to-subtree)
         (add-to-list 'entries
                      (cons
                       (elt (org-heading-components) 4)
                       (buffer-string))
                      t)))
     (format "HINT=\"%s\"" id))

    ;; generate menu string
    (dolist (i (number-sequence 1 (length entries)))
      (setq menu (concat menu (format "[%s] %s\n" (- i 1)
                                      (car (elt entries (- i 1)))))))

    (setq choice (elt entries (read-number (concat menu "Your choice: ") 0)))
    ;; this feels a little clunky. Maybe I could just save a marker to
    ;; the headline, and open it in a narrowed indirect buffer.
    (when choice
      (switch-to-buffer "*hint*")
      (erase-buffer)
      (insert (cdr choice))
      
      (org-mode)
      (show-subtree)
      (setq buffer-read-only t)
      (use-local-map (copy-keymap org-mode-map))
      (local-set-key "q" #'(lambda () (interactive) (kill-buffer)))
      )))
hint

4 Summary

This seems like an interesting way to provide hints, or related information in org-mode. You could also consider using tags, or more sophisticated code to determine what else is relevant. For example, you might keep track of some performance metric, and use some heuristic algorithm that ranks the related information. Or perhaps fuzzy text searching, or combinations of criteria. If the number of hits got large, then the menu approach here might not be the best one. Then, something like the occur interface might be more suitable.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

A context-sensitive file link menu in org-mode

| categories: org | tags:

I am still interested in various ways to get more functionality of org-links. For example, we looked at:

  1. enabling right clicks on links
  2. new links with menus

When you click on a link, the function org-open-at-point runs, which is a large function that does a lot of things. One of them is to check if the link is defined in org-link-protocols, and to run the function definition there if it is. Here is a list of links defined for me. I defined a lot of these in org-ref, and my own init files, so you may not see these on your system.

(mapcar 'car org-link-protocols)
google ResearcherID orcid message mac-outlook skim addressbook x-together-item rmail mhe irc info gnus docview bibtex bbdb ans exercise solution assignment doi bibentry Autocites autocites supercites Textcites textcites Smartcites smartcites footcitetexts footcites Parencites parencites Cites cites fnotecite Pnotecite pnotecite Notecite notecite footfullcite fullcite citeurl citedate* citedate citetitle* citetitle Citeauthor* Autocite* autocite* Autocite autocite supercite parencite* cite* Smartcite smartcite Textcite textcite footcitetext footcite Parencite parencite Cite Citeauthor Citealp Citealt Citep Citet citeyear* citeyear citeauthor* citeauthor citetext citenum citealp* citealp citealt* citealt citep* citep citet* citet nocite cite eqref nameref pageref ref label list-of-tables list-of-figures addbibresource bibliographystyle printbibliography nobibliography bibliography pydoc index attachfile msx id file+emacs file+sys

Interestingly, file links are not defined in org-link-protocols, they are handled separately. I would like to change the behavior of file+emacs links. Instead of just opening the file, I want a menu to give me the option to create the file if it does not exist, and to open it in emacs, or with a system program if the file does exist. Let us see what this link does.

(assoc "file+emacs" org-link-protocols)
file+emacs org-open-file-with-emacs nil

When you click on the link, it runs org-open-file-with-emacs, and there is no formatting function defined.

So, let us define a list of functions that could make a menu. A new variation we use in this post is that each element of the list will be a (key menu-name action-func visible-p) list. visible-p will be a function that determines if the function is listed in the menu. That way, our menu will be context specific.

We want an option to create a file if it does not exist, and if it does exist, a choice to open in emacs, or a system program. So the idea here is to create the menu in a variable (so it easy to add to later), then when you click on the link it will run a menu function that filters the functions to run, and then prompt you for a selection.

(defvar file+emacs-menu '()
  "list of menu entries. (key name action visible).
key is a character to select.
name is what shows in the menu as [key]name
action is a function that takes a path
visible is a function that determines if the entry is in the menu.")

(setq file+emacs-menu
      '(("c" "reate"
         find-file ; action function
         (lambda (x) (not (file-exists-p x)))) ; visible-p
        ("o" "pen"
         org-open-file-with-emacs
         (lambda (x) (file-exists-p x)))
        ("e" "xternal open"
         (lambda (x) (org-open-file path '(16)))
         (lambda (x) (file-exists-p x)))))


(defun file+emacs-menu (path)
  "menu command for file+emacs links"
  (interactive)
  (let* ((filtered-menu-list (-filter
                              (lambda (x) (funcall (car (last x)) path))
                              file+emacs-menu))
         (menu-string (concat
                       (mapconcat
                        (lambda (tup)
                          (concat "[" (elt tup 0) "]"
                                  (elt tup 1) " "))
                        filtered-menu-list
                        "") ": "))
         (input (read-char-exclusive menu-string nil 1))
         (selected-func (and
                         input
                         (elt
                          (assoc
                           (char-to-string input) filtered-menu-list)
                          2))))
    (when selected-func
      (funcall selected-func path))))
file+emacs-menu

Now we need to change the link definition in org-link-protocols. setf comes to the rescue. We just get the whole entry, and then setf the second position in it like this.

(setf (elt (assoc "file+emacs" org-link-protocols) 1)
  'file+emacs-menu)
file+emacs-menu

Here we just confirm we set it.

(assoc "file+emacs" org-link-protocols)
file+emacs file+emacs-menu nil

Now, when we click on these links, we get our context specific menu. When

This file exists: ase-db.org so we see this menu:

This file does not exist: test.noext So we see:

For these, we can select to open them in a pdf reader or MS Word from our new menu. attaching-code-blocks-to-a-pdf.pdf

org-to-word.docx

I admit this example was a little contrived. You can do most of these things with prefix commands, or more specific commands in emacs. But, I rarely remember those. I would have preferred to use the file link in this example, but it is not defined in org-link-protocols, so this style of modification would not work, and I did not want to add it to org-link-protocols just to show how to change it this way.

This general approach would be very useful for links where there may be multiple contexts or actions that make sense. For file links, you may want do different things if the file already exists, or if it does not exist. As another example, my doi link gives me a menu to:

  1. open in https://doi.org
  2. open the doi in Web of Science
  3. find citing articles in Web of Science
  4. search the doi in Google Scholar
  5. open the doi in CrossRef
  6. open the doi in Pubmed
  7. find the doi in my bibtex file
  8. get a bibtex entry for the doi

I get all that from a click! org-ref offers similar functionality for cite links, where you might want to do different things from a click:

  1. See preview of the citation
  2. open the bibtex entry
  3. open the pdf if you have it
  4. open the url for the entry
  5. any of the things I listed for the doi example above.

I am sure there are many other things that might be useful to do!

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

Accessing web of science entry, citing and related articles from a doi in emacs

| categories: orgmode, emacs, org-ref | tags:

I have been investigating how to more deeply integrate online resources, scientific bibliographies and writing in Emacs. One feature I have been wanting is integration with Web Of Science , especially to find citing and related articles from a DOI. This service is not free, but is available at many places where science is done. I came across this API http://wokinfo.com/media/pdf/OpenURL-guide.pdf to make links to the things I am interested in here. Based on that document, here are three links based on a 10.1021/jp047349j that take you to different Web Of Science (WOS) pages.

  1. go to article in WOS: http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info:doi/10.1021/jp047349j
  2. citing articles: http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F10.1021/jp047349j&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.citing=yes
  3. related articles: http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F10.1021/jp047349j&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.related=yes

These are pretty easy to construct, so we can write functions that will create them and open the url in our browser. There are some other options that could be considered, but since we usually have a doi, it seems like the best way to go for creating the links. Here are the functions.

(defun doi-utils-wos (doi)
  "Open Web of Science entry for DOI"
  (interactive "sDOI: ")
  (browse-url
   (format
    "http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info:doi/%s" doi)))

(defun doi-utils-wos-citing (doi)
  "Open Web of Science citing articles entry. May be empty if none are found"
  (interactive "sDOI: ")
  (browse-url
   (concat
    "http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F"
    doi
    "&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.citing=yes")))

(defun doi-utils-wos-related (doi)
  "Open Web of Science related articles page."
  (interactive "sDOI: ")
  (browse-url
   (concat "http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F"
           doi
           "&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.related=yes")))
doi-utils-wos-related

These are exciting because they could be integrated into org-ref or doi-utils to make citations in an org-document even more functional! There are some other interesting things here about Scopus and issues with ISI that I note for reference to future me.

Some of these are now included in jmax-bibtex.el and doi-utils.org .

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter
« Previous Page -- Next Page »