0

I am using Common Lisp, SBCL, Emacs, and Slime. During a code review, a co-worker mentioned that the indentation of an s-expression was wrong.

Usually, I just use indent-sexp(bounded to C-M-q) to fix this kind of situation. In this case, I forgot to use it before code review. After his comment, I just executed the command.

This was the previous code:

(defun bookmark-frequent-visit (threshold)
  "Check if current URL is frequently visited and not included in the
bookmarks. If this is the case, prompt the user about bookmarking it."
  (labels ((bookmarked-url-p (url-address)
             "The local function `bookmarked-url-p' returns the URL
           address itself if it is new to the bookmark list or NIL if it is
           already there."
             (let ((bookmarks-address-list
                     (mapcar #'(lambda (e) (render-url (url e)))
                             (with-data-unsafe (bookmarks (bookmarks-path (current-buffer)))
                               bookmarks))))
               (if (member url-address bookmarks-address-list :test #'string=)
                   nil
                   url-address))))
    (sera:and-let* ((history-entries (with-data-unsafe (history (history-path (current-buffer)))
                                        (mapcar #'htree:data (alex:hash-table-keys (htree:entries history)))))
                    (current-url-history
                      (find (url (current-buffer)) history-entries :test #'equalp :key #'url))
                    (implicit-visits-value
                      (nyxt::implicit-visits current-url-history))
                    (current-url-address
                      (render-url (url current-url-history)))
                    (threshold threshold))
                   (run-thread  ;; identation is off here
                    (if (and (> implicit-visits-value threshold)
                             (bookmarked-url-p current-url-address))
                      (if-confirm ("Bookmark ~a?" current-url-address)
                                  (bookmark-url :url current-url-address)))))))

This is the new code with my attempt to fix it:

(defun bookmark-frequent-visit (threshold)
  "Check if current URL is frequently visited and not included in the
bookmarks. If this is the case, prompt the user about bookmarking it."
  (labels ((bookmarked-url-p (url-address)
             "The local function `bookmarked-url-p' returns the URL
           address itself if it is new to the bookmark list or NIL if it is
           already there."
             (let ((bookmark-url-strings
                     (mapcar #'(lambda (e) (render-url (url e)))
                             (with-data-unsafe (bookmarks (bookmarks-path (current-buffer)))
                               bookmarks))))
               (if (member url-address bookmark-url-strings :test #'string=)
                   nil
                   url-address))))
    (sera:and-let* ((history-entries (with-data-unsafe (history (history-path (current-buffer)))
                                       (mapcar #'htree:data (alex:hash-table-keys (htree:entries history)))))
                    (current-url-history
                     (find (url (current-buffer)) history-entries :test #'equalp :key #'url))
                    (implicit-visits-value
                     (nyxt::implicit-visits current-url-history))
                    (current-url-string
                     (render-url (url current-url-history)))
                    (threshold threshold))
      (run-thread ;; attempt to fix the indentation with C-M-q
        (if (and (> implicit-visits-value threshold)
                 (bookmarked-url-p current-url-string))
            (if-confirm ("Bookmark ~a?" current-url-string)
                        (bookmark-url :url current-url-string)))))))

One thing intrigued me, though. He said: "make sure the system is loaded".

Why having the system loaded makes any difference for indentation?

Pedro Delfino
  • 1,369
  • 3
  • 13
  • 1
    I think you need to ask your coworker what "system" he meant: there is not enough information here to divine an answer. – NickD Dec 09 '21 at 02:30
  • Ok, sorry. I thought that was irrelevant. The system was the `nyxt browser` software I was working on. This project: https://github.com/atlas-engineer/nyxt/ – Pedro Delfino Dec 09 '21 at 15:07
  • 1
    That *is* probably irrelevant. In particular, the relationship between `nyxt` and `emacs` is unclear: so you can either write a short essay describing that relationship *or you can just ask you coworker* why he thinks it makes a difference (I highly recommend the latter). – NickD Dec 09 '21 at 15:18
  • Macros have a different indentation rule compared to functions. But SLY/Slime/Emacs cannot know if a symbol is a macro or a function unless the system is loaded. I think you could be more gentle with words, bro. No need to use things like "divine answer", "write a short essay"... Maybe you should go surfing someday. I wish you good vibes only and thanks for the help :) – Pedro Delfino Dec 09 '21 at 18:37
  • 1
    Yes, I saw the answer. Understood and properly chastised: there were too many things here that were undreamt of in my philosophy... I will try to do better. – NickD Dec 09 '21 at 18:45
  • Great! I am glad to read that, @NIckD :) – Pedro Delfino Dec 09 '21 at 18:49

1 Answers1

2

SLIME examines the argument list of macros to determine how to indent them correctly. Note that the indentation for the code supplied to the sera:and-let* macro went from being aligned with the first argument to being indented by two spaces from the macro name.

For reference, the macro is defined here: https://github.com/ruricolist/serapeum/blob/21b683a6969236f4b02066c1ce40a27cafbba512/binding.lisp#L266

Chapter 3.14 Semantic indentation of the SLIME manual says:

SLIME automatically discovers how to indent the macros in your Lisp system. To do this the Lisp side scans all the macros in the system and reports to Emacs all the ones with &body arguments. Emacs then indents these specially, putting the first arguments four spaces in and the “body” arguments just two spaces, as usual.

If you don’t actually have your project’s system loaded into your lisp environment, then SLIME cannot do this, and it will simply assume that everything is a function and that all the args should be lined up vertically.

db48x
  • 15,741
  • 1
  • 19
  • 23