Skip to content

Commit

Permalink
Fix range comparison (#1525)
Browse files Browse the repository at this point in the history
  • Loading branch information
jindong-zhannng committed Jun 3, 2024
1 parent 6971c83 commit c3fd862
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 4 deletions.
11 changes: 7 additions & 4 deletions packages/langium/src/utils/cst-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,14 @@ export enum RangeComparison {
After = 1,
OverlapFront = 2,
OverlapBack = 3,
Inside = 4
Inside = 4,
Outside = 5,
}

export function compareRange(range: Range, to: Range): RangeComparison {
if (range.end.line < to.start.line || (range.end.line === to.start.line && range.end.character < range.start.character)) {
if (range.end.line < to.start.line || (range.end.line === to.start.line && range.end.character <= to.start.character)) {
return RangeComparison.Before;
} else if (range.start.line > to.end.line || (range.start.line === to.end.line && range.start.character > to.end.character)) {
} else if (range.start.line > to.end.line || (range.start.line === to.end.line && range.start.character >= to.end.character)) {
return RangeComparison.After;
}
const startInside = range.start.line > to.start.line || (range.start.line === to.start.line && range.start.character >= to.start.character);
Expand All @@ -96,8 +97,10 @@ export function compareRange(range: Range, to: Range): RangeComparison {
return RangeComparison.Inside;
} else if (startInside) {
return RangeComparison.OverlapBack;
} else {
} else if (endInside) {
return RangeComparison.OverlapFront;
} else {
return RangeComparison.Outside;
}
}

Expand Down
80 changes: 80 additions & 0 deletions packages/langium/test/utils/cst-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { expandToString } from 'langium/generate';
import { createLangiumGrammarServices } from 'langium/grammar';
import { parseHelper } from 'langium/test';
import { describe, expect, test } from 'vitest';
import { RangeComparison, compareRange } from '../../src/utils/cst-utils.js';

const { findLeafNodeAtOffset, findLeafNodeBeforeOffset } = CstUtils;

Expand Down Expand Up @@ -60,3 +61,82 @@ describe('findLeafNode', () => {
return leafnode;
}
});

describe('compareRange', () => {
test.each([
// Different lines
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }, { start: { line: 99, character: 1 }, end: { line: 99, character: 10 } }],
// Same line, second range is far behind first
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }, { start: { line: 1, character: 9999 }, end: { line: 1, character: 10000 } }],
// Same line, second range is next to first
/* Range start is zero-based in LSP, for example:
* _|A A A A A B B B B
* 0 1 2 3 4 5 6 7 8 9
* ○----A----|
* ○---B---|
* Range A: 0-5, Range B: 5-9
*/
[{ start: { line: 1, character: 0 }, end: { line: 1, character: 5 } }, { start: { line: 1, character: 5 }, end: { line: 1, character: 9 } }],
])('Before', (range, to) => {
const result = compareRange(range, to);
expect(result).toEqual(RangeComparison.Before);
});

test.each([
// Different lines
[{ start: { line: 99, character: 1 }, end: { line: 99, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }],
// Same line, second range is far before first
[{ start: { line: 1, character: 9999 }, end: { line: 1, character: 10000 } }, { start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }],
// Same line, second range is in front of first
[{ start: { line: 1, character: 5 }, end: { line: 1, character: 9 } }, { start: { line: 1, character: 0 }, end: { line: 1, character: 5 } }],
])('After', (range, to) => {
const result = compareRange(range, to);
expect(result).toEqual(RangeComparison.After);
});

test.each([
// Same end position, different start lines
[{ start: { line: 9, character: 1 }, end: { line: 9, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 9, character: 10 } }],
// Same end position, same start line but different start characters
[{ start: { line: 1, character: 9 }, end: { line: 9, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 9, character: 10 } }],
// Same start position, different end lines
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 10, character: 10 } }],
// Same start position, same end line but different end characters
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 1, character: 11 } }],
// Same start and end position
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }],
])('Inside', (range, to) => {
const result = compareRange(range, to);
expect(result).toEqual(RangeComparison.Inside);
});

test.each([
// Multiple lines
[{ start: { line: 1, character: 1 }, end: { line: 3, character: 10 } }, { start: { line: 2, character: 1 }, end: { line: 4, character: 10 } }],
// Same line
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }, { start: { line: 1, character: 5 }, end: { line: 1, character: 15 } }],
])('OverlapFront', (range, to) => {
const result = compareRange(range, to);
expect(result).toEqual(RangeComparison.OverlapFront);
});

test.each([
// Multiple lines
[{ start: { line: 2, character: 1 }, end: { line: 4, character: 10 } }, { start: { line: 1, character: 1 }, end: { line: 3, character: 10 } }],
// Same line
[{ start: { line: 1, character: 5 }, end: { line: 1, character: 15 } }, { start: { line: 1, character: 1 }, end: { line: 1, character: 10 } }],
])('OverlapBack', (range, to) => {
const result = compareRange(range, to);
expect(result).toEqual(RangeComparison.OverlapBack);
});

test.each([
// Multiple lines
[{ start: { line: 1, character: 1 }, end: { line: 4, character: 10 } }, { start: { line: 2, character: 1 }, end: { line: 3, character: 10 } }],
// Same line
[{ start: { line: 1, character: 1 }, end: { line: 1, character: 99 } }, { start: { line: 1, character: 5 }, end: { line: 1, character: 10 } }],
])('Outside', (range, to) => {
const result = compareRange(range, to);
expect(result).toEqual(RangeComparison.Outside);
});
});

0 comments on commit c3fd862

Please sign in to comment.