Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<MarkdownTableEditorVersionMajor Condition="'$(MarkdownTableEditorVersionMajor)'==''">11</MarkdownTableEditorVersionMajor>
<MarkdownTableEditorVersionMinor Condition="'$(MarkdownTableEditorVersionMinor)'==''">0</MarkdownTableEditorVersionMinor>
<MarkdownTableEditorVersionPatch Condition="'$(MarkdownTableEditorVersionPatch)'==''">2</MarkdownTableEditorVersionPatch>
<MarkdownTableEditorVersionPatch Condition="'$(MarkdownTableEditorVersionPatch)'==''">3</MarkdownTableEditorVersionPatch>
<MarkdownTableEditorVersionBuild Condition="'$(MarkdownTableEditorVersionBuild)'==''">0</MarkdownTableEditorVersionBuild>
<MarkdownTableEditorVersion Condition="'$(MarkdownTableEditorVersion)'==''">$(MarkdownTableEditorVersionMajor).$(MarkdownTableEditorVersionMinor).$(MarkdownTableEditorVersionPatch)</MarkdownTableEditorVersion>
</PropertyGroup>
Expand Down
75 changes: 68 additions & 7 deletions src/MarkdownTableCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <algorithm>
#include <cctype>
#include <clocale>
#include <cstdlib>
#include <utility>

Expand Down Expand Up @@ -1631,17 +1632,75 @@ std::size_t rowById(const Table &table, std::size_t id, std::size_t fallback)
return closestEditableRow(table, fallback);
}

bool isAsciiDigit(char ch)
{
return ch >= '0' && ch <= '9';
}

// Both plugin cores accept the same strict grammar: [+-]? digits [. digits?] | [+-]? . digits,
// with an optional [eE][+-]digits exponent. Hex floats, "inf"/"nan" spellings, and Java-only
// suffixes like "1.5f" stay textual so both plugins sort them identically.
bool isStrictDecimalNumber(const std::string &value)
{
const std::size_t length = value.size();
std::size_t pos = 0;
if (pos < length && (value[pos] == '+' || value[pos] == '-'))
++pos;
std::size_t mantissaDigits = 0;
while (pos < length && isAsciiDigit(value[pos]))
{
++pos;
++mantissaDigits;
}
if (pos < length && value[pos] == '.')
{
++pos;
while (pos < length && isAsciiDigit(value[pos]))
{
++pos;
++mantissaDigits;
}
}
if (mantissaDigits == 0)
return false;
if (pos < length && (value[pos] == 'e' || value[pos] == 'E'))
{
++pos;
if (pos < length && (value[pos] == '+' || value[pos] == '-'))
++pos;
std::size_t exponentDigits = 0;
while (pos < length && isAsciiDigit(value[pos]))
{
++pos;
++exponentDigits;
}
if (exponentDigits == 0)
return false;
}
return pos == length;
}

double parseStrictDecimal(const std::string &value)
{
#ifdef _MSC_VER
// The host process may run under a comma-decimal locale; pin LC_NUMERIC to "C" so the
// validated '.'-decimal grammar always parses the same way as Double.parseDouble in the
// IntelliJ plugin core.
static const _locale_t cLocale = _create_locale(LC_NUMERIC, "C");
return _strtod_l(value.c_str(), NULL, cLocale);
#else
return std::strtod(value.c_str(), NULL);
#endif
}

bool tryParseNumber(const std::string &value, double &number)
{
const std::string trimmed = trim(value);
if (trimmed.empty())
if (!isStrictDecimalNumber(trimmed))
return false;

char *end = NULL;
number = std::strtod(trimmed.c_str(), &end);
while (end && *end != '\0' && isSpace(static_cast<unsigned char>(*end)))
++end;
return end && *end == '\0';
number = parseStrictDecimal(trimmed);
return true;
}

bool isUtf8Continuation(unsigned char ch)
Expand Down Expand Up @@ -1855,7 +1914,9 @@ SortKey makeSortKey(const std::string &value)

int compareSortKeys(const SortKey &left, const SortKey &right)
{
if (left.numeric && right.numeric)
if (left.numeric != right.numeric)
return left.numeric ? -1 : 1;
if (left.numeric)
{
if (left.number < right.number)
return -1;
Expand Down
4 changes: 2 additions & 2 deletions src/PluginDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2909,11 +2909,11 @@ void restoreEnterColumn(HWND hwnd, std::size_t column)
std::string getSelectedText(HWND scintilla)
{
const LRESULT lengthResult = ::SendMessage(scintilla, SCI_GETSELTEXT, 0, 0);
if (lengthResult <= 1)
if (lengthResult <= 0)
return std::string();

const std::size_t length = static_cast<std::size_t>(lengthResult);
std::vector<char> buffer(length, '\0');
std::vector<char> buffer(length + 1, '\0');
::SendMessage(scintilla, SCI_GETSELTEXT, 0, reinterpret_cast<LPARAM>(&buffer[0]));
return std::string(&buffer[0]);
}
Expand Down
66 changes: 66 additions & 0 deletions test-fixtures/markdown-table-core-golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,72 @@
"| Þorn |"
]
},
{
"name": "sort groups strict decimal numbers before text",
"action": "SORT_ASCENDING",
"row": 2,
"column": 0,
"input": [
"| Value |",
"| --- |",
"| 10 |",
"| 1z |",
"| 9 |",
"| .5 |",
"| 0x10 |",
"| inf |",
"| 1.5f |",
"| 2e3 |",
"| 5e-1 |",
"| 1e+2 |",
"| 7e |",
"| 3E2 |",
"| - |",
"| a­b |",
"| 󰀁w |"
],
"lines": [
"| Value |",
"| ----- |",
"| .5 |",
"| 5e-1 |",
"| 9 |",
"| 10 |",
"| 1e+2 |",
"| 3E2 |",
"| 2e3 |",
"| - |",
"| 0x10 |",
"| 1.5f |",
"| 1z |",
"| 7e |",
"| a­b |",
"| inf |",
"| 󰀁w |"
],
"targetRow": 5,
"targetColumn": 0
},
{
"name": "sort treats plus and minus zero as equal",
"action": "SORT_ASCENDING",
"row": 2,
"column": 0,
"input": [
"| V |",
"| --- |",
"| -0 |",
"| +0 |"
],
"lines": [
"| V |",
"| --- |",
"| +0 |",
"| -0 |"
],
"targetRow": 3,
"targetColumn": 0
},
{
"name": "next cell appends row",
"action": "NEXT_CELL",
Expand Down
Loading