1

Interspersed in a text file I have 0:36Skip to 0 minutes and 36 seconds blah blah, always at the beginning of a line. I am looking for a function to search and delete these. Failing, I have

(defun mddtt ()
  (interactive)
  (re-search-forward "[0-9]:")
  (goto-char (line-beginning-position))
  (push-mark nil 1 t)
  (search-forward "seconds")
  (setq st (pop-mark))
  (setq en (point))
  (delete-region st en))

The error is mddtt: Wrong type argument: integer-or-marker-p, nil (not sure where).

I'm running this manually, I will introduce checking for failed searches, but I'm happy just to exit the function.

Improvements pleas?

  • "not sure where [the error is]" - try instrumenting with edebug (C-u C-M-x) and stepping through. – npostavs Jun 13 '16 at 12:13
  • Also, look at the docstring of set-mark (mentioned from push-mark) where it says "Novice Emacs Lisp programmers often try to use the mark for the wrong purposes", this applies to your case. – npostavs Jun 13 '16 at 12:17
  • The error is signaled by delete-region because the value of st is nil. The reason is that pop-mark is only used for its side effects; it is not expected to return a value. – nispio Jun 13 '16 at 19:58
  • Yes, I think you are right. Now solved: solution below meets my needs. (defun mddtt () """ Delete time marker in future learn video scripts Use C-x z, z to repeat """ (interactive) (re-search-forward "[0-9]:") (goto-char (line-beginning-position)) (setq st (point) ) (search-forward "seconds") (setq en (point)) (message (number-to-string en)) (delete-region st en)) – Dave P Jun 14 '16 at 16:07
5

Search and replace already exists in Emacs!

C-M-% ^.*?[0-9]:.*?seconds RET RET .

C-M-% invokes query-replace-regexp. You enter a regular expression, then the replacement text (empty). The final . says to perform one replacement only, you can press Y instead to replace all remaining occurrences, y or n to replace the current occurrence or not and move to the next one without stopping search-and-replace, etc.

The regular expression I wrote mimics what you do:

  • start at the beginning of a line (^);
  • skip to the first occurrence of a digit followed by a colon (.*? uses a shortest-match repeat operator so it stops at the first occurrence of what follows, .* would skip to the last digit-colon that's followed by seconds on the same line);
  • skip to the first subsequent occurrence of seconds;
  • the whole match is deleted since the replacement text is empty.

In a Lisp program, you can write (replace-regexp "^.*?[0-9]:.*?seconds" "") to replace all occurrences from the point to the end of the buffer, but as the documentation of replace-regexp tells you, most of the work of replace-regexp is related to its interactive use, and in Lisp you're encouraged to write your own loop:

(while (re-search-forward "^.*?[0-9]:.*?seconds")
  (replace-match "" nil nil))

If you wanted to replace just one occurrence (like your function), that would be

(if (re-search-forward "^.*?[0-9]:.*?seconds")
    (replace-match "" nil nil))

Writing nontrivial Lisp code would be needed if your search pattern was too complex to be recognized by a regular expression. When you write Lisp code, don't use the mark: that's a user-level feature which is rarely appropriate for internal use. To store a position in a buffer, use a local variable defined by let. Always use let to define variables that you use locally in a function, otherwise your calls to setq pollute global variables. The current position is (point); this is a number that doesn't change if the buffer is subsequently modified. If you need a location that tracks when text is inserted or removed before it — which is not the case here — use a marker.

(defun mddtt ()
  (interactive "@*")
  (re-search-forward "[0-9]:")
  (let ((st (line-beginning-position)))
    (search-forward "seconds")
    (delete-region st (point))))

To summarize:

  • Look for built-in features before reinventing the wheel.
  • Use let for local variables.
  • Don't use the mark for internal purposes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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