Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion plugins/find-and-replace.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
}

/**
* Show a find-and-replace dialog.
* Show a find-and-replace dialog. The first match focused is the first one at or
* after the caret's position when this is called, wrapping back round to the start
* of the code if there is no such match.
* @param {codeInput.CodeInput} codeInputElement the `<code-input>` element.
* @param {boolean} replacePartExpanded whether the replace part of the find-and-replace dialog should be expanded
*/
Expand Down Expand Up @@ -412,6 +414,12 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
dialog.selectionStart = codeInputElement.textareaElement.selectionStart;
dialog.selectionEnd = codeInputElement.textareaElement.selectionEnd;

// Start searching from the caret's position at the moment the dialog was
// opened, like most IDEs do. The findMatchState is only constructed when the
// dialog is first created, so without this every reopening of the dialog would
// carry on from wherever the previous search left off instead.
dialog.findMatchState.focusedMatchStartIndex = dialog.selectionStart;

if(dialog.selectionStart < dialog.selectionEnd) {
// Copy selected text to Find input
let textToFind = codeInputElement.textareaElement.value.substring(dialog.selectionStart, dialog.selectionEnd);
Expand Down
32 changes: 32 additions & 0 deletions tests/tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,38 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keydown", { "key": "Escape" }));
codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keyup", { "key": "Escape" }));

// Opening the dialog searches from the caret's position at that moment, rather than
// carrying on from wherever the previous search left off. In the code above, "hi" is
// at indexes 3, 16 and 29.
// Open the dialog with the caret at the given index, then close it, which selects the
// focused match; returns the index that match starts at.
async function findFromCaret(caret) {
findInput.value = "hi";
textarea.focus();
textarea.selectionStart = textarea.selectionEnd = caret;
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "metaKey": true }));
} else {
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "ctrlKey": true }));
}
await waitAsync(250); // Wait for highlighting so matches update

const dialog = codeInputElement.querySelector(".code-input_find-and-replace_dialog");
dialog.dispatchEvent(new KeyboardEvent("keydown", { "key": "Escape" }));
dialog.dispatchEvent(new KeyboardEvent("keyup", { "key": "Escape" }));
await waitAsync(150); // Wait for the dialog to close and select the focused match

return textarea.selectionStart;
}

// These two are ordered so the match the previous one left focused differs from the
// correct answer, so they fail if the caret's position is ignored.
assertEqual("FindAndReplace", "Finds First Match After Caret when Reopened", await findFromCaret(17), 29);
assertEqual("FindAndReplace", "Finds Match Starting Exactly at Caret", await findFromCaret(16), 16);
// These two lock in the surrounding behaviour, and pass either way.
assertEqual("FindAndReplace", "Wraps to First Match when Caret is After Last Match", await findFromCaret(30), 3);
assertEqual("FindAndReplace", "Keeps Focused Match when Reopened without Moving Caret", await findFromCaret(3), 3);

// GoToLine
// Replace all code
textarea.selectionStart = 0;
Expand Down