Skip to content

Commit f05d965

Browse files
committed
refactor and decouple processing
fix updating PHP method (offset was off) fixes #5 fixes #6
1 parent 349108d commit f05d965

2 files changed

Lines changed: 135 additions & 64 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ If the original phrase is inside a dockblock then it updates the method using th
88

99
Install
1010
-----
11-
Install by downloading the [0.3 release zip](https://github.com/bogdananton/Sublime-testNameGenerator/releases/download/0.3/testNameGenerator.zip) and unpacking it into the \Packages folder (SublimeText Menu \ Preferences \ Browse Packages).
11+
Install by downloading the [1.0.1 release zip](https://github.com/bogdananton/Sublime-testNameGenerator/releases/download/1.0.1/testNameGenerator.zip) and unpacking it into the \Packages folder (SublimeText Menu \ Preferences \ Browse Packages).
1212

1313
Usage
1414
-----

TestNameGenerator.py

Lines changed: 134 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,152 @@
1-
# v0.1
21
import sublime, sublime_plugin, re
32

43
class 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

Comments
 (0)