tl;dr Wrap pattern with \%V atom, that is \%Vpattern\%V, and extend the end of selection by one position could do the job. And remember u and gv for trying a fixed pattern.

I am sure when you have been using Vim long enough, you would encounter this situation once or twice: a few words need to be replaced but not in the entire line, just a certain portion of a line, which you use visual mode or visual block to mark it.

The substitute command operates on each line, it requires \%V atom to include selection in the pattern, from :help /\%V:

\%V
Match inside the Visual area. […]

The substitute command would be like:


:'<,'>s/\%Vpattern/replacement/g

It doesn’t change how the substitute command operates, which is still per line basis. For example, the | marks where the Ctrl+V block selection is at, not in the testing text:


z|XXXz|z
z|zXXX|z
z|zzXX|X

:'<,'>s/XXX/YYY/g results:


z|YYYz|z
z|zYYY|z
z|zzYY|Y

As expected, all the XXX‘s are replaced even the third one crossing over the visual block selection. Attempting to use the atom to match the selection area, :'<,'>s/\%VXXX/YYY/g results:


z|YYYz|z
z|zYYY|z
z|zzYY|Y

Even with the atom, the third line is still matched and replaced, however, it’s the correct result since the atom does match the inside of selection. To correct the pattern, another atom can be use to wrap around the pattern, ensuring it’s surrounded by selection, that is :'<,'>s/\%VXXX%V/YYY/g:


z|YYYz|z
z|zXXX|z
z|zzXX|X

The third line is now what we want, but not the second which is expected to be replaced, nonetheless, it’s the correct result, too, because the atom can’t match the end of selection. Unlike \< and \> for matching the beginning and the end of a word; for selection match, we only have \%V that match the inside of selection. It can only match when the position is within selection, after the last X of the second line, the selection ends and that’s why that atom doesn’t match.

In order to help match the last X, we need to move the atom one position ahead, :'<,'>s/\%VXX%VX/YYY/g:


z|YYYz|z
z|zYYY|z
z|zzXX|X

The result is what we are looking for, although it is not an ideal pattern we are looking for. A quick workaround is intentionally to extend the end of selection by one position, with that, you can wrap the pattern, and you could get the result you want.

If you forget to include \%V, u to undo first, then gv to reselect the visual selection and replace again with the atom.