Skip to content
Merged
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -1,42 +1,89 @@
// Stable Diffusion WebUI - Bracket checker
// By Hingashi no Florin/Bwin4L & @akx
// Stable Diffusion WebUI - Bracket Checker
// By @Bwin4L, @akx, @w-e-w, @Haoming02
// Counts open and closed brackets (round, square, curly) in the prompt and negative prompt text boxes in the txt2img and img2img tabs.
// If there's a mismatch, the keyword counter turns red and if you hover on it, a tooltip tells you what's wrong.
// If there's a mismatch, the keyword counter turns red, and if you hover on it, a tooltip tells you what's wrong.

function checkBrackets(textArea, counterElt) {
const counts = {};
textArea.value.matchAll(/(?<!\\)(?:\\\\)*?([(){}[\]])/g).forEach(bracket => {
counts[bracket[1]] = (counts[bracket[1]] || 0) + 1;
});
const errors = [];
(function() {
Copy link
Collaborator

@w-e-w w-e-w Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functionally wise looks good to me
just one more question / issue

is there a particular reason that you decided to wrap this entire thing inside a function?

(function() {
    /*...*/
})();

if you did this because to avoid namespace collisions
considering that all these functions already existed for a long time in global space
there shouldn't be any collision issues
apart from only new variable added pairs
I would suggest you rename pairs to something more like promptBracketCheckerPairs
and leave the structure other as is

or is there some other considerations that makes you move this entire thing into a function?
I feel like it's advantageous to allow other scripts to have access to pairs / promptBracketChecker
this way for some reason an extension wishes to add some additional syntax they could

const pairs = [
['(', ')', 'round brackets'],
['[', ']', 'square brackets'],
['{', '}', 'curly brackets']
];

function checkBrackets(textArea, counterElem) {
const counts = {};
const errors = new Set();
let i = 0;

while (i < textArea.value.length) {
let char = textArea.value[i];
let escaped = false;
while (char === '\\' && i + 1 < textArea.value.length) {
escaped = !escaped;
i++;
char = textArea.value[i];
}

for (const [open, close, label] of pairs) {
const lb = escaped ? `escaped ${label}` : label;

function checkPair(open, close, kind) {
if (counts[open] !== counts[close]) {
errors.push(
`${open}...${close} - Detected ${counts[open] || 0} opening and ${counts[close] || 0} closing ${kind}.`
);
if (char === open) {
counts[lb] = (counts[lb] || 0) + 1;
} else if (char === close) {
counts[lb] = (counts[lb] || 0) - 1;
if (counts[lb] < 0) {
errors.add(`Incorrect order of ${lb}.`);
}
}
}

i++;
}

for (const [open, close, label] of pairs) {
if (counts[label] == undefined) {
continue;
}

if (counts[label] > 0) {
errors.add(`${open} ... ${close} - Detected ${counts[label]} more opening than closing ${label}.`);
} else if (counts[label] < 0) {
errors.add(`${open} ... ${close} - Detected ${-counts[label]} more closing than opening ${label}.`);
}
}

for (const [open, close, label] of pairs) {
const lb = `escaped ${label}`;
if (counts[lb] == undefined) {
continue;
}

const op = `\\${open}`;
const cl = `\\${close}`;
if (counts[lb] > 0) {
errors.add(`${op} ... ${cl} - Detected ${counts[lb]} more opening than closing ${lb}.`);
} else if (counts[lb] < 0) {
errors.add(`${op} ... ${cl} - Detected ${-counts[lb]} more closing than opening ${lb}.`);
}
}
}

checkPair('(', ')', 'round brackets');
checkPair('[', ']', 'square brackets');
checkPair('{', '}', 'curly brackets');
counterElt.title = errors.join('\n');
counterElt.classList.toggle('error', errors.length !== 0);
}
counterElem.title = [...errors].join('\n');
counterElem.classList.toggle('error', errors.size !== 0);
}

function setupBracketChecking(id_prompt, id_counter) {
var textarea = gradioApp().querySelector("#" + id_prompt + " > label > textarea");
var counter = gradioApp().getElementById(id_counter);
function setupBracketChecking(id_prompt, id_counter) {
const textarea = gradioApp().querySelector(`#${id_prompt} > label > textarea`);
const counter = gradioApp().getElementById(id_counter);

if (textarea && counter) {
textarea.addEventListener("input", () => checkBrackets(textarea, counter));
if (textarea && counter) {
onEdit(`${id_prompt}_BracketChecking`, textarea, 400, () => checkBrackets(textarea, counter));
}
}
}

onUiLoaded(function() {
setupBracketChecking('txt2img_prompt', 'txt2img_token_counter');
setupBracketChecking('txt2img_neg_prompt', 'txt2img_negative_token_counter');
setupBracketChecking('img2img_prompt', 'img2img_token_counter');
setupBracketChecking('img2img_neg_prompt', 'img2img_negative_token_counter');
});

onUiLoaded(function() {
setupBracketChecking('txt2img_prompt', 'txt2img_token_counter');
setupBracketChecking('txt2img_neg_prompt', 'txt2img_negative_token_counter');
setupBracketChecking('img2img_prompt', 'img2img_token_counter');
setupBracketChecking('img2img_neg_prompt', 'img2img_negative_token_counter');
});
})();
Loading