Skip to content

Commit ef8e05b

Browse files
committed
[Feature]: Sort ascending by key
closes #61
1 parent 7232536 commit ef8e05b

File tree

7 files changed

+180
-6
lines changed

7 files changed

+180
-6
lines changed

NppJSONViewer/NppJsonViewer/Define.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ enum class CallBackID : int
77
SHOW_DOC_PANEL = 0,
88
FORMAT,
99
COMPRESS,
10+
SORT_BY_KEY,
1011
SEP_1,
1112
SETTING,
1213
ABOUT
@@ -22,6 +23,7 @@ const TCHAR TITLE_JSON_PANEL[] = TEXT("JSON Viewer Panel");
2223
const TCHAR MENU_SHOW_JSON_PANEL[] = TEXT("Show &JSON Viewer");
2324
const TCHAR MENU_FORMAT_JSON[] = TEXT("&Format JSON");
2425
const TCHAR MENU_COMPRESS_JSON[] = TEXT("&Compress JSON");
26+
const TCHAR MENU_SORT_BY_KEY[] = TEXT("Sort by &key (ascending)");
2527
const TCHAR MENU_SETTING[] = TEXT("&Settings");
2628
const TCHAR MENU_ABOUT[] = TEXT("&About");
2729
const TCHAR MENU_SEPERATOR[] = TEXT("-SEPARATOR-");

NppJSONViewer/NppJsonViewer/JsonHandler.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#include <vector>
2+
#include <algorithm>
3+
14
#include "JsonHandler.h"
25

36
namespace rj = rapidjson;
@@ -27,10 +30,127 @@ auto JsonHandler::FormatJson(const std::string &jsonText, LE le, LF lf, char ind
2730
return ParseJson<flgBaseWriter>(jsonText, sb, handler);
2831
}
2932

33+
auto JsonHandler::SortJsonByKey(const std::string &jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result
34+
{
35+
auto res = ValidateJson(jsonText);
36+
if (res.success)
37+
{
38+
// Sort the JSON string
39+
auto sorted = SortJsonText(jsonText);
40+
res = FormatJson(sorted, le, lf, indentChar, indentLen);
41+
}
42+
return res;
43+
}
44+
3045
auto JsonHandler::ValidateJson(const std::string &jsonText) -> const Result
3146
{
3247
rj::StringBuffer sb;
3348
rj::Writer<rj::StringBuffer, rj::UTF8<>, rj::UTF8<>, rj::CrtAllocator, rj::kWriteNanAndInfFlag> handler(sb);
3449

3550
return ParseJson<flgBaseWriter>(jsonText, sb, handler);
3651
}
52+
53+
void JsonHandler::SortJsonObject(rj::Value &jsonObject, rj::Document::AllocatorType &allocator) const
54+
{
55+
if (!jsonObject.IsObject())
56+
{
57+
return;
58+
}
59+
60+
std::vector<std::string> keys;
61+
62+
// Collect keys
63+
for (rj::Value::ConstMemberIterator itr = jsonObject.MemberBegin(); itr != jsonObject.MemberEnd(); ++itr)
64+
{
65+
keys.push_back(itr->name.GetString());
66+
}
67+
68+
// Sort keys alphabetically
69+
std::sort(keys.begin(), keys.end());
70+
71+
// Create a new sorted object
72+
rj::Value sortedObject(rj::kObjectType);
73+
74+
// Add members to the sorted object in sorted order
75+
for (const auto &key : keys)
76+
{
77+
rj::Value name(key.c_str(), allocator); // Create key as a RapidJSON value
78+
rj::Value &value = jsonObject[key.c_str()]; // Get corresponding value
79+
sortedObject.AddMember(name, value, allocator); // Add key-value pair to sorted object
80+
}
81+
82+
// Replace the original object with the sorted one
83+
jsonObject = std::move(sortedObject);
84+
}
85+
86+
void JsonHandler::SortJsonRecursively(rj::Value &jsonValue, rj::Document::AllocatorType &allocator) const
87+
{
88+
if (jsonValue.IsObject())
89+
{
90+
SortJsonObject(jsonValue, allocator);
91+
92+
// Recursively sort any nested objects
93+
for (rj::Value::MemberIterator itr = jsonValue.MemberBegin(); itr != jsonValue.MemberEnd(); ++itr)
94+
{
95+
SortJsonRecursively(itr->value, allocator);
96+
}
97+
}
98+
else if (jsonValue.IsArray())
99+
{
100+
// If it's an array, sort each element (in case of nested objects)
101+
for (rj::SizeType i = 0; i < jsonValue.Size(); i++)
102+
{
103+
SortJsonRecursively(jsonValue[i], allocator);
104+
}
105+
}
106+
}
107+
108+
auto JsonHandler::SortJsonText(const std::string &jsonString) const -> std::string
109+
{
110+
rj::Document document;
111+
112+
// TODO: Find some better way
113+
constexpr auto flgBase_comment = flgBaseReader | rj::kParseCommentsFlag;
114+
constexpr auto flgBase_comma = flgBaseReader | rj::kParseTrailingCommasFlag;
115+
constexpr auto flgBase_Both = flgBase_comma | flgBase_comment;
116+
117+
if (m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma)
118+
{
119+
if (document.Parse<flgBase_Both>(jsonString.c_str()).HasParseError())
120+
{
121+
return "";
122+
}
123+
}
124+
125+
else if (!m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma)
126+
{
127+
if (document.Parse<flgBase_comma>(jsonString.c_str()).HasParseError())
128+
{
129+
return "";
130+
}
131+
}
132+
133+
else if (m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma)
134+
{
135+
if (document.Parse<flgBase_comment>(jsonString.c_str()).HasParseError())
136+
{
137+
return "";
138+
}
139+
}
140+
141+
else if (!m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma)
142+
{
143+
if (document.Parse<flgBaseReader>(jsonString.c_str()).HasParseError())
144+
{
145+
return "";
146+
}
147+
}
148+
149+
SortJsonRecursively(document, document.GetAllocator());
150+
151+
rj::StringBuffer buffer;
152+
rj::Writer<rj::StringBuffer> writer(buffer);
153+
document.Accept(writer);
154+
155+
return buffer.GetString();
156+
}

NppJSONViewer/NppJsonViewer/JsonHandler.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <rapidjson/reader.h>
66
#include <rapidjson/writer.h>
7+
#include "rapidjson/document.h"
78
#include <rapidjson/prettywriter.h>
89
#include <rapidjson/stringbuffer.h>
910
#include <rapidjson/error/en.h>
@@ -36,10 +37,16 @@ class JsonHandler
3637

3738
auto GetCompressedJson(const std::string &jsonText) -> const Result;
3839
auto FormatJson(const std::string &jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result;
40+
auto SortJsonByKey(const std::string &jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result;
3941
auto ValidateJson(const std::string &jsonText) -> const Result;
4042

4143
template <unsigned format, typename Handler>
4244
auto ParseJson(const std::string &jsonText, rj::StringBuffer &sb, Handler &handler) -> const Result;
45+
46+
private:
47+
void SortJsonObject(rj::Value &jsonObject, rj::Document::AllocatorType &allocator) const;
48+
void SortJsonRecursively(rj::Value &jsonValue, rj::Document::AllocatorType &allocator) const;
49+
auto SortJsonText(const std::string &jsonString) const -> std::string;
4350
};
4451

4552
template <unsigned flgBase, typename Handler>
@@ -52,9 +59,9 @@ inline auto JsonHandler::ParseJson(const std::string &jsonText, rj::StringBuffer
5259
rj::StringStream ss(jsonText.c_str());
5360

5461
// TODO: Find some better way
55-
constexpr auto flgBase_commemt = flgBase | rj::kParseCommentsFlag;
62+
constexpr auto flgBase_comment = flgBase | rj::kParseCommentsFlag;
5663
constexpr auto flgBase_comma = flgBase | rj::kParseTrailingCommasFlag;
57-
constexpr auto flgBase_Both = flgBase_comma | flgBase_commemt;
64+
constexpr auto flgBase_Both = flgBase_comma | flgBase_comment;
5865

5966
if (m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma)
6067
{
@@ -68,7 +75,7 @@ inline auto JsonHandler::ParseJson(const std::string &jsonText, rj::StringBuffer
6875

6976
else if (m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma)
7077
{
71-
success = reader.Parse<flgBase_commemt>(ss, handler) && sb.GetString();
78+
success = reader.Parse<flgBase_comment>(ss, handler) && sb.GetString();
7279
}
7380

7481
else if (!m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma)

NppJSONViewer/NppJsonViewer/JsonViewDlg.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ void JsonViewDlg::CompressJson()
109109
}
110110
}
111111

112+
void JsonViewDlg::SortJsonByKey()
113+
{
114+
const auto selectedText = m_Editor->GetJsonText();
115+
auto [le, lf, indentChar, indentLen] = GetFormatSetting();
116+
117+
Result res = JsonHandler(m_pSetting->parseOptions).SortJsonByKey(selectedText, le, lf, indentChar, indentLen);
118+
119+
if (res.success)
120+
{
121+
m_Editor->ReplaceSelection(res.response);
122+
HighlightAsJson();
123+
}
124+
else
125+
{
126+
if (CheckForTokenUndefined(JsonViewDlg::eMethod::SortJsonByKey, selectedText, res, NULL))
127+
return;
128+
129+
ReportError(res);
130+
}
131+
}
132+
112133
bool JsonViewDlg::CheckForTokenUndefined(eMethod method, std::string selectedText, Result &res, HTREEITEM tree_root)
113134
{
114135
auto [le, lf, indentChar, indentLen] = GetFormatSetting();
@@ -148,10 +169,14 @@ bool JsonViewDlg::CheckForTokenUndefined(eMethod method, std::string selectedTex
148169
case eMethod::ValidateJson:
149170
res = JsonHandler(m_pSetting->parseOptions).ValidateJson(text);
150171
break;
172+
case eMethod::SortJsonByKey:
173+
res = JsonHandler(m_pSetting->parseOptions).SortJsonByKey(text, le, lf, indentChar, indentLen);
174+
break;
151175
}
152176
if (res.success)
153177
{
154-
m_Editor->ReplaceSelection((method == eMethod::ParseJson || method == eMethod::ValidateJson) ? text : res.response);
178+
bool bShouldReplace = method == eMethod::ParseJson || method == eMethod::ValidateJson || method == eMethod::SortJsonByKey;
179+
m_Editor->ReplaceSelection(bShouldReplace ? text : res.response);
155180
HighlightAsJson();
156181
return true;
157182
}

NppJSONViewer/NppJsonViewer/JsonViewDlg.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class JsonViewDlg : public DockingDlgInterface
2525
FormatJson,
2626
GetCompressedJson,
2727
ParseJson,
28-
ValidateJson
28+
ValidateJson,
29+
SortJsonByKey
2930
};
3031

3132
public:
@@ -35,6 +36,7 @@ class JsonViewDlg : public DockingDlgInterface
3536
void ShowDlg(bool bShow);
3637
void FormatJson();
3738
void CompressJson();
39+
void SortJsonByKey();
3840
void HandleTabActivated();
3941

4042
HTREEITEM InsertToTree(HTREEITEM parent, const std::string &text);
@@ -81,7 +83,7 @@ class JsonViewDlg : public DockingDlgInterface
8183
bool CheckForTokenUndefined(eMethod method, std::string selectedText, Result &res, HTREEITEM tree_root);
8284

8385
protected:
84-
virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);
86+
virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) override;
8587

8688
private:
8789
int m_nDlgId = -1;

NppJSONViewer/NppJsonViewer/NppJsonPlugin.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ void NppJsonPlugin::InitCommandMenu()
121121
m_shortcutCommands.SetShortCut(CallBackID::COMPRESS, {true, true, true, 'C'});
122122
m_shortcutCommands.SetCommand(CallBackID::COMPRESS, MENU_COMPRESS_JSON, Callback::CompressJson, false);
123123

124+
m_shortcutCommands.SetShortCut(CallBackID::SORT_BY_KEY, {true, true, true, 'K'});
125+
m_shortcutCommands.SetCommand(CallBackID::SORT_BY_KEY, MENU_SORT_BY_KEY, Callback::SortJsonByKey, false);
126+
124127
m_shortcutCommands.SetCommand(CallBackID::SEP_1, MENU_SEPERATOR, NULL, true);
125128

126129
m_shortcutCommands.SetCommand(CallBackID::SETTING, MENU_SETTING, Callback::OpenSettingDlg, false);
@@ -200,6 +203,16 @@ void NppJsonPlugin::CompressJson()
200203
}
201204
}
202205

206+
void NppJsonPlugin::SortJsonByKey()
207+
{
208+
ConstructJsonDlg();
209+
210+
if (m_pJsonViewDlg) // Hope it is constructed by now.
211+
{
212+
m_pJsonViewDlg->SortJsonByKey();
213+
}
214+
}
215+
203216
void NppJsonPlugin::OpenSettingDlg()
204217
{
205218
ConstructSetting();

NppJSONViewer/NppJsonViewer/NppJsonPlugin.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ class NppJsonPlugin
5454
{
5555
m_pNppJsonPlugin->CompressJson();
5656
}
57+
static void SortJsonByKey()
58+
{
59+
m_pNppJsonPlugin->SortJsonByKey();
60+
}
5761
static void OpenSettingDlg()
5862
{
5963
m_pNppJsonPlugin->OpenSettingDlg();
@@ -77,6 +81,7 @@ class NppJsonPlugin
7781
void ShowJsonDlg();
7882
void FormatJson();
7983
void CompressJson();
84+
void SortJsonByKey();
8085
void OpenSettingDlg();
8186
void ShowAboutDlg();
8287

0 commit comments

Comments
 (0)