Skip to content

Commit 9f0f69c

Browse files
authored
fix: blank lines split embedded TextMate partitions (#961)
1 parent 6e449be commit 9f0f69c

2 files changed

Lines changed: 52 additions & 31 deletions

File tree

org.eclipse.tm4e.ui.tests/src/main/java/org/eclipse/tm4e/ui/tests/internal/text/TMPartitionerTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*******************************************************************************/
1212
package org.eclipse.tm4e.ui.tests.internal.text;
1313

14-
import static org.assertj.core.api.Assertions.assertThat;
14+
import static org.assertj.core.api.Assertions.*;
1515

1616
import java.nio.file.Paths;
1717

@@ -317,6 +317,7 @@ void markdownFencedJavascriptIsSinglePartition() throws Exception {
317317
318318
```javascript
319319
function foo() { return 1; }
320+
320321
// comment
321322
```
322323
""");
@@ -510,6 +511,8 @@ void setup() {
510511
<body>
511512
<script>
512513
function x() { return 1; }
514+
515+
function y() { return 2; }
513516
</script>
514517
</body>
515518
</html>

org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/text/TMPartitioner.java

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -832,44 +832,62 @@ private void recomputeRange(final int startOffset, final int endOffset) throws B
832832
for (final TMToken t : tokens) {
833833
final String prefNorm = t.grammarScope == null ? null : normalizeVariantScope(t.grammarScope);
834834
if (prefNorm != null && !Objects.equals(normalizeBaseScope(prefNorm), baseRootNN)) {
835-
if (firstEmbeddedStart < 0)
835+
if (firstEmbeddedStart < 0) {
836836
firstEmbeddedStart = t.startIndex;
837+
}
837838
lastEmbeddedStart = t.startIndex;
838839
}
839840
}
840-
841-
for (final TMToken tok : tokens) {
842-
final String prefNorm = tok.grammarScope == null ? null : normalizeVariantScope(tok.grammarScope);
843-
final boolean isBase = prefNorm == null || Objects.equals(normalizeBaseScope(prefNorm), baseRootNN);
844-
final String root = isBase ? basePartitionType : scopeToPartitionType(prefNorm != null ? prefNorm : baseRootNN);
845-
846-
if (currentType == null) {
847-
currentType = root;
848-
final boolean isEmbedded = !isBase;
849-
final int firstTokenStart = lineOffset + tok.startIndex;
850-
currentStart = Math.max(currentStart, isEmbedded ? lineOffset : firstTokenStart);
851-
currentGrammarScopeStr = isBase ? baseRootNN : prefNorm;
852-
continue;
841+
final boolean lineHasEmbedded = firstEmbeddedStart >= 0;
842+
boolean skipWhitespaceBaseLineInEmbeddedRun = false;
843+
if (!lineHasEmbedded && currentType != null && !currentType.equals(basePartitionType)) {
844+
// Line has only base tokens but we are currently inside an embedded run.
845+
// If the text covered by this line within [startOffset,endOffset) is whitespace-only,
846+
// treat it as part of the embedded partition instead of starting a base segment.
847+
final int spanStart = Math.max(lineOffset, startOffset);
848+
final int spanEnd = Math.min(lineEnd, endOffset);
849+
if (spanEnd > spanStart) {
850+
final String slice = doc.get(spanStart, spanEnd - spanStart);
851+
if (slice.isBlank()) {
852+
skipWhitespaceBaseLineInEmbeddedRun = true;
853+
}
853854
}
855+
}
856+
857+
if (!skipWhitespaceBaseLineInEmbeddedRun) {
858+
for (final TMToken tok : tokens) {
859+
final String prefNorm = tok.grammarScope == null ? null : normalizeVariantScope(tok.grammarScope);
860+
final boolean isBase = prefNorm == null || Objects.equals(normalizeBaseScope(prefNorm), baseRootNN);
861+
final String root = isBase ? basePartitionType : scopeToPartitionType(prefNorm != null ? prefNorm : baseRootNN);
862+
863+
if (currentType == null) {
864+
currentType = root;
865+
final boolean isEmbedded = !isBase;
866+
final int firstTokenStart = lineOffset + tok.startIndex;
867+
currentStart = Math.max(currentStart, isEmbedded ? lineOffset : firstTokenStart);
868+
currentGrammarScopeStr = isBase ? baseRootNN : prefNorm;
869+
continue;
870+
}
854871

855-
if (!currentType.equals(root)) {
856-
// While inside an embedded run, ignore base tokens that appear
857-
// either at the start of the line (indentation) when another embedded token exists later,
858-
// or anywhere before the last embedded token on this line (transient gaps between embedded tokens).
859-
if (!currentType.equals(basePartitionType) && basePartitionType.equals(root)) {
860-
final boolean baseAtLineStartWithEmbedLater = tok.startIndex == 0 && firstEmbeddedStart > 0;
861-
final boolean baseBeforeLastEmbedded = lastEmbeddedStart > 0 && tok.startIndex <= lastEmbeddedStart;
862-
if (baseAtLineStartWithEmbedLater || baseBeforeLastEmbedded) {
863-
// keep currentType and currentStart as-is
864-
continue;
872+
if (!currentType.equals(root)) {
873+
// While inside an embedded run, ignore base tokens that appear
874+
// either at the start of the line (indentation) when another embedded token exists later,
875+
// or anywhere before the last embedded token on this line (transient gaps between embedded tokens).
876+
if (!currentType.equals(basePartitionType) && basePartitionType.equals(root)) {
877+
final boolean baseAtLineStartWithEmbedLater = tok.startIndex == 0 && firstEmbeddedStart > 0;
878+
final boolean baseBeforeLastEmbedded = lastEmbeddedStart > 0 && tok.startIndex <= lastEmbeddedStart;
879+
if (baseAtLineStartWithEmbedLater || baseBeforeLastEmbedded) {
880+
// keep currentType and currentStart as-is
881+
continue;
882+
}
865883
}
884+
// Token type changed: finalize previous segment and start a new one
885+
final int segEnd = lineOffset + tok.startIndex;
886+
addSeg(newSegs, currentStart, segEnd, currentType, currentGrammarScopeStr, baseScope);
887+
currentType = root;
888+
currentStart = segEnd;
889+
currentGrammarScopeStr = isBase ? baseRootNN : prefNorm;
866890
}
867-
// Token type changed: finalize previous segment and start a new one
868-
final int segEnd = lineOffset + tok.startIndex;
869-
addSeg(newSegs, currentStart, segEnd, currentType, currentGrammarScopeStr, baseScope);
870-
currentType = root;
871-
currentStart = segEnd;
872-
currentGrammarScopeStr = isBase ? baseRootNN : prefNorm;
873891
}
874892
}
875893
}

0 commit comments

Comments
 (0)