meow-edit / meow

Yet another modal editing on Emacs / 猫态编辑
GNU General Public License v3.0
1.07k stars 128 forks source link

Meow-visit only in grabbed region in beacon-mode #553

Closed Haxxflaxx closed 2 months ago

Haxxflaxx commented 3 months ago

Is it possible to limit the symbols in meow-visit to only those within the grabbed selection?

A really nice work flow is to make a selection that I want to edit, grab it, and call visit to make cursors at each occurrence. The way it is now, all of the other symbols in the document are also included, so after making my selection and calling visit, I end up on the next match, not one in my grab. (This is assuming making the selection top to bottom)

DogLooksGood commented 3 months ago

If a symbol inside the secondary selection is marked, then beacons will be created, it doesn't matter where you did meow-visit. The beacon state is toggled according to your cursor position, so as long as your cursor is inside the secondary selection, it do what you expect.

Haxxflaxx commented 3 months ago

I am aware. It is mostly a convenience thing. Maybe there are some other way of doing this that I'm thinking of.

If I right now want to change a symbol in a region, I grab it into a secondary selection, then either need to go to the beginning of the region followed by visit; or reverse isearch for the symbol followed by mark-word.

I tried out helix for a bit and was amazed how friction less multi select and editing was. There you would mark ta region, press 's' for select and type the symbol name. This would create a cursor at each occurrence of the symbol.

This is a really nice way of working, and I was wondering if a similar behavior could be mimicked in meow.

Haxxflaxx commented 3 months ago

Hmm... maybe this is a skill issue on my part. After playing around a bit and reading the implementation I realize that I can run visit with an arbitrary regex if I disable sanitization.

Maybe the problem I have is that I don't really understand how to get cursors where I want them within the grabbed region. It seems to me that it is only useful in very specific circumstances i.e. change all occurrences of symbol, or change everything in column x.

I can't see how to do more elaborate things. Maybe this is a mistake on my side, expecting it to be similar to multiple cursors.

DogLooksGood commented 3 months ago

When you want to apply the same operations to multiple places, there are actually two cases.

  1. You have a pattern that describes the places, for example, all occurs, the end of each word, every line, etc.
  2. You don't have such pattern.

Beacon state is only for case 1. For case 2, the common solution with kmacro is to record the operations at the first place, then navigate to each place and apply the macro manually. Compare to how multiple-cursor works, it takes the same amount of effort, it's just different in the order of steps.

Haxxflaxx commented 3 months ago

After looking more into the documentation I found some helpful things like make selection, grab region, record macro to play on each line. And after some experimentation I found I could do things like select every other line by prefix with 2.

I think maybe there is a bit of a mental shift that needs to happen on my part. I have been using multiple cursors for a while. Then i used helix, which doesn't really have cursors, it has selections instead. There you could do things like select an arbitrary regex. Something like select space would select all whitespace, similar to what negative find space would do in meow, but you could also select something like "-|-". This you could do with meow-visit, but it is a bit cumbersome and you can't without disabling sanitization.

I'm thinking maybe implementing a function like meow-visit, but more similar to how isearch works.

DogLooksGood commented 3 months ago

Meow uses regexp-search-ring for meow-visit and meow-search, so meow-search continues with the input from isearch-forward-regexp. Unfortunately, there's no wrapped meow-isearch-xxx commands yet.

There are several searching packages, e.g. ctrlf, phi-search, etc. It's better to have a solution that works with all of them.

Haxxflaxx commented 3 months ago

I think meow-visit does what I'm looking for. The things that get in the way is the regex sanitization (which can be disabled), and completing-read.

I don't know if it's reasonable to add a function like meow-select, or it would be better to have an option to disable the completion popup. I can see how you would use both for different purposes.

DogLooksGood commented 3 months ago

Are you aware of that you can submit your raw input even with completing-read, I don't know which completion UX you are using, but it should be always supported.

Haxxflaxx commented 3 months ago

It is. I'm using vertico, so I can simply press up to deselect the completion. The more painful part is using completion with the unsanitized list, as it becomes a bit messy.

It's mostly as a convenience thing. I use isearch quite a lot for navigation, and it doesn't interact well when a region is active, nor does the result end up selected.

Both of these are handled by meow-visit but that seem more build around the case when you want to go to a word or symbol, not when you want to go back a few words selecting "n|xe>t" so you can fix it and go back to typing.

Maybe a better example would be using a regex like select all numbers in a grabbed region or something.

Haxxflaxx commented 3 months ago

Seems like there is an open PR that looks like what I'm talking about #501

DogLooksGood commented 3 months ago

Don't forget we have query-replace and query-replace-regexp, you don't have to use beacon mode for simple query replace, unless you want to apply some complex changes that is hard to achieve by query replace.

Haxxflaxx commented 3 months ago

A good point, as well as regular old macros could probably solve this as well. An interesting side note about helix is that they don't have any form of search and replace at all (outside of piping to sed), instead they rely fully on multiple selections.

Maybe a better way of viewing this is that isearch integrates poorly with meow right now. It could probably be improved if mark is cleared before running, but it would be even better if the search term was selected, and even better if it used the search ring so that I can use it the same as other parts of meow.

I assume you primarily use meow-visit. I think it's useful, especially when looking for things with long names. I use it from time to time but often fall back to isearch for quick navigation, and in large files like logs where meow-visit is really slow. (I think it's from the first step where it gathers symbols for the completion. When running meow-mark-word it does the selection more or less instantly.)

I think having both would be really nice. I see a lot of overlap between meow-visitand imenu (possible optional backend maybe?)

DogLooksGood commented 3 months ago

I do use both meow-visit and isearch. You can't just replace isearch with meow-visit.

And you are right, we don't have a nice incremental search integration at the moment. On one side, there are many incremental search packages, it's hard to find a general way to integrate all of them. On the other side, there are a lot features in isearch and its alternatives, it's not enough to just cancel selection before it starts and create selection on the result.

https://github.com/meow-edit/meow/discussions/175#discussioncomment-1933970

Haxxflaxx commented 3 months ago

I think I see some of the problems with just wrapping isearch. I can also see how any change to how isearch works may break how you can use it in insert mode.

I really just want a command to make arbitrary selections that integrates with meow-search. I'm thinking of just making a stripped down version of meow-visit, without sanitization and completion.

jixiuf commented 3 months ago

Meow uses regexp-search-ring for meow-visit and meow-search, so meow-search continues with the input from isearch-forward-regexp. Unfortunately, there's no wrapped meow-isearch-xxx commands yet.

There are several searching packages, e.g. ctrlf, phi-search, etc. It's better to have a solution that works with all of them.

I implements some command with isearch as backend meow-isearch is a replacement for meow-search

(defun meow-isearch (arg)
  "Repeat the forward search and then exit isearch immediately."
  (interactive "P")
  (let ((case-fold-search nil)          ; always be nil in meow
        (reverse (xor (meow--with-negative-argument-p arg)
                      (meow--direction-backward-p)))
        region-str)
    (when (region-active-p)
      (setq region-str (buffer-substring-no-properties
                        (region-beginning)(region-end)))
      (when (string-equal isearch-string region-str)
        (setq region-str nil)))
    (if region-str
        (progn
          (if reverse
              (call-interactively #'isearch-backward-regexp)
            (call-interactively #'isearch-forward-regexp))
          (isearch-yank-string region-str)
          (isearch-search-and-update))
      (if reverse
          (call-interactively #'isearch-repeat-backward)
        (isearch-repeat-forward))))
    (isearch-done) ; (isearch-exit)
    ;; M-x:lazy-highlight-cleanup to cleanup highlight
    (unless lazy-highlight-cleanup
      (isearch-lazy-highlight-new-loop))
  ;; (meow--highlight-regexp-in-buffer isearch-string)
  )

(defun meow-isearch-visit (arg)
  "isearch version of `meow-visit'"
  (interactive "P")
  (let* ((case-fold-search nil)          ; always be nil in meow
        (reverse (xor (meow--with-negative-argument-p arg)
                      (meow--direction-backward-p)))
        ;; should update meow--prompt-symbol-and-words for t
        (text (meow--prompt-symbol-and-words
               (if arg "Vhisit backward: " "Visit: ")
               (point-min) (point-max)))
        )
    (if reverse
        (call-interactively #'isearch-backward-regexp)
      (call-interactively #'isearch-forward-regexp))
    (isearch-process-search-string
   text (mapconcat 'isearch-text-char-description text ""))
    (isearch-search-and-update)
    (isearch-done) ; (isearch-exit)
    ;; M-x:lazy-highlight-cleanup to cleanup highlight
    (unless lazy-highlight-cleanup
      (isearch-lazy-highlight-new-loop))

    ))
(defun meow-lazy-count-hook()
  (when (and (meow-normal-mode-p)
             (region-active-p))
    (save-mark-and-excursion
      (meow--remove-search-indicator)
      (when isearch-lazy-count-current
        (meow--show-indicator (point) (isearch-lazy-count-format)))
      ))
  )
(add-hook 'lazy-count-update-hook #'meow-lazy-count-hook)

some highlight related variables:

(setq isearch-lazy-count t)
(setq lazy-highlight-cleanup nil)
(setq lazy-highlight-initial-delay 0)
(setq lazy-highlight-no-delay-length 0)
(setq lazy-highlight-buffer t)

image I think meow-search/meow-visit/meow--add-beacons-for-match should rewrite using isearch, isearch respect case-fold-search search-invisible and has more features.

need: https://github.com/meow-edit/meow/discussions/175#discussioncomment-1933970

Haxxflaxx commented 3 months ago

If isearch can be integrated then I think this is a good way to go. Is the idea to completely replace the meow search internals with isearch in this case?

I created a stripped down version of meow-visit replacing the input function with read-regex

  (defun meow-select (arg)
    (interactive "P")
    (let* ((reverse arg)
           (pos (point))
           (prompt (if arg "Select backward: " "Select: "))
           (text (read-regexp prompt))
           (visit-point (meow--visit-point text reverse)))
      (if visit-point
          (let* ((m (match-data))
                 (marker-beg (car m))
                 (marker-end (cadr m))
                 (beg (if (> pos visit-point) (marker-position marker-end) (marker-position marker-beg)))
                 (end (if (> pos visit-point) (marker-position marker-beg) (marker-position marker-end))))
            (thread-first
              (meow--make-selection '(select . visit) beg end)
              (meow--select))
            (meow--push-search text)
            (meow--ensure-visible)
            (meow--highlight-regexp-in-buffer text)
            (setq meow--dont-remove-overlay t))
        (message "Select: %s failed" text))))

It does more or less what I want it to do, but lack some nice conveniences like: highlighting as you type, case insensitivity etc... I don't know if there is a fancy built in function for doing this or if I'd have to write my own.

DogLooksGood commented 3 months ago

We should take other isearch alternatives into account as well.

Haxxflaxx commented 2 months ago

After better understanding how meow-search works, this can do what I want out of a select command. Just select whatever string of characters I'm interested in editing, hit n to select all of them within the grabbed region, then multi-edit away.