Nyxt freezes when prompt is called in a class

Hi, so I’m still working on a prompt feature for a download destination directory.

I’ve narrowed down to data-storage.lisp and that’s where I’ve been looking at. I’ve figured out that the download path is specified in the download-data-path class in its dirname slot value and this slot value calls the function xdg-download-dir to get download directory.

So I tried to call a prompt in the xdg-download-dir function. I tried this line (setf dir ((promp :input (namestring *default-pathname-defaults* :prompt "Open download directory" :sources (list (make-instance 'file-source)))))) and I added it to xdg-download-dir thinking that it would assign the variable dir with whatever value the prompt receives from the user and assigns this value to the download-data-path slot value.

But when I load the data-storage.lisp file my Nyxt browser just freezes. I don’t get any error ouput or anything it just freezes and I’m not sure why. I’ve been scratching my head with this problem for a few days now but I still don’t know what’s wrong. I would appreciate any thoughts or input please because I have no idea what to do. Thank you.

I have a theory! Could you please post a full code sample so I can see if it is correct? Alternatively if you’ve forked Nyxt just post the url to your fork and branch :slight_smile:

Here’s my fork

Looking at your source, I believe my theory to be correct. By overriding the xdg-download-dir function to be interactive you are making it lock.

Why? Creating a new buffer invokes this function. In order for the new buffer creation to finish, it will have to invoke your modified xdg-download-dir function. If Nyxt is in a state where it cannot yet receive input, then the program will not start.

I would instead look at gtk.lisp in the ffi-buffer-download.

We see the important source in question:

      (alex:when-let* ((download-dir (download-path buffer))

So, what we could do here is make download-path of the buffer class somehow configurable. We could make it prompt the user perhaps? We could turn it into a function! From there we would just have:

(defmethod download-path ((buffer buffer))
  ...)

Within the download-path method, you could check the slot-value of download-path of the buffer and see if it is not set, or set to some special value. For example if the user does:

(define-configuration buffer
  ((download-path :interactive)))

then the download-path method could query the user for /where/ to download things, otherwise download to the default path as specified by the slot download-path.

That is just one idea of course, there are many ways to do this :slight_smile:
If you need more help, please do not hesitate to ask!

Thanks for the suggestions. I’m still confused about why my solution didn’t work though.

From my understanding, it’s because Nyxt is not in a state where it can receive input so there’s sort of a deadlock happening where my modified xdg-download-dir calls for input but Nyxt is in a state where it cannot receive input so it has to wait until it does. Hence why it’s freezing because it’s waiting to enter that state where it can receive input. Is that right?

That’s correct! :-).

But why is turning download-path into a function a working solution? Don’t we also need to invoke this function and we’ll run into the same problem where Nyxt is not in a state where it can receive input so it has to wait?

Here is why:

the define-class macro we are using produces an initform. The following form:

(define-class tomato () ((seeds (make-instance 'seed)))

translates to:

(defclass tomato () ((seeds :initform (make-instance 'seed)))

when we make an instance of tomato (make-instance 'tomato) we’ll also make an instance of seed. If (make-instance 'seed) is problematic for whatever reason, then we would never be able to instantiate a tomato.

Now consider this example:

(defclass tomato () ((seeds)))

Now, (make-instance 'tomato) will not make any seeds. It doesn’t matter if (make-instance 'seed) is problematic. We can always make tomatoes. Sure, we won’t have any seeds automatically made, but that is OK!

Taking it a step further:

(defclass tomato () ((seeds)))

(defmethod seeds ((tomato tomato))
  (if (slot-boundp tomato 'seeds)
    (slot-value tomato 'seeds)
    (setf (slot-value tomato 'seeds) (make-instance 'seed)))

As you can now see, we can check the value of seeds and set it or do any arbitrary action when (seeds tomato) is invoked. This is DIFFERENT than:

(defclass tomato () ((seeds :accessor seeds)))

We have full control over what happens when (seeds tomato) is invoked.


Bringing it back to what we are talking about:

(define-class buffer ()
   ((download-path (xdg-download-path) :accessor nil)))

(defmethod download-path ((buffer buffer))
  (if (eq :query-user-for-path (slot-value buffer 'download-path))
       (query-user-for-a-path)
       (slot-value buffer 'download-path))))

Now, if the user set’s the value like this:

(define-configuration buffer ((download-path :query-user-for-path)))

they will be queried for a path whenever (download-path buffer) is invoked, OTHERWISE, it would use the path calculated by (xdg-download-path). Important distinction: MAKING an instance of the buffer will NOT query the user.

Disclaimer: all of the above is pseudocode, parenthesis may not line up etc, despite this, I hope it is clear.

Please let me know if that helps :slight_smile:

Right, so by defining a method download-path we allow Nyxt to make an instance of buffer without causing any problems (using the tomato and seed example it doesn’t matter if seed instance is problematic we can still make tomatoes), thus allowing it to enter into a state where it can receive input. And then we can invoke this method to get a download path from the user. Is that right?

That is correct! :slight_smile:

1 Like

Hi, it seems that I’m facing the same issue of Nyxt freezing as well when I try to implement the solution you proposed.

I’ve tried to implement the following code according to your suggestion:

(defmethod download-path ((buffer buffer))
  (if (equal (uiop:getenv "HOME") (slot-value buffer 'download-path))
      (prompt
       :input "Hello world!"
       :prompt "Confirm download path"
       :sources (list (make-instance 'file-source)))
      (slot-value buffer 'download-path)))

(define-configuration buffer ((download-path (uiop:getenv "HOME"))))

So in this case I’ve set the value of download-path as the home directory so that the buffer should prompt the user. But once again when I run the code it freezes again. I’m not sure why.

Could it be that the issue is with prompt itself? Perhaps for some reason prompt cannot be called within a function or a class?

I see. Thank you for the update. I’ll have to give it a try when I have a little more time! Sorry for the delay!