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?

6
  • 1
    I think you need to ask your coworker what "system" he meant: there is not enough information here to divine an answer.
    – NickD
    Commented Dec 9, 2021 at 2:30
  • Ok, sorry. I thought that was irrelevant. The system was the nyxt browser software I was working on. This project: github.com/atlas-engineer/nyxt Commented Dec 9, 2021 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
    Commented Dec 9, 2021 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 :) Commented Dec 9, 2021 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
    Commented Dec 9, 2021 at 18:45

1 Answer 1

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.

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.