1

When I grade students' essays, I frequently have to write the same comments. I would like to write an emacs function to help me reuse the most used comments in a file potential comments.

The function should:

  1. Ask user for a string (the string corresponds to a keyword in a comment)
  2. Use occur to list all comments where this string occurs
  3. Switches to the occur buffer and allows the user to select the comment they want
  4. Copy the selected comment into the clipboard (so that the user can paste it into the student's essay.

I need help with step 3 (I know how do all of the other steps). I don't know how to tell the function to "pause" running while I search for the relevant comment, and to resume once I have put the cursor on the comment I need and have pressed enter (or c).


UPDATE: I added two snippets of code below as an answer in case anyone wants to use them.

1
  • Post how you format your comment or a sample text file that use for recording the student performance ... and I will share a recipe with screenshots
    – mxnt
    Mar 29 at 1:28

3 Answers 3

1

I don't know how to tell the function to "pause" running while I search for the relevant comment, and to resume once I have put the cursor on the comment I need and have pressed enter.

Welcome to the event-driven world !

You should NOT try to "pause" the running function !

You should just write functions which are triggered by the actions of the user.

I would suggest the following:

  • You create a new sub-mode of occur-mode (using define-derived-mode).

  • In this sub-mode, you write a function <sub-mode>-copy-comment, which selects the whole comment of the current line and copies it to the clipboard.

  • In the keymap of this new sub-mode, you bind the RET key (or c) to this function.

  • In the function you already wrote, which calls one of the "occur" functions, you really switch to the *occur* buffer (using set-buffer).

  • In the same function, you call <your-sub-mode>-mode, to apply this sub-mode to the current *occur* buffer.

  • And basta ! The function has done it's job.

  • Now the user is in the *occur*, and she is able to navigate this buffer freely.

  • If the user presses the RET key (or c), it triggers the <sub-mode>-copy-comment function, which copies into the clipboard the whole comment of the current line, and maybe kills the current *occur* buffer or switches to another one.

HTH

1
  • I believe you are right; "pausing" the function is not a good starting point.. I've used your idea to create a single function to do this (posted below). My solution, involves a single function (that assigns a local key so that the user can copy it). This function, however, does not separate the function into two. Are there reasons why it may be better to do it as you suggest (i.e., to divide the function into two separate functions, one that leads the user to the *Occur buffer and another one that selects the relevant sentence upon pressing a specific key).
    – SaMeji
    Mar 29 at 15:47
1

I usually do something like:

(let ((enable-recursive-minibuffers t)
      ;; https://emacs.stackexchange.com/q/80307/15606
      (set-message-function nil))

  (occur "regexp")
  (read-string "Make your operations and press [ENTER]"))

enable-recursive-minibuffer allows you to switch to the occur buffer (or any other buffer), perform your operations, and return to the minibuffer to press ENTER.

1
  • Thanks for the suggestion. But it is not instrumental to what I have in mind. In step 3, the user should be positioned in the *Occur buffer, be able to navigate this buffer freely, and when the user presses a specific key (say "Enter"), the program selects the whole comment and copies it to the mini-buffer. Enable-recursive-minibuffer does pause the process, but it forces me to switch from the minibuffer to the *Occur buffer and then, after I have selected the text, to switch back to the minibuffer to press "Enter"
    – SaMeji
    Mar 29 at 13:08
1

Here are two solutions, both inspired by duthen's answer (binding c to the desired functionality within the *Occur buffer):

Solution 1 (using a single function)

(defun select-and-copy-comment-for-students ()
  "Search for a string using occur, then copy to the clipboard the sentence containing the selected occurrence."
  (interactive)
  (occur  (read-string "Enter string or keyword to search: "))
  (switch-to-buffer-other-window "*Occur*")
  (message "Select the occurrence and press 'c' to copy it to the clipboard.")
  (local-set-key (kbd "c")
     (lambda ()
        (interactive)
        ;; Jump to the original buffer and line
        (occur-mode-goto-occurrence)
        ;; Find the beginning and end of the sentence with the selected keyword
        (let ((sentence-end (progn (forward-sentence) (point)))
        (sentence-start (progn (backward-sentence) (point))))
        ;; Copy the sentence to the clipboard
        (copy-region-as-kill sentence-start sentence-end)))))

Solution 2 (splitting the functions)

(defun santi-occur-list-keywords ()
  "Use occur to list all occurrences in the current buffer containing keyword and switch to the occur buffer"
  (interactive)
  ;; Run occur on the keyword provided by the user
  (occur  (read-string "Enter search  string or keyword: "))
  (switch-to-buffer-other-window "*Occur*"))


(defun santi-occcur-copy-sentence-of-selected-keyword ()
  "Copies to the clipboard the sentence containing the occurrence that the user selected."
  (interactive)
  (occur-mode-goto-occurrence)
  ;; Ensure we are at the beginning of the occurrence
  ;; Find the beginning of the sentence
  (let ((sentence-end (progn (forward-sentence) (point)))
        (sentence-start (progn (backward-sentence) (point))))
    ;; Copy the sentence to the clipboard
    (copy-region-as-kill sentence-start sentence-end)))

(define-key occur-mode-map (kbd "c") 'santi-occcur-copy-sentence-of-selected-keyword)

I am planning on using the second method, since I may want to use the function to copy the sentence with the selected keyword for other applications beyond this one.

4
  • You don't have to write any custom code at all if you are using consult-line and embark-insert. Needless to say, add orderless to the mix.
    – mxnt
    Mar 30 at 3:25
  • mxnt... this seems promising, but I am not sure I fully get it... can you elaborate (I have never used consult-line or emark-insert).
    – SaMeji
    Mar 30 at 11:25
  • Imho both solutions are ok. I would NOT say that sol#1 uses a single function... since it uses a function AND a lambda (which is an anonymous function) ! In sol#1, you could replace the lambda by the name you give it in sol#2 : (defun select-and-copy-comment-for-students () ... (local-set-key (kbd "c") 'santi-occcur-copy-sentence-of-selected-keyword). Note that select-and-copy-comment-for-students is NOT paused. Once in the *occur* buffer, the user is allowed to switch to another buffer if she wants, for example. Sorry, I don't know "consult.el".
    – duthen
    Apr 4 at 20:23
  • Thanks. You are right (again)!
    – SaMeji
    Apr 12 at 1:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.