// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assert } from 'chai';
import { createFakeSetting, describeWithEnvironment, } from '../../../testing/EnvironmentHelpers.js';
import * as TextEditor from './text_editor.js';
describeWithEnvironment('AutocompleteHistory', () => {
    let setting;
    let history;
    beforeEach(() => {
        setting = createFakeSetting('history', []);
        history = new TextEditor.AutocompleteHistory.AutocompleteHistory(setting);
    });
    describe('pushHistoryItem', () => {
        it('stores the commited text in the setting', () => {
            history.pushHistoryItem('sample input');
            assert.deepEqual(setting.get(), ['sample input']);
        });
        it('ignores sub-sequent identical inputs', () => {
            history.pushHistoryItem('entry x');
            history.pushHistoryItem('entry x');
            assert.deepEqual(setting.get(), ['entry x']);
        });
        it('resets the history navigation back to the beginning', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            history.previous('');
            history.previous('');
            // Even though the user navigated back in the history, they can still commit a different text
            history.pushHistoryItem('entry 3');
            assert.isUndefined(history.next(''));
            assert.strictEqual(history.previous(''), 'entry 3');
        });
    });
    describe('previous', () => {
        it('returns "undefined" for an empty history', () => {
            assert.isUndefined(history.previous(''));
        });
        it('moves backwards through history', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            assert.strictEqual(history.previous(''), 'entry 2');
            assert.strictEqual(history.previous(''), 'entry 1');
            assert.isUndefined(history.previous(''));
        });
        it('supports navigating backwards with empty current text', () => {
            history.pushHistoryItem('{a:1, b:2}');
            assert.strictEqual(history.previous(''), '{a:1, b:2}');
        });
        describe('prefix-based history filtering (zsh-style)', () => {
            it('filters history by prefix when navigating backward', () => {
                history.pushHistoryItem('console.log("a")');
                history.pushHistoryItem('alert("hello")');
                history.pushHistoryItem('console.log("b")');
                history.pushHistoryItem('document.getElementById("x")');
                history.pushHistoryItem('console.log("c")');
                // Type "console" and press Up - should only get console.log entries
                let currentText = 'console';
                assert.strictEqual(history.previous(currentText), 'console.log("c")');
                currentText = 'console.log("c")';
                assert.strictEqual(history.previous(currentText), 'console.log("b")');
                currentText = 'console.log("b")';
                assert.strictEqual(history.previous(currentText), 'console.log("a")');
                currentText = 'console.log("a")';
                assert.isUndefined(history.previous(currentText)); // No more matches
            });
            it('returns all entries when prefix is empty', () => {
                history.pushHistoryItem('console.log("a")');
                history.pushHistoryItem('alert("hello")');
                history.pushHistoryItem('console.log("b")');
                // Empty prefix - should get all entries (existing behavior)
                assert.strictEqual(history.previous(''), 'console.log("b")');
                assert.strictEqual(history.previous(''), 'alert("hello")');
                assert.strictEqual(history.previous(''), 'console.log("a")');
            });
            it('returns all entries when prefix has no matches', () => {
                history.pushHistoryItem('console.log("a")');
                history.pushHistoryItem('alert("hello")');
                history.pushHistoryItem('console.log("b")');
                // No match for prefix - should fall back to unfiltered history navigation.
                assert.strictEqual(history.previous('nomatch'), 'console.log("b")');
                assert.strictEqual(history.previous('console.log("b")'), 'alert("hello")');
                assert.strictEqual(history.previous('alert("hello")'), 'console.log("a")');
                assert.isUndefined(history.previous('console.log("a")'));
            });
            it('re-evaluates prefix filtering after commit', () => {
                history.pushHistoryItem('console.log("a")');
                history.pushHistoryItem('alert("hello")');
                // Navigate with prefix
                history.previous('console');
                // Commit a new command
                history.pushHistoryItem('new command');
                // Uses the new prefix instead of the previous filtered state.
                assert.strictEqual(history.previous('alert'), 'alert("hello")');
            });
        });
    });
    describe('next', () => {
        it('returns "undefined" for an empty history', () => {
            assert.isUndefined(history.next(''));
        });
        it('returns "undefined" when not navigating through the history', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            assert.isUndefined(history.next(''));
        });
        it('moves forwards through history', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            history.previous('');
            history.previous('');
            history.previous('');
            assert.strictEqual(history.next(''), 'entry 1');
            assert.strictEqual(history.next(''), 'entry 2');
        });
        describe('prefix-based history filtering (zsh-style)', () => {
            it('navigates forward through filtered history', () => {
                history.pushHistoryItem('console.log("a")');
                history.pushHistoryItem('alert("hello")');
                history.pushHistoryItem('console.log("b")');
                // Navigate back with prefix filter
                assert.strictEqual(history.previous('console'), 'console.log("b")');
                assert.strictEqual(history.previous('console.log("b")'), 'console.log("a")');
                // Navigate forward - should still be filtered
                assert.strictEqual(history.next(), 'console.log("b")');
            });
            it('preserves the typed prefix when returning to start', () => {
                history.pushHistoryItem('console.log("a")');
                history.pushHistoryItem('alert("hello")');
                // Type "console" and navigate back
                assert.strictEqual(history.previous('console'), 'console.log("a")');
                // Navigate forward - should return to the typed prefix
                assert.strictEqual(history.next(), 'console');
            });
        });
    });
    it('stores the "temporary input" on the first "previous" call with a non-empty history', () => {
        history.pushHistoryItem('entry 1');
        history.pushHistoryItem('entry 2');
        assert.strictEqual(history.previous('incomplete user inp'), 'entry 2');
        assert.strictEqual(history.next(''), 'incomplete user inp');
    });
    it('does not write the temporary user input to the setting', () => {
        history.pushHistoryItem('entry 1');
        assert.strictEqual(history.previous('incomplete user inp'), 'entry 1');
        assert.deepEqual(setting.get(), ['entry 1']);
    });
    describe('matchingEntries', () => {
        it('returns the appropriate matches', () => {
            history.pushHistoryItem('x === 5');
            history.pushHistoryItem('y < 42');
            history.pushHistoryItem('x > 20');
            const matches = history.matchingEntries('x ');
            assert.deepEqual([...matches], ['x > 20', 'x === 5']);
        });
        it('respects the "limit" argument', () => {
            for (let i = 0; i < 20; ++i) {
                history.pushHistoryItem(`x === ${i}`);
            }
            const matches = history.matchingEntries('x ', 3);
            assert.deepEqual([...matches], ['x === 19', 'x === 18', 'x === 17']);
        });
    });
    describe('edit preservation during navigation', () => {
        it('preserves edits made to history entries when navigating backward', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            history.pushHistoryItem('entry 3');
            // Navigate to entry 2
            history.previous(''); // now at entry 3
            history.previous(''); // now at entry 2
            // Edit entry 2 and navigate further back (passing edited text to previous)
            history.previous('entry 2 EDITED'); // now at entry 1
            // Navigate forward - should see the edit
            assert.strictEqual(history.next(''), 'entry 2 EDITED');
        });
        it('preserves edits made to history entries when navigating forward', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            history.pushHistoryItem('entry 3');
            // Navigate to entry 2
            history.previous(''); // now at entry 3
            history.previous(''); // now at entry 2
            // Edit entry 2 and navigate forward
            history.next('entry 2 EDITED'); // now at entry 3
            // Navigate back - should see the edit
            assert.strictEqual(history.previous(''), 'entry 2 EDITED');
        });
        it('preserves multiple edits at different history positions', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            history.pushHistoryItem('entry 3');
            // Edit entry 3 by passing edit when navigating away
            history.previous('entry 3 EDITED'); // save edit, now at entry 2
            // Edit entry 2 by passing edit when navigating away
            history.previous('entry 2 EDITED'); // save edit, now at entry 1
            // Navigate forward through edits
            assert.strictEqual(history.next(''), 'entry 2 EDITED');
            assert.strictEqual(history.next(''), 'entry 3 EDITED');
        });
        it('clears edits when a new command is committed', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            // Edit entry 2 by passing edit when navigating away
            history.previous('entry 2 EDITED');
            // Commit a new command
            history.pushHistoryItem('entry 3');
            // Navigate back - edit should be gone
            assert.strictEqual(history.previous(''), 'entry 3');
            assert.strictEqual(history.previous(''), 'entry 2'); // Original, not edited
        });
        it('preserves uncommitted input alongside history edits', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            // Start typing, then navigate back
            history.previous('my uncommitted input'); // now at entry 2
            // Edit entry 2 and go further back
            history.previous('entry 2 EDITED'); // now at entry 1
            // Navigate all the way forward
            assert.strictEqual(history.next(''), 'entry 2 EDITED');
            assert.strictEqual(history.next(''), 'my uncommitted input');
        });
        it('restores original if edit matches original', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            // Navigate to entry 2, "edit" it to same value
            history.previous('');
            history.previous('entry 2'); // edit back to original
            // Should still work
            assert.strictEqual(history.next(''), 'entry 2');
        });
        it('restores original if text was emptied', () => {
            history.pushHistoryItem('entry 1');
            history.pushHistoryItem('entry 2');
            // Navigate to entry 2, edit it
            history.previous('');
            history.previous('entry 2 EDITED'); // now at entry 1 with edit saved
            // Verify edit is there
            assert.strictEqual(history.next(''), 'entry 2 EDITED');
            // Now empty the text and navigate away
            history.previous(''); // empty text, navigate back to entry 1
            // Edit should be cleared, original restored
            assert.strictEqual(history.next(''), 'entry 2');
        });
    });
});
//# sourceMappingURL=AutocompleteHistory.test.js.map