Skip to content
Merged
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
86 changes: 85 additions & 1 deletion src/handlers/quickTools.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import quickTools from "components/quickTools";
import actionStack from "lib/actionStack";
import searchHistory from "lib/searchHistory";
import appSettings from "lib/settings";
import searchSettings from "settings/searchSettings";
import KeyboardEvent from "utils/keyboardEvent";
Expand Down Expand Up @@ -75,6 +76,67 @@ appSettings.on("update:quicktoolsItems:after", () => {
}, 100);
});

// Initialize history navigation
function setupHistoryNavigation() {
const { $searchInput, $replaceInput } = quickTools;

// Search input history navigation
if ($searchInput.el) {
$searchInput.el.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") {
e.preventDefault();
const newValue = searchHistory.navigateSearchUp($searchInput.el.value);
$searchInput.el.value = newValue;
// Trigger search
if (newValue) find(0, false);
} else if (e.key === "ArrowDown") {
e.preventDefault();
const newValue = searchHistory.navigateSearchDown(
$searchInput.el.value,
);
$searchInput.el.value = newValue;
// Trigger search
if (newValue) find(0, false);
} else if (e.key === "Enter" || e.key === "Escape") {
// Reset navigation on enter or escape
searchHistory.resetSearchNavigation();
}
});

// Reset navigation when user starts typing
$searchInput.el.addEventListener("input", () => {
searchHistory.resetSearchNavigation();
});
}

// Replace input history navigation
if ($replaceInput.el) {
$replaceInput.el.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") {
e.preventDefault();
const newValue = searchHistory.navigateReplaceUp(
$replaceInput.el.value,
);
$replaceInput.el.value = newValue;
} else if (e.key === "ArrowDown") {
e.preventDefault();
const newValue = searchHistory.navigateReplaceDown(
$replaceInput.el.value,
);
$replaceInput.el.value = newValue;
} else if (e.key === "Enter" || e.key === "Escape") {
// Reset navigation on enter or escape
searchHistory.resetReplaceNavigation();
}
});

// Reset navigation when user starts typing
$replaceInput.el.addEventListener("input", () => {
searchHistory.resetReplaceNavigation();
});
}
}

export const key = {
get shift() {
return state.shift;
Expand Down Expand Up @@ -169,10 +231,16 @@ export default function actions(action, value) {
return true;

case "search-prev":
if (quickTools.$searchInput.el.value) {
searchHistory.addToHistory(quickTools.$searchInput.el.value);
}
find(1, true);
return true;

case "search-next":
if (quickTools.$searchInput.el.value) {
searchHistory.addToHistory(quickTools.$searchInput.el.value);
}
find(1, false);
return true;

Expand All @@ -181,10 +249,16 @@ export default function actions(action, value) {
return true;

case "search-replace":
if ($replaceInput.value) {
searchHistory.addToHistory($replaceInput.value);
}
editor.replace($replaceInput.value || "");
return true;

case "search-replace-all":
if ($replaceInput.value) {
searchHistory.addToHistory($replaceInput.value);
}
editor.replaceAll($replaceInput.value || "");
return true;

Expand Down Expand Up @@ -227,9 +301,15 @@ function toggleSearch() {
};

$searchInput.onsearch = function () {
if (this.value) find(1, false);
if (this.value) {
searchHistory.addToHistory(this.value);
find(1, false);
}
};

// Setup history navigation for search inputs
setupHistoryNavigation();

setFooterHeight(2);
find(0, false);

Expand Down Expand Up @@ -327,6 +407,10 @@ function removeSearch() {
$footer.removeAttribute("data-searching");
$searchRow1.remove();
$searchRow2.remove();

// Reset history navigation when search is closed
searchHistory.resetAllNavigation();

const { activeFile } = editorManager;

// Check if current tab is a terminal
Expand Down
25 changes: 17 additions & 8 deletions src/lib/openFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import alert from "dialogs/alert";
import confirm from "dialogs/confirm";
import loader from "dialogs/loader";
import { reopenWithNewEncoding } from "palettes/changeEncoding";
import { decode } from "utils/encodings";
import { decode, detectEncoding } from "utils/encodings";
import helpers from "utils/helpers";
import EditorFile from "./editorFile";
import fileTypeHandler from "./fileTypeHandler";
Expand Down Expand Up @@ -84,7 +84,7 @@ export default async function openFile(file, options = {}) {
const fileInfo = await fs.stat();
const name = fileInfo.name || file.filename || uri;
const readOnly = fileInfo.canWrite ? false : true;
const createEditor = (isUnsaved, text) => {
const createEditor = (isUnsaved, text, detectedEncoding) => {
new EditorFile(name, {
uri,
text,
Expand All @@ -93,7 +93,7 @@ export default async function openFile(file, options = {}) {
render,
onsave,
readOnly,
encoding,
encoding: detectedEncoding || encoding,
SAFMode: mode,
});
};
Expand Down Expand Up @@ -385,12 +385,21 @@ export default async function openFile(file, options = {}) {
}

const binData = await fs.readFile();
const fileContent = await decode(
binData,
file.encoding || appSettings.value.defaultFileEncoding,
);

createEditor(false, fileContent);
// Detect encoding if not explicitly provided
let detectedEncoding = file.encoding || encoding;
if (!detectedEncoding) {
try {
detectedEncoding = await detectEncoding(binData);
} catch (error) {
console.warn("Encoding detection failed, using default:", error);
detectedEncoding = appSettings.value.defaultFileEncoding;
}
}

const fileContent = await decode(binData, detectedEncoding);

createEditor(false, fileContent, detectedEncoding);
if (mode !== "single") recents.addFile(uri);
return;
} catch (error) {
Expand Down
188 changes: 188 additions & 0 deletions src/lib/searchHistory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**
* Search and Replace History Manager
* Manages search/replace history using localStorage
*/

const HISTORY_KEY = "acode.searchreplace.history";
const MAX_HISTORY_ITEMS = 20;

class SearchHistory {
constructor() {
this.history = this.loadHistory(HISTORY_KEY);
this.searchIndex = -1; // Current position in history for search input
this.replaceIndex = -1; // Current position in history for replace input
this.tempSearchValue = ""; // Temporary storage for current search input
this.tempReplaceValue = ""; // Temporary storage for current replace input
}

/**
* Load history from localStorage
* @param {string} key Storage key
* @returns {Array<string>} History items
*/
loadHistory(key) {
try {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : [];
} catch (error) {
console.warn("Failed to load search history:", error);
return [];
}
}

/**
* Save history to localStorage
*/
saveHistory() {
try {
localStorage.setItem(HISTORY_KEY, JSON.stringify(this.history));
} catch (error) {
console.warn("Failed to save search history:", error);
}
}

/**
* Add item to history
* @param {string} item Item to add
*/
addToHistory(item) {
if (!item || typeof item !== "string" || item.trim().length === 0) {
return;
}

const trimmedItem = item.trim();

// Remove existing item if present
this.history = this.history.filter((h) => h !== trimmedItem);

// Add to beginning
this.history.unshift(trimmedItem);

// Limit history size
this.history = this.history.slice(0, MAX_HISTORY_ITEMS);

this.saveHistory();
}

/**
* Get history
* @returns {Array<string>} History items
*/
getHistory() {
return [...this.history];
}

/**
* Clear all history
*/
clearHistory() {
this.history = [];
this.saveHistory();
}

/**
* Navigate up in search history (terminal-like)
* @param {string} currentValue Current input value
* @returns {string} Previous history item or current value
*/
navigateSearchUp(currentValue) {
if (this.history.length === 0) return currentValue;

// Store current value if we're at the beginning
if (this.searchIndex === -1) {
this.tempSearchValue = currentValue;
this.searchIndex = this.history.length - 1;
} else if (this.searchIndex > 0) {
this.searchIndex--;
}

return this.history[this.searchIndex] || currentValue;
}

/**
* Navigate down in search history (terminal-like)
* @param {string} currentValue Current input value
* @returns {string} Next history item or original value
*/
navigateSearchDown(currentValue) {
if (this.history.length === 0 || this.searchIndex === -1) {
return currentValue;
}

this.searchIndex++;

// If we've gone past the end, return to original value
if (this.searchIndex >= this.history.length) {
this.searchIndex = -1;
return this.tempSearchValue;
}

return this.history[this.searchIndex];
}

/**
* Navigate up in replace history (terminal-like)
* @param {string} currentValue Current input value
* @returns {string} Previous history item or current value
*/
navigateReplaceUp(currentValue) {
if (this.history.length === 0) return currentValue;

// Store current value if we're at the beginning
if (this.replaceIndex === -1) {
this.tempReplaceValue = currentValue;
this.replaceIndex = this.history.length - 1;
} else if (this.replaceIndex > 0) {
this.replaceIndex--;
}

return this.history[this.replaceIndex] || currentValue;
}

/**
* Navigate down in replace history (terminal-like)
* @param {string} currentValue Current input value
* @returns {string} Next history item or original value
*/
navigateReplaceDown(currentValue) {
if (this.history.length === 0 || this.replaceIndex === -1) {
return currentValue;
}

this.replaceIndex++;

// If we've gone past the end, return to original value
if (this.replaceIndex >= this.history.length) {
this.replaceIndex = -1;
return this.tempReplaceValue;
}

return this.history[this.replaceIndex];
}

/**
* Reset search history navigation
*/
resetSearchNavigation() {
this.searchIndex = -1;
this.tempSearchValue = "";
}

/**
* Reset replace history navigation
*/
resetReplaceNavigation() {
this.replaceIndex = -1;
this.tempReplaceValue = "";
}

/**
* Reset all navigation state
*/
resetAllNavigation() {
this.resetSearchNavigation();
this.resetReplaceNavigation();
}
}

export default new SearchHistory();
Loading