Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
501a93d
Expand AttributedScopeStackTests with full coverage
udlose Feb 21, 2026
1c2b5f3
Fixes #116 AttributedScopeStack.GetHashCode() throws for normal stack…
udlose Feb 22, 2026
8ee9f02
Refactor AttributedScopeStack and expand test coverage, remove multip…
udlose Feb 22, 2026
22c2215
### BREAKING CHANGE: the MergeAttributes method used to (implcitly) t…
udlose Feb 22, 2026
6ad857b
Add .gitignore rules for Visual Studio Live Unit Testing files
udlose Feb 22, 2026
c5ac86e
Refactor dictionary access in SimpleJSON for clarity
udlose Feb 27, 2026
9a6dc13
Add comprehensive StateStack.ToString() unit tests
udlose Feb 27, 2026
0e98cd5
Refactor StateStack:ToString methods for allocations
udlose Feb 27, 2026
a3d00bc
Refactor rule type checks to use pattern matching to avoid casts
udlose Feb 27, 2026
9eb4185
Add comprehensive unit tests for theme parsing and logic
udlose Feb 27, 2026
b20ba91
Expand test coverage for ParsedTheme and Theme classes
udlose Feb 28, 2026
6b3a859
Optimize theme parsing: reduce allocations, add thread safety
udlose Feb 28, 2026
c36e00c
Optimize AttributedScopeStack scope building by minimizing List-resiz…
udlose Mar 2, 2026
a67a707
Remove unnecessary List allocation on success path in ParseTheme method
udlose Mar 2, 2026
916274d
Add comprehensive unit tests for Raw class
udlose Mar 2, 2026
4ec5115
Improve type safety and performance in Raw.cs
udlose Mar 2, 2026
d9e89aa
Add extensive unit tests for matcher logic and fix namespaces
udlose Mar 2, 2026
aa5ce9c
Improve robustness and performance across matcher code
udlose Mar 2, 2026
9e3543c
Fix scope matching logic in IMatchesName implementation
udlose Mar 2, 2026
3e538dd
Optimize rule list allocation and fix resource naming
udlose Mar 2, 2026
34a167c
fixed race condition in concurrency test. Use Volatile.Read for threa…
udlose Mar 2, 2026
f023aa2
Remove unused 'priority' parameter from theme parsing
udlose Mar 2, 2026
6423dc5
PR review feedback (remove extraneous comment)
udlose Mar 2, 2026
467acde
Update ParseTheme call to match new method signature
udlose Mar 2, 2026
da8eab4
Expand and modernize AttributedScopeStack unit tests, and remove unit…
udlose Mar 4, 2026
d3bc964
Refactor AttributedScopeStack equality and hash logic
udlose Mar 4, 2026
52c78aa
Add comprehensive unit tests for StateStack value semantics
udlose Mar 4, 2026
28fdd2f
Expand AttributedScopeStack test coverage and edge cases
udlose Mar 4, 2026
9f53a61
Add ToString() to AttributedScopeStack for scope display to match ups…
udlose Mar 4, 2026
abff6a9
Improve StateStack equality, hashing, and documentation
udlose Mar 4, 2026
29e09fe
Add unit test for param validation in HasSameRuleAs
udlose Mar 4, 2026
b3972dc
Optimize matcher list allocation with initial capacity
udlose Mar 4, 2026
50acd6b
Use ReferenceEquals for null checks, improve performance to avoid O(n…
udlose Mar 4, 2026
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore

# User-specific files
*.rsuser
Expand Down Expand Up @@ -43,6 +43,9 @@ Generated\ Files/
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# VisualStudio live unit testing
*.lutconfig

# NUnit
*.VisualState.xml
TestResult.xml
Expand Down
342 changes: 342 additions & 0 deletions src/TextMateSharp.Tests/Grammar/StateStackTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
using NUnit.Framework;
using TextMateSharp.Grammars;
using TextMateSharp.Internal.Grammars;
using TextMateSharp.Internal.Rules;

namespace TextMateSharp.Tests.Grammar
{
[TestFixture]
public class StateStackTests
{
private const int RuleIdSingleDepth = 42;
private const int RuleIdDepthTwo = 100;
private const int RuleIdDepthThree = 200;
private const int EnterPosition = 0;
private const int AnchorPosition = 0;

[Test]
public void ToString_SingleDepthState_ReturnsFormattedString()
{
// Arrange
StateStack stack = new StateStack(
null,
RuleId.Of(RuleIdSingleDepth),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(42)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_TwoDepthState_ReturnsFormattedStringWithBothRules()
{
// Arrange
StateStack parent = new StateStack(
null,
RuleId.Of(RuleIdSingleDepth),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

StateStack stack = parent.Push(
RuleId.Of(RuleIdDepthTwo),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(42), (100)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_ThreeDepthState_ReturnsFormattedStringWithAllRules()
{
// Arrange
StateStack level1 = new StateStack(
null,
RuleId.Of(RuleIdSingleDepth),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

StateStack level2 = level1.Push(
RuleId.Of(RuleIdDepthTwo),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

StateStack level3 = level2.Push(
RuleId.Of(RuleIdDepthThree),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(42), (100), (200)]";

// Act
string result = level3.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_NullStaticInstance_ReturnsFormattedNoRuleString()
{
// Arrange
StateStack stack = StateStack.NULL;
const string expectedOutput = "[(0)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_StateWithNoRuleId_ReturnsFormattedNoRuleString()
{
// Arrange
StateStack stack = new StateStack(
null,
RuleId.NO_RULE,
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(0)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_StateWithEndRuleId_ReturnsFormattedEndRuleString()
{
// Arrange
StateStack stack = new StateStack(
null,
RuleId.END_RULE,
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(-1)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_StateWithWhileRuleId_ReturnsFormattedWhileRuleString()
{
// Arrange
StateStack stack = new StateStack(
null,
RuleId.WHILE_RULE,
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(-2)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_BoundaryDepthZero_ReturnsNullStackString()
{
// Arrange - depth 0 returns StateStack.NULL
const int depthZero = 0;
StateStack stack = CreateStateStackWithDepth(depthZero);
const string expectedOutput = "[(0)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
Assert.AreSame(StateStack.NULL, stack);
}

[Test]
public void ToString_BoundaryDepthOne_ReturnsSinglePushString()
{
// Arrange - depth 1 is one push on NULL
const int depthOne = 1;
StateStack stack = CreateStateStackWithDepth(depthOne);
const string expectedOutput = "[(0), (0)]";

// Act
string result = stack.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

[Test]
public void ToString_BoundaryVeryLargeDepth_ReturnsFormattedStringWithAllLevels()
{
// Arrange - test large depth to verify performance and correctness
const int veryLargeDepth = 100;
StateStack stack = CreateStateStackWithDepth(veryLargeDepth);

// Act
string result = stack.ToString();

// Assert
Assert.IsNotNull(result);
Assert.IsTrue(result.StartsWith("[(0)"));
Assert.IsTrue(result.EndsWith("(9900)]"));

// Verify correct number of elements: NULL (1) + 100 pushes = 101 elements
const int expectedCommaCount = 100;
int actualCommaCount = result.Split(',').Length - 1;
Assert.AreEqual(expectedCommaCount, actualCommaCount);
}

[Test]
public void ToString_CalledMultipleTimes_ReturnsSameResult()
{
// Arrange
StateStack stack = new StateStack(
null,
RuleId.Of(RuleIdSingleDepth),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

// Act
string result1 = stack.ToString();
string result2 = stack.ToString();
string result3 = stack.ToString();

// Assert
Assert.AreEqual(result1, result2);
Assert.AreEqual(result2, result3);
}

[Test]
public void ToString_StackWithMixedRuleIds_ReturnsCorrectOrderFromRootToCurrent()
{
// Arrange
StateStack root = new StateStack(
null,
RuleId.Of(RuleIdSingleDepth),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

StateStack middle = root.Push(
RuleId.Of(RuleIdDepthTwo),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

StateStack current = middle.Push(
RuleId.Of(RuleIdDepthThree),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());

const string expectedOutput = "[(42), (100), (200)]";

// Act
string result = current.ToString();

// Assert
Assert.AreEqual(expectedOutput, result);
}

#region Helper Methods

private static AttributedScopeStack CreateTestScopeStack()
{
return new AttributedScopeStack(null, "test.scope", 0);
}

private static StateStack CreateStateStackWithDepth(int depth)
{
StateStack stack = StateStack.NULL;
const int ruleIdDepthMultiplier = 100;
for (int depthIndex = 0; depthIndex < depth; depthIndex++)
{
int ruleId = depthIndex * ruleIdDepthMultiplier;
stack = stack.Push(
RuleId.Of(ruleId),
EnterPosition,
AnchorPosition,
false,
null,
CreateTestScopeStack(),
CreateTestScopeStack());
}

return stack;
}

#endregion
}
}
Loading
Loading