6

I'm trying to write a SublimeText snippet for javascript import statements. I want the format the format to be like:

import MyFooBar from 'my-foo-bar';

The input to my regex is MyFooBar, and the output needs to be my-foo-bar. I found an answer that almost works in Regex - CamelCase to lower case with underscores:

Search for

((?<=.)[A-Z][a-zA-Z]*)|((?<=[a-zA-Z])\d+)

Replace with

-$1$2

The answer said to just use javascript's .toLowerCase() method for the lowercasing, but SublimeText snippets use perl, of which I have the briefest of knowledge. A quick search said that for lowercasing, I can use \L at the beginning of my replacement.

/((?<=.)[A-Z][a-zA-Z0-9]*)|((?<=[a-zA-Z])\d+)/\L-\1\2/g

This works on all but the first segment's character, so MyFooBar becomes My-foo-bar.

I thought maybe I could run two regexes in sequence, but either perl or Sublime doesn't recognize that.

Thoughts?

Edit:

When I say it uses perl, I just mean it uses a perl regex. As far as I can tell, I can't actually execute arbitrary code; I can only specify a regex that perl can execute.

Here's the full text of my snippet:

<snippet>
    <content><![CDATA[
import ${1:module} from '${2:./path/}${1/((?<=.)[A-Z][a-zA-Z0-9]*)|((?<=[a-zA-Z])\d+)/\L-\1\2/g}';
]]></content>
    <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
    <tabTrigger>import</tabTrigger>
    <!-- Optional: Set a scope to limit where the snippet will trigger -->
    <scope>source.js</scope>
</snippet>
Community
  • 1
  • 1
dx_over_dt
  • 13,240
  • 17
  • 54
  • 102
  • 1
    Why does it *have* to be a regex? This would be more easily handled by a proper search-and-replace function, which perl has... – cat Jan 27 '16 at 18:55
  • @cat I'm just learning how to create snippets, but http://sublimetext.info/docs/en/extensibility/snippets.html#substitutions only shows one way to do replacements. – dx_over_dt Jan 27 '16 at 18:56
  • 1
    @anubhava OPs last sentence is the problem here. You run 2 regexes to correct the "error" on the first Character, which apparently does not work. – Christoph Jan 27 '16 at 19:07
  • Would you please upload your current full snippet? – revo Jan 27 '16 at 20:09
  • @revo I've updated the question – dx_over_dt Jan 27 '16 at 20:16
  • You either need a conditional replacement pattern (and PCRE does not support that) or check how to use two regexps on the same value at a row. BTW, are you sure it is PCRE? Not Boost? – Wiktor Stribiżew Jan 27 '16 at 21:51
  • That's the conclusion I'm starting to draw. I had hoped there was some way to make a group match an imaginary hyphen, or make a replacement group loop, or something. Thanks all for your help! – dx_over_dt Jan 27 '16 at 21:53
  • BTW, in real Boost, as in Notepad++, it would suffice to use `(?(?:(?<=\W)|(?<=^))[A-Z][a-z]*)|(?[A-Z][a-z]*)|(?(?<=[a-z])\d+)` and replace with `\L(?{a1}$+{a1}:(?{a2}-$2:-$3))` – Wiktor Stribiżew Jan 27 '16 at 22:22

3 Answers3

1

I changed RegEx to something more useful and probably simple.

** Updated

<snippet>
    <content><![CDATA[
import ${1:module} from '${2:./path/}${1/(^[A-Z][a-z]+|[a-z])([A-Z])/\L\1-\2/g}';
]]></content>
    <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
    <tabTrigger>import</tabTrigger>
    <scope>source.js</scope>
</snippet>
revo
  • 47,783
  • 14
  • 74
  • 117
  • That'll prepend a `-` to my module file name. I can just delete that, but that's basically what I have to do right now with replacing the first capitalized character. – dx_over_dt Jan 27 '16 at 21:34
  • @dfoverdx Oh yes, but when I do `enter` that extra dash goes away! – revo Jan 27 '16 at 21:35
  • Weird that it does not for me... In fact, when I pressed enter, it replaced what I had entered with the regex'd value in both spots. – dx_over_dt Jan 27 '16 at 21:38
  • @dfoverdx Changed regex. Check it now. – revo Jan 27 '16 at 22:13
  • 1
    So close. I thought it worked until I tried "Environment". If there is no second capital letter, then the word isn't replaced. Not a big issue to just replace the first character. Alas, I don't think this particular endeavor is possible. – dx_over_dt Jan 29 '16 at 19:00
  • I doubt if it could be possible. Since that dash is outside the regex and you can't make it conditional. It's always there. @dfoverdx – revo Jan 30 '16 at 10:47
  • 2
    It is possible to make it conditional by changing `-\2` to `(?2-\2)`. The full regex line: `import ${1:module} from '${2:./path/}${1/(^[A-Z][a-z]*|[a-z])([A-Z])?/\L\1(?2-\2)/g}';` – r-stein Feb 24 '16 at 18:21
0

Ugly (substr()) but works:

$x = "MyFooBar";
$x =~ s/(?:^|(?<=[a-z])(?=[A-Z]))(.)/-lc($1)/eg;
$x = substr($x, 1);
print $x;
// my-foo-bar

See a demo on ideone.com.

Jan
  • 42,290
  • 8
  • 54
  • 79
0

This one took me a little while, but I believe it does what you want, all within a single regex:

use warnings;
use strict;

while (<DATA>){
    chomp;
    s/([[:upper:]].*?)(?=(?:[[:upper:]]|$))/$+[0]!=length($_) ? lc($1).'-' : lc($1)/ge;
    print "$_\n";
}

__DATA__
MyCamelCase
AVeryLongCamelCaseStringWMXThat

Output:

my-camel-case
a-very-long-camel-case-string-w-m-x-that

Explanation: It looks for an upper case letter, and captures into $1 that letter, along with all other letters until it sees another capital or end of string within a zero-width lookahead. in the substitution side, we use the /e modifier which means the right-hand side is an expression (executable code). If the end of the most recent match ($+[0]) is less than the length of the string, we do a lower (lc()) and append a -. If the length of the string is the same as the end of the last match, we lowercase it and we're done.

stevieb
  • 9,065
  • 3
  • 26
  • 36
  • This won't work because the Sublime snippets aren't actually interpreting Perl---they're just using PCRE syntax. – Matt Jacob Jan 27 '16 at 20:50
  • I copied and pasted that into my snippet and ran it. `import MyFooBar from './path/My[1]!=length$_-1 ? lcMy.'-' : lcMyFoo[1]!=length$_-1 ? lcFoo.'-' : lcFooBar[1]!=length$_-1 ? lcBar.'-' : lcBar';` Good attempt though! – dx_over_dt Jan 27 '16 at 20:51
  • ahhh crap :) I missed the part about code not executing. – stevieb Jan 27 '16 at 20:55
  • 2
    Well, it was the first time I've ever used `$+[0]` and `$-[0]`, so at least one of us gained from it :) – stevieb Jan 27 '16 at 21:03