diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /devtools/client/webconsole/reducers/history.js | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/webconsole/reducers/history.js')
-rw-r--r-- | devtools/client/webconsole/reducers/history.js | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/devtools/client/webconsole/reducers/history.js b/devtools/client/webconsole/reducers/history.js new file mode 100644 index 0000000000..adfca885c5 --- /dev/null +++ b/devtools/client/webconsole/reducers/history.js @@ -0,0 +1,244 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { + APPEND_TO_HISTORY, + CLEAR_HISTORY, + EVALUATE_EXPRESSION, + HISTORY_LOADED, + UPDATE_HISTORY_POSITION, + HISTORY_BACK, + HISTORY_FORWARD, + REVERSE_SEARCH_INPUT_TOGGLE, + REVERSE_SEARCH_INPUT_CHANGE, + REVERSE_SEARCH_BACK, + REVERSE_SEARCH_NEXT, + SET_TERMINAL_INPUT, + SET_TERMINAL_EAGER_RESULT, +} = require("resource://devtools/client/webconsole/constants.js"); + +/** + * Create default initial state for this reducer. + */ +function getInitialState() { + return { + // Array with history entries + entries: [], + + // Holds position (index) in history entries that the user is + // currently viewing. This is reset to this.entries.length when + // APPEND_TO_HISTORY action is fired. + position: undefined, + + // Backups the original user value (if any) that can be set in + // the input field. It might be used again if the user doesn't + // pick up anything from the history and wants to return all + // the way back to see the original input text. + originalUserValue: null, + + reverseSearchEnabled: false, + currentReverseSearchResults: null, + currentReverseSearchResultsPosition: null, + + terminalInput: null, + terminalEagerResult: null, + }; +} + +function history(state = getInitialState(), action, prefsState) { + switch (action.type) { + case APPEND_TO_HISTORY: + case EVALUATE_EXPRESSION: + return appendToHistory(state, prefsState, action.expression); + case CLEAR_HISTORY: + return clearHistory(state); + case HISTORY_LOADED: + return historyLoaded(state, action.entries); + case UPDATE_HISTORY_POSITION: + return updateHistoryPosition(state, action.direction, action.expression); + case REVERSE_SEARCH_INPUT_TOGGLE: + return reverseSearchInputToggle(state, action); + case REVERSE_SEARCH_INPUT_CHANGE: + return reverseSearchInputChange(state, action.value); + case REVERSE_SEARCH_BACK: + return reverseSearchBack(state); + case REVERSE_SEARCH_NEXT: + return reverseSearchNext(state); + case SET_TERMINAL_INPUT: + return setTerminalInput(state, action.expression); + case SET_TERMINAL_EAGER_RESULT: + return setTerminalEagerResult(state, action.result); + } + return state; +} + +function appendToHistory(state, prefsState, expression) { + // Clone state + state = { ...state }; + state.entries = [...state.entries]; + + // Append new expression only if it isn't the same as + // the one recently added. + if (expression.trim() != state.entries[state.entries.length - 1]) { + state.entries.push(expression); + } + + // Remove entries if the limit is reached + if (state.entries.length > prefsState.historyCount) { + state.entries.splice(0, state.entries.length - prefsState.historyCount); + } + + state.position = state.entries.length; + state.originalUserValue = null; + + return state; +} + +function clearHistory(state) { + return getInitialState(); +} + +/** + * Handling HISTORY_LOADED action that is fired when history + * entries created in previous Firefox session are loaded + * from async-storage. + * + * Loaded entries are appended before the ones that were + * added to the state in this session. + */ +function historyLoaded(state, entries) { + const newEntries = [...entries, ...state.entries]; + return { + ...state, + entries: newEntries, + // Default position is at the end of the list + // (at the latest inserted item). + position: newEntries.length, + originalUserValue: null, + }; +} + +function updateHistoryPosition(state, direction, expression) { + // Handle UP arrow key => HISTORY_BACK + // Handle DOWN arrow key => HISTORY_FORWARD + if (direction == HISTORY_BACK) { + if (state.position <= 0) { + return state; + } + + // Clone state + state = { ...state }; + + // Store the current input value when the user starts + // browsing through the history. + if (state.position == state.entries.length) { + state.originalUserValue = expression || ""; + } + + state.position--; + } else if (direction == HISTORY_FORWARD) { + if (state.position >= state.entries.length) { + return state; + } + + state = { + ...state, + position: state.position + 1, + }; + } + + return state; +} + +function reverseSearchInputToggle(state, action) { + const { initialValue = "" } = action; + + // We're going to close the reverse search, let's clean the state + if (state.reverseSearchEnabled) { + return { + ...state, + reverseSearchEnabled: false, + position: undefined, + currentReverseSearchResults: null, + currentReverseSearchResultsPosition: null, + }; + } + + // If we're enabling the reverse search, we treat it as a reverse search input change, + // since we can have an initial value. + return reverseSearchInputChange(state, initialValue); +} + +function reverseSearchInputChange(state, searchString) { + if (searchString === "") { + return { + ...state, + position: undefined, + currentReverseSearchResults: null, + currentReverseSearchResultsPosition: null, + }; + } + + searchString = searchString.toLocaleLowerCase(); + const matchingEntries = state.entries.filter(entry => + entry.toLocaleLowerCase().includes(searchString) + ); + // We only return unique entries, but we want to keep the latest entry in the array if + // it's duplicated (e.g. if we have [1,2,1], we want to get [2,1], not [1,2]). + // To do that, we need to reverse the matching entries array, provide it to a Set, + // transform it back to an array and reverse it again. + const uniqueEntries = new Set(matchingEntries.reverse()); + const currentReverseSearchResults = Array.from( + new Set(uniqueEntries) + ).reverse(); + + return { + ...state, + position: undefined, + currentReverseSearchResults, + currentReverseSearchResultsPosition: currentReverseSearchResults.length - 1, + }; +} + +function reverseSearchBack(state) { + let nextPosition = state.currentReverseSearchResultsPosition - 1; + if (nextPosition < 0) { + nextPosition = state.currentReverseSearchResults.length - 1; + } + + return { + ...state, + currentReverseSearchResultsPosition: nextPosition, + }; +} + +function reverseSearchNext(state) { + let previousPosition = state.currentReverseSearchResultsPosition + 1; + if (previousPosition >= state.currentReverseSearchResults.length) { + previousPosition = 0; + } + + return { + ...state, + currentReverseSearchResultsPosition: previousPosition, + }; +} + +function setTerminalInput(state, expression) { + return { + ...state, + terminalInput: expression, + terminalEagerResult: !expression ? null : state.terminalEagerResult, + }; +} + +function setTerminalEagerResult(state, result) { + return { + ...state, + terminalEagerResult: result, + }; +} + +exports.history = history; |