|
1 | 1 | const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
|
2 | 2 |
|
| 3 | +const fs = require('fs') |
| 4 | +const path = require('path') |
3 | 5 | const dedent = require('dedent')
|
4 | 6 | const TextBuffer = require('text-buffer')
|
5 | 7 | const {Point} = TextBuffer
|
6 | 8 | const TextEditor = require('../src/text-editor')
|
7 | 9 | const TreeSitterGrammar = require('../src/tree-sitter-grammar')
|
8 | 10 | const TreeSitterLanguageMode = require('../src/tree-sitter-language-mode')
|
| 11 | +const Random = require('../script/node_modules/random-seed') |
| 12 | +const {getRandomBufferRange, buildRandomLines} = require('./helpers/random') |
9 | 13 |
|
10 | 14 | const cGrammarPath = require.resolve('language-c/grammars/tree-sitter-c.cson')
|
11 | 15 | const pythonGrammarPath = require.resolve('language-python/grammars/tree-sitter-python.cson')
|
@@ -789,6 +793,97 @@ describe('TreeSitterLanguageMode', () => {
|
789 | 793 | })
|
790 | 794 | })
|
791 | 795 |
|
| 796 | + describe('highlighting after random changes', () => { |
| 797 | + let originalTimeout |
| 798 | + |
| 799 | + beforeEach(() => { |
| 800 | + originalTimeout = jasmine.getEnv().defaultTimeoutInterval |
| 801 | + jasmine.getEnv().defaultTimeoutInterval = 60 * 1000 |
| 802 | + }) |
| 803 | + |
| 804 | + afterEach(() => { |
| 805 | + jasmine.getEnv().defaultTimeoutInterval = originalTimeout |
| 806 | + }) |
| 807 | + |
| 808 | + it('matches the highlighting of a freshly-opened editor', async () => { |
| 809 | + jasmine.useRealClock() |
| 810 | + |
| 811 | + const text = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample.js'), 'utf8') |
| 812 | + atom.grammars.loadGrammarSync(jsGrammarPath) |
| 813 | + atom.grammars.assignLanguageMode(buffer, 'source.js') |
| 814 | + buffer.getLanguageMode().syncOperationLimit = 0 |
| 815 | + |
| 816 | + const initialSeed = Date.now() |
| 817 | + for (let i = 0, trial_count = 10; i < trial_count; i++) { |
| 818 | + let seed = initialSeed + i |
| 819 | + // seed = 1541201470759 |
| 820 | + const random = Random(seed) |
| 821 | + |
| 822 | + // Parse the initial content and render all of the screen lines. |
| 823 | + buffer.setText(text) |
| 824 | + buffer.clearUndoStack() |
| 825 | + await buffer.getLanguageMode().parseCompletePromise() |
| 826 | + editor.displayLayer.getScreenLines() |
| 827 | + |
| 828 | + // Make several random edits. |
| 829 | + for (let j = 0, edit_count = 1 + random(4); j < edit_count; j++) { |
| 830 | + const editRoll = random(10) |
| 831 | + const range = getRandomBufferRange(random, buffer) |
| 832 | + |
| 833 | + if (editRoll < 2) { |
| 834 | + const linesToInsert = buildRandomLines(random, range.getExtent().row + 1) |
| 835 | + // console.log('replace', range.toString(), JSON.stringify(linesToInsert)) |
| 836 | + buffer.setTextInRange(range, linesToInsert) |
| 837 | + } else if (editRoll < 5) { |
| 838 | + // console.log('delete', range.toString()) |
| 839 | + buffer.delete(range) |
| 840 | + } else { |
| 841 | + const linesToInsert = buildRandomLines(random, 3) |
| 842 | + // console.log('insert', range.start.toString(), JSON.stringify(linesToInsert)) |
| 843 | + buffer.insert(range.start, linesToInsert) |
| 844 | + } |
| 845 | + |
| 846 | + // console.log(buffer.getText()) |
| 847 | + |
| 848 | + // Sometimes, let the parse complete before re-rendering. |
| 849 | + // Sometimes re-render and move on before the parse completes. |
| 850 | + if (random(2)) await buffer.getLanguageMode().parseCompletePromise() |
| 851 | + editor.displayLayer.getScreenLines() |
| 852 | + } |
| 853 | + |
| 854 | + // Revert the edits, because Tree-sitter's error recovery is somewhat path-dependent, |
| 855 | + // and we want a state where the tree parse result is guaranteed. |
| 856 | + while (buffer.undo()) {} |
| 857 | + |
| 858 | + // Create a fresh buffer and editor with the same text. |
| 859 | + const buffer2 = new TextBuffer(buffer.getText()) |
| 860 | + const editor2 = new TextEditor({buffer: buffer2}) |
| 861 | + atom.grammars.assignLanguageMode(buffer2, 'source.js') |
| 862 | + |
| 863 | + // Verify that the the two buffers have the same syntax highlighting. |
| 864 | + await buffer.getLanguageMode().parseCompletePromise() |
| 865 | + await buffer2.getLanguageMode().parseCompletePromise() |
| 866 | + expect(buffer.getLanguageMode().tree.rootNode.toString()).toEqual( |
| 867 | + buffer2.getLanguageMode().tree.rootNode.toString(), `Seed: ${seed}` |
| 868 | + ) |
| 869 | + |
| 870 | + for (let j = 0, n = editor.getScreenLineCount(); j < n; j++) { |
| 871 | + const tokens1 = editor.tokensForScreenRow(j) |
| 872 | + const tokens2 = editor2.tokensForScreenRow(j) |
| 873 | + expect(tokens1).toEqual(tokens2, `Seed: ${seed}, screen line: ${j}`) |
| 874 | + if (jasmine.getEnv().currentSpec.results().failedCount > 0) { |
| 875 | + console.log(tokens1) |
| 876 | + console.log(tokens2) |
| 877 | + debugger |
| 878 | + break |
| 879 | + } |
| 880 | + } |
| 881 | + |
| 882 | + if (jasmine.getEnv().currentSpec.results().failedCount > 0) break |
| 883 | + } |
| 884 | + }) |
| 885 | + }) |
| 886 | + |
792 | 887 | describe('folding', () => {
|
793 | 888 | it('can fold nodes that start and end with specified tokens', async () => {
|
794 | 889 | const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
|
0 commit comments