Custom mode pipe delimiter: section count mode (#4105) albertying, miodec

* Add section input

* Restore code

* Cleaner approach

* Correct current index

* Remove import

* Call punctuation, numbers, funboxes on all words in section

* Plus n adds n words instead of n sections

* Undo

* Fix normal test

* Replace word with section when delimiter is checked

* Restore

* Set progress to current section / section total

* Fix memory funbox

* Fix randomcase

* Fix text generation when random is not enabled

* no word limit if in random section mode

* instead of using a for loop, using a while loop and stopping dynamically

* handling getting next section
tracking section history to avoid repeating sections

* test

* Revert "test"

This reverts commit 50d20fff3d.

* allowing infinite

* fixed words not being added after the first word generation

* not checking section index if infinite word

* throwing if word contains a space (should never happen)

* removed/reverted old approach to this feature

* using input history everytime for now until a better solution is found

* removed unused import

* exporting section index and current section

* code used by the previous approach, most likely will be deleted

* revered a change this pr makes (idk why this was deleted)

* missing comment

* removed comment

* added section progress tracking
word generator now returns a list of section indexes, which is stored in test words module
this array is then used to determine which section the current word belongs to inside timer progress

* better errors

* replacing multiple spaces with one
replacing starting and trailing spaces before splitting

* fixed pipe delmiter non random custom text not working

* extracted logic into constants to make the if statement easier to understand and expand in the future

---------

Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
Albert 2023-05-15 05:45:38 -04:00 committed by GitHub
parent 1c483760fa
commit 67daa1d409
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 213 additions and 42 deletions

View file

@ -74,8 +74,16 @@ $(`${popup} .delimiterCheck input`).on("change", () => {
let delimiter;
if ($(`${popup} .delimiterCheck input`).prop("checked")) {
delimiter = "|";
$(`${popup} .randomInputFields .sectioncount `).removeClass("hidden");
$(`${popup} .randomInputFields .wordcount input `).val("");
$(`${popup} .randomInputFields .wordcount `).addClass("hidden");
} else {
delimiter = " ";
$(`${popup} .randomInputFields .sectioncount input `).val("");
$(`${popup} .randomInputFields .sectioncount `).addClass("hidden");
$(`${popup} .randomInputFields .wordcount `).removeClass("hidden");
}
if (
$(`${popup} textarea`).val() != CustomText.text.join(CustomText.delimiter)
@ -113,10 +121,19 @@ export function hide(options = {} as HideOptions): void {
options.noAnim ? 0 : 125,
() => {
if (options.resetState) {
const newText = CustomText.text.map((word) => {
if (word[word.length - 1] == "|") {
word = word.slice(0, -1);
}
return word;
});
CustomText.setPopupTextareaState(
CustomText.text.join(CustomText.delimiter)
// CustomText.text.join(CustomText.delimiter)
newText.join(CustomText.delimiter)
);
}
$(wrapper).addClass("hidden");
Skeleton.remove(skeletonId);
}
@ -177,14 +194,22 @@ $(`${popup} textarea`).on("keypress", (e) => {
$(`${popup} .randomInputFields .wordcount input`).on("keypress", () => {
$(`${popup} .randomInputFields .time input`).val("");
$(`${popup} .randomInputFields .sectioncount input`).val("");
});
$(`${popup} .randomInputFields .time input`).on("keypress", () => {
$(`${popup} .randomInputFields .wordcount input`).val("");
$(`${popup} .randomInputFields .sectioncount input`).val("");
});
$(`${popup} .randomInputFields .sectioncount input`).on("keypress", () => {
$(`${popup} .randomInputFields .time input`).val("");
$(`${popup} .randomInputFields .wordcount input`).val("");
});
function apply(): void {
let text = ($(`${popup} textarea`).val() as string).normalize();
text = text.trim();
// text = text.replace(/[\r]/gm, " ");
text = text.replace(/\\\\t/gm, "\t");
@ -218,6 +243,7 @@ function apply(): void {
}
// text = Misc.remove_non_ascii(text);
text = text.replace(/[\u2060]/g, "");
CustomText.setText(text.split(CustomText.delimiter));
CustomText.setWord(
@ -227,6 +253,9 @@ function apply(): void {
parseInt(($(`${popup} .time input`).val() as string) || "-1")
);
CustomText.setSection(
parseInt(($(`${popup} .sectioncount input`).val() as string) || "-1")
);
CustomText.setIsWordRandom(
$(`${popup} .randomWordsCheckbox input`).prop("checked") &&
CustomText.word > -1
@ -235,11 +264,15 @@ function apply(): void {
$(`${popup} .randomWordsCheckbox input`).prop("checked") &&
CustomText.time > -1
);
CustomText.setIsSectionRandom(
$(`${popup} .randomWordsCheckbox input`).prop("checked") &&
CustomText.section > -1
);
if (
$(`${popup} .randomWordsCheckbox input`).prop("checked") &&
!CustomText.isTimeRandom &&
!CustomText.isWordRandom
!CustomText.isWordRandom &&
!CustomText.isSectionRandom
) {
Notifications.add(
"You need to specify word count or time in seconds to start a random custom test",

View file

@ -11,8 +11,10 @@ export let text = [
];
export let isWordRandom = false;
export let isTimeRandom = false;
export let isSectionRandom = false;
export let word = -1;
export let time = -1;
export let section = -1;
export let delimiter = " ";
export let popupTextareaState = "The quick brown fox jumps over the lazy dog";
@ -40,6 +42,10 @@ export function setIsTimeRandom(val: boolean): void {
isTimeRandom = val;
}
export function setIsSectionRandom(val: boolean): void {
isSectionRandom = val;
}
export function setTime(val: number): void {
time = val;
}
@ -48,6 +54,10 @@ export function setWord(val: number): void {
word = val;
}
export function setSection(val: number): void {
section = val;
}
export function setDelimiter(val: string): void {
delimiter = val;
}

View file

@ -349,7 +349,7 @@ export function restart(options = {} as RestartOptions): void {
toPush.push(TestWords.words.get(i));
}
TestWords.words.reset();
toPush.forEach((word) => TestWords.words.push(word));
toPush.forEach((word, index) => TestWords.words.push(word, index));
}
}
if (!options.withSameWordset && !shouldQuoteRepeat) {
@ -544,8 +544,11 @@ export async function init(): Promise<void> {
}
let generatedWords: string[];
let generatedSectionIndexes: number[];
try {
generatedWords = await WordsGenerator.generateWords(language);
const gen = await WordsGenerator.generateWords(language);
generatedWords = gen.words;
generatedSectionIndexes = gen.sectionIndexes;
} catch (e) {
console.error(e);
if (e instanceof WordsGenerator.WordGenError) {
@ -572,8 +575,8 @@ export async function init(): Promise<void> {
}
}
for (const word of generatedWords) {
TestWords.words.push(word);
for (let i = 0; i < generatedWords.length; i++) {
TestWords.words.push(generatedWords[i], generatedSectionIndexes[i]);
}
if (Config.keymapMode === "next" && Config.mode !== "zen") {
@ -586,6 +589,10 @@ export async function init(): Promise<void> {
TestUI.setLigatures(language.ligatures ?? false);
TestUI.showWords();
console.debug("Test initialized with words", generatedWords);
console.debug(
"Test initialized with section indexes",
generatedSectionIndexes
);
}
//add word during the test
@ -608,9 +615,16 @@ export async function addWord(): Promise<void> {
(Config.mode === "custom" &&
!CustomText.isWordRandom &&
!CustomText.isTimeRandom &&
!CustomText.isSectionRandom &&
TestWords.words.length >= CustomText.text.length) ||
(Config.mode === "quote" &&
TestWords.words.length >= (TestWords.randomQuote.textSplit?.length ?? 0))
TestWords.words.length >=
(TestWords.randomQuote.textSplit?.length ?? 0)) ||
(Config.mode === "custom" &&
CustomText.isSectionRandom &&
WordsGenerator.sectionIndex >= CustomText.section &&
WordsGenerator.currentSection.length === 0 &&
CustomText.section != 0)
) {
return;
}
@ -637,12 +651,13 @@ export async function addWord(): Promise<void> {
if (section === undefined) return;
let wordCount = 0;
for (const word of section.words) {
for (let i = 0; i < section.words.length; i++) {
const word = section.words[i];
if (wordCount >= Config.words && Config.mode == "words") {
break;
}
wordCount++;
TestWords.words.push(word);
TestWords.words.push(word, i);
TestUI.addWord(word);
}
}
@ -667,16 +682,8 @@ export async function addWord(): Promise<void> {
TestWords.words.get(TestWords.words.length - 2)
);
const split = randomWord.split(" ");
if (split.length > 1) {
split.forEach((word) => {
TestWords.words.push(word);
TestUI.addWord(word);
});
} else {
TestWords.words.push(randomWord);
TestUI.addWord(randomWord);
}
TestWords.words.push(randomWord.word, randomWord.sectionIndex);
TestUI.addWord(randomWord.word);
}
interface CompletedEvent extends MonkeyTypes.Result<MonkeyTypes.Mode> {
@ -1036,15 +1043,23 @@ export async function finish(difficultyFailed = false): Promise<void> {
(Config.mode === "custom" &&
!CustomText.isWordRandom &&
!CustomText.isTimeRandom &&
!CustomText.isSectionRandom &&
CustomText.text.length < 10) ||
(Config.mode === "custom" &&
CustomText.isWordRandom &&
!CustomText.isTimeRandom &&
!CustomText.isSectionRandom &&
CustomText.word < 10) ||
(Config.mode === "custom" &&
!CustomText.isWordRandom &&
!CustomText.isSectionRandom &&
CustomText.isTimeRandom &&
CustomText.time < 15) ||
(Config.mode === "custom" &&
!CustomText.isWordRandom &&
!CustomText.isTimeRandom &&
CustomText.isSectionRandom &&
TestWords.words.length < 10) ||
(Config.mode === "zen" && completedEvent.testDuration < 15)
) {
Notifications.add("Test invalid - too short", 0);

View file

@ -1,9 +1,12 @@
class Words {
public list: string[];
public sectionIndexList: number[];
public length: number;
public currentIndex: number;
constructor() {
this.list = [];
this.sectionIndexList = [];
this.length = 0;
this.currentIndex = 0;
}
@ -26,12 +29,15 @@ class Words {
getLast(): string {
return this.list[this.list.length - 1];
}
push(word: string): void {
push(word: string, sectionIndex: number): void {
this.list.push(word);
this.sectionIndexList.push(sectionIndex);
this.length = this.list.length;
}
reset(): void {
this.list = [];
this.sectionIndexList = [];
this.currentIndex = 0;
this.length = this.list.length;
}

View file

@ -94,6 +94,14 @@ const miniTimerNumberElement = document.querySelector(
"#miniTimerAndLiveWpm .time"
);
function getCurrentCount(): number {
if (Config.mode === "custom" && CustomText.isSectionRandom) {
return TestWords.words.sectionIndexList[TestWords.words.currentIndex] - 1;
} else {
return TestInput.input.history.length;
}
}
export function update(): void {
const time = Time.get();
if (
@ -144,6 +152,8 @@ export function update(): void {
if (Config.mode === "custom") {
if (CustomText.isWordRandom) {
outof = CustomText.word;
} else if (CustomText.isSectionRandom) {
outof = CustomText.section;
} else {
outof = CustomText.text.length;
}
@ -172,7 +182,7 @@ export function update(): void {
} else {
if (timerNumberElement !== null) {
timerNumberElement.innerHTML =
"<div>" + `${TestInput.input.history.length}/${outof}` + "</div>";
"<div>" + `${getCurrentCount()}/${outof}` + "</div>";
}
}
} else if (Config.timerStyle === "mini") {
@ -182,7 +192,7 @@ export function update(): void {
}
} else {
if (miniTimerNumberElement !== null) {
miniTimerNumberElement.innerHTML = `${TestInput.input.history.length}/${outof}`;
miniTimerNumberElement.innerHTML = `${getCurrentCount()}/${outof}`;
}
}
}

View file

@ -355,6 +355,9 @@ export function getWordsLimit(): number {
) {
limit = 100;
}
if (Config.mode === "custom" && CustomText.delimiter === "|") {
limit = 100;
}
//funboxes
if (funboxToPush) {
@ -367,6 +370,7 @@ export function getWordsLimit(): number {
}
if (
Config.mode === "custom" &&
!CustomText.isSectionRandom &&
!CustomText.isTimeRandom &&
CustomText.isWordRandom &&
CustomText.word !== 0 &&
@ -378,6 +382,7 @@ export function getWordsLimit(): number {
Config.mode === "custom" &&
!CustomText.isTimeRandom &&
!CustomText.isWordRandom &&
!CustomText.isSectionRandom &&
CustomText.text.length !== 0 &&
CustomText.text.length < limit
) {
@ -407,11 +412,21 @@ let currentQuote: string[] = [];
export async function generateWords(
language: MonkeyTypes.LanguageObject
): Promise<string[]> {
): Promise<{
words: string[];
sectionIndexes: number[];
}> {
currentQuote = [];
currentSection = [];
sectionIndex = 0;
const ret: string[] = [];
sectionHistory = [];
const ret: {
words: string[];
sectionIndexes: number[];
} = {
words: [],
sectionIndexes: [],
};
const limit = getWordsLimit();
let wordList = language.words;
@ -431,21 +446,53 @@ export async function generateWords(
) {
const funboxSection = await getFunboxSection(limit);
if (funboxSection.length > 0) {
return funboxSection;
const indexes = [];
for (let i = 0; i < funboxSection.length; i++) {
indexes.push(i);
}
return {
words: funboxSection,
sectionIndexes: indexes,
};
}
for (let i = 0; i < limit; i++) {
let stop = false;
let i = 0;
while (stop === false) {
const nextWord = await getNextWord(
wordset,
i,
language,
limit,
Misc.nthElementFromArray(ret, -1) ?? "",
Misc.nthElementFromArray(ret, -2) ?? ""
Misc.nthElementFromArray(ret.words, -1) ?? "",
Misc.nthElementFromArray(ret.words, -2) ?? ""
);
ret.push(nextWord);
ret.words.push(nextWord.word);
ret.sectionIndexes.push(nextWord.sectionIndex);
const randomSectionStop =
CustomText.isSectionRandom &&
CustomText.section !== 0 &&
sectionIndex >= CustomText.section;
const nonRandomSectionStop =
!CustomText.isSectionRandom &&
!CustomText.isTimeRandom &&
sectionIndex >= wordset.length;
const customModeStop =
Config.mode === "custom" &&
currentSection.length === 0 &&
CustomText.delimiter === "|" &&
(randomSectionStop || nonRandomSectionStop);
if (customModeStop || ret.words.length >= limit) {
stop = true;
}
i++;
}
}
sectionHistory = []; //free up a bit of memory? is that even a thing?
return ret;
}
@ -453,8 +500,17 @@ async function generateQuoteWords(
language: MonkeyTypes.LanguageObject,
wordset: Wordset.Wordset,
limit: number
): Promise<string[]> {
const ret: string[] = [];
): Promise<{
words: string[];
sectionIndexes: number[];
}> {
const ret: {
words: string[];
sectionIndexes: number[];
} = {
words: [],
sectionIndexes: [],
};
const languageToGet = language.name.startsWith("swiss_german")
? "german"
: language.name;
@ -528,16 +584,18 @@ async function generateQuoteWords(
i,
language,
limit,
Misc.nthElementFromArray(ret, -1) ?? "",
Misc.nthElementFromArray(ret, -2) ?? ""
Misc.nthElementFromArray(ret.words, -1) ?? "",
Misc.nthElementFromArray(ret.words, -2) ?? ""
);
ret.push(nextWord);
ret.words.push(nextWord.word);
ret.sectionIndexes.push(i);
}
return ret;
}
let sectionIndex = 0;
let currentSection: string[] = [];
export let sectionIndex = 0;
export let currentSection: string[] = [];
let sectionHistory: string[] = [];
//generate next word
export async function getNextWord(
@ -547,7 +605,10 @@ export async function getNextWord(
wordsBound: number,
previousWord: string,
previousWord2: string
): Promise<string> {
): Promise<{
word: string;
sectionIndex: number;
}> {
console.debug("Getting next word", {
wordset,
wordIndex,
@ -568,15 +629,32 @@ export async function getNextWord(
} else if (
Config.mode == "custom" &&
!CustomText.isWordRandom &&
!CustomText.isTimeRandom
!CustomText.isTimeRandom &&
!CustomText.isSectionRandom
) {
randomWord = CustomText.text[sectionIndex];
} else if (
Config.mode == "custom" &&
(CustomText.isWordRandom || CustomText.isTimeRandom) &&
(CustomText.isWordRandom ||
CustomText.isTimeRandom ||
CustomText.isSectionRandom) &&
(wordset.length < 4 || PractiseWords.before.mode !== null)
) {
randomWord = wordset.randomWord(funboxFrequency);
} else if (Config.mode === "custom" && CustomText.isSectionRandom) {
randomWord = wordset.randomWord(funboxFrequency);
const previousSection = Misc.nthElementFromArray(sectionHistory, -1);
const previousSection2 = Misc.nthElementFromArray(sectionHistory, -2);
let regenerationCount = 0;
while (
regenerationCount < 100 &&
(previousSection === randomWord || previousSection2 === randomWord)
) {
regenerationCount++;
randomWord = wordset.randomWord(funboxFrequency);
}
} else {
let regenarationCount = 0; //infinite loop emergency stop button
let firstAfterSplit = randomWord.split(" ")[0].toLowerCase();
@ -600,17 +678,28 @@ export async function getNextWord(
firstAfterSplit = randomWord.split(" ")[0];
}
}
randomWord = randomWord.replace(/ +/g, " ");
randomWord = randomWord.replace(/(^ )|( $)/g, "");
currentSection = [...randomWord.split(" ")];
sectionHistory.push(randomWord);
randomWord = currentSection.shift() as string;
sectionIndex++;
} else {
randomWord = currentSection.shift() as string;
}
if (!randomWord) {
if (randomWord === undefined) {
throw new WordGenError("Random word is undefined");
}
if (randomWord === "") {
throw new WordGenError("Random word is empty");
}
if (/ /g.test(randomWord)) {
throw new WordGenError("Random word contains spaces");
}
if (
Config.mode !== "custom" &&
Config.mode !== "quote" &&
@ -657,5 +746,8 @@ export async function getNextWord(
console.debug("Word:", randomWord);
return randomWord;
return {
word: randomWord,
sectionIndex: sectionIndex,
};
}

View file

@ -472,11 +472,16 @@
Words
<input type="number" value="" min="1" max="10000" />
</label>
<label class="sectioncount hidden">
Sections
<input type="number" value="" min="1" max="10000" />
</label>
<div style="color: var(--sub-color)">or</div>
<label class="time">
Time
<input type="number" value="" min="1" max="10000" />
</label>
<!-- <div style="color: var(--sub-color)">or</div> -->
</div>
<label class="checkbox typographyCheck">
<input type="checkbox" checked />