-
Notifications
You must be signed in to change notification settings - Fork 12
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
Patch function without providing complete source in patch #50
Comments
I certainly do see the utility, as I have also experienced frequent breakage of patches due to unrelated changes in function internals. The feature would be nontrivial to implement for a few reasons:
That said, I think it would be a great idea! |
I thought that el-patch does something similar when validating the patch and I was hoping that I can leverage that implementation.
Good point. I wonder if it’s better to output a full el-patch into a file and only do the slow operation of actually generating the full el-patch when the patch validation fails.
Indeed, that’s why I wanted to get input before actually working on the pull request. Do you see a problem with the interface I suggested above? One issue that I thought about is what if the user wants to do multiple patches to the same function in different locations. But that can be done by passing multiple forms to the fictional ‘patch-el-patch’. EDIT: A different proposal that might also mitigate the same problem (and be easier to implement but a bit more complicated to use) is to introduce a function like (el-patch-defun foo nil
(el-patch-until 'let) ;; This matches all and any forms until the form starting with let is found
(let ((el-patch-until))
(el-patch-until 'bar)
(bar arg (el-patch-add new-arg) )
(el-patch-until) ;; This matches all forms until the end of the current one
)) EDIT2: Another way (sorry for spamming). While implementing the previous solution, I realized that the user doesn't actually need to provide the form until which matching is done (since it should be the one right after that keyword). So the final solution would look like this. (el-patch-defun foo nil
el-patch-filler
(let (el-patch-filler)
el-patch-filler
(bar arg (el-patch-add new-arg) )
el-patch-filler ;; This matches all forms until the end of the current one
)) BTW, this way is not great if, for example, one wants to change the n'th (el-patch-defun foo nil
el-patch-filler
(let el-patch-filler)
el-patch-filler
(let el-patch-filler)
el-patch-filler
(let (el-patch-filler)
el-patch-filler
(bar arg (el-patch-add new-arg) )
el-patch-filler ;; This matches all forms until the end of the current one
)) |
Yes, that is certainly the way to do it. I was just saying that if you did so, then it would Emacs would be opening a file and doing various other sketchy operations during macroexpansion, whereas typically macroexpansion is expected to be stateless and fast. There's no specific problem that will be caused by doing something more complicated; it's just a sign to be careful and consider if there is another approach. Not that I have a better suggestion for how to implement a feature like this.
This gave me an idea for how to avoid the usability problems that I described in my previous comment. If we were to implement what you've suggested, notice that it's kind of the same as what we have currently, except:
So why not solve the underlying problem directly, and provide a command that will take a patch template, and generate a patch for you? That more or less eliminates the busywork of addressing irrelevant changes in the upstream code, without introducing any issues for the user like having to byte-compile their init-file to use this feature with reasonable performance.
OK, I thought about it for a while, and I have a suggestion. I translated all instances of (el-patch-template (defvar ruby-electric-mode-map)
(el-patch-remove
(dolist ...))
(el-patch-concat
"Keymap used in ruby-electric-mode"
(el-patch-add ".\n\nThe single-character bindings have been removed.")))
(el-patch-template (defun TeX-update-style)
(el-patch-concat
"Run style specific hooks"
(el-patch-add
", silently,")
" for the current document.
Only do this if it has not been done before, or if optional argument
FORCE is not nil.")
(el-patch-remove
(message "Applying style hooks..."))
(el-patch-remove
(message "Applying style hooks...done")))
(el-patch-template (defun with-editor--setup)
(el-patch-splice 2
(when (server-running-p server-name)
(setq server-name (format "server%s" (emacs-pid)))
...)))
(el-patch-template (defun magit-maybe-start-credential-cache-daemon)
(el-patch-wrap 2
(with-current-buffer
(get-buffer-create " *git-credential-cache--daemon*")
(start-process "git-credential-cache--daemon"
(el-patch-swap
" *git-credential-cache--daemon*"
(current-buffer))
magit-git-executable
"credential-cache--daemon"
magit-credential-cache-daemon-socket)
(el-patch-add
(set-process-query-on-exit-flag
(get-buffer-process (current-buffer)) nil)))))
(el-patch-template (defun (el-patch-swap restart-emacs radian-new-emacs))
(el-patch-concat
(el-patch-swap
"Restart Emacs."
"Start a new Emacs session without killing the current one.")
"
When called interactively ARGS is interpreted as follows
- with a single `universal-argument' (`C-u') Emacs is "
(el-patch-swap "restarted" "started")
"
with `--debug-init' flag
- with two `universal-argument' (`C-u') Emacs is "
(el-patch-swap "restarted" "started")
" with
`-Q' flag
- with three `universal-argument' (`C-u') the user prompted for
the arguments
When called non-interactively ARGS should be a list of arguments
with which Emacs should be "
(el-patch-swap "restarted" "started")
".")
(el-patch-swap
(save-buffers-kill-emacs)
(restart-emacs--launch-other-emacs restart-args))) Notice:
Thoughts? |
Very nice syntax! I think this should cover a majority of cases. Having the requirement of exactly one match is really important to avoid unintended consequences. The implementation of the patch template should not require any change to el-patch (except perhaps for some refactoring).
Yeah, I think combining both ideas (pattern matching and a filler form) would provide the most power and the shortest templates.
I thought about using smaller symbols (although I did not think of the natural One thing that would need to be ironed out is the behaviour of
We can go from the least-automated workflow:
To the most automated workflow:
The automated workflow is kinda ugly and I prefer the manual one. In any case, I think the manual workflow should be the first target.
Sounds good! |
Ideally (i.e. from a user perspective), you'd be able to put it anywhere. I guess it depends how challenging the pattern-matching turns out to be, though, in terms of implementation.
I've personally never seen
Good catch, I'm not sure what to do in that case. For
Agreed on all points.
This seems fine. Here's a wild idea for the future:
That way, we'd support the manual workflow you suggested, but also, people who byte-compile their init-file could just put in the template form and have it magically work without added startup cost (just slower byte-compilation). |
EDIT: The matching with backtracking turned out to be easier than expected. We can see the effect on the performance and re-adjust if necessary. In any case, I think we are converging on a similar viewpoint. I will see how complicated the implementation is and we can discuss further. EDIT 2: the implementation ideas above do not work nicely with |
I have a question regarding a possible extension of
el-patch
. In most cases, I just need to change a single form in a possibly long definition of a particular function. Such changes frequently break when the other code in the function, which I haven't patched, changes even though I know that such changes should not break my patch.So I am wondering if it would be possible, in theory, to build a patch given a form. Something like this
and one would search the definition of
foo
for all forms which match(function-call arg1 arg2)
and add an argumentarg3
. One could also add key forms to make sure that they exist in the function definition if such forms are necessary for the patch. Of course this assumes that one has the definition offoo
rather than its compilation.To be clear, I know that this is not implemented in
el-patch
, but I am wondering if people see a problem with such a methodology.The text was updated successfully, but these errors were encountered: