Merge branch 'master' into features/json-lines

This commit is contained in:
TheSecEng 2021-06-28 12:37:40 -07:00
commit be7dad59f3
No known key found for this signature in database
GPG Key ID: 4D98046FE19FF417
8 changed files with 294 additions and 185 deletions

View File

@ -1,15 +1,15 @@
[ [
{ // {
"keys": [ // "keys": [
"ctrl+alt+j" // "ctrl+alt+j"
], // ],
"command": "pretty_json" // "command": "pretty_json"
}, // },
{ // {
"keys": ["ctrl+r"], // "keys": ["ctrl+r"],
"command": "pretty_json_goto_symbol", // "command": "pretty_json_goto_symbol",
"context": [ // "context": [
{ "key": "selector", "operator": "equal", "operand": "source.json" } // { "key": "selector", "operator": "equal", "operand": "source.json" }
] // ]
} // }
] ]

View File

@ -1,15 +1,15 @@
[ [
{ // {
"keys": [ // "keys": [
"super+ctrl+j" // "super+ctrl+j"
], // ],
"command": "pretty_json" // "command": "pretty_json"
}, // },
{ // {
"keys": ["super+r"], // "keys": ["super+r"],
"command": "pretty_json_goto_symbol", // "command": "pretty_json_goto_symbol",
"context": [ // "context": [
{ "key": "selector", "operator": "equal", "operand": "source.json" } // { "key": "selector", "operator": "equal", "operand": "source.json" }
] // ]
} // }
] ]

View File

@ -1,22 +1,22 @@
[ [
{ // {
"keys": [ // "keys": [
"ctrl+alt+j" // "ctrl+alt+j"
], // ],
"command": "pretty_json" // "command": "pretty_json"
}, // },
{ // {
"keys": [ // "keys": [
"ctrl+alt+m" // "ctrl+alt+m"
], // ],
"command": "un_pretty_json" // "command": "un_pretty_json"
}, // },
{ // {
"keys": ["ctrl+r"], // "keys": ["ctrl+r"],
"command": "pretty_json_goto_symbol", // "command": "pretty_json_goto_symbol",
"context": [ // "context": [
{ "key": "selector", "operator": "equal", "operand": "source.json" } // { "key": "selector", "operator": "equal", "operand": "source.json" }
] // ]
} // }
] ]

View File

@ -26,7 +26,8 @@
{ {
"caption": "Preferences: Pretty JSON Settings", "caption": "Preferences: Pretty JSON Settings",
"command": "edit_settings", "command": "edit_settings",
"args": { "args":
{
"base_file": "${packages}/Pretty JSON/Pretty JSON.sublime-settings", "base_file": "${packages}/Pretty JSON/Pretty JSON.sublime-settings",
"default": "{\n\t$0\n}\n" "default": "{\n\t$0\n}\n"
} }

View File

@ -2,7 +2,7 @@
"use_entire_file_if_no_selection": true, "use_entire_file_if_no_selection": true,
"indent": 4, "indent": 4,
"sort_keys": false, "sort_keys": false,
"ensure_ascii": true, "ensure_ascii": false,
"line_separator": ",", "line_separator": ",",
"value_separator": ": ", "value_separator": ": ",
"keep_arrays_single_line": false, "keep_arrays_single_line": false,
@ -19,9 +19,9 @@
"jq_binary": "jq", "jq_binary": "jq",
"jq_errors": false, "jq_errors": false,
"as_json":[ "as_json":[
"Packages/JSON/JSON.sublime-syntax", "Packages/JSON/JSON.sublime-syntax",
"Packages/PackageDev/Package/Sublime Text Commands/Sublime Text Commands.sublime-syntax", "Packages/PackageDev/Package/Sublime Text Commands/Sublime Text Commands.sublime-syntax",
"Packages/PackageDev/Package/Sublime Text Settings/Sublime Text Settings.sublime-syntax", "Packages/PackageDev/Package/Sublime Text Settings/Sublime Text Settings.sublime-syntax",
"Packages/PackageDev/Package/Sublime Text Menu/Sublime Text Menu.sublime-syntax" "Packages/PackageDev/Package/Sublime Text Menu/Sublime Text Menu.sublime-syntax"
] ]
} }

View File

@ -15,10 +15,8 @@ from .lib.simplejson import OrderedDict
PREVIOUS_CONTENT = [str(), str()] PREVIOUS_CONTENT = [str(), str()]
PREVIOUS_QUERY_LEN = int() PREVIOUS_QUERY_LEN = int()
s = sublime.load_settings('Pretty JSON.sublime-settings') xml_syntax = "Packages/XML/XML.sublime-syntax"
json_syntax = "Packages/JSON/JSON.sublime-syntax"
xml_syntax = 'Packages/XML/XML.tmLanguage'
json_syntax = 'Packages/JSON/JSON.tmLanguage'
jq_exists = bool() jq_exists = bool()
jq_init = bool() jq_init = bool()
@ -27,17 +25,18 @@ jq_path = str()
def check_jq(): def check_jq():
global jq_init, jq_exists, jq_path global jq_init, jq_exists, jq_path
settings = sublime.load_settings("Pretty JSON.sublime-settings")
if jq_init: if jq_init:
return return
jq_init = True jq_init = True
jq_test = s.get('jq_binary', 'jq') jq_test = settings.get("jq_binary", "jq")
try: try:
jq_path = shutil.which(jq_test) jq_path = shutil.which(jq_test)
jq_exists = True jq_exists = True
except OSError as ex: except OSError as ex:
print(str(ex)) sublime.message_dialog(f"[Error]: {ex}")
jq_exists = False jq_exists = False
@ -45,32 +44,34 @@ class PrettyJsonBaseCommand:
phantom_set = sublime.PhantomSet phantom_set = sublime.PhantomSet
phantoms = list() phantoms = list()
force_sorting = False force_sorting = False
json_char_matcher = re.compile(r'char (\d+)') json_char_matcher = re.compile(r"char (\d+)")
brace_newline = re.compile(r'^((\s*)".*?":)\s*([{])', re.MULTILINE) brace_newline = re.compile(r'^((\s*)".*?":)\s*([{])', re.MULTILINE)
bracket_newline = re.compile(r'^((\s*)".*?":)\s*([\[])', re.MULTILINE) bracket_newline = re.compile(r'^((\s*)".*?":)\s*([\[])', re.MULTILINE)
@staticmethod @staticmethod
def json_loads(selection: str, object_pairs_hook=OrderedDict) -> dict: def json_loads(selection: str, object_pairs_hook=OrderedDict):
return json.loads( return json.loads(
selection, object_pairs_hook=object_pairs_hook, parse_float=decimal.Decimal selection, object_pairs_hook=object_pairs_hook, parse_float=decimal.Decimal
) )
@staticmethod @staticmethod
def json_dumps(obj, minified: bool = False) -> str: def json_dumps(obj, minified: bool = False, force_sorting: bool = False) -> str:
sort_keys = s.get('sort_keys', False) settings = sublime.load_settings("Pretty JSON.sublime-settings")
if PrettyJsonBaseCommand.force_sorting:
sort_keys = settings.get("sort_keys", False)
if force_sorting:
sort_keys = True sort_keys = True
line_separator = s.get('line_separator', ',') line_separator = settings.get("line_separator", ",")
value_separator = s.get('value_separator', ': ') value_separator = settings.get("value_separator", ": ")
if minified: if minified:
line_separator = line_separator.strip() line_separator = line_separator.strip()
value_separator = value_separator.strip() value_separator = value_separator.strip()
output_json = json.dumps( output_json = json.dumps(
obj, obj,
indent=None if minified else s.get('indent', 2), indent=None if minified else settings.get("indent", 2),
ensure_ascii=s.get('ensure_ascii', False), ensure_ascii=settings.get("ensure_ascii", False),
sort_keys=sort_keys, sort_keys=sort_keys,
separators=(line_separator, value_separator), separators=(line_separator, value_separator),
use_decimal=True, use_decimal=True,
@ -78,67 +79,90 @@ class PrettyJsonBaseCommand:
if minified: if minified:
return output_json return output_json
if s.get('keep_arrays_single_line', False): if settings.get("keep_arrays_single_line", False):
matches = re.findall(r'(\[[^\[\]]+?\])', output_json) matches = re.findall(r"(\[[^\[\]]+?\])", output_json)
matches.sort(key=len, reverse=True) matches.sort(key=len, reverse=True)
join_separator = line_separator.ljust(2) join_separator = line_separator.ljust(2)
for m in matches: for m in matches:
content = m[1:-1] content = m[1:-1].strip()
items = [a.strip() for a in content.split(line_separator.strip())] items = [a.strip() for a in content.split(os.linesep)]
items = [item[:-1] if item[-1] == "," else item for item in items]
replacement = "["
for index, item in enumerate(items):
if item in ('{', '}'):
replacement = replacement + item
if item == '}':
if index!= len(items)-1:
replacement = replacement + ','
else:
replacement = replacement + item
if index != len(items)-1:
if items[items.index(item)+1] != '}':
replacement = replacement + ','
replacement = replacement + ']'
replacement = f'[{join_separator.join(items)}]' if len(replacement) <= settings.get("max_arrays_line_length", 120):
if len(replacement) <= s.get('max_arrays_line_length', 120):
output_json = output_json.replace(m, replacement, 1) output_json = output_json.replace(m, replacement, 1)
elif s.get("bracket_newline", True):
output_json = PrettyJsonBaseCommand.bracket_newline.sub(r'\1\n\2\3', output_json)
if s.get("brace_newline", True): elif settings.get("bracket_newline", True):
output_json = PrettyJsonBaseCommand.brace_newline.sub(r'\1\n\2\3', output_json) output_json = PrettyJsonBaseCommand.bracket_newline.sub(
r"\1\n\2\3", output_json
)
if settings.get("brace_newline", True):
output_json = PrettyJsonBaseCommand.brace_newline.sub(
r"\1\n\2\3", output_json
)
return output_json return output_json
@staticmethod @staticmethod
def get_selection_from_region( def get_selection_from_region(
region: sublime.Region, regions_length: int, view: sublime.View region: sublime.Region, regions_length: int, view: sublime.View
) -> sublime.Region: ):
settings = sublime.load_settings("Pretty JSON.sublime-settings")
entire_file = False entire_file = False
if region.empty() and regions_length > 1: if region.empty() and regions_length > 1:
return None, None return None, None
elif region.empty() and s.get("use_entire_file_if_no_selection", True): elif region.empty() and settings.get("use_entire_file_if_no_selection", True):
region = sublime.Region(0, view.size()) region = sublime.Region(0, view.size())
entire_file = True entire_file = True
return region, entire_file return region, entire_file
def reindent(self, text: str, selection: str): def reindent(self, text: str, selection: sublime.Region):
settings = sublime.load_settings("Pretty JSON.sublime-settings")
current_line = self.view.line(selection.begin()) current_line = self.view.line(selection.begin())
text_before_sel = sublime.Region(current_line.begin(), selection.begin()) text_before_sel = sublime.Region(current_line.begin(), selection.begin())
indent_space = ""
reindent_mode = s.get('reindent_block', False) reindent_mode = settings.get("reindent_block", False)
if reindent_mode == 'start': if reindent_mode == "start":
space_number = text_before_sel.size() space_number = text_before_sel.size()
indent_space = ' ' * space_number indent_space = " " * space_number
elif reindent_mode == 'minimal': elif reindent_mode == "minimal":
indent_space = re.search(r'^\s*', self.view.substr(text_before_sel)).group(0) indent_space = re.search(r"^\s*", self.view.substr(text_before_sel)).group(
0
)
lines = text.split('\n') lines = text.split("\n")
i = 1 i = 1
while i < len(lines): while i < len(lines):
lines[i] = f'{indent_space}{lines[i]}' lines[i] = f"{indent_space}{lines[i]}"
i += 1 i += 1
return '\n'.join(lines) return "\n".join(lines)
def show_exception(self, region: sublime.Region = None, msg=str()): def show_exception(self, region: sublime.Region = None, msg=str()):
sublime.message_dialog(f'[Error]: {msg}') sublime.message_dialog(f"[Error]: {msg}")
if region is None: if region is None:
sublime.message_dialog(f'[Error]: {msg}') sublime.message_dialog(f"[Error]: {msg}")
return return
self.highlight_error(region=region, message=f'{msg}') self.highlight_error(region=region, message=f"{msg}")
def highlight_error(self, region: sublime.Region, message: str): def highlight_error(self, region: sublime.Region, message: str):
self.phantom_set = sublime.PhantomSet(self.view, 'json_errors') self.phantom_set = sublime.PhantomSet(self.view, "json_errors")
char_match = self.json_char_matcher.search(message) char_match = self.json_char_matcher.search(message)
if char_match: if char_match:
@ -152,21 +176,21 @@ class PrettyJsonBaseCommand:
self.phantoms.append( self.phantoms.append(
sublime.Phantom( sublime.Phantom(
region, region,
self.create_phantom_html(message, 'error'), self.create_phantom_html(message, "error"),
sublime.LAYOUT_BELOW, sublime.LAYOUT_BELOW,
self.navigation, self.navigation,
) )
) )
self.phantom_set.update(self.phantoms) self.phantom_set.update(self.phantoms)
self.view.show(region) self.view.show(region)
self.view.set_status('json_errors', message) self.view.set_status("json_errors", message)
# Description: Taken from # Description: Taken from
# - https://github.com/sublimelsp/LSP/blob/master/plugin/diagnostics.py # - https://github.com/sublimelsp/LSP/blob/master/plugin/diagnostics.py
# - Thanks to the LSP Team # - Thanks to the LSP Team
def create_phantom_html(self, content: str, severity: str) -> str: def create_phantom_html(self, content: str, severity: str) -> str:
stylesheet = sublime.load_resource('Packages/Pretty JSON/phantom.css') stylesheet = sublime.load_resource("Packages/Pretty JSON/phantom.css")
return f'''<body id=inline-error> return f"""<body id=inline-error>
<style>{stylesheet}</style> <style>{stylesheet}</style>
<div class="{severity}-arrow"></div> <div class="{severity}-arrow"></div>
<div class="{severity} container"> <div class="{severity} container">
@ -175,21 +199,24 @@ class PrettyJsonBaseCommand:
</div> </div>
<div class="content">{content}</div> <div class="content">{content}</div>
</div> </div>
</body>''' </body>"""
def navigation(self, href: str): def navigation(self, href: str):
self.clear_phantoms() self.clear_phantoms()
def clear_phantoms(self): def clear_phantoms(self):
if isinstance(self.phantom_set, type): if isinstance(self.phantom_set, type):
self.phantom_set = sublime.PhantomSet(self.view, 'json_errors') self.phantom_set = sublime.PhantomSet(self.view, "json_errors")
self.phantoms = list() self.phantoms = list()
self.phantom_set.update(self.phantoms) self.phantom_set.update(self.phantoms)
def syntax_to_json(self): def syntax_to_json(self):
syntax = os.path.splitext(os.path.basename(self.view.settings().get('syntax')))[0] settings = sublime.load_settings("Pretty JSON.sublime-settings")
as_json = [i.lower() for i in s.get('as_json', ['JSON'])] syntax = os.path.splitext(os.path.basename(self.view.settings().get("syntax")))[
0
]
as_json = [i.lower() for i in settings.get("as_json", ["JSON"])]
if syntax.lower() not in as_json: if syntax.lower() not in as_json:
self.view.set_syntax_file(json_syntax) self.view.set_syntax_file(json_syntax)
@ -200,9 +227,8 @@ class PrettyJsonValidate(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
regions = self.view.sel() regions = self.view.sel()
for region in regions: for region in regions:
region, _ = self.get_selection_from_region( region, _ = self.get_selection_from_region(
region=region, region=region, regions_length=len(region), view=self.view
regions_length=len(region), )
view=self.view)
if region is None: if region is None:
continue continue
@ -224,39 +250,37 @@ class PrettyJsonValidate(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
class PrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand): class PrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
''' """
Description: Pretty Print JSON Description: Pretty Print JSON
''' """
def run(self, edit): def run(self, edit):
settings = sublime.load_settings("Pretty JSON.sublime-settings")
self.clear_phantoms() self.clear_phantoms()
pos = self.view.viewport_position()
regions = self.view.sel() regions = self.view.sel()
for region in regions: for region in regions:
region, entire_file = self.get_selection_from_region( region, entire_file = self.get_selection_from_region(
region=region, region=region, regions_length=len(region), view=self.view
regions_length=len(region), )
view=self.view)
if region is None: if region is None:
continue continue
selection_text = self.view.substr(region)
try: try:
selection_text = self.view.substr(region)
obj = self.json_loads(selection_text) obj = self.json_loads(selection_text)
json_text = self.json_dumps(obj=obj, minified=False, force_sorting=self.force_sorting)
json_text = self.json_dumps(obj=obj, minified=False) if not entire_file and settings.get("reindent_block", False):
if not entire_file and s.get('reindent_block', False):
json_text = self.reindent(json_text, region) json_text = self.reindent(json_text, region)
self.view.replace(edit, region, json_text) self.view.replace(edit, region, json_text)
if entire_file: if entire_file:
self.syntax_to_json() self.syntax_to_json()
except Exception as ex: except Exception as ex:
try: try:
count_single_quotes = re.findall( count_single_quotes = re.findall(r"(\'[^\']+\'?)", selection_text)
r"(\'[^\']+\'?)", selection_text
)
amount_of_double_quotes = re.findall( amount_of_double_quotes = re.findall(
r"(\"[^\"]+\"?)", selection_text r"(\"[^\"]+\"?)", selection_text
) )
@ -268,10 +292,13 @@ class PrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
obj = self.json_loads(modified_text) obj = self.json_loads(modified_text)
json_text = self.json_dumps(obj=obj, minified=False) json_text = self.json_dumps(obj=obj, minified=False)
if not entire_file and s.get('reindent_block', False): if not entire_file and settings.get("reindent_block", False):
json_text = self.reindent(json_text, region) json_text = self.reindent(json_text, region)
pos = self.view.viewport_position()
self.view.replace(edit, region, json_text) self.view.replace(edit, region, json_text)
self.view.set_viewport_position(pos)
if entire_file: if entire_file:
self.syntax_to_json() self.syntax_to_json()
else: else:
@ -280,10 +307,65 @@ class PrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
self.show_exception(region=region, msg=ex) self.show_exception(region=region, msg=ex)
class PrettyJsonLinesCommand(PrettyJsonCommand, sublime_plugin.TextCommand):
"""
Description: Pretty print json lines https://jsonlines.org
"""
def run(self, edit):
self.clear_phantoms()
regions = self.view.sel()
for region in regions:
(selection, selected_entire_file,) = self.get_selection_from_region(
region=region, regions_length=len(regions), view=self.view
)
if selection is None:
continue
for jsonl in sorted(self.view.split_by_newlines(selection), reverse=True):
if self.view.substr(jsonl).strip() == "":
continue
if jsonl.empty() and len(jsonl) > 1:
continue
try:
selection_text = self.view.substr(jsonl)
obj = self.json_loads(selection_text)
self.view.replace(edit, jsonl, self.json_dumps(obj))
if selected_entire_file:
self.syntax_to_json()
except Exception:
try:
amount_of_single_quotes = re.findall(
r"(\'[^\']+\'?)", selection_text
)
amount_of_double_quotes = re.findall(
r"(\"[^\"]+\"?)", selection_text
)
if len(amount_of_single_quotes) >= len(amount_of_double_quotes):
selection_text_modified = re.sub(
r"(?:\'([^\']+)\'?)", r'"\1"', selection_text
)
obj = self.json_loads(selection_text_modified)
self.view.replace(edit, jsonl, self.json_dumps(obj))
if selected_entire_file:
self.syntax_to_json()
else:
self.show_exception()
except:
self.show_exception()
class PrettyJsonAndSortCommand(PrettyJsonCommand, sublime_plugin.TextCommand): class PrettyJsonAndSortCommand(PrettyJsonCommand, sublime_plugin.TextCommand):
''' """
Description: Pretty print json with forced sorting Description: Pretty print json with forced sorting
''' """
def run(self, edit): def run(self, edit):
self.force_sorting = True self.force_sorting = True
@ -292,26 +374,23 @@ class PrettyJsonAndSortCommand(PrettyJsonCommand, sublime_plugin.TextCommand):
class UnPrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand): class UnPrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
''' """
Description: Compress/minify JSON - it makes json as one-liner Description: Compress/minify JSON - it makes json as one-liner
''' """
def run(self, edit): def run(self, edit):
self.clear_phantoms() self.clear_phantoms()
regions = self.view.sel() regions = self.view.sel()
for region in regions: for region in regions:
region, entire_file = self.get_selection_from_region( region, entire_file = self.get_selection_from_region(
region=region, region=region, regions_length=len(region), view=self.view
regions_length=len(region), )
view=self.view)
if region is None: if region is None:
continue continue
try: try:
obj = self.json_loads(self.view.substr(region)) obj = self.json_loads(self.view.substr(region))
self.view.replace( self.view.replace(edit, region, self.json_dumps(obj=obj, minified=True))
edit, region, self.json_dumps(obj=obj, minified=True)
)
if entire_file: if entire_file:
self.syntax_to_json() self.syntax_to_json()
@ -322,16 +401,17 @@ class UnPrettyJsonCommand(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
class JqInsertPrettyJsonCommand(sublime_plugin.TextCommand): class JqInsertPrettyJsonCommand(sublime_plugin.TextCommand):
def run(self, edit, string): def run(self, edit, string):
self.view.set_read_only(False)
self.view.replace(edit, sublime.Region(0, self.view.size()), string) self.view.replace(edit, sublime.Region(0, self.view.size()), string)
self.view.set_read_only(True)
class JqPrettyJsonCommand(sublime_plugin.TextCommand): class JqPrettyJsonCommand(sublime_plugin.TextCommand):
def run(self, edit): def run(self, edit):
original_view = self.view syntax_file = self.view.settings().get("syntax")
syntax_file = original_view.settings().get("syntax")
total_region = sublime.Region(0, original_view.size()) total_region = sublime.Region(0, self.view.size())
content = original_view.substr(total_region) content = self.view.substr(total_region)
sublime.run_command("new_window") sublime.run_command("new_window")
preview_window = sublime.active_window() preview_window = sublime.active_window()
@ -348,10 +428,12 @@ class JqPrettyJsonCommand(sublime_plugin.TextCommand):
preview_window.focus_group(1) preview_window.focus_group(1)
preview_view = preview_window.new_file() preview_view = preview_window.new_file()
preview_view.set_scratch(True) preview_view.set_scratch(True)
preview_view.set_read_only(True)
preview_view.set_name("Preview") preview_view.set_name("Preview")
preview_view.sel().clear() preview_view.sel().clear()
preview_window.focus_group(0) preview_window.focus_group(0)
jq_view = preview_window.new_file() jq_view = preview_window.new_file()
jq_view.run_command("jq_insert_pretty_json", {"string": content}) jq_view.run_command("jq_insert_pretty_json", {"string": content})
jq_view.set_read_only(True) jq_view.set_read_only(True)
@ -359,7 +441,7 @@ class JqPrettyJsonCommand(sublime_plugin.TextCommand):
jq_view.sel().clear() jq_view.sel().clear()
jq_view.set_syntax_file(syntax_file) jq_view.set_syntax_file(syntax_file)
preview_view.set_syntax_file(json_syntax) preview_view.set_syntax_file(syntax_file)
class JqQueryPrettyJson(sublime_plugin.WindowCommand): class JqQueryPrettyJson(sublime_plugin.WindowCommand):
@ -367,13 +449,33 @@ class JqQueryPrettyJson(sublime_plugin.WindowCommand):
Description: ./jq integration Description: ./jq integration
""" """
def is_enabled(self):
settings = sublime.load_settings("Pretty JSON.sublime-settings")
if not self.window:
return
view = self.window.active_view()
if not view:
return
as_json = settings.get("as_json", ["JSON"])
return any(syntax in view.settings().get("syntax", "") for syntax in as_json)
def is_visible(self):
return self.is_enabled()
def run(self): def run(self):
check_jq() check_jq()
if jq_exists: if jq_exists:
preview_view = self.window.active_view() preview_view = self.window.active_view()
preview_view.run_command("jq_pretty_json") preview_view.run_command("jq_pretty_json")
sublime.active_window().show_input_panel( sublime.active_window().show_input_panel(
"Enter ./jq filter expression", ".", self.done, self.send_query, None, "Enter ./jq filter expression",
".",
self.done,
self.send_query,
None,
) )
else: else:
sublime.status_message( sublime.status_message(
@ -381,9 +483,9 @@ class JqQueryPrettyJson(sublime_plugin.WindowCommand):
) )
def get_content(self): def get_content(self):
""" returns content of active view or selected region """ """returns content of active view or selected region"""
view = self.window.active_view_in_group(0) view = self.window.active_view()
selection = str() selection = ""
regions = view.sel() regions = view.sel()
for region in regions: for region in regions:
if region.empty() and len(regions) > 1: if region.empty() and len(regions) > 1:
@ -396,6 +498,7 @@ class JqQueryPrettyJson(sublime_plugin.WindowCommand):
def send_query(self, query: str): def send_query(self, query: str):
global PREVIOUS_CONTENT, PREVIOUS_QUERY_LEN global PREVIOUS_CONTENT, PREVIOUS_QUERY_LEN
settings = sublime.load_settings("Pretty JSON.sublime-settings")
try: try:
p = subprocess.Popen( p = subprocess.Popen(
[jq_path, query], [jq_path, query],
@ -405,15 +508,18 @@ class JqQueryPrettyJson(sublime_plugin.WindowCommand):
) )
QUERY_LEN = len(query) QUERY_LEN = len(query)
raw_json = self.get_content() raw_json = self.get_content()
if PREVIOUS_CONTENT[0] == "":
if not PREVIOUS_CONTENT[0]:
PREVIOUS_CONTENT[0] = raw_json PREVIOUS_CONTENT[0] = raw_json
if PREVIOUS_CONTENT[1] == "":
if not PREVIOUS_CONTENT[1]:
PREVIOUS_CONTENT[1] = raw_json PREVIOUS_CONTENT[1] = raw_json
out, err = p.communicate(bytes(raw_json, "UTF-8")) out, err = p.communicate(bytes(raw_json, "UTF-8"))
output = out.decode("UTF-8").replace(os.linesep, "\n").strip() output = out.decode("UTF-8").replace(os.linesep, "\n").strip()
errors = err.decode("UTF-8").replace(os.linesep, "\n").strip() errors = err.decode("UTF-8").replace(os.linesep, "\n").strip()
jq_view = sublime.active_window().active_view_in_group(1) jq_view = sublime.active_window().active_view_in_group(1)
if output and output != "null": if output and output != "null":
if QUERY_LEN > PREVIOUS_QUERY_LEN: if QUERY_LEN > PREVIOUS_QUERY_LEN:
PREVIOUS_CONTENT[0] = PREVIOUS_CONTENT[1] PREVIOUS_CONTENT[0] = PREVIOUS_CONTENT[1]
@ -422,7 +528,7 @@ class JqQueryPrettyJson(sublime_plugin.WindowCommand):
PREVIOUS_CONTENT[1] = PREVIOUS_CONTENT[0] PREVIOUS_CONTENT[1] = PREVIOUS_CONTENT[0]
PREVIOUS_CONTENT[0] = output PREVIOUS_CONTENT[0] = output
PREVIOUS_QUERY_LEN = len(query) PREVIOUS_QUERY_LEN = len(query)
elif s.get("jq_errors", False) and errors: elif settings.get("jq_errors", False) and errors:
output = errors output = errors
else: else:
if PREVIOUS_QUERY_LEN <= QUERY_LEN: if PREVIOUS_QUERY_LEN <= QUERY_LEN:
@ -440,37 +546,38 @@ class JqQueryPrettyJson(sublime_plugin.WindowCommand):
class JsonToXml(PrettyJsonBaseCommand, sublime_plugin.TextCommand): class JsonToXml(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
''' """
Description: converts Json to XML Description: converts Json to XML
''' """
def run(self, edit): def run(self, edit):
settings = sublime.load_settings("Pretty JSON.sublime-settings")
self.clear_phantoms() self.clear_phantoms()
regions = self.view.sel() regions = self.view.sel()
for region in regions: for region in regions:
region, entire_file = self.get_selection_from_region( region, entire_file = self.get_selection_from_region(
region=region, region=region, regions_length=len(region), view=self.view
regions_length=len(region), )
view=self.view)
if region is None: if region is None:
continue continue
try: try:
h = json.loads(self.view.substr(region)) h = json.loads(self.view.substr(region))
root = et.Element('root') root = et.Element("root")
root = self.traverse(root, h) root = self.traverse(root, h)
xml_string = '<?xml version=\'1.0\' encoding=\'UTF-8\' ?>\n' xml_string = "<?xml version='1.0' encoding='UTF-8' ?>\n"
rtn = et.tostring(root, 'utf-8') rtn = et.tostring(root, "utf-8")
if type(rtn) is bytes: if type(rtn) is bytes:
rtn = rtn.decode('utf-8') rtn = rtn.decode("utf-8")
xml_string += rtn xml_string += rtn
if type(xml_string) is bytes: if type(xml_string) is bytes:
xml_string = xml_string.decode('utf-8') xml_string = xml_string.decode("utf-8")
if not entire_file and s.get('reindent_block', False): if not entire_file and settings.get("reindent_block", False):
xml_string = self.reindent(xml_string, region) xml_string = self.reindent(xml_string, region)
self.view.replace(edit, region, xml_string) self.view.replace(edit, region, xml_string)
@ -482,18 +589,18 @@ class JsonToXml(PrettyJsonBaseCommand, sublime_plugin.TextCommand):
self.show_exception(region=region, msg=ex) self.show_exception(region=region, msg=ex)
def traverse(self, element, json_dict): def traverse(self, element, json_dict):
''' recursive traverse through dict and build xml tree ''' """recursive traverse through dict and build xml tree"""
if type(json_dict) is dict and json_dict.keys(): if type(json_dict) is dict and json_dict.keys():
for i in json_dict.keys(): for i in json_dict.keys():
e = et.Element(i) e = et.Element(i)
element.append(self.traverse(e, json_dict[i])) element.append(self.traverse(e, json_dict[i]))
elif type(json_dict) is list: elif type(json_dict) is list:
e_items = et.Element('items') e_items = et.Element("items")
for i in json_dict: for i in json_dict:
e_items.append(self.traverse(et.Element('item'), i)) e_items.append(self.traverse(et.Element("item"), i))
element.append(e_items) element.append(e_items)
else: else:
element.set('value', str(json_dict)) element.set("value", str(json_dict))
return element return element
@ -509,7 +616,7 @@ class PrettyJsonGotoSymbolCommand(PrettyJsonBaseCommand, sublime_plugin.TextComm
content = self.view.substr(sublime.Region(0, self.view.size())) content = self.view.substr(sublime.Region(0, self.view.size()))
try: try:
json_data = self.json_loads(content) json_data = self.json_loads(content)
self.generate_items(json_data, '') self.generate_items(json_data, "")
sublime.active_window().show_quick_panel(self.items, self.goto) sublime.active_window().show_quick_panel(self.items, self.goto)
except Exception as ex: except Exception as ex:
self.show_exception(region=None, msg=ex) self.show_exception(region=None, msg=ex)
@ -517,14 +624,14 @@ class PrettyJsonGotoSymbolCommand(PrettyJsonBaseCommand, sublime_plugin.TextComm
def generate_items(self, json_data, root_key): def generate_items(self, json_data, root_key):
if isinstance(json_data, OrderedDict): if isinstance(json_data, OrderedDict):
for key in json_data: for key in json_data:
new_key_name = f'{root_key}.{key}' new_key_name = f"{root_key}.{key}"
self.items.append(f'"{new_key_name}"') self.items.append(f'"{new_key_name}"')
self.goto_items.append(f'"{key}"') self.goto_items.append(f'"{key}"')
self.generate_items(json_data[key], new_key_name) self.generate_items(json_data[key], new_key_name)
elif isinstance(json_data, list): elif isinstance(json_data, list):
for index, item in enumerate(json_data): for index, item in enumerate(json_data):
if isinstance(item, str): if isinstance(item, str):
self.items.append(f'{root_key}.{item}') self.items.append(f"{root_key}.{item}")
self.goto_items.append(f'"{item}"') self.goto_items.append(f'"{item}"')
def goto(self, pos): def goto(self, pos):
@ -540,8 +647,8 @@ class PrettyJsonGotoSymbolCommand(PrettyJsonBaseCommand, sublime_plugin.TextComm
regions = self.view.find_all(string_to_search, sublime.LITERAL) regions = self.view.find_all(string_to_search, sublime.LITERAL)
for i, r in enumerate(regions): for i, r in enumerate(regions):
line = self.view.substr(self.view.full_line(r)) line = self.view.substr(self.view.full_line(r))
if ':' in line: if ":" in line:
split = line.split(':') split = line.split(":")
val = split[1].strip() val = split[1].strip()
if string_to_search in val: if string_to_search in val:
del regions[i] del regions[i]
@ -550,8 +657,3 @@ class PrettyJsonGotoSymbolCommand(PrettyJsonBaseCommand, sublime_plugin.TextComm
self.view.sel().clear() self.view.sel().clear()
self.view.sel().add(region) self.view.sel().add(region)
self.view.show(region) self.view.show(region)
def plugin_loaded():
global s
s = sublime.load_settings('Pretty JSON.sublime-settings')

View File

@ -8,14 +8,11 @@ s = sublime.load_settings("Pretty JSON.sublime-settings")
class PrettyJsonLintListener(sublime_plugin.EventListener, PrettyJsonBaseCommand): class PrettyJsonLintListener(sublime_plugin.EventListener, PrettyJsonBaseCommand):
def on_post_save(self, view): def on_post_save(self, view):
if not s.get('validate_on_save', True): if not s.get("validate_on_save", True):
return return
as_json = s.get('as_json', ['JSON']) as_json = s.get("as_json", ["JSON"])
if any( if any(syntax in view.settings().get("syntax") for syntax in as_json):
syntax in view.settings().get("syntax") for syntax in as_json
):
self.view = view
self.clear_phantoms() self.clear_phantoms()
json_content = view.substr(sublime.Region(0, view.size())) json_content = view.substr(sublime.Region(0, view.size()))
try: try:
@ -26,11 +23,9 @@ class PrettyJsonLintListener(sublime_plugin.EventListener, PrettyJsonBaseCommand
class PrettyJsonAutoPrettyOnSaveListener(sublime_plugin.EventListener): class PrettyJsonAutoPrettyOnSaveListener(sublime_plugin.EventListener):
def on_pre_save(self, view): def on_pre_save(self, view):
if not s.get('pretty_on_save', False): if not s.get("pretty_on_save", False):
return return
as_json = s.get('as_json', ['JSON']) as_json = s.get("as_json", ["JSON"])
if any( if any(syntax in view.settings().get("syntax") for syntax in as_json):
syntax in view.settings().get('syntax') for syntax in as_json view.run_command("pretty_json")
):
sublime.active_window().active_view().run_command('pretty_json')

View File

@ -2,11 +2,20 @@
Prettify/Minify/Query/Goto/Validate/Lint JSON plugin for Sublime Text 3 & 4 Prettify/Minify/Query/Goto/Validate/Lint JSON plugin for Sublime Text 3 & 4
## Updates
All keybindings have been removed in favor of the Command Palette. And to allow
for users to configure their own specific key bindings.
This also prevents key binding overrides which conflict with other packages. For
good documentation on key bindings I recommend you review the [Offical Docs][] or
[Community Docs][]
## Installation ## Installation
### Package Control (Recommended) ### Package Control (Recommended)
Install this sublime text 3/4 package via [Package Control][] Install this sublime text 3 / 4 package via [Package Control][]
search for package: "[**Pretty JSON**][]" search for package: "[**Pretty JSON**][]"
### Manual Installation ### Manual Installation
@ -29,13 +38,7 @@ No longer supported
## Usage ## Usage
To prettify JSON, make selection of json To prettify JSON, make selection of json
(or else it will try to use full view buffer) and press keys: (or else it will try to use full view buffer) and through Command Palette <kbd>Ctrl+Shift+P</kbd>
- Linux: <kbd>ctrl+alt+j</kbd>
- Windows: <kbd>ctrl+alt+j</kbd>
- OS X: <kbd>cmd+ctrl+j</kbd>
or through Command Palette <kbd>Ctrl+Shift+P</kbd>
find "Pretty JSON: Format JSON" find "Pretty JSON: Format JSON"
(you can search for part of it like 'pretty format') (you can search for part of it like 'pretty format')
@ -69,6 +72,11 @@ you can add a setting like this to your .sublime-keymap file
{ "keys": [ "ctrl+alt+m" ], "command": "un_pretty_json" } { "keys": [ "ctrl+alt+m" ], "command": "un_pretty_json" }
``` ```
#### List of commands that can be mapped to shortcuts
- `pretty_json`
- `un_pretty_json`
- `pretty_json_goto_symbol`
### Convert JSON to XML ### Convert JSON to XML
Using Command Palette <kbd>Ctrl+Shift+P</kbd> search for Using Command Palette <kbd>Ctrl+Shift+P</kbd> search for
@ -192,3 +200,6 @@ If you YAMLing then maybe you interested in this plugin: [PrettyYAML][]
[**Pretty JSON**]: https://packagecontrol.io/packages/Pretty%20JSON [**Pretty JSON**]: https://packagecontrol.io/packages/Pretty%20JSON
[PrettyYAML]: https://github.com/aukaost/SublimePrettyYAML [PrettyYAML]: https://github.com/aukaost/SublimePrettyYAML
[./jq]: http://stedolan.github.io/jq/ [./jq]: http://stedolan.github.io/jq/
[Offical Docs]: https://www.sublimetext.com/docs/key_bindings.html
[Community Docs]: https://docs.sublimetext.io/guide/customization/key_bindings.html