Regex for SublimeText Snippet

2019-05-07 13:54发布

问题:

I've been stuck for a while on this Sublime Snippet now.

I would like to display the correct package name when creating a new class, using TM_FILEPATH and TM_FILENAME.

When printing TM_FILEPATH variable, I get something like this:

/Users/caubry/d/[...]/src/com/[...]/folder/MyClass.as

I would like to transform this output, so I could get something like:

com.[...].folder

This includes:

  • Removing anything before /com/[...]/folder/MyClass.as;
  • Removing the TM_FILENAME, with its extension; in this example MyClass.as;
  • And finally finding all the slashes and replacing them by dots.

So far, this is what I've got:

${1:${TM_FILEPATH/.+(?:src\/)(.+)\.\w+/\l$1/}}

and this displays:

com/[...]/folder/MyClass

I do understand how to replace splashes with dots, such as:

${1:${TM_FILEPATH/\//./g/}}

However, I'm having difficulties to add this logic to the previous one, as well as removing the TM_FILENAME at the end of the logic.

I'm really inexperienced with Regex, thanks in advance.

:]

EDIT: [...] indicates variable number of folders.

回答1:

We can do this in a single replacement with some trickery. What we'll do is, we put a few different cases into our pattern and do a different replacement for each of them. The trick to accomplish this is that the replacement string must contain no literal characters, but consist entirely of "backreferences". In that case, those groups that didn't participate in the match (because they were part of a different case) will simply be written back as an empty string and not contribute to the replacement. Let's get started.

First, we want to remove everything up until the last src/ (to mimic the behaviour of your snippet - use an ungreedy quantifier if you want to remove everything until the first src/):

^.+/src/

We just want to drop this, so there's no need to capture anything - nor to write anything back.

Now we want to match subsequent folders until the last one. We'll capture the folder name, also match the trailing /, but write back the folder name and a .. But I said no literal text in the replacement string! So the . has to come from a capture as well. Here comes the assumption into play, that your file always has an extension. We can grab the period from the file name with a lookahead. We'll also use that lookahead to make sure that there's at least one more folder ahead:

^.+/src/|\G([^/]+)/(?=[^/]+/.*([.]))

And we'll replace this with $1$2. Now if the first alternative catches, groups $1 and $2 will be empty, and the leading bit is still removed. If the second alternative catches, $1 will be the folder name, and $2 will have captured a period. Sweet. The \G is an anchor that ensures that all matches are adjacent to one another.

Finally, we'll match the last folder and everything that follows it, and only write back the folder name:

^.+/src/|\G([^/]+)/(?=[^/]+/.*([.]))|\G([^/]+)/[^/]+$

And now we'll replace this with $1$2$3 for the final solution. Demo.

A conceptually similar variant would be:

^.+/src/|\G([^/]+)/(?:(?=[^/]+/.*([.]))|[^/]+$)

replaced with $1$2. I've really only factored out the beginning of the second and third alternative. Demo.

Finally, if Sublime is using Boost's extended format string syntax, it is actually possible to get characters into the replacement conditionally (without magically conjuring them from the file extension):

^.+/src/|\G(/)?([^/]+)|\G/[^/]+$

Now we have the first alternative for everything up to src (which is to be removed), the third alternative for the last slash and file name (which is to be removed), and the middle alternative for all folders you want to keep. This time I put the slash to be replaced optionally at the beginning. With a conditional replacement we can write a . there if and only if that slash was matched:

(?1.:)$2

Unfortunately, I can't test this right now and I don't know an online tester that uses Boost's regex engine. But this should do the trick just fine.