Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip functional config #397

Closed
wants to merge 11 commits into from
Closed

Wip functional config #397

wants to merge 11 commits into from

Conversation

Ambrevar
Copy link
Member

Let me know if you like #381 (comment).

If so, I'll update the documentation and merge.

This gives the user has a entry point to configure all of Next without
using (setf (get-default ...)).
We have to introducde the +platform-port-command+ global.  It's more consistent
and easier for users and packagers anyways.

DEFINE-KEY cannot set to mode default keymaps anymore, so this simplifies the
function a great lot.

With the typing information added to define-key, KEYMAPP was not needed anymore.
@vindarel
Copy link
Contributor

Please wait a bit, this requires more thinking and tests (at least on my side), thanks.

@Ambrevar
Copy link
Member Author

This is my new init.lisp:

(in-package :next)

;; Use development platform port.
(setf +platform-port-command+
      "~/common-lisp/next/ports/gtk-webkit/next-gtk-webkit")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun eval-in-emacs (&rest s-exps)
  "Evaluate S-exps with `emacsclient'."
  (let ((s-exps-string (str:replace-all
                        ;; Discard the package prefix.
                        "next::" ""
                        (write-to-string
                         `(progn ,@s-exps) :case :downcase))))
    (log:debug "Sending to Emacs: ~a" s-exps-string)
    (ignore-errors (uiop:run-program
                    (list "emacsclient" "--eval" s-exps-string)))))

(defvar *my-keymap* (make-keymap)
  "Keymap for `my-mode'.")

(define-command org-capture (&optional (buffer (current-buffer)))
  "Org-capture current page."
  (eval-in-emacs
   `(org-link-set-parameters
     "next"
     :store (lambda ()
              (org-store-link-props
               :type "next"
               :link ,(url buffer)
               :description ,(title buffer))))
   `(org-capture)))
(define-key :keymap *my-keymap* "C-M-o" #'org-capture)

(define-command youtube-dl-current-page (&optional (buffer (current-buffer)))
  "Download a video in the currently open buffer."
  (eval-in-emacs
   (if (search "youtu" (url buffer))
       `(progn (youtube-dl ,(url buffer)) (youtube-dl-list))
       `(ambrevar/youtube-dl-url ,(url buffer)))))
(define-key :keymap *my-keymap* "C-M-c d" 'youtube-dl-current-page)

(define-command play-video-in-current-page (&optional (buffer (current-buffer)))
  "Play video in the currently open buffer."
  (uiop:run-program (list "mpv" (url buffer))))
(define-key :keymap *my-keymap* "C-M-c v" #'play-video-in-current-page)

(define-mode my-mode ()
  "Dummy mode for the custom key bindings in `*my-keymap*'."
  ((keymap-schemes :initform (list :emacs-map *my-keymap*
                                   :vi-normal *my-keymap*))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar +youtube-dl-command+ "youtube-dl"
  "Path to the 'youtube-dl' program.")

(defun auto-yt-dl-handler (url)
  "Download a Youtube URL asynchronously to /tmp/videos/.
Videos are downloaded with `+youtube-dl-command+'."
  (let ((uri (quri:uri url)))
    (when (and uri
               (member-string (quri:uri-domain uri) '("youtube.com" "youtu.be"))
               (string= (quri:uri-path uri) "/watch"))
      (log:info "Youtube: downloading ~a" url)
      (uiop:launch-program (list +youtube-dl-command+ url "-o" "/tmp/videos/%(title)s.%(ext)s"))))
  url)

(defun old-reddit-handler (url)
  (let* ((uri (quri:uri url)))
    (if (search "www.reddit" (quri:uri-host uri))
        (progn
          (setf (quri:uri-host uri) "old.reddit.com")
          (let ((new-url (quri:render-uri uri)))
            (log:info "Switching to old Reddit: ~a" new-url)
            new-url))
        url)))

(defvar *my-unproxied-domains*
  '("jit.si"
    "wikipedia.org"))

(defun auto-proxy-handler (url)
  (let* ((uri (quri:uri url))
         (domain (and uri (quri:uri-domain uri))))
    (when domain
      (next/proxy-mode:proxy-mode
       :activate
       (not (member-string domain *my-unproxied-domains*)))))
  url)

(defvar *my-blocked-hosts*
  '("platform.twitter.com"
    "syndication.twitter.com"
    "m.media-amazon.com"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun my-buffer-defaults (buffer)
  (dolist (mode '(vi-normal-mode proxy-mode my-mode))
    (pushnew mode (default-modes buffer)))
  ;; TODO: Pros and cons of the following approach to mode init?
  ;; The problem with DEFAULT-MODES is that it does not allow for configuring
  ;; the modes.
  (next/blocker-mode:blocker-mode :hostlists *my-blocked-hosts*
                                  :buffer buffer)
  (dolist (handler (list #'old-reddit-handler #'auto-proxy-handler))
    (hooks:add-to-hook (hooks:object-hook buffer 'load-hook)
                       handler)))

(defun my-window-defaults (window)
  (setf (minibuffer-open-height window) 400))

(defun my-minibuffer-defaults (minibuffer)
  (setf
   (minibuffer-line-height minibuffer) "1.1em"
   (minibuffer-font-size minibuffer) "1.1em"))

(defun my-interface-defaults ()
  (setf (download-directory *interface*) "~/temp")
  (hooks:add-to-hook (hooks:object-hook *interface* 'buffer-make-hook)
                     #'my-buffer-defaults)
  (hooks:add-to-hook (hooks:object-hook *interface* 'minibuffer-make-hook)
                     #'my-minibuffer-defaults)
  (hooks:add-to-hook (hooks:object-hook *interface* 'window-make-hook)
                     #'my-window-defaults))

(hooks:add-to-hook '*after-init-hook* #'my-interface-defaults)

It eases access from the user init.lisp.
@Ambrevar
Copy link
Member Author

OK, I've tested it quite extensively and this seems to be in pretty good shape.

My only concern is that I'm not sure how to rewire the open-file bindings: now that all root-mode bindings are set (in stone) at the definition site of root mode, I dont think we can define "open-file" on top.

I can think of the following solution: define an open-file-mode which only contains the C-x C-f binding and have this mode added to the default-modes list. Thoughts? @vindarel

@Ambrevar Ambrevar mentioned this pull request Sep 26, 2019
@Ambrevar
Copy link
Member Author

Ambrevar commented Sep 27, 2019

I'm thinking of leaving the setf get-default definition as well as add-to-default-list for backward compatibility until next version. We add a deprecation warning so that users have time to adapt their config.

@Ambrevar
Copy link
Member Author

Regarding the file-mode-manager bindings: actually it's no problem to bind them in the root-mode definition directly, so that's even simpler.

@Ambrevar
Copy link
Member Author

Let me know if you think this is good to merge, besides the above 2 fixes.

@jmercouris
Copy link
Member

I'm OK, not too opinionated about this, that said I don't think most people will like this way of doing things.

@jmercouris
Copy link
Member

Mostly because it feels a rather convoluted way of setting settings.. Rather than what one would expect with a simple setf or other such macro.

@vindarel
Copy link
Contributor

same feeling.

@vindarel
Copy link
Contributor

So we have such a slot:

   (minibuffer-line-height :initarg :minibuffer-line-height
                           :accessor minibuffer-line-height
                           :type string
                           :initform ""
                           :documentation "CSS line height for the minibuffer.
Value is a string, e.g. '1em'.
You might want to configure the value on HiDPI screen.")

Your proposal is to configure it with

(defun my-minibuffer-defaults (minibuffer)
  (setf
   (minibuffer-line-height minibuffer) "1.1em")

then

(defun my-interface-defaults ()
  (hooks:add-to-hook (hooks:object-hook *interface* 'minibuffer-make-hook)
                     #'my-minibuffer-defaults))

then

(hooks:add-to-hook '*after-init-hook* #'my-interface-defaults)

As a CL developer, I expect something like

(setf next/minibuffer:*minibuffer-line-height* "1.1em")

Did we already talk about that?

@Ambrevar
Copy link
Member Author

Ambrevar commented Sep 27, 2019 via email

@Ambrevar
Copy link
Member Author

Ambrevar commented Sep 27, 2019 via email

@vindarel
Copy link
Contributor

unload B would restore A.

When a mode is activated, can we not use a closure ? The previous value of the global would be preserved.

@Ambrevar
Copy link
Member Author

Ambrevar commented Sep 27, 2019 via email

@Ambrevar
Copy link
Member Author

Nothing is carved in stone even if we merge this. After all if we keep (setf (get-default ...)) until next version, we can always step back. Or iterate and come up with something better. Or use more globals / packages if we deem it fit. What do you think?

I'd like to merge tomorrow, let me know if I should hold off for a little longer.

@jmercouris
Copy link
Member

Like I said before, it is OK to. merge :-)

I still have my reservations. I understand statelessness and all that... but there must be a more simple way, if not for the sake of the end-user. Otherwise it would be nice if make the core FAR more minimal and expose more things in packages (such that we can use globals that are easily SETF'able).

@Ambrevar
Copy link
Member Author

In my understanding, we don't chose what the "minimal core is": it's a matter of semantic.
The minibuffer is part of the core, because feature-wise it's a the center of everything.
Should we move it to a separate package, it would not change how central it is, and as such we should avoid using globals for it in my opinion.

For now, in my understanding the core is made of the following classes:

  • remote-interface
  • window
  • buffer
  • minibuffer
  • command
  • root-mode
  • web-mode

But maybe some parts could be moved away from the slots.
For example, could move bookmarks path / importer / export to separate global variables? Maybe, but that would prevent third-party packages from cleanly customizing the importer(s)/exporter(s).

I also thought of other approaches, such as keeping a git-like history of the state of everything. This would be awesome, but that's much more complex :p

@Ambrevar
Copy link
Member Author

Maybe a better solution for users: considering the Lisp might already be too hard to write to the uninitiated, we probably need a graphical configuration interface anyways. So we could have a web page were the user can set the default values for window, buffer, etc. and upon saving, it would generate a configuration file akin to what I wrote above.

Best of both worlds! :)

@jmercouris
Copy link
Member

I see you are already thinking of a customization buffer :-)

Well, this is still a ways away I think, but surely we will not pollute the users config as it is in Emacs' default, we should make a customizations.lisp as the default :D

Anyways, please merge this in whenever you are ready!

@Ambrevar
Copy link
Member Author

Ambrevar commented Sep 28, 2019 via email

@Ambrevar
Copy link
Member Author

Merged in c8a210b.

@Ambrevar Ambrevar closed this Sep 28, 2019
@jmercouris jmercouris deleted the wip-functional-config branch January 18, 2020 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants