1- # v0.1
21import sublime , sublime_plugin , re
32
43class ConvertTestNameCommand (sublime_plugin .TextCommand ):
4+ def isAllowedSyntax (self , syntax ):
5+ return ((syntax == "PHP" ) | (syntax == "JavaScript" ))
6+
7+ # keep only alphanum and ",", ".", "(", ")"
8+ def getCleanLineContents (self , lineContents ):
9+ pattern = TextHelper .patternCleanLine ()
10+ return re .sub (pattern , '' , lineContents )
11+
12+ def getFallbackLineContent (self , i ):
13+ return "blank" + str (i )
14+
15+ # convert to CamelCase and keep only alphanumeric chars
16+ def getMethodName (self , lineContents ):
17+ pattern = TextHelper .patternMethodName ()
18+ return re .sub (pattern , '' , lineContents .title ())
19+
20+ def getExistingMethod (self , lineContents , cursorLine ):
21+ pageContents = SublimeConnect .getPageContents (cursorLine )
22+ lineContents = re .escape (lineContents )
23+ pattern = TextHelper .patternExistingMethodPHP (lineContents )
24+
25+ searchExisting = re .search (pattern , pageContents )
26+ if searchExisting != None :
27+ if (searchExisting .group (7 ) is None ) == False :
28+ return searchExisting .group (7 )
29+ return False
30+
31+ # get a list of [test name with separate words, test name with no spaces in between and camel cased] from the current cursor(s) or selection(s)
532 def run (self , edit ):
6- # get a list of [test name with separate words, test name with no spaces in between and camel cased] from the current cursor(s) or selection(s)
33+ SublimeConnect .init (self )
34+ currentSyntax = SublimeConnect .getSyntax ()
735 extractedTestNames = []
8-
9- currentSyntax = self .getSyntax ()
10- if ((currentSyntax != "PHP" ) & (currentSyntax != "JavaScript" )):
36+
37+ if False == self .isAllowedSyntax (currentSyntax ):
1138 return
1239
13- cursor = 0
14- # iterate over all cursors
15- for region in self .view .sel ():
16- # extract line contents from cursor(s)
17- line = self .view .line (region )
18- lineContents = self .view .substr (line ).strip ()
19-
20- # keep only alphanum and ",", ".", "(", ")"
21- testNameExpanded = re .sub (r'[^a-zA-Z0-9 \(\)_\:\.\,\[\]]' , '' , lineContents ).strip ()
40+ i = 0
41+ for cursor in SublimeConnect .getCursors ():
42+ cursorLine = SublimeConnect .getLine (cursor )
43+ lineContents = SublimeConnect .getLineContents (cursorLine )
44+ phrase = self .getCleanLineContents (lineContents )
2245
2346 # if extracted line contents are empty, use "blank"
24- if not testNameExpanded :
25- testNameExpanded = "blank"
26- if cursor > 0 :
27- testNameExpanded += str (cursor )
28- cursor += 1
29-
30- # convert to CamelCase and keep only alphanumeric chars
31- testNameContracted = testNameExpanded .title ()
32- testNameContracted = re .sub (r'[^a-zA-Z0-9_]' , '' , testNameContracted )
47+ if not phrase :
48+ phrase = self .getFallbackLineContent (i )
49+ i += 1
3350
3451 if (currentSyntax == "PHP" ):
35- content = self .view .substr (sublime .Region (0 , self .view .size ()))
36- r = (re .search (r'\/\*([\*\n\s]+)' + re .escape (lineContents .strip ()) + '([a-zA-Z0-9\n \s\*@]+)\*\/([\s]+)(public)?(\s)?function(\s)?test([a-zA-Z0-9_]+)(\s\n )?\(' , content ))
37- if (r == None ):
38- # insert new test method
39- self .view .replace (edit , line , self .prepareTestContent (testNameExpanded , testNameContracted , currentSyntax ))
52+ methodName = self .getMethodName (phrase )
53+ existingMethod = self .getExistingMethod (lineContents , cursorLine )
54+ print (existingMethod )
55+
56+ if existingMethod == False :
57+ SublimeConnect .insertMethodName (edit , cursorLine , TextHelper .prepareTestBlockPHP (phrase , methodName ))
4058 else :
41- if (r .group (7 ) != None ):
42- s = "test" + r .group (7 ) # search for current test name
43- methodPosition = content .find (s )
44- line = self .view .line (sublime .Region (methodPosition , methodPosition + len (s )))
45- tab = self .getWhitespaceTab ()
46- lineContents = self .view .substr (line ).strip ()
47- lineContents = lineContents .replace (s , "test" + testNameContracted )
48- self .view .replace (edit , line , tab + lineContents )
59+ SublimeConnect .updateMethodNamePHP (edit , cursorLine , methodName , existingMethod )
4960
5061 else :
5162 # JS will add a new test method because the text is inside the test method
52- # replace current line
53- self .view .replace (edit , line , self .prepareTestContent (testNameExpanded , testNameContracted , currentSyntax ))
54-
55- def prepareTestContent (self , testNameExpanded , testNameContracted , languageSyntax ):
56- returnContent = ""
57-
58- tab = self .getWhitespaceTab ()
59-
60- if (languageSyntax == "PHP" ):
61- returnContent = tab + "/**\n " + tab + " * " + testNameExpanded + "\n " + tab + " */\n " + tab + "public function test" + testNameContracted + "()\n " + tab + "{\n \n " + tab + "}\n \n " + tab
62-
63- elif (languageSyntax == "JavaScript" ):
64- # will generate Jasmine blocks
65- examineNameTypeParts = testNameExpanded .split (" " )
66-
67- if (examineNameTypeParts [0 ].lower () == "describe" ):
68- examineNameTypeParts .pop (0 ) # remove the "describe" prefix
69- returnContent = tab + "describe('" + (" " .join (examineNameTypeParts )) + "', function () {\n \n " + tab + "});\n \n " + tab
70- else :
71- returnContent = tab + tab + "it('" + testNameExpanded + "', function () {\n \n " + tab + "" + tab + "});\n \n " + tab
72-
73- return returnContent
63+ SublimeConnect .insertMethodName (edit , cursorLine , TextHelper .prepareTestBlockJS (phrase ))
64+
65+ class TextHelper ():
66+ def patternExistingMethodPHP (lineContents ):
67+ # complete block: return r'\/\*([\*\n\s]+)' + lineContents + '([a-zA-Z0-9\n\s\*@]+)\*\/([\s]+)(public)?(\s)?function(\s)?test([a-zA-Z0-9_]+)(\s\n)?\('
68+ return r'([\*\n\s]+)' + lineContents + '([a-zA-Z0-9\n \s\*@]+)\*\/([\s]+)(public)?(\s)?function(\s)?test([a-zA-Z0-9_]+)(\s\n )?\('
69+
70+ def patternCleanLine ():
71+ return r'[^a-zA-Z0-9 \(\)_\:\.\,\[\]]'
72+
73+ def patternMethodName ():
74+ return r'[^a-zA-Z0-9_]'
75+
76+ def prepareTestBlockPHP (phrase , methodName ):
77+ tab = SublimeConnect .getWhitespaceTab ()
78+ return tab + "/**\n " + tab + " * " + phrase + "\n " + tab + " */\n " + tab + "public function test" + methodName + "()\n " + tab + "{\n \n " + tab + "}\n \n " + tab
79+
80+ # will generate Jasmine blocks
81+ def prepareTestBlockJS (phrase ):
82+ tab = SublimeConnect .getWhitespaceTab ()
83+ examineNameTypeParts = phrase .split (" " )
84+
85+ if (examineNameTypeParts [0 ].lower () == "describe" ):
86+ phrase = " " .join (examineNameTypeParts .pop (0 )) # remove the "describe" prefix
87+ return self .getJasmineDescribeBlock (phrase , tab )
88+
89+ else :
90+ return self .getJasmineItBlock (phrase , tab )
91+
92+ def getJasmineDescribeBlock (phrase , tab ):
93+ return tab + "describe('" + phrase + "', function () {\n \n " + tab + "});\n \n " + tab
94+
95+ def getJasmineItBlock (phrase , tab ):
96+ return tab + tab + "it('" + phrase + "', function () {\n \n " + tab + "" + tab + "});\n \n " + tab
97+
98+ # connector to the sublime api
99+ class SublimeConnect ():
100+ context = False
101+
102+ @classmethod
103+ def getPageContents (self , cursorLine ):
104+ cursorPosition = cursorLine .begin ()
105+ return self .context .view .substr (sublime .Region (0 , self .context .view .size ()))
106+
107+ @classmethod
108+ def updateMethodNamePHP (self , edit , cursorLine , methodName , existingMethodResults ):
109+ tab = SublimeConnect .getWhitespaceTab ()
110+ searchExisting = "test" + existingMethodResults # search for current test name
111+ methodPosition = self .getPageContents (cursorLine ).find (searchExisting )
112+ region = sublime .Region (methodPosition , methodPosition + len (searchExisting ))
113+ # debug: self.context.view.add_regions('highlighted_lines', [region] , 'mark', 'dot', sublime.DRAW_OUTLINED)
114+
115+ lineToUpdate = self .context .view .line (region )
116+ lineContents = self .context .view .substr (lineToUpdate ).strip ().replace (searchExisting , "test" + methodName )
117+
118+ self .context .view .replace (edit , lineToUpdate , tab + lineContents )
119+
120+ @classmethod
121+ def insertMethodName (self , edit , cursorLine , testBlock ):
122+ self .context .view .replace (edit , cursorLine , testBlock )
123+
124+ @classmethod
125+ def getLine (self , region ):
126+ return self .context .view .line (region )
127+
128+ @classmethod
129+ def getLineContents (self , cursorLine ):
130+ return self .context .view .substr (cursorLine ).strip ()
131+
132+ @classmethod
133+ def getCursors (self ):
134+ return self .context .view .sel ()
135+
136+ @classmethod
137+ def init (self , mainContext ):
138+ self .context = mainContext
139+ return self
140+
141+ # returns: PHP,JavaScript,...
142+ @classmethod
143+ def getSyntax (self ):
144+ return self .context .view .settings ().get ('syntax' ).replace ('.tmLanguage' , '' ).split ("/" )[1 ]
74145
146+ # returns tab character or spaces as an indent unit, based on the editor's settings
147+ @classmethod
75148 def getWhitespaceTab (self ):
76- if (self .view .settings ().get ('translate_tabs_to_spaces' )):
77- return "" .rjust (self .view .settings ().get ('tab_size' ), ' ' )
149+ settings = self .context .view .settings ()
150+ if (settings .get ('translate_tabs_to_spaces' )):
151+ return "" .rjust (settings .get ('tab_size' ), ' ' )
78152 return "\t " ;
79-
80- def getSyntax (self ):
81- return self .view .settings ().get ('syntax' ).replace ('.tmLanguage' , '' ).split ("/" )[1 ]
0 commit comments