-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathJSCS-Formatter.py
165 lines (129 loc) · 5.41 KB
/
JSCS-Formatter.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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import sublime, sublime_plugin
import platform
import os, sys, subprocess, codecs, webbrowser
from subprocess import Popen, PIPE
try:
import commands
except ImportError:
pass
PROJECT_NAME = "JSCS-Formatter"
SETTINGS_FILE = PROJECT_NAME + ".sublime-settings"
KEYMAP_FILE = "Default ($PLATFORM).sublime-keymap"
IS_WINDOWS = platform.system() == 'Windows'
class FormatJavascriptCommand(sublime_plugin.TextCommand):
def run(self, edit):
# Save the current viewport position to scroll to it after formatting.
previous_selection = list(self.view.sel()) # Copy.
previous_position = self.view.viewport_position()
# Save the already folded code to refold it after formatting.
# Backup of folded code is taken instead of regions because the start and end pos
# of folded regions will change once formatted.
folded_regions_content = [self.view.substr(r) for r in self.view.folded_regions()]
# Get the current text in the buffer and save it in a temporary file.
# This allows for scratch buffers and dirty files to be linted as well.
entire_buffer_region = sublime.Region(0, self.view.size())
text_selection_region = self.view.sel()[0]
buffer_text = self.get_buffer_text(entire_buffer_region)
output = self.run_script_on_text(buffer_text)
# If the prettified text length is nil, the current syntax isn't supported.
if output == None or len(output) < 1:
return
# Replace the text only if it's different.
if output != buffer_text:
self.view.replace(edit, entire_buffer_region, output)
self.refold_folded_regions(folded_regions_content, output)
self.view.set_viewport_position((0, 0), False)
self.view.set_viewport_position(previous_position, False)
self.view.sel().clear()
# Restore the previous selection if formatting wasn't performed only for it.
# if not is_formatting_selection_only:
for region in previous_selection:
self.view.sel().add(region)
def get_buffer_text(self, region):
buffer_text = self.view.substr(region)
return buffer_text
def run_script_on_text(self, data):
try:
node_path = PluginUtils.get_node_path()
jscs_path = PluginUtils.get_jscs_path()
if jscs_path == False:
sublime.error_message('JSCS could not be found on your path')
return;
cmd = [node_path, jscs_path, '--fix']
config_path = PluginUtils.get_pref("config_path")
if config_path:
print("Using configuration from {0}".format(config_path))
cmd.extend(["--config", config_path])
if self.view.file_name():
cdir = os.path.dirname(self.view.file_name())
else:
cdir = "/"
output = PluginUtils.get_output(cmd, cdir, data)
return output;
except:
# Something bad happened.
msg = str(sys.exc_info()[1])
print("Unexpected error({0}): {1}".format(sys.exc_info()[0], msg))
sublime.error_message(msg)
def refold_folded_regions(self, folded_regions_content, entire_file_contents):
self.view.unfold(sublime.Region(0, len(entire_file_contents)))
region_end = 0
for content in folded_regions_content:
region_start = entire_file_contents.index(content, region_end)
if region_start > -1:
region_end = region_start + len(content)
self.view.fold(sublime.Region(region_start, region_end))
class JscsFormatterEventListeners(sublime_plugin.EventListener):
@staticmethod
def on_pre_save(view):
if PluginUtils.get_pref("format_on_save"):
extensions = PluginUtils.get_pref("format_on_save_extensions")
extension = os.path.splitext(view.file_name())[1][1:]
# Default to using filename if no extension
if not extension:
extension = os.path.basename(view.file_name())
# Skip if extension is not whitelisted
if extensions and not extension in extensions:
return
view.run_command("format_javascript")
class PluginUtils:
@staticmethod
def get_pref(key):
global_settings = sublime.load_settings(SETTINGS_FILE)
value = global_settings.get(key)
# Load active project settings
project_settings = sublime.active_window().active_view().settings()
# Overwrite global config value if it's defined
if project_settings.has(PROJECT_NAME):
value = project_settings.get(PROJECT_NAME).get(key, value)
return value
@staticmethod
def get_node_path():
platform = sublime.platform()
node = PluginUtils.get_pref("node_path").get(platform)
print("Using node.js path on '" + platform + "': " + node)
return node
@staticmethod
def get_jscs_path():
platform = sublime.platform()
jscs = PluginUtils.get_pref("jscs_path").get(platform)
print("Using jscs path on '" + platform + "': " + jscs)
return jscs
@staticmethod
def get_output(cmd, cdir, data):
try:
p = Popen(cmd,
stdout=PIPE, stdin=PIPE, stderr=PIPE,
cwd=cdir, shell=IS_WINDOWS)
except OSError:
raise Exception('Couldn\'t find Node.js. Make sure it\'s in your $PATH by running `node -v` in your command-line.')
stdout, stderr = p.communicate(input=data.encode('utf-8'))
stdout = stdout.decode('utf-8')
stderr = stderr.decode('utf-8')
if stderr:
raise Exception('Error: %s' % stderr)
else:
return stdout