mirror of
https://github.com/esm7/obsidian-vimrc-support.git
synced 2026-07-22 05:00:25 +00:00
feat: implement looping around
This commit is contained in:
parent
2adf45cdbc
commit
19d095883e
1 changed files with 55 additions and 20 deletions
|
|
@ -5,6 +5,12 @@ import { shim as matchAllShim } from "string.prototype.matchall";
|
|||
// Polyfill for String.prototype.matchAll, in case it's not available (pre-ES2020)
|
||||
matchAllShim();
|
||||
|
||||
/**
|
||||
* Returns the position of the repeat-th instance of a pattern from a given starting position, in
|
||||
* the given direction; looping to the other end of the document when reaching one end.
|
||||
*
|
||||
* Under the hood, we avoid repeated loops around the document by using modulo arithmetic.
|
||||
*/
|
||||
export function jumpToPattern({
|
||||
cm,
|
||||
oldPosition,
|
||||
|
|
@ -31,8 +37,11 @@ export function jumpToPattern({
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the index of (up to) the n-th next instance of a pattern in a string after a given
|
||||
* starting index. If the pattern is not found at all, returns undefined.
|
||||
* Returns the index (from the start of the content) of the nth-next instance of a pattern after a
|
||||
* given starting index.
|
||||
*
|
||||
* "Loops" to the top of the document when the bottom is reached; under the hood, we avoid repeated
|
||||
* loops by using modulo arithmetic.
|
||||
*/
|
||||
function findNthNextRegexMatch({
|
||||
content,
|
||||
|
|
@ -46,22 +55,23 @@ function findNthNextRegexMatch({
|
|||
n: number;
|
||||
}): number | undefined {
|
||||
const globalRegex = makeGlobalRegex(regex);
|
||||
globalRegex.lastIndex = startingIdx + 1;
|
||||
let currMatch, lastMatch;
|
||||
let numMatchesFound = 0;
|
||||
while (
|
||||
numMatchesFound < n &&
|
||||
(currMatch = globalRegex.exec(content)) != null
|
||||
) {
|
||||
lastMatch = currMatch;
|
||||
numMatchesFound++;
|
||||
const allMatches = [...content.matchAll(globalRegex)];
|
||||
const previousMatches = allMatches.filter((match) => match.index <= startingIdx);
|
||||
const nextMatches = allMatches.filter((match) => match.index > startingIdx);
|
||||
const nModulo = n % allMatches.length;
|
||||
const effectiveN = nModulo === 0 ? allMatches.length : nModulo;
|
||||
if (effectiveN <= nextMatches.length) {
|
||||
return nextMatches[effectiveN - 1].index;
|
||||
}
|
||||
return lastMatch?.index;
|
||||
return previousMatches[effectiveN - nextMatches.length - 1].index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of (up to) the nth-previous instance of a pattern in a string before a given
|
||||
* starting index. If the pattern is not found at all, returns undefined.
|
||||
* Returns the index (from the start of the content) of the nth-previous instance of a pattern
|
||||
* before a given starting index.
|
||||
*
|
||||
* "Loops" to the bottom of the document when the top is reached; under the hood, we avoid repeated
|
||||
* loops by using modulo arithmetic.
|
||||
*/
|
||||
function findNthPreviousRegexMatch({
|
||||
content,
|
||||
|
|
@ -75,12 +85,11 @@ function findNthPreviousRegexMatch({
|
|||
n: number;
|
||||
}): number | undefined {
|
||||
const globalRegex = makeGlobalRegex(regex);
|
||||
const contentToSearch = content.substring(0, startingIdx);
|
||||
const previousMatches = [...contentToSearch.matchAll(globalRegex)];
|
||||
if (previousMatches.length < n) {
|
||||
return previousMatches[0]?.index;
|
||||
}
|
||||
return previousMatches[previousMatches.length - n].index;
|
||||
const allMatches = [...content.matchAll(globalRegex)];
|
||||
const previousMatches = allMatches.filter((match) => match.index < startingIdx);
|
||||
const nextMatches = allMatches.filter((match) => match.index >= startingIdx);
|
||||
const match = getNthPreviousMatch(previousMatches, nextMatches, n);
|
||||
return match?.index;
|
||||
}
|
||||
|
||||
function makeGlobalRegex(regex: RegExp): RegExp {
|
||||
|
|
@ -92,3 +101,29 @@ function getGlobalFlags(regex: RegExp): string {
|
|||
const { flags } = regex;
|
||||
return flags.includes("g") ? flags : `${flags}g`;
|
||||
}
|
||||
|
||||
function getNthPreviousMatch(
|
||||
previousMatches: RegExpExecArray[],
|
||||
nextMatches: RegExpExecArray[],
|
||||
n: number
|
||||
): RegExpExecArray | undefined {
|
||||
const numMatches = previousMatches.length + nextMatches.length;
|
||||
const effectiveN = n % numMatches; // every `numMatches` is a full loop
|
||||
if (effectiveN <= previousMatches.length) {
|
||||
return getNthItemFromEnd(previousMatches, effectiveN);
|
||||
}
|
||||
return getNthItemFromEnd(nextMatches, effectiveN - previousMatches.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nth (1-indexed) item from the end of an array. Expects 1 <= n <= items.length, but
|
||||
* just returns undefined if n is out of bounds.
|
||||
*/
|
||||
function getNthItemFromEnd<T>(items: T[], n: number): T | undefined {
|
||||
const numItems = items.length;
|
||||
if (n < 1 || n > numItems) {
|
||||
console.warn(`Invalid n: ${n} for array of length ${numItems}`);
|
||||
return undefined;
|
||||
}
|
||||
return items[numItems - n];
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue