Skip to content

Commit 5a96063

Browse files
committed
Added more unit test and minor code improvement
2 parents 38f9c40 + f1f50d4 commit 5a96063

File tree

7 files changed

+614
-12
lines changed

7 files changed

+614
-12
lines changed

src/NppJsonViewer/SettingsDlg.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,27 @@ void SettingsDlg::InitDlg()
202202
CUtility::SetEditCtrlText(::GetDlgItem(_hSelf, IDC_EDT_INDENT_SPACECOUNT), std::to_wstring(m_pSetting->indent.len));
203203
ShowSpaceCountCtrls(m_pSetting->indent.style == IndentStyle::SPACE);
204204

205-
CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_FOLLOW_CURRENT_DOC), m_pSetting->bFollowCurrentTab);
206-
CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_FORMAT_ON_OPEN), m_pSetting->bAutoFormat);
207-
CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_JSON_HIGHLIGHT), m_pSetting->bUseJsonHighlight);
208-
CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_IGNORE_COMMA), m_pSetting->parseOptions.bIgnoreTrailingComma);
209-
CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_IGNORE_COMMENT), m_pSetting->parseOptions.bIgnoreComment);
210-
CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_REPLACE_UNDEFINED), m_pSetting->parseOptions.bReplaceUndefined);
205+
SyncUIControlsWithSettings();
206+
}
207+
208+
void SettingsDlg::SyncUIControlsWithSettings()
209+
{
210+
auto setCheckboxIfValid = [this](int controlId, bool checked)
211+
{
212+
HWND hCtrl = ::GetDlgItem(_hSelf, controlId);
213+
if (hCtrl != nullptr)
214+
{
215+
CUtility::SetCheckboxStatus(hCtrl, checked);
216+
}
217+
};
218+
219+
// Set all checkbox controls
220+
setCheckboxIfValid(IDC_CHK_FOLLOW_CURRENT_DOC, m_pSetting->bFollowCurrentTab);
221+
setCheckboxIfValid(IDC_CHK_FORMAT_ON_OPEN, m_pSetting->bAutoFormat);
222+
setCheckboxIfValid(IDC_CHK_JSON_HIGHLIGHT, m_pSetting->bUseJsonHighlight);
223+
setCheckboxIfValid(IDC_CHK_IGNORE_COMMA, m_pSetting->parseOptions.bIgnoreTrailingComma);
224+
setCheckboxIfValid(IDC_CHK_IGNORE_COMMENT, m_pSetting->parseOptions.bIgnoreComment);
225+
setCheckboxIfValid(IDC_CHK_REPLACE_UNDEFINED, m_pSetting->parseOptions.bReplaceUndefined);
211226
}
212227

213228
void SettingsDlg::ShowSpaceCountCtrls(bool bShow)

src/NppJsonViewer/SettingsDlg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class SettingsDlg : public StaticDialog
2222
bool WriteINI();
2323
void InitDlg();
2424
void ShowSpaceCountCtrls(bool bShow);
25+
void SyncUIControlsWithSettings();
2526

2627
private:
2728
int m_nCmdId = -1;

src/UtilityLib/StringHelper.cpp

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,128 @@
11
#include <regex>
2+
#include <algorithm>
3+
#include <cctype>
24

35
#include "StringHelper.h"
46

7+
// Private helper function to escape regex special characters
8+
std::string StringHelper::EscapeRegex(const std::string& str)
9+
{
10+
static constexpr std::string_view regex_chars = R"(\.+*?[]{}()|^$)";
11+
std::string result;
12+
result.reserve(str.size() * 2); // Reserve space to avoid reallocations
13+
14+
for (char c : str)
15+
{
16+
if (regex_chars.find(c) != std::string::npos)
17+
{
18+
result += '\\';
19+
}
20+
result += c;
21+
}
22+
23+
return result;
24+
}
25+
26+
std::wstring StringHelper::EscapeRegex(const std::wstring& wstr)
27+
{
28+
static constexpr std::wstring_view regex_chars = LR"(\.+*?[]{}()|^$)";
29+
std::wstring result;
30+
result.reserve(wstr.size() * 2);
31+
32+
for (wchar_t c : wstr)
33+
{
34+
if (regex_chars.find(c) != std::wstring::npos)
35+
{
36+
result += L'\\';
37+
}
38+
result += c;
39+
}
40+
41+
return result;
42+
}
43+
544
std::string StringHelper::ReplaceAll(const std::string& str, const std::string& search, const std::string& replace)
645
{
7-
return std::regex_replace(str, std::regex(search), replace);
46+
if (search.empty())
47+
{
48+
return str;
49+
}
50+
51+
try
52+
{
53+
return std::regex_replace(str, std::regex(search), replace);
54+
}
55+
catch (const std::regex_error&)
56+
{
57+
// If regex is invalid, fall back to literal replacement
58+
return ReplaceLiteral(str, search, replace);
59+
}
860
}
961

1062
std::wstring StringHelper::ReplaceAll(const std::wstring& wstr, const std::wstring& search, const std::wstring& replace)
1163
{
12-
return std::regex_replace(wstr, std::wregex(search), replace);
64+
if (search.empty())
65+
{
66+
return wstr;
67+
}
68+
69+
try
70+
{
71+
return std::regex_replace(wstr, std::wregex(search), replace);
72+
}
73+
catch (const std::regex_error&)
74+
{
75+
// If regex is invalid, fall back to literal replacement
76+
return ReplaceLiteral(wstr, search, replace);
77+
}
78+
}
79+
80+
std::string StringHelper::ReplaceLiteral(const std::string& str, const std::string& search, const std::string& replace)
81+
{
82+
if (search.empty())
83+
{
84+
return str;
85+
}
86+
87+
std::string result;
88+
result.reserve(str.size());
89+
size_t lastPos = 0;
90+
size_t pos = str.find(search);
91+
92+
while (pos != std::string::npos)
93+
{
94+
result.append(str, lastPos, pos - lastPos);
95+
result.append(replace);
96+
lastPos = pos + search.length();
97+
pos = str.find(search, lastPos);
98+
}
99+
100+
result.append(str, lastPos);
101+
return result;
102+
}
103+
104+
std::wstring StringHelper::ReplaceLiteral(const std::wstring& wstr, const std::wstring& search, const std::wstring& replace)
105+
{
106+
if (search.empty())
107+
{
108+
return wstr;
109+
}
110+
111+
std::wstring result;
112+
result.reserve(wstr.size());
113+
size_t lastPos = 0;
114+
size_t pos = wstr.find(search);
115+
116+
while (pos != std::wstring::npos)
117+
{
118+
result.append(wstr, lastPos, pos - lastPos);
119+
result.append(replace);
120+
lastPos = pos + search.length();
121+
pos = wstr.find(search, lastPos);
122+
}
123+
124+
result.append(wstr, lastPos);
125+
return result;
13126
}
14127

15128
std::wstring StringHelper::ToWstring(const std::string& str, UINT codePage)
@@ -57,9 +170,17 @@ std::string StringHelper::ToString(const std::wstring& wstr, UINT codePage)
57170

58171
std::vector<std::string> StringHelper::Split(const std::string& input, const std::string& delim)
59172
{
60-
// Vector is created on stack and copied on return
61173
std::vector<std::string> tokens;
62174

175+
if (input.empty() || delim.empty())
176+
{
177+
if (!input.empty())
178+
{
179+
tokens.push_back(input);
180+
}
181+
return tokens;
182+
}
183+
63184
// Skip delimiters at beginning.
64185
auto lastPos = input.find_first_not_of(delim, 0);
65186
// Find first "non-delimiter".
@@ -74,14 +195,23 @@ std::vector<std::string> StringHelper::Split(const std::string& input, const std
74195
// Find next "non-delimiter"
75196
pos = input.find_first_of(delim, lastPos);
76197
}
198+
77199
return tokens;
78200
}
79201

80202
std::vector<std::wstring> StringHelper::Split(const std::wstring& input, const std::wstring& delim)
81203
{
82-
// Vector is created on stack and copied on return
83204
std::vector<std::wstring> tokens;
84205

206+
if (input.empty() || delim.empty())
207+
{
208+
if (!input.empty())
209+
{
210+
tokens.push_back(input);
211+
}
212+
return tokens;
213+
}
214+
85215
// Skip delimiters at beginning.
86216
auto lastPos = input.find_first_not_of(delim, 0);
87217
// Find first "non-delimiter".
@@ -96,6 +226,7 @@ std::vector<std::wstring> StringHelper::Split(const std::wstring& input, const s
96226
// Find next "non-delimiter"
97227
pos = input.find_first_of(delim, lastPos);
98228
}
229+
99230
return tokens;
100231
}
101232

src/UtilityLib/StringHelper.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,22 @@ class StringHelper
1212
static std::string ReplaceAll(const std::string& str, const std::string& search, const std::string& replace);
1313
static std::wstring ReplaceAll(const std::wstring& wstr, const std::wstring& search, const std::wstring& replace);
1414

15+
static std::string ReplaceLiteral(const std::string& str, const std::string& search, const std::string& replace);
16+
static std::wstring ReplaceLiteral(const std::wstring& wstr, const std::wstring& search, const std::wstring& replace);
17+
1518
static std::wstring ToWstring(const std::string& str, UINT codePage = CP_THREAD_ACP);
1619
static std::string ToString(const std::wstring& wstr, UINT codePage = CP_THREAD_ACP);
1720

1821
static std::vector<std::string> Split(const std::string& input, const std::string& delim);
1922
static std::vector<std::wstring> Split(const std::wstring& input, const std::wstring& delim);
2023

21-
static bool Contains(const std::string& input, const std::string& search, bool ignorecase = true);
22-
static bool Contains(const std::wstring& input, const std::wstring& search, bool ignorecase = true);
24+
static bool Contains(const std::string& input, const std::string& search, bool ignoreCase = true);
25+
static bool Contains(const std::wstring& input, const std::wstring& search, bool ignoreCase = true);
2326

2427
static void ToLower(std::string& input);
2528
static void ToLower(std::wstring& input);
29+
30+
private:
31+
static std::string EscapeRegex(const std::string& str);
32+
static std::wstring EscapeRegex(const std::wstring& wstr);
2633
};

tests/UnitTest/NavigationTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,37 @@ namespace JsonNavigation
225225
ASSERT_EQ(posValue.nKeyLength, each.second.nKeyLength) << "For key: " << each.first << std::endl;
226226
}
227227
}
228+
229+
TEST_F(NavigationTest, Position_Validation)
230+
{
231+
// ENHANCEMENT: Add validation for position data
232+
std::vector<std::pair<std::string, Position>> testPositions {
233+
{R"([0] : "HelloWorld")", Position {.nLine = 12, .nColumn = 9, .nKeyLength = 10}},
234+
{R"(Tata)", Position {.nLine = 16, .nColumn = 7, .nKeyLength = 4}},
235+
{R"([0] : "TataByeBye")", Position {.nLine = 17, .nColumn = 9, .nKeyLength = 10}},
236+
237+
{R"(Nan : NaN)", Position {.nLine = 21, .nColumn = 3, .nKeyLength = 3}},
238+
239+
{R"(Inf)", Position {.nLine = 22, .nColumn = 3, .nKeyLength = 3}},
240+
{R"([0] : -Infinity)", Position {.nLine = 23, .nColumn = 4, .nKeyLength = 9}},
241+
{R"([1] : Infinity)", Position {.nLine = 24, .nColumn = 4, .nKeyLength = 8}},
242+
{R"([2] : -Infinity)", Position {.nLine = 25, .nColumn = 4, .nKeyLength = 9}},
243+
{R"([3] : Infinity)", Position {.nLine = 26, .nColumn = 4, .nKeyLength = 8}},
244+
245+
{R"(Object)", Position {.nLine = 28, .nColumn = 3, .nKeyLength = 6}},
246+
{R"(Test1 : "Test1")", Position {.nLine = 28, .nColumn = 13, .nKeyLength = 5}},
247+
{R"(Test2 : "Test2")", Position {.nLine = 28, .nColumn = 30, .nKeyLength = 5}},
248+
};
249+
250+
// ENHANCEMENT: Validate all positions have positive values
251+
for (const auto& [text, pos] : testPositions)
252+
{
253+
EXPECT_GE(pos.nLine, 0) << "Invalid line number for: " << text;
254+
EXPECT_GE(pos.nColumn, 0) << "Invalid column for: " << text;
255+
EXPECT_GT(pos.nKeyLength, 0) << "Invalid key length for: " << text;
256+
}
257+
258+
std::string jsonText = R"({...})"; // Your JSON content
259+
m_pTSS = std::make_shared<TrackingStream>(jsonText);
260+
}
228261
} // namespace JsonNavigation

0 commit comments

Comments
 (0)