2

I would like to get not the first, but the second occurrence of my pattern using search('pattern', 'myflags').

I could not find any flag or option in search() documentation that could help me do that.

I really liked my flags, especially 'b' to search backwards and 'n' to avoid cursor movement, so I wanted to see if there was a way to reach the second occurrence of my pattern with this function.

Edit

I wanted the question to be as short and general as possible, here's a bit more context : I'm coding in Fortran and I want the name of the subroutine I'm currently in to be displayed in my statusline. I therefore ended up with a little function (thanks to this post) :

function! SubName() abort
   let n_line = search('subroutine ', 'bcnW')  " find previous occurrence of 'subroutine'
   return matchstr(getline(n_line), 'subroutine \zs\w\+')  " extract the word right after 'subroutine', which should be its name
endfunction

However, I sometimes write the word 'subroutine' in my code comments (for instance, This subroutine really useful is incomprehensible.). In that case, right after the comment line, the function will extract "really" thinking it's my subroutine's name.

So I came up with a condition to detect comments (if the beginning of the line is a '!' or a 'c' character). But if this condition is true, I need to do my search() again, this time up to the second match, so that the 'subroutine' pattern that is found is really the declaration of the subroutine, and not just one of my comments.

A generic fortran code could be

subroutine mySubroutine  
! This is an useful subroutine yeah.
! Stuff happens 
end 

Here, I would like my function to extract "mySubroutine" instead of "yeah", even if my cursor is at the bottom.

4
  • 3
    I'm not really sure to get what you want but maybe you can use the function twice? Also it wouldn't hurt if you put a minimum of context in the question (and not just a link) :-)
    – statox
    Jun 15, 2016 at 12:39
  • 3
    So what you actually want is just to change your pattern to something like ^\s*subroutine ?
    – VanLaser
    Jun 15, 2016 at 13:14
  • Eeeer maybe. I'm not good at regexp. This makes sure that there is nothing / only whitespaces before the "subroutine" word ?
    – Feffe
    Jun 15, 2016 at 13:19
  • 2
    Ok that seems perfect and very simple ! If you want to write it as an answer, it seems to work (and to be a little simpler than the previous answer)
    – Feffe
    Jun 15, 2016 at 13:24

1 Answer 1

2

The following will get the name of the subroutine that the cursor is within:

function! SubName() abort
  let line = search('^\(\s*\)subroutine\>\_.\{-}\%#\_.\{-}\_^\1end\>', 'ncbW')
  if line
    return matchstr(getline(line), '^\s*subroutine\s\+\zs\k\+')
  endif
  return ''
endfunction

Explanation

  • ^\(\s*\) - Search for a line that starts with any amount of whitespace and capture it into group 1.
  • subroutine\> - Match subroutine when it's not followed by keywords.
  • \_.\{-}\%#\_.\{-} - Match any character, including newlines, surrounding \%# (the cursor).
  • \_^\1end\> - A line that begins with the same amount as whitespace as the beginning line (capture group 1), and is followed with end.

In the matchstr() call, the pattern uses \k to match keywords (:h iskeyword) instead of \w. I don't know if this matters in Fortran, but \k is a better way to match a keyword regardless of what the filetype is.

Caveat

If your cursor is on the beginning subroutine line, but before the subroutine name, there will be no match. This is because the cursor is matched after matching the word subroutine.

To get around that, you could move the cursor to the end of the line before running search() then return it afterwards.

5
  • I would think the following should suffice: search('^\s*subroutine\s\+\k\+', 'ncbW'). This should match any line that begins a subroutine. If it matches something else, then I am pretty sure it will be erroneous Fortran code. --- To be clear: I think your solution may be simplified. Jun 16, 2016 at 5:51
  • @KarlYngveLervåg Now that I'm looking at it again, \%(!\|c\>\) is definitely not needed. But, your simplification would just search for the previous matching line without taking into account the cursor being past the end. I don't know Fortran well enough to say if it matters, though.
    – Tommy A
    Jun 16, 2016 at 6:10
  • Good point. However, in that case, I would rather perform two searches: One for the beginning and one for the end, then check that the current position is between these positions. Of course, one should use the name provided by the first search as part of the second search. Obviously, this is just my opinion, and I guess that your complex regex will work as expected. Jun 16, 2016 at 10:27
  • @KarlYngveLervåg Performing a second search would just move the complexity out of the regexp and into the script if the goal is to ensure the cursor is within the subroutine. The first search would need to move the cursor to the match before searching for the end to reliably test that the cursor is between the two matches. Then the cursor would need to be restored. If we want to avoid moving the cursor at all, the second search would need a negative match for a subroutine beginning.
    – Tommy A
    Jun 19, 2016 at 14:17
  • So far I am using the smaller regexp as proposed by @VanLaser, but I'll accept yours as he did not wrote his own answer, and it would seem that you deal with more "exotic" cases (like the end subroutine problem).
    – Feffe
    Jun 20, 2016 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.