-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathtext_2_stata.py
233 lines (194 loc) · 9.3 KB
/
text_2_stata.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
import os
import sublime_plugin
import sublime
import re
import subprocess
settingsfile = "Stata Enhanced.sublime-settings"
def strip_inline_comments(text):
# This is really brute force and hackish. Ideally we could use ST scopes to
# only remove comments instead of trying to parse everything with regex
# (since there will inevitably be edge cases that should/shouldn't be
# removed). ST kind of has that functionality:
# self.view.find_by_selector('comment')
# But this is a good stopgap for now.
clean = text
# Take care of line continuation
clean = re.sub("/{3,}.*\\n", " ", clean)
# Remove /* ... */ comments (handles multiple lines) + // comments
#
# Bonus! Handles these cases correctly:
# * "This is a /* string */"
# * "This is // also a string"
# * "URLs work too. http://www.example.com"
#
# Code adapted from http://stackoverflow.com/a/18381470/120898
def remove_comments(string):
pattern = r"(\".*?(?<!\\)\"|\'.*?(?<!\\)\')|(/\*.*?\*/|//[^\r\n]*$)"
# pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)" # Original
# First group captures quoted strings (double or single)
# Second group captures comments (//single-line or /* multi-line */)
regex = re.compile(pattern, re.MULTILINE | re.DOTALL)
def _replacer(match):
# If the 2nd group (capturing comments) is not None,
# it means we have captured a non-quoted (real) comment string.
if match.group(2) is not None:
return "" # So we will return empty to remove the comment
else: # Otherwise, we will return the 1st group
return match.group(1) # Captured quoted-string
return regex.sub(_replacer, string)
clean = remove_comments(clean)
# Escape $s, since AppleScript parses them as variables
# clean = re.sub("\\$", "\\$", clean)
return(clean)
def get_stata_version(stata_name):
if stata_name != "auto": # get application identifier for user-set Stata
cmd = ["osascript", "-e", "return id of application \"{}\"".format(stata_name)]
try:
stata_app_id = subprocess.check_output(cmd)
except subprocess.CalledProcessError:
sublime.error_message("User-defined version of Stata not found.")
raise Exception("User-defined version of Stata not found.")
stata_app_id = stata_app_id.decode("utf-8").strip()
version = int(stata_app_id[-2:])
return((version, stata_app_id))
else: # figure out Stata version automatically
cmd = """osascript<< END
try
tell me to get application id "com.stata.stata15"
set stata to 15
end try
try
tell me to get application id "com.stata.stata14"
set stata to 14
end try
try
tell me to get application id "com.stata.stata13"
set stata to 13
end try
try
tell me to get application id "com.stata.stata12"
set stata to 12
end try
try
tell me to get application id "com.stata.stata11"
set stata to 11
end try
return stata
END"""
try:
version = subprocess.check_output(cmd, shell=True)
except subprocess.CalledProcessError:
sublime.error_message("No version of Stata found.")
raise Exception("No version of Stata found.")
version = version.decode("utf-8").strip()
return((int(version), "com.stata.stata{}".format(version)))
class StataRunCompleteCommand(sublime_plugin.WindowCommand):
""" Runs the complete do file in Stata. """
def run(self):
self.window.run_command('save')
file_name = self.window.active_view().file_name()
settings = sublime.load_settings(settingsfile)
# Switch focus to Stata or not after sending a command depending on a setting
if settings.get('switch_focus_to_stata'):
switch_focus = "activate"
else:
switch_focus = ""
if sublime.platform() == "osx":
version, stata_app_id = get_stata_version(settings.get('stata_name'))
if sublime.platform() == "osx" and version >= 13:
cmd = """osascript<< END
tell application id "{}"
{}
DoCommandAsync "do \\\"{}\\\"" with addToReview
end tell
END""".format(stata_app_id, switch_focus, file_name)
os.system(cmd)
elif sublime.platform() == "osx" and version <= 12:
# Get ST version for returning focus to ST
if int(sublime.version()) > 3000:
st_name = "Sublime Text"
else:
st_name = "Sublime Text 2"
cmd = """osascript -e 'tell application "Finder" to open POSIX file "{0}"' -e 'tell application "{1}" to activate' &""".format(file_name, st_name)
os.system(cmd)
elif sublime.platform() == "windows":
filepath = os.path.dirname(file_name)
vbs_cmd = """CreateObject("WScript.Shell").Run \"\""{0}\"" \""{1}\"\"", 4, false """.format(settings.get('stata_name'), file_name)
vbs_path = os.path.join(filepath, 'open_in_stata.vbs')
vbs_file = open(vbs_path, 'w')
vbs_file.write(vbs_cmd)
vbs_file.close()
cmd = "cscript.exe {0}".format("\"" + vbs_path + "\"")
os.system(cmd)
os.remove(vbs_path)
class text_2_stataCommand(sublime_plugin.TextCommand):
""" Run selection or current line *directly* in Stata"""
def run(self, edit):
settings = sublime.load_settings(settingsfile)
# Get the selection; if nothing is selected, get the current line
all_text = ""
sels = self.view.sel()
for sel in sels:
all_text = all_text + self.view.substr(sel)
if len(all_text) == 0:
all_text = self.view.substr(self.view.line(sel))
all_text = all_text + "\n"
# Get rid of inline comments
all_text = strip_inline_comments(all_text)
# Switch focus to Stata or not after sending a command depending on a setting
if settings.get('switch_focus_to_stata'):
switch_focus = "activate"
else:
switch_focus = ""
if sublime.platform() == "osx":
version, stata_app_id = get_stata_version(settings.get('stata_name'))
if sublime.platform() == "osx" and version >= 13:
all_text = all_text.replace('\\', '\\\\\\').replace('"', '\\"'). \
replace('`', '\\`').replace('$', "\\$").strip()
# Stata only allows 8192 characters.
if len(all_text)<8192:
# Send the command to Stata with AppleScript
cmd = """osascript<< END
tell application id "{}"
{}
DoCommandAsync "{}" with addToReview
end tell
END""".format(stata_app_id, switch_focus, all_text)
print(cmd)
os.system(cmd)
else:
if sublime.ok_cancel_dialog("This selection is too long to run.\n\nWould you like to run the whole do file?", "Run complete file"):
self.view.window().run_command("stata_run_complete")
elif sublime.platform() == "osx" and version <= 12:
# Get the path to the current file
filename = self.view.file_name()
filepath = os.path.dirname(filename)
# Write the selection to a temporary file in the current directory
dofile_path = os.path.join(filepath, 'sublime2stata.do')
this_file = open(dofile_path, 'w')
this_file.write(all_text)
this_file.close()
# Get ST version for returning focus to ST
if int(sublime.version()) > 3000:
st_name = "Sublime Text"
else:
st_name = "Sublime Text 2"
cmd = """osascript -e 'tell application "Finder" to open POSIX file "{0}"' -e 'tell application "{1}" to activate' &""".format(dofile_path, st_name)
os.system(cmd)
elif sublime.platform() == "windows":
# Get the path to the current file
filename = self.view.file_name()
filepath = os.path.dirname(filename)
# Write the selection to a temporary file in the current directory
dofile_path = os.path.join(filepath, 'sublime2stata.do')
this_file = open(dofile_path, 'w')
this_file.write(all_text)
this_file.close()
vbs_cmd = """CreateObject("WScript.Shell").Run \"\""{0}\"" \""{1}\"\"", 4, false """.format(settings.get('stata_name'), dofile_path)
vbs_path = os.path.join(filepath, 'open_in_stata.vbs')
vbs_file = open(vbs_path, 'w')
vbs_file.write(vbs_cmd)
vbs_file.close()
cmd = "cscript.exe {0}".format("\"" + vbs_path + "\"")
os.system(cmd)
os.remove(vbs_path)