diff --git a/pkgs/sass_language_services/lib/src/lsp/text_document.dart b/pkgs/sass_language_services/lib/src/lsp/text_document.dart index f2c6cde..ff3bdb6 100644 --- a/pkgs/sass_language_services/lib/src/lsp/text_document.dart +++ b/pkgs/sass_language_services/lib/src/lsp/text_document.dart @@ -115,8 +115,10 @@ class TextDocument { } var line = low - 1; - offset = - _ensureBeforeEndOfLine(offset: offset, lineOffset: lineOffsets[line]); + offset = _ensureBeforeEndOfLine( + offset: offset, + lineOffset: lineOffsets[line], + ); return Position(character: offset - lineOffsets[line], line: line); } @@ -151,8 +153,12 @@ class TextDocument { lineOffsets[i + startLine + 1] = addedLineOffsets[i]; } } else { + // Avoid going outside the range on weird range inputs. lineOffsets.replaceRange( - startLine + 1, endLine - startLine, addedLineOffsets); + min(startLine + 1, lineOffsets.length), + min(endLine + 1, lineOffsets.length), + addedLineOffsets, + ); } var diff = text.length - (endOffset - startOffset); diff --git a/pkgs/sass_language_services/test/lsp/text_document_test.dart b/pkgs/sass_language_services/test/lsp/text_document_test.dart index 7849035..da27077 100644 --- a/pkgs/sass_language_services/test/lsp/text_document_test.dart +++ b/pkgs/sass_language_services/test/lsp/text_document_test.dart @@ -16,20 +16,25 @@ lsp.Either2 updateIncremental(String text, lsp.Range range) { - return lsp.Either2.t2(lsp.TextDocumentContentChangeEvent2(text: text)); + return lsp.Either2.t1( + lsp.TextDocumentContentChangeEvent1( + text: text, + range: range, + ), + ); } lsp.Range forSubstring(TextDocument document, String substring) { var i = document.getText().indexOf(substring); - return lsp.Range( - start: document.positionAt(i), - end: document.positionAt(i + substring.length), - ); + var start = document.positionAt(i); + var end = document.positionAt(i + substring.length); + var range = lsp.Range(start: start, end: end); + return range; } lsp.Range afterSubstring(TextDocument document, String substring) { var i = document.getText().indexOf(substring); - var pos = document.positionAt(i); + var pos = document.positionAt(i + substring.length); return lsp.Range(start: pos, end: pos); } @@ -72,10 +77,7 @@ void main() { }); test('multiple lines', () { - var content = '''abcde -fghij -klmno -'''; + var content = 'abcde\nfghij\nklmno\n'; var document = createDocument(content); expect(document.lineCount, equals(4)); @@ -201,7 +203,7 @@ klmno }); group('incremental updates', () { - void checkLineNumbers(TextDocument document) { + void expectLineAtOffsets(TextDocument document) { // Assuming \n. var text = document.getText(); var characters = text.split(''); @@ -212,10 +214,325 @@ klmno expected += 1; } } - expect(document.positionAt(text.length), equals(expected)); + expect(document.positionAt(text.length).line, equals(expected)); } - test('incrementally removing content', () {}); + test('removing content', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [updateIncremental('', forSubstring(document, 'abcde'))], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('\nfghij\nklmno')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('removing content over multiple lines', () { + var document = createDocument('abcde\nfghij\nklmno\npqrst'); + expect(document.version, equals(0)); + expect(document.lineCount, equals(4)); + expectLineAtOffsets(document); + document.update( + [updateIncremental('', forSubstring(document, 'fghij\nklmno'))], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), 'abcde\n\npqrst'); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('adding content', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [updateIncremental('12345', afterSubstring(document, 'abcde\n'))], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('abcde\n12345fghij\nklmno')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('adding content over multiple lines', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + '12345\n67890\n', + afterSubstring(document, 'abcde\n'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('abcde\n12345\n67890\nfghij\nklmno')); + expect(document.lineCount, equals(5)); + expectLineAtOffsets(document); + }); + + test('replacing single-line content with more content', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + '1234567890', + forSubstring(document, 'abcde'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('1234567890\nfghij\nklmno')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('replacing single-line content with less content', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + '1', + forSubstring(document, 'abcde'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('1\nfghij\nklmno')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('replacing single-line content with same amount of characters', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + '12345', + forSubstring(document, 'abcde'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('12345\nfghij\nklmno')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('replacing multi-line content with more lines', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + '12345\n67890\nABCDE\n', + forSubstring(document, 'abcde\n'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('12345\n67890\nABCDE\nfghij\nklmno')); + expect(document.lineCount, equals(5)); + expectLineAtOffsets(document); + }); + + test('replacing multi-line content with fewer lines', () { + var document = createDocument('12345\n67890\nABCDE\nfghij\nklmno'); + expect(document.lineCount, equals(5)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + 'abcde\n', + forSubstring(document, '12345\n67890\nABCDE\n'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('abcde\nfghij\nklmno')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('replacing multi-line content with same amounts', () { + var document = createDocument('12345\n67890\nABCDE\nfghij\nklmno'); + expect(document.lineCount, equals(5)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + 'abcde\nFGHJI', + forSubstring(document, 'ABCDE\nfghij'), + ) + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('12345\n67890\nabcde\nFGHJI\nklmno')); + expect(document.lineCount, equals(5)); + expectLineAtOffsets(document); + }); + + test('replace large number of lines', () { + var document = createDocument('12345\n67890\nasdf'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + var text = ''; + for (var i = 0; i < 20000; i++) { + text += 'asdf\n'; + } + document.update( + [updateIncremental(text, forSubstring(document, '67890\n'))], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), '12345\n${text}asdf'); + expect(document.lineCount, 20002); + expectLineAtOffsets(document); + }); + + test('several incremental changes', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + expect(document.version, equals(0)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental('BCD', forSubstring(document, 'bcd')), + updateIncremental('LMN', forSubstring(document, 'lmn')), + updateIncremental('GHI', forSubstring(document, 'ghi')), + ], + 1, + ); + expect(document.version, equals(1)); + expect(document.getText(), equals('aBCDe\nfGHIj\nkLMNo')); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + }); + + test('append', () { + var document = createDocument('abcde'); + expect(document.lineCount, equals(1)); + document.update( + [ + updateIncremental( + '\nfghij\nklmno', + afterSubstring(document, 'abcde'), + ), + ], + 1, + ); + expect(document.getText(), equals('abcde\nfghij\nklmno')); + expect(document.lineCount, equals(3)); + }); + + test('delete', () { + var document = createDocument('abcde\nfghij\nklmno'); + expect(document.lineCount, equals(3)); + document.update( + [ + updateIncremental( + '', + forSubstring(document, 'o'), + ), + ], + 1, + ); + expect(document.getText(), equals('abcde\nfghij\nklmn')); + expect(document.version, equals(1)); + expect(document.lineCount, equals(3)); + expectLineAtOffsets(document); + document.update( + [ + updateIncremental( + '', + forSubstring(document, 'fghij\nklmn'), + ), + ], + 2, + ); + expect(document.getText(), equals('abcde\n')); + expect(document.version, equals(2)); + expect(document.lineCount, equals(2)); + expectLineAtOffsets(document); + }); + + test('handles weird update ranges', () { + var document = createDocument('abcde\nfghij'); + document.update( + [ + updateIncremental('1234', range(-4, 0, -2, 3)), + ], + 1, + ); + expect(document.getText(), equals('1234abcde\nfghij')); + + document = createDocument('abcde\nfghij'); + document.update( + [ + updateIncremental('1234', range(-1, 0, 0, 5)), + ], + 1, + ); + expect(document.getText(), equals('1234\nfghij')); + + document = createDocument('abcde\nfghij'); + document.update( + [ + updateIncremental('1234', range(1, 0, 13, 14)), + ], + 1, + ); + expect(document.getText(), equals('abcde\n1234')); + + document = createDocument('abcde\nfghij'); + document.update( + [ + updateIncremental('1234', range(13, 0, 35, 14)), + ], + 1, + ); + expect(document.getText(), equals('abcde\nfghij1234')); + + document = createDocument('abcde\nfghij'); + document.update( + [ + updateIncremental('1234', range(-13, 0, 35, 14)), + ], + 1, + ); + expect(document.getText(), equals('1234')); + }); }); group('applyEdits', () {