Skip to content

Commit f1f50d4

Browse files
committed
Tests
1 parent 7c17d91 commit f1f50d4

10 files changed

Lines changed: 485 additions & 157 deletions

File tree

src/NppJsonViewer/JsonHandler.cpp

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -109,41 +109,13 @@ auto JsonHandler::SortJsonText(const std::string& jsonString) const -> std::stri
109109
{
110110
rj::Document document;
111111

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)
112+
// ENHANCEMENT: Use helper function to select appropriate parse flags
113+
// based on ParseOptions instead of nested if-else structure
114+
auto parseFlags = GetParseFlags(m_parseOptions);
115+
116+
if (document.Parse<parseFlags>(jsonString.c_str()).HasParseError())
118117
{
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-
}
118+
return "";
147119
}
148120

149121
SortJsonRecursively(document, document.GetAllocator());
@@ -154,3 +126,16 @@ auto JsonHandler::SortJsonText(const std::string& jsonString) const -> std::stri
154126

155127
return buffer.GetString();
156128
}
129+
130+
unsigned JsonHandler::GetParseFlags(const ParseOptions& options) noexcept
131+
{
132+
unsigned flags = flgBaseReader;
133+
134+
if (options.bIgnoreComment)
135+
flags |= rj::kParseCommentsFlag;
136+
137+
if (options.bIgnoreTrailingComma)
138+
flags |= rj::kParseTrailingCommasFlag;
139+
140+
return flags;
141+
}

src/NppJsonViewer/JsonHandler.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,31 @@ namespace rj = rapidjson;
1616

1717
struct Result
1818
{
19-
bool success = false;
20-
int error_pos = -1;
21-
int error_code = -1;
22-
std::string error_str;
23-
std::string response;
19+
bool success = false; // Operation succeeded
20+
int error_pos = -1; // Position of error in JSON (-1 if no error)
21+
int error_code = -1; // Error code (-1 if no error)
22+
std::string error_str; // Human-readable error message
23+
std::string response; // Result data or error details
24+
25+
/// <summary>
26+
/// Check if result indicates success
27+
/// </summary>
28+
/// <returns>true if operation was successful</returns>
29+
bool IsValid() const noexcept
30+
{
31+
return success && error_code == -1;
32+
}
33+
34+
/// <summary>
35+
/// Get error message or empty string if successful
36+
/// </summary>
37+
/// <returns>Error message string</returns>
38+
std::string GetErrorMessage() const noexcept
39+
{
40+
if (success)
41+
return "";
42+
return !error_str.empty() ? error_str : "Unknown error at position " + std::to_string(error_pos);
43+
}
2444
};
2545

2646
using LE = rj::LineEndingOption;

src/NppJsonViewer/JsonViewDlg.cpp

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "Define.h"
66
#include "Utility.h"
77
#include "StringHelper.h"
8-
#include "RapidJsonHandler.h"
8+
#include "RapidJsonHandler"
99
#include "ScintillaEditor.h"
1010
#include "Profile.h"
1111

@@ -177,55 +177,62 @@ bool JsonViewDlg::CheckForTokenUndefined(eMethod method, std::string selectedTex
177177
{
178178
auto [le, lf, indentChar, indentLen] = GetFormatSetting();
179179

180-
if (m_pSetting->parseOptions.bReplaceUndefined)
180+
// ENHANCEMENT: Add null check and error handling
181+
if (m_pSetting == nullptr)
181182
{
182-
auto text = selectedText.substr(res.error_pos, 9);
183-
StringHelper::ToLower(text);
183+
return res; // Return empty result
184+
}
184185

185-
if (text == "undefined")
186+
if (m_pSetting->parseOptions.bReplaceUndefined)
187+
{
188+
// Validate error position is within bounds
189+
if (res.error_pos >= 0 && res.error_pos + 9 <= static_cast<int>(selectedText.size()))
186190
{
187-
try
191+
auto text = selectedText.substr(res.error_pos, 9);
192+
StringHelper::ToLower(text);
193+
194+
if (text == "undefined")
188195
{
189-
std::regex regex("([:\\[,])([\\s]*?)undefined([\\s,}]*?)", std::regex_constants::icase);
190-
text = std::regex_replace(selectedText, regex, "$1$2null");
191-
switch (method)
192-
{
193-
case eMethod::FormatJson:
194-
res = JsonHandler(m_pSetting->parseOptions).FormatJson(text, le, lf, indentChar, indentLen);
195-
break;
196-
case eMethod::GetCompressedJson:
197-
res = JsonHandler(m_pSetting->parseOptions).GetCompressedJson(text);
198-
break;
199-
case eMethod::ParseJson:
196+
try
200197
{
201-
RapidJsonHandler handler(this, tree_root);
202-
rapidjson::StringBuffer sb;
203-
res = JsonHandler(m_pSetting->parseOptions).ParseJson<flgBaseReader>(text, sb, handler);
204-
break;
198+
// ENHANCEMENT: Use ReplaceLiteral for safer replacement
199+
std::string processedText = StringHelper::ReplaceLiteral(
200+
selectedText,
201+
"undefined",
202+
"null"
203+
);
204+
205+
switch (method)
206+
{
207+
case eMethod::FormatJson:
208+
res = JsonHandler(m_pSetting->parseOptions).FormatJson(
209+
processedText, le, lf, indentChar, indentLen
210+
);
211+
break;
212+
case eMethod::GetCompressedJson:
213+
res = JsonHandler(m_pSetting->parseOptions).GetCompressedJson(processedText);
214+
break;
215+
case eMethod::ParseJson:
216+
{
217+
RapidJsonHandler handler(this, tree_root);
218+
rapidjson::StringBuffer sb;
219+
res = JsonHandler(m_pSetting->parseOptions).ParseJson<flgBaseReader>(text, sb, handler);
220+
break;
221+
}
222+
case eMethod::ValidateJson:
223+
res = JsonHandler(m_pSetting->parseOptions).ValidateJson(text);
224+
break;
225+
case eMethod::SortJsonByKey:
226+
res = JsonHandler(m_pSetting->parseOptions).SortJsonByKey(text, le, lf, indentChar, indentLen);
227+
break;
228+
}
205229
}
206-
case eMethod::ValidateJson:
207-
res = JsonHandler(m_pSetting->parseOptions).ValidateJson(text);
208-
break;
209-
case eMethod::SortJsonByKey:
210-
res = JsonHandler(m_pSetting->parseOptions).SortJsonByKey(text, le, lf, indentChar, indentLen);
211-
break;
212-
}
213-
if (res.success)
230+
catch (const std::exception& ex)
214231
{
215-
bool bShouldReplace = method == eMethod::ParseJson || method == eMethod::ValidateJson || method == eMethod::SortJsonByKey;
216-
m_pEditor->ReplaceSelection(bShouldReplace ? text : res.response);
217-
HighlightAsJson();
218-
return true;
232+
// Log exception and return error result
233+
res.success = false;
234+
res.error_str = "Exception processing undefined replacement: " + std::string(ex.what());
219235
}
220-
else
221-
{
222-
m_pEditor->ReplaceSelection(text);
223-
m_pEditor->MakeSelection(m_pEditor->GetSelectionStart(), text.length());
224-
m_pEditor->RefreshSelectionPos();
225-
}
226-
}
227-
catch (const std::exception&)
228-
{
229236
}
230237
}
231238
}
@@ -261,6 +268,14 @@ void JsonViewDlg::ProcessScintillaData(const ScintillaData& scintillaData, std::
261268
text.clear();
262269
code = ScintillaCode::Unknown;
263270

271+
// ENHANCEMENT: Add null pointer guard
272+
if (!std::holds_alternative<std::string>(scintillaData) &&
273+
!std::holds_alternative<ScintillaCode>(scintillaData))
274+
{
275+
return;
276+
}
277+
278+
// Use visitor pattern safely with type checking
264279
std::visit(
265280
[&text, &code](auto&& arg)
266281
{

src/NppJsonViewer/JsonViewDlg.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,84 @@ class JsonViewDlg
5353
void AppendNodeCount(HTREEITEM node, unsigned elementCount, bool bArray) override;
5454

5555
private:
56+
/// <summary>
57+
/// Draw the JSON tree structure in the tree view
58+
/// </summary>
5659
void DrawJsonTree();
60+
61+
/// <summary>
62+
/// Redraw tree with optional force refresh
63+
/// </summary>
64+
/// <param name="bForce">Force redraw even if data hasn't changed</param>
5765
void ReDrawJsonTree(bool bForce = false);
66+
67+
/// <summary>
68+
/// Apply JSON syntax highlighting to editor
69+
/// </summary>
70+
/// <param name="bForcefully">Force highlighting even if already highlighted</param>
5871
void HighlightAsJson(bool bForcefully = false) const;
72+
73+
/// <summary>
74+
/// Populate tree using SAX parser for streaming large files
75+
/// </summary>
76+
/// <param name="tree_root">Root item in tree view</param>
77+
/// <param name="jsonText">JSON text to parse</param>
78+
/// <returns>Error message if parsing fails, empty optional if successful</returns>
5979
auto PopulateTreeUsingSax(HTREEITEM tree_root, const std::string& jsonText) -> std::optional<std::wstring>;
6080

81+
/// <summary>
82+
/// Validate current JSON and display any errors
83+
/// </summary>
6184
void ValidateJson();
6285

86+
/// <summary>
87+
/// Update the node path display for current selection
88+
/// </summary>
89+
/// <param name="htiNode">Tree node item</param>
6390
void UpdateNodePath(HTREEITEM htiNode) const;
91+
92+
/// <summary>
93+
/// Navigate to specific line in editor
94+
/// </summary>
95+
/// <param name="nLineToGo">Line number to go to</param>
6496
void GoToLine(size_t nLineToGo) const;
97+
98+
/// <summary>
99+
/// Navigate to specific position in editor
100+
/// </summary>
101+
/// <param name="nLineToGo">Line number</param>
102+
/// <param name="nPos">Column position</param>
103+
/// <param name="nLen">Length of selection</param>
65104
void GoToPosition(size_t nLineToGo, size_t nPos, size_t nLen) const;
66105

106+
/// <summary>
107+
/// Perform search operation in JSON tree
108+
/// </summary>
67109
void SearchInTree();
68110

111+
/// <summary>
112+
/// Get formatted title for this dialog
113+
/// </summary>
114+
/// <returns>Title string with file information</returns>
69115
auto GetTitleFileName() const -> std::wstring;
116+
117+
/// <summary>
118+
/// Initialize toolbar buttons
119+
/// </summary>
70120
void PrepareButtons();
121+
122+
/// <summary>
123+
/// Set icon and tooltip for toolbar button
124+
/// </summary>
125+
/// <param name="ctrlType">Button type identifier</param>
126+
/// <param name="toolTip">Tooltip text to display</param>
71127
void SetIconAndTooltip(eButton ctrlType, const std::wstring& toolTip);
72128

129+
/// <summary>
130+
/// Adjust document panel size based on window dimensions
131+
/// </summary>
132+
/// <param name="nWidth">New panel width</param>
133+
/// <param name="nHeight">New panel height</param>
73134
void AdjustDocPanelSize(int nWidth, int nHeight);
74135

75136
// Context menu related functions

src/NppJsonViewer/NPPJSONViewer.vcxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@
120120
<SubSystem>Windows</SubSystem>
121121
<TargetMachine>MachineX64</TargetMachine>
122122
</Link>
123+
<PostBuildEvent>
124+
<Command>if not exist "E:\Code\OpenSource\repos\npp\PowerEditor\visual.net\x64\Debug\plugins\NPPJSONViewer" (
125+
mkdir "E:\Code\OpenSource\repos\npp\PowerEditor\visual.net\x64\Debug\plugins\NPPJSONViewer"
126+
)
127+
copy /Y "$(TargetPath)" "E:\Code\OpenSource\repos\npp\PowerEditor\visual.net\x64\Debug\plugins\NPPJSONViewer\"</Command>
128+
</PostBuildEvent>
123129
</ItemDefinitionGroup>
124130
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
125131
<ClCompile>

0 commit comments

Comments
 (0)