index rss mastodon twitter github linkedin email
Álvaro Ramírez
sponsor

Álvaro Ramírez

18 May 2023 Sprinkle me logs

At times, basic prints/logs are just about the right debugging strategy. Sure, we have debuggers and REPLs which are super useful, but sometimes you just know that sprinkling your code with a handful of temporary prints/logs will get you enough info to fix an issue.

I must confess, my temporary print statements are fairly uninspiring. Sometimes I log the name of the method/function, but I also resort to less creative options like print("Yay") or print("Got here").

My laziness and lack of creativity knows no boundaries, so if I need multiple unique entries, I often copy, paste, and append numbers to my entries: print("Yay 2"), print("Yay 3"), print("Yay 4")… I know, are you judging yet?

So rather than develop the creative muscle, I've decided to lean on laziness and old habits, so let's make old habit more efficient :) I no longer want to copy, paste, and increment my uncreative log statements. Instead, I'll let Emacs do it for me!

log-elisp.gif

There isn't a whole lot to the implementation. It searches the current buffer for other instances of the same logging string and captures the largest counter found. It subsequently prints the same string with the counter incremented. This can be done in a few lines of elisp, but I figure I wanted some additional features like auto indenting and changing the logging string when using a prefix.

(defvar ar/unique-log-word "Yay")

(defun ar/insert-unique-log-word (prefix)
  "Inserts `ar/unique-log-word' incrementing counter.

With PREFIX, change `ar/unique-log-word'."
  (interactive "P")
  (let* ((word (cond (prefix
                      (setq ar/unique-log-word
                            (read-string "Log word: ")))
                     ((region-active-p)
                      (setq ar/unique-log-word
                            (buffer-substring (region-beginning)
                                              (region-end))))
                     (ar/unique-log-word
                      ar/unique-log-word)
                     (t
                      "Reached")))
         (config
          (cond
           ((equal major-mode 'emacs-lisp-mode)
            (cons (format "(message \"%s: \\([0-9]+\\)\")" word)
                  (format "(message \"%s: %%s\")" word)))
           ((equal major-mode 'swift-mode)
            (cons (format "print(\"%s: \\([0-9]+\\)\")" word)
                  (format "print(\"%s: %%s\")" word)))
           ((equal major-mode 'ada-mode)
            (cons (format "Ada.Text_Io.Put_Line (\"%s: \\([0-9]+\\)\");" word)
                  (format "Ada.Text_Io.Put_Line (\"%s: %%s\");" word)))
           ((equal major-mode 'c++-mode)
            (cons (format "std::cout << \"%s: \\([0-9]+\\)\" << std::endl;" word)
                  (format "std::cout << \"%s: %%s\" << std::endl;" word)))
           (t
            (error "%s not supported" major-mode))))
         (match-regexp (car config))
         (format-string (cdr config))
         (max-num 0)
         (case-fold-search nil))
    (when ar/unique-log-word
      (save-excursion
        (goto-char (point-min))
        (while (re-search-forward match-regexp nil t)
          (when (> (string-to-number (match-string 1)) max-num)
            (setq max-num (string-to-number (match-string 1))))))
      (setq max-num (1+ max-num)))
    (unless (looking-at-p "^ *$")
      (end-of-line))
    (insert (concat
             (if (looking-at-p "^ *$") "" "\n")
             (format format-string
                     (if ar/unique-log-word
                         (number-to-string (1+ max-num))
                       (string-trim
                        (shell-command-to-string
                         "grep -E '^[a-z]{6}$' /usr/share/dict/words | shuf -n 1"))))))
    (call-interactively 'indent-for-tab-command)))

Note: This snippet may evolve independently of this post. For the latest, chech my Emacs config's fe-prog.el.

I want to be lazy in other languages, so the function can now be extended to support other languages. Here's the Swift counterpart.

log-swift.gif

Since I sometimes log function names, I figured making it region-aware would help with that.

log-selection.gif

I'm sure there's a package out there that does something similar, but I figure this would be a fun little elisp hack.

Happy logging!

Update 1

Set ar/unique-log-word to nil and let it generate a random word. Maybe I get to learn new words as I debug ;)

word.gif

Update 2

Added Ada and C++ support, thanks to James Dyer's post.