jeudi 29 octobre 2015

Delete lines matching a regex; Keep lines matching a regexp

kill-lines-rx will prompt you for a regex and delete all lines matching it. Use with a prefix argument (C-u) to highlight and ask before removing.
(defun kill-lines-rx (&optional ask)
  (interactive "P")
  (save-excursion
    (goto-char (point-min))
    (let ((rx (read-regexp "Rx: ")))
      (while (search-forward-regexp rx nil t)
 (beginning-of-line)
 (set-mark (save-excursion (end-of-line) (point)))
 (when (or (not ask) (y-or-n-p "Kill? "))
   (delete-region (point) (mark))
   (delete-char 1))))))
kill-lines-unless-rx will prompt you for a regex and delete all lines not matching it. Use with a prefix argument (C-u) to highlight and ask before keeping.
(defun kill-lines-unless-rx (&optional ask)
  (interactive "P")
  (let (keep)
    (save-excursion
      (goto-char (point-min))
      (let ((rx (read-regexp "Rx: ")))
 (while (search-forward-regexp rx nil t)
   (beginning-of-line)
   (set-mark (save-excursion (end-of-line) (point)))
   (when (or (not ask) (y-or-n-p "Keep? "))
     (push (buffer-substring (point) (mark)) keep))
   (forward-line))))
    (erase-buffer)
    (dolist (e (nreverse keep))
      (insert e "\n"))))

vendredi 4 septembre 2015

org-mode live html export preview in the browser

Using the MozRepl Firefox extension along with Emacs moz.el package you can export your current org document to html on each buffer change or on each save. There's a bunch of JS code to make Firefox resuse existing tabs instead of opening new ones. Run M-x toggle-live-preview or M-x toggle-preview-on-save depending on what you want.
(defvar moz-useful-functions "
function find_tab_with_url(url) {
    var bs = gBrowser.browsers
    for (var i = 0; i < bs.length; i++) {
 try {
     if (bs[i].currentURI.spec == url)
  return i
 } catch (e) {}
    }
    return -1
}

function select_tab(t) {
    if (gBrowser.selectedTab != t)
        gBrowser.selectedTab = t
}

function add_or_reload_url (url) {
    var i = find_tab_with_url(url)
    var t = ''
    if (i < 0) {
 t = gBrowser.addTab(url)
    } else {
 gBrowser.browsers[i].reload()
        t = gBrowser.tabs[i]
    }
    select_tab(t) 
}
")

(defun preview-buffer-in-firefox ()
  (interactive)
  (require 'moz)
  (moz-send-string moz-useful-functions)
  (let ((fn (org-html-export-to-html)))
    (moz-send-string (concat "add_or_reload_url(\"file://" (expand-file-name fn) "\");\n"))))

(defun live-preview (&optional beg end len)
  (when (or (not (and beg end len)) (and beg end len (/= (1+ (- end beg)) len)))
    (message "%s: update!" (format-time-string "%r"))
    (preview-buffer-in-firefox)))

(defun toggle-live-preview ()
  (interactive)
  (if (memq 'live-preview first-change-hook)
      (remove-hook 'first-change-hook 'live-preview 'buffer-local)
    (add-hook 'first-change-hook 'live-preview nil 'buffer-local)))

(defun toggle-preview-on-save ()
  (interactive)
  (if (memq 'live-preview after-save-hook)
      (remove-hook 'after-save-hook 'live-preview 'buffer-local)
    (add-hook 'after-save-hook 'live-preview nil 'buffer-local)))

lundi 17 août 2015

Quickly edit your init file

Nothing fancy about this, just handy: opens my init file and place cursor at the end.
(defun init ()
  (interactive)
  (find-file "~/.emacs.d/init.el")
  (goto-char (point-max)))

Rename file and buffer

I don't know why this is not built-in...
(defun rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive "sNew name: ")
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not filename)
        (message "Buffer '%s' is not visiting a file!" name)
      (if (get-buffer new-name)
          (message "A buffer named '%s' already exists!" new-name)
        (progn
          (rename-file name new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil))))))

Open current file as root

Don't think I've written this one myself and I don't remember where it comes from. Oh well.
(defun sudo-edit (&optional arg)
  "Edit currently visited file as root.

With a prefix ARG prompt for a file to visit.
Will also prompt for a file to visit if current
buffer is not visiting a file."
  (interactive "P")
  (if (or arg (not buffer-file-name))
      (find-file (concat "/sudo:root@localhost:"
                         (ido-read-file-name "Find file(as root): ")))
    (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))

dimanche 16 août 2015

Number line in region using custom printf-like format

By default starts at zero, but you can use C-u 50 M-x my-number-line to make it start at 50...
The commands asks for a printf format that will be inserted at the start of each line, so you can do right alignment, hex, whatever.

Example region:
Pellentesque tristique imperdiet tortor. Cras placerat accumsan
nulla. Donec hendrerit tempor tellus. Nam a sapien. Nam vestibulum
accumsan nisl. Donec at pede. Pellentesque dapibus suscipit
ligula. Nunc porta vulputate tellus. Nunc aliquet, augue nec
adipiscing interdum, lacus tellus malesuada massa, quis varius mi
purus non odio. Nunc rutrum turpis sed pede. Etiam vel tortor sodales
tellus ultricies commodo.
Mark all lines, M-x my-number-line RET 0x%03x SPC RET and BAM, lines are prefixed with hex line numbers:
0x000 Pellentesque tristique imperdiet tortor. Cras placerat accumsan
0x001 nulla. Donec hendrerit tempor tellus. Nam a sapien. Nam vestibulum
0x002 accumsan nisl. Donec at pede. Pellentesque dapibus suscipit
0x003 ligula. Nunc porta vulputate tellus. Nunc aliquet, augue nec
0x004 adipiscing interdum, lacus tellus malesuada massa, quis varius mi
0x005 purus non odio. Nunc rutrum turpis sed pede. Etiam vel tortor sodales
0x006 tellus ultricies commodo.
(defun my-number-line (arg beg end fmt)
  (interactive "P\nr\nsformat: ")
  (let ((n (if (numberp arg) arg 0)))
    (save-excursion
      (goto-char beg)
      (beginning-of-line)
      (while (< (point) end)
        (beginning-of-line)
        (let ((s (format fmt n)))
          (insert s)
          (incf end (length s)))
        (incf n)
        (forward-line)
        (beginning-of-line)))))

Put readable file encoding and line endings mode in the mode-line

I was never able to decipher the default mode-line information about encoding/line endings so I changed it:

(defvar my-mode-line-coding-format
  '(:eval
    (let* ((code (symbol-name buffer-file-coding-system))
           (eol-type (coding-system-eol-type buffer-file-coding-system))
           (eol (if (eq 0 eol-type) "UNIX"
                  (if (eq 1 eol-type) "DOS"
                    (if (eq 2 eol-type) "MAC"
                      "???")))))
      (concat code " " eol " "))))
(put 'my-mode-line-coding-format 'risky-local-variable t)
(setq-default mode-line-format (substitute 'my-mode-line-coding-format 'mode-line-mule-info mode-line-format))
Now you get "utf-8 UNIX" in place of the cryptic "U*:%" whatever bullshit it was.

Make delete-forward do what I mean

  • If you're on a non-space character, delete forward until the next space character.
  • If you're on a space character, delete forward until the next non-space character
(defun my-delete-space-forward ()
  (interactive)
  (let* ((char (buffer-substring-no-properties (point) (1+ (point))))
         (notspace-rx (rx (not (any "\t\n "))))
         (space-rx (rx (any "\t\n ")))
         (rx (if (string-match-p space-rx char) notspace-rx space-rx))
         (end-pos (save-excursion
                    (search-forward-regexp rx nil 'end))))

    (delete-region (point) (if end-pos (1- end-pos) (point-max)))))

Make current file executable

Small snippet to make the script you're editing executable:

(defun chmodx (file)
  (interactive
   (list (buffer-file-name)))
  (cond ((null file)
         (error "file is nil"))
        ((not (stringp file))
         (error "file is not a string"))
        ((not (file-exists-p file))
         (error "%s doesn't exists" file)))
  (shell-command (concat "chmod +x " (shell-quote-argument file))))

Generate gtkdoc-like comments in C code

I've worked on a C project where I needed to document C functions a certain way (similar to GtkDoc I think) using comments. I made a C prototype parser that mostly work.
The cursor has to be anywhere before the opening brace {. Given this example from PulseAudio:
int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
    pa_strbuf *buf = NULL;
    int c;
    int b;

    pa_assert(conf);
    pa_assert(argc > 0);
    pa_assert(argv);
    /* yada yada... */
}
You get this, with the cursor placed on the function description (right of the -):
/**
 * pa_cmdline_parse - 
 * @conf: 
 * @argc: 
 * @argv: 
 * @d: 
 *
 *
 */
int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
    pa_strbuf *buf = NULL;
    int c;
    int b;

    pa_assert(conf);
    pa_assert(argc > 0);
    pa_assert(argv);
    /* yada yada... */
}
Pretty cool huh? Here's the shitty C parser and doc generator:
(defun my-c-parse-function ()
  (interactive)
  (require 'cl)
  (let ((beg (point))
        end
        text
        fname
        args)
    (save-excursion
      (when (search-forward "{" nil t)
        (setq end (point)
              text (buffer-substring-no-properties beg end))
        (setq text (replace-regexp-in-string (rx (+ space)) " " text))
        (setq text (replace-regexp-in-string (rx (+ space)) " " text))
        (when (string-match (rx (group (+? (not space))) (* space) "(" (group (* any)) ")") text)
          (setq fname (match-string 1 text)
                args (match-string 2 text))

          (setq args (split-string args (rx (? space) "," (? space)) t))
          (setq args (mapcar (lambda (arg)
                               (setq arg (replace-regexp-in-string (rx bos (+ space)) "" arg))
                               (setq arg (replace-regexp-in-string (rx (+ space) eos) "" arg))
                               (setq arg (replace-regexp-in-string (rx
                                                                    "[" (* (not (any "]"))) "]")
                                                                   "" arg))
                               (if (string= arg "void")
                                   arg
                                 (let ((words (split-string arg (rx (any "*" space)) t)))
                                   (nth (1- (length words)) words)))) args))

          (message "fname = <%s>" fname)
          (dolist (a args)
            (message "arg<%s>" a))
          (append (list fname) (remove-if (lambda (x) (string= x "void")) args))
          )))))


(defun my-c-doc-function ()
  (interactive)
  (let* ((res (my-c-parse-function))
         (fname (car res))
         (args (cdr res))
         p)

    (insert "/**\n * " fname " - ")
    (setq p (point))
    (insert "\n")
    (dolist (a args)
      (insert " * @" a ": \n"))
    (insert " *\n *\n */")
    (goto-char p)))

shell-mode: make C-l clear the output and remove duplicate input lines

C-l usually calls recenter-top-bottom which I never use... On the other end, I often use C-l in terminals to erase/clean them.

Also shell-mode writes the input buffer twice by default. Let's fix that.
(defun my-shell-mode-hook ()
  "Set shell mode stuff"
  (setq comint-process-echoes 1)
  (local-set-key (kbd "C-l") 'erase-buffer))

(add-hook 'shell-mode-hook 'my-shell-mode-hook)