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

Add getregionpos() #14617

Closed
wants to merge 27 commits into from
Closed

Add getregionpos() #14617

wants to merge 27 commits into from

Conversation

Shougo
Copy link
Contributor

@Shougo Shougo commented Apr 22, 2024

For #14609

@zeertzjq
Copy link
Member

zeertzjq commented Apr 22, 2024

When the selection is blockwise, shouldn't there be a pair of positions for every line? They may have different col and off values when there are TABs or multibyte chars.

* "getregionpos()" function
*/
static void
f_getregionpos(typval_T *argvars, typval_T *rettv)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests for the new getregionpos() function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It should. But current tests are very many. It is hard.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without tests, this cannot be merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes...

Copy link
Contributor

@girishji girishji Apr 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add one more test similar to def Test_getregion() in test_vim9_builtin.vim, say Test_getregionpos() and check for E1209 and E1211?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I have added them.

@Shougo
Copy link
Contributor Author

Shougo commented Apr 23, 2024

When the selection is blockwise, shouldn't there be a pair of positions for
every line? They may have different col and off values when there are TABs or
multibyte chars.

I have implemented it. Please check the implementation.

@Shougo
Copy link
Contributor Author

Shougo commented Apr 24, 2024

I have added tests.

NOTE: I cannot test for list_alloc() and list_append_list() though...

runtime/doc/builtin.txt Outdated Show resolved Hide resolved
runtime/doc/builtin.txt Outdated Show resolved Hide resolved
runtime/doc/builtin.txt Outdated Show resolved Hide resolved
runtime/doc/builtin.txt Outdated Show resolved Hide resolved
Comment on lines 4336 to 4337
Note: When the selection is blockwise, it returns a pair of
positions for every line:
[[{pos_start}, {pos_end}], ...]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this note is not needed now, right? because the same structure is always returned

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same structure is returned. But it may be hard to understand why the positions are list.

Copy link
Contributor

@girishji girishji Apr 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you say instead:

Same as |getregion()|, but returns a list of positions describing the buffer text
segments bound by {pos1} and {pos2}.

You can remove:

See |getregion()| for the arguments and notes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I have fixed it.

@chrisbra
Copy link
Member

So, is this ready? Have a few people tried it out?

@jessepav
Copy link
Contributor

I've been running with this PR applied, and it works as well as getregion() for me.

The only issue I've faced with both of these functions is that they error out (E964: Invalid column number: 2147483647) when you make a linewise visual selection and use the '< and '> positions, since the column of pos2 is set to v:maxcol.

I don't know if that is unintended behavior, though, and is easy enough to check for in scripts.

@girishji
Copy link
Contributor

girishji commented Apr 29, 2024

So, is this ready? Have a few people tried it out?

  1. I tried this test, and it gave error E964: Invalid column number: 2147483647
./vim -Nu NONE -S <(cat <<EOF
    vim9script
    :set shortmess=I
    autocmd TextYankPost * {
        if v:event.operator ==? 'y'
            :echom getregionpos(getpos("'["), getpos("']"))
        endif
    }
    setline(1, ['abcd', 'efghij'])
    :normal yy
EOF
)

Please check if my test itself is wrong. I wrote it in a hurry.

  1. It seems it is not exactly like getregion(). If I were to select a visual region using v or V, I think it should return {pos} of all intermediate lines, not just first and last line. It does this for ctrl-v. I understood this from reading the doc. I have not tested this.

@Shougo
Copy link
Contributor Author

Shougo commented Apr 29, 2024

I have fixed columns error. It already exists when getregion() implementation though.

It seems it is not exactly like getregion(). If I were to select a visual region using v or V, I think it should return {pos} of all intermediate lines, not just first and last line. It does this for ctrl-v. I understood this from reading the doc. I have not tested this.

I know the feature. I can change the behavior. But I think it is not useful.
<c-v> behavior is changed because of #14617 (comment).
Please fix my documentation if it is not exact.

@jessepav
Copy link
Contributor

After the most recent commit, this PR works great for me – and now getregion() works for 'V' mode too.

@girishji
Copy link
Contributor

girishji commented Apr 30, 2024

The test I mentioned above still fails. Same error (E964).

Somewhere it is mentioned that the motivation for this API is this, which roughly translates to this test:

./vim -Nu NONE -S <(cat <<EOF
    vim9script
    :set shortmess=I
    autocmd TextYankPost * {
        if v:event.operator ==? 'y'
            var pos = getregionpos(getpos("'["), getpos("']"))->mapnew((_, v) => [v[0][1], v[0][2], v[1][2] - v[0][2] + 1])
            var m = matchaddpos('IncSearch', pos)
            timer_start(300, (_) => m->matchdelete())
        endif
    }
    setline(1, ['abcd', 'efghij'])
    :normal yy
EOF
)

This test does not pass. I'm afraid this PR is not well thought through.

@jessepav
Copy link
Contributor

The test given above doesn't fail for me with the latest commit.

pos is set to [[1, 1, 5]] by the autocmd when I test it.

@Shougo
Copy link
Contributor Author

Shougo commented Apr 30, 2024

Yes. It works for me. I think you don't build the latest version.

@girishji
Copy link
Contributor

girishji commented Apr 30, 2024

The second test fails though. There is no value to getregionpos() if you are just returning two getpos(), which are the exact arguments I am supplying!

Try the following. It does not highlight both lines, but only the first line. This is because getregionpos is returning [[[1, 1, 1, 0], [1, 2, 7, 0]]] instead of [[[1, 1, 1, 0], [1, 1, 5, 0]], [[1, 2, 1, 0], [1, 2, 7, 0]]].

./vim -Nu NONE -S <(cat <<EOF
    vim9script
    :set shortmess=I
    autocmd TextYankPost * {
        if v:event.operator ==? 'y'
            var pos = getregionpos(getpos("'["), getpos("']"))->mapnew((_, v) => [v[0][1], v[0][2], v[1][2] - v[0][2] + 1])
            var m = matchaddpos('IncSearch', pos)
            timer_start(300, (_) => m->matchdelete())
        endif
    }
    setline(1, ['abcd', 'efghij'])
    :normal yj
EOF
)

I am just a little weary of this PR.

@girishji
Copy link
Contributor

girishji commented Apr 30, 2024

I found another bug. <c-v> does not work. The values returned are incorrect. It just returns first and last positions, instead of every line in the visual block.

./vim -Nu NONE -S <(cat <<EOF
    vim9script
    :set shortmess=I
    autocmd TextYankPost * {
        if v:event.operator ==? 'y'
            :echom getregionpos(getpos("'["), getpos("']"))
        endif
    }
    setline(1, ['abcd', 'efghij'])
    :exec "normal \<c-v>"
    :normal 3ljy
EOF
)

@Shougo
Copy link
Contributor Author

Shougo commented Apr 30, 2024

I found another bug. <c-v> does not work. The values returned are incorrect. It just returns first and last positions, instead of every line in the visual block.

It is not bug. Please see getregion() documentation.

		The optional argument {opts} is a Dict and supports the
		following items:

			type		Specify the region's selection type
					(default: "v"):
			    "v"		for |characterwise| mode
			    "V"		for |linewise| mode
			    "<CTRL-V>"	for |blockwise-visual| mode

			exclusive	If |TRUE|, use exclusive selection
					for the end position
					(default: follow 'selection')

The default type is v. You need to use visualmode().

It works for me.

vim9script
:set shortmess=I
autocmd TextYankPost * {
    if v:event.operator ==? 'y'
      :echom getregionpos(getpos("'["), getpos("']"), { type: visualmode() })
    endif
}
setline(1, ['abcd', 'efghij'])
:exec "normal \<c-v>"
:normal 3ljy

@girishji
Copy link
Contributor

girishji commented Apr 30, 2024

Thanks, now I get E475: Invalid value for argument type: when I do this:

./vim -Nu NONE -S <(cat <<EOF
    vim9script
    :set shortmess=I
    autocmd TextYankPost * {
        if v:event.operator ==? 'y'
            var pos = getregionpos(getpos("'["), getpos("']"), { type: visualmode() })
            var m = matchaddpos('IncSearch', pos->mapnew((_, v) => [v[0][1], v[0][2], v[1][2] - v[0][2] + 1]))
            timer_start(300, (_) => m->matchdelete())
        endif
    }
    setline(1, ['abcd', 'efghij'])
    :normal yj
EOF
)

Also, my other concern (see post above) about this api not adding additional value by not returning intermediate lines during 'v' and 'V' still stands.

@jessepav
Copy link
Contributor

Visual mode was never entered in your test script, so visualmode() is returning an empty string.

@Shougo
Copy link
Contributor Author

Shougo commented Apr 30, 2024

Try the following. It does not highlight both lines, but only the first line. This is because getregionpos is returning [[[1, 1, 1, 0], [1, 2, 7, 0]]] instead of [[[1, 1, 1, 0], [1, 1, 5, 0]], [[1, 2, 1, 0], [1, 2, 7, 0]]].

OK. I get the problem. I will change the result value.

@girishji
Copy link
Contributor

girishji commented Apr 30, 2024

Visual mode was never entered in your test script, so visualmode() is returning an empty string.

Thanks. Modified the script.

I see 2 issues:

  • <c-v> is reporting one additional character (to the right) than what was selected. Is this intended? How are the tests passing?
  • intermediate lines are ignored in v and V

You can use the script, visual select, and verify.

/vim -Nu NONE -S <(cat <<EOF                                                                                                        [getregion_pos]
    vim9script
    :set shortmess=I
    autocmd TextYankPost * {
        if v:event.operator ==? 'y'
            var [begin, end, vmode] = [getpos("'["), getpos("']"), visualmode()]
            var pos = getregionpos(begin, end, vmode == null_string ? {} : {type: vmode})
            :echom pos
            var m = matchaddpos('IncSearch', pos->mapnew((_, v) => [v[0][1], v[0][2], v[1][2] - v[0][2] + 1]))
            timer_start(300, (_) => m->matchdelete())
        endif
    }
    setline(1, ['abcd', 'efghij'])
    :normal yj
EOF
)

@Shougo
Copy link
Contributor Author

Shougo commented Apr 30, 2024

<c-v> is reporting one additional character (to the right) than what was selected. Is this intended? How are the tests passing?

Hm... It seems broken. I will fix.

@Shougo
Copy link
Contributor Author

Shougo commented Apr 30, 2024

I have fixed the problems. Please test.

@chrisbra
Copy link
Member

@justinmk does that do what you want?

@girishji
Copy link
Contributor

girishji commented May 1, 2024

I have fixed the problems. Please test.

I found many problems:

  • Do reverse selection in v (begin position after end position), and it gives wrong results.
  • Have two words foo bar, and cursor on b, and then yank the word ye, it returns wrong results (highlights foo instead of bar).
  • v does not work unless cursor is on first column

I updated the test to include the offset and exclusive. If anyone has time, please try the script below. Yank some text (with and without visual selection) and verify the values printed.

./vim -Nu NONE -S <(cat <<EOF
    vim9script
    def HighlightYank()
        if v:event.operator ==? 'y'
            var [beg, end, vmode] = [getpos("'["), getpos("']"), visualmode()]
            var pos = getregionpos(beg, end, vmode != null_string ? {type: vmode} : {})
            :echom pos
            var m = matchaddpos('IncSearch', pos->mapnew((_, v) => {
                var col_beg = v[0][2] + v[0][3]
                var col_end = v[1][2] + v[1][3] + (!v:event.inclusive ? 1 : 0)
                return [v[0][1], col_beg, col_end - col_beg + 1]
            }))
            timer_start(300, (_) => m->matchdelete())
        endif
    enddef
    autocmd TextYankPost * HighlightYank()
    setline(1, ['abcd', 'efghij', 'foo bar', '	baz	 bza', 'ぬfooぼ barі'])
    :set ruler
    :normal 2lv2jy
EOF
)

@Shougo
Copy link
Contributor Author

Shougo commented May 3, 2024

I have rebased it and fixed the conflict.

@chrisbra
Copy link
Member

chrisbra commented May 7, 2024

Thanks all. I have made one minor change using mb_ptr2len() instead of utfc_ptr2len().

@chrisbra chrisbra closed this in b4757e6 May 7, 2024
@Shougo Shougo deleted the getregion_pos branch May 8, 2024 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants