From 5817da709c99dd96b05f39eeb8a57fab38db999a Mon Sep 17 00:00:00 2001 From: N-Shar-ma Date: Mon, 25 Sep 2023 08:59:20 +0530 Subject: [PATCH] compose: Refactor and generalise functions for formatting. The logic for formatting code is generalised to make it reusable for multiple styles of formatting (added in the next commits). Co-authored-by: N-Shar-ma --- web/src/compose_ui.js | 68 ++++++++++++++++++++++-------------- web/tests/compose_ui.test.js | 67 +++++++++++++++++++---------------- 2 files changed, 77 insertions(+), 58 deletions(-) diff --git a/web/src/compose_ui.js b/web/src/compose_ui.js index a85c7b4cd1..ab5c1113aa 100644 --- a/web/src/compose_ui.js +++ b/web/src/compose_ui.js @@ -377,19 +377,18 @@ export function format_text($textarea, type, inserted_content) { range = $textarea.range(); const selected_text = range.text; - const is_selection_formatted = (syntax) => - // First check if there are enough characters before/after selection. - range.start >= syntax.length && - text.length - range.end >= syntax.length && - // And then if the characters have syntax around them. - text.slice(range.start - syntax.length, range.start) === syntax && - text.slice(range.end, range.end + syntax.length) === syntax; + // Check if the selection is already surrounded by syntax + const is_selection_formatted = (syntax_start, syntax_end = syntax_start) => + range.start >= syntax_start.length && + text.length - range.end >= syntax_end.length && + text.slice(range.start - syntax_start.length, range.start) === syntax_start && + text.slice(range.end, range.end + syntax_end.length) === syntax_end; - const is_inner_text_formatted = (syntax) => - // Check if selected text itself has bold_syntax inside it. - range.length > 4 && - selected_text.slice(0, syntax.length) === syntax && - selected_text.slice(-syntax.length) === syntax; + // Check if selected text itself has syntax inside it. + const is_inner_text_formatted = (syntax_start, syntax_end = syntax_start) => + range.length >= syntax_start.length + syntax_end.length && + selected_text.slice(0, syntax_start.length) === syntax_start && + selected_text.slice(-syntax_end.length) === syntax_end; const section_off_selected_lines = () => { // Divide all lines of text (separated by `\n`) into those entirely or @@ -505,31 +504,46 @@ export function format_text($textarea, type, inserted_content) { } }; - const format = (syntax) => { - // If the selection is already surrounded by syntax, - // remove it rather than adding another copy. - if (is_selection_formatted(syntax)) { - // Remove the syntax from text. + const format = (syntax_start, syntax_end = syntax_start) => { + let linebreak_start = ""; + let linebreak_end = ""; + if (syntax_start[0] === "\n") { + linebreak_start = "\n"; + } + if (syntax_end.at(-1) === "\n") { + linebreak_end = "\n"; + } + if (is_selection_formatted(syntax_start, syntax_end)) { text = - text.slice(0, range.start - syntax.length) + + text.slice(0, range.start - syntax_start.length) + + linebreak_start + text.slice(range.start, range.end) + - text.slice(range.end + syntax.length); + linebreak_end + + text.slice(range.end + syntax_end.length); set(field, text); - field.setSelectionRange(range.start - syntax.length, range.end - syntax.length); + field.setSelectionRange( + range.start - syntax_start.length, + range.end - syntax_start.length, + ); return; - } else if (is_inner_text_formatted(syntax)) { + } else if (is_inner_text_formatted(syntax_start, syntax_end)) { // Remove syntax inside the selection, if present. text = text.slice(0, range.start) + - text.slice(range.start + syntax.length, range.end - syntax.length) + + linebreak_start + + text.slice(range.start + syntax_start.length, range.end - syntax_end.length) + + linebreak_end + text.slice(range.end); set(field, text); - field.setSelectionRange(range.start, range.end - syntax.length * 2); + field.setSelectionRange( + range.start, + range.end - syntax_start.length - syntax_end.length, + ); return; } - - // Otherwise, we don't have syntax, so we add it. - wrapSelection(field, syntax); + + // Otherwise, we don't have syntax within or around, so we add it. + wrapSelection(field, syntax_start, syntax_end); }; switch (type) { @@ -555,7 +569,7 @@ export function format_text($textarea, type, inserted_content) { if (is_selection_formatted(bold_syntax)) { // If text has bold_syntax around it. if ( - range.start >= 3 && + range.start > bold_syntax.length && text.length - range.end >= bold_and_italic_syntax.length ) { // If text is both bold and italic. diff --git a/web/tests/compose_ui.test.js b/web/tests/compose_ui.test.js index 26932de68b..1d59c2c678 100644 --- a/web/tests/compose_ui.test.js +++ b/web/tests/compose_ui.test.js @@ -485,42 +485,45 @@ run_test("test_compose_height_changes", ({override, override_rewire}) => { assert.ok(!compose_box_top_set); }); -run_test("format_text", ({override}) => { - let set_text = ""; - let wrap_selection_called = false; - let wrap_syntax = ""; +let set_text = ""; +let wrap_selection_called = false; +let wrap_syntax_start = ""; +let wrap_syntax_end = ""; +function reset_state() { + set_text = ""; + wrap_selection_called = false; + wrap_syntax_start = ""; + wrap_syntax_end = ""; +} + +const $textarea = $("#compose-textarea"); +$textarea.get = () => ({ + setSelectionRange(start, end) { + $textarea.range = () => ({ + start, + end, + text: $textarea.val().slice(start, end), + length: end - start, + }); + }, +}); + +function init_textarea(val, range) { + $textarea.val = () => val; + $textarea.range = () => range; +} + +run_test("format_text - bold and italic", ({override}) => { override(text_field_edit, "set", (_field, text) => { set_text = text; }); - override(text_field_edit, "wrapSelection", (_field, syntax) => { + override(text_field_edit, "wrapSelection", (_field, syntax_start, syntax_end) => { wrap_selection_called = true; - wrap_syntax = syntax; + wrap_syntax_start = syntax_start; + wrap_syntax_end = syntax_end || syntax_start; }); - function reset_state() { - set_text = ""; - wrap_selection_called = false; - wrap_syntax = ""; - } - - const $textarea = $("#compose-textarea"); - $textarea.get = () => ({ - setSelectionRange(start, end) { - $textarea.range = () => ({ - start, - end, - text: $textarea.val().slice(start, end), - length: end - start, - }); - }, - }); - - function init_textarea(val, range) { - $textarea.val = () => val; - $textarea.range = () => range; - } - const italic_syntax = "*"; const bold_syntax = "**"; @@ -535,7 +538,8 @@ run_test("format_text", ({override}) => { compose_ui.format_text($textarea, "bold"); assert.equal(set_text, ""); assert.equal(wrap_selection_called, true); - assert.equal(wrap_syntax, bold_syntax); + assert.equal(wrap_syntax_start, bold_syntax); + assert.equal(wrap_syntax_end, bold_syntax); // Undo bold selected text, syntax not selected reset_state(); @@ -572,7 +576,8 @@ run_test("format_text", ({override}) => { compose_ui.format_text($textarea, "italic"); assert.equal(set_text, ""); assert.equal(wrap_selection_called, true); - assert.equal(wrap_syntax, italic_syntax); + assert.equal(wrap_syntax_start, italic_syntax); + assert.equal(wrap_syntax_end, italic_syntax); // Undo italic selected text, syntax not selected reset_state();