noctuid / things.el

Extensions to thingatpt.el
51 stars 0 forks source link

Smart argument text object #4

Open noctuid opened 5 years ago

noctuid commented 5 years ago

Argument text objects could just be implemented as a separator text object constrained to a pair. Using the pair text object to check that a separator is not in a nested pair would allow for it to work properly with nested function calls. The comment and string text objects could potentially used in the same way as they are in the regexp pair text object to invalidate separators using string/comment boundaries (wellle/targets.vim#107 and wellle/targets.vim#188). The issue with this is again figuring out how to do things in a performant way since builtin Emacs functions use syntax tables and only support single characters.

I think it makes sense to handle counts the same way as in (relevant wellle/targets.vim#140), meaning seeking will enter into nested arguments. This behavior would be consistent with the current spec/behavior for nestable text objects (pairs).

noctuid commented 5 years ago

An argument thing should be possible using constraints with something like this:

(things-define-separator 'things-comma "," t)
(things-evil-define argument (things-comma
                              :constraint things-paren
                              :ignore (things-paren things-string things-aggregated-comment))
                    :last-key "p" :keys "a")

With :constraint, the comma thing will be required to be inside parens. The combination with :ignore handles nested arguments. For example:

;; | as evil point (on next character unlike default emacs cursor)
func(arg1, |func(nested_arg1, nested_arg2), arg2)
;; via to select an inner argument gives
func(arg1,~ func(nested_arg1, nested_arg2|), arg2)
;; instead of this
func(arg1,~ func(nested_arg|1, nested_arg2), arg2)

Some messing with things-evil to handle visual state may still be necessary. For example

;; dia works but via will give the wrong selection
func(arg1,| (nested_arg1, nested_arg2), arg2)

The main problem right now (besides constraint performance) is seeking and not including delimiters. The current implementation of optionally letting the buffer bounds be a bound of one side of the separator is problematic. Seeking only goes to the separator or a the beginning or end of the buffer. It won't go to a paren (like it should) with a paren constraint meaning that overlays are missing with remote selection.o

One way to fix this would be to actually optionally pass a thing to the separator that it would also seek to the thing, bound to it, and also exclude the outer portion of it. Either separator has to do everything and not rely on constraint at all... or there could be some hybrid solution.

I really don't like the idea of a thing implementation having to consider that :constraint exists at all, but separator already does with include-buffer-bounds just in an insufficient way. It's not possible to handle things entirely outside of the separator thing, and implementing everything inside the separator thing without relying on :constraint would be harder/duplicate work. One possibility would be to implement a custom "try seek" function that also seeks to the constraint thing. This seems like the simplest solution.

I should also consider constraining to the "inner bounds" for things-with-constraint and requiring a things-inner-op. This would prevent parens from being included in the selection. The saved bounds (currently things--narrowed) should not be the inner bounds though (unless they are the same). This wouldn't allow removing the check in for things--remove-things to see if a bounds matches the constrained bounds because something like a paragraph thing will have the same bounds for an inner thing (would be an issue if have such a thing both in :constraint and :ignore).