-
-
Notifications
You must be signed in to change notification settings - Fork 413
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
Conversation
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.
Please wait a bit, this requires more thinking and tests (at least on my side), thanks. |
This is my new (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.
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 I can think of the following solution: define an |
This is required if we want to change the default zoom level.
I'm thinking of leaving the |
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. |
Let me know if you think this is good to merge, besides the above 2 fixes. |
I'm OK, not too opinionated about this, that said I don't think most people will like this way of doing things. |
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. |
same feeling. |
So we have such a slot:
Your proposal is to configure it with
then
then
As a CL developer, I expect something like
Did we already talk about that? |
I understand, but what's really at play here is the _paradigm_.
Most of us are used to a "all global, non-functional" approach to
configuring programs (assuming they can be configured).
Take Emacs: Historically it had dynamic scoping by default, for the very
reason that it made it easier for the user. This proved
to be a rather big mistake over time, because it makes programming
errors much more pervasive.
Still in Emacs: so many globals all over the place, it's really hard to
modify a value without impacting everything else.
The consequence: every couple of days, my Emacs breaks down in
mysterious ways and I have to restart it.
What I'm proposing here is a system in which the _whole state_ of the
program is a first-class object. This gives us a lot of flexibility.
With Common Lisp, we are gifted with the package system. So we can much
more easily isolate stuff.
While I'm insisting on this functional approach for the _core
of Next_, I don't think the external, "leaf" modules have to be this
way. For instance, the VCS module has a couple of global variables and
that's probably alright if no other module is going to depend on it.
All in all, while this approach of configuring the _core of Next_ might
seem a bit convoluted, it has many long term benefits in my opinion:
- Full control of the whole state.
- If a slot is set from 2 different place, there is trace of this in the
hooks. In a way, this gives us a history of all values that are set
to a slot.
- Resilient _core_: with no globals, it's much harder to break the state
of Next. Any broken _part_ can be re-instantiated.
- Locality: every value can be set per-buffer, or per-window, etc. It's
like Emacs' buffer-local variables, but for more objects.
- Less programming errors.
To conclude, the main issue I see with the above approach is that we
need to educate our audience to this "new" paradigm.
But hey, it's Lisp after all, the parentheses alone make a lot of people
feel uneasy already :p
|
See my answer above: I think that package globals are OK when used in
"leaf" packages (packages that are not going to be re-used by anyone).
For minibuffers, my fear is that it's too central to risk exposing
globals.
Example:
- Mode/Package A is activated and changes the default value of line-height.
- Same for Mode/Package B.
- When Mode/Package B is turned off / unloaded, the value of A is gone.
If we don't have a global but use the hook approach instead, unload B
would restore A. In my opinion this is awesome.
|
When a mode is activated, can we not use a closure ? The previous value of the global would be preserved. |
It's an interesting idea.
The closure could be stored in a "deactivation function".
I can see an integrity problem though:
- Enable A.
- Enable B.
- Disable A.
Now we are back with the default value, while B is still active.
What about those things that don't have a deactivation function?
How do you implement this in practice? It seems to me that you'd need a
dedicated function, so if the user relies on `setf` it won't work.
|
Nothing is carved in stone even if we merge this. After all if we keep I'd like to merge tomorrow, let me know if I should hold off for a little longer. |
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). |
In my understanding, we don't chose what the "minimal core is": it's a matter of semantic. For now, in my understanding the core is made of the following classes:
But maybe some parts could be moved away from the slots. 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 |
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 Best of both worlds! :) |
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 Anyways, please merge this in whenever you are ready! |
Will do!
|
Merged in c8a210b. |
Let me know if you like #381 (comment).
If so, I'll update the documentation and merge.