diff --git a/web/src/compose_ui.js b/web/src/compose_ui.js
index eebb209ac8..d4d03b1358 100644
--- a/web/src/compose_ui.js
+++ b/web/src/compose_ui.js
@@ -702,6 +702,30 @@ export function format_text($textarea, type, inserted_content) {
$textarea.caret(range.end + `[](${inserted_content})`.length);
break;
}
+ case "latex": {
+ const inline_latex_syntax = "$$";
+ let block_latex_syntax_start = "```math\n";
+ let block_latex_syntax_end = "\n```";
+ // If there is no text selected or the selected text is either multiline or
+ // already using multiline math syntax, we use multiline math syntax.
+ if (
+ selected_text === "" ||
+ selected_text.includes("\n") ||
+ is_selection_formatted(block_latex_syntax_start, block_latex_syntax_end)
+ ) {
+ // Add newlines before and after, if not already present.
+ if (range.start > 0 && text[range.start - 1] !== "\n") {
+ block_latex_syntax_start = "\n" + block_latex_syntax_start;
+ }
+ if (range.end < text.length && text[range.end] !== "\n") {
+ block_latex_syntax_end = block_latex_syntax_end + "\n";
+ }
+ format(block_latex_syntax_start, block_latex_syntax_end);
+ } else {
+ format(inline_latex_syntax);
+ }
+ break;
+ }
}
}
diff --git a/web/templates/popovers/compose_control_buttons/compose_control_buttons_in_popover.hbs b/web/templates/popovers/compose_control_buttons/compose_control_buttons_in_popover.hbs
index d6ad0562e0..cfc35d7729 100644
--- a/web/templates/popovers/compose_control_buttons/compose_control_buttons_in_popover.hbs
+++ b/web/templates/popovers/compose_control_buttons/compose_control_buttons_in_popover.hbs
@@ -7,5 +7,7 @@
|
+
+ |
diff --git a/web/tests/compose_ui.test.js b/web/tests/compose_ui.test.js
index 3caeae99e0..24af865775 100644
--- a/web/tests/compose_ui.test.js
+++ b/web/tests/compose_ui.test.js
@@ -810,6 +810,141 @@ run_test("format_text - strikethrough", ({override}) => {
assert.equal(set_text, "abc");
});
+run_test("format_text - latex", ({override}) => {
+ override(text_field_edit, "set", (_field, text) => {
+ set_text = text;
+ });
+ override(text_field_edit, "wrapSelection", (_field, syntax_start, syntax_end) => {
+ wrap_selection_called = true;
+ wrap_syntax_start = syntax_start;
+ wrap_syntax_end = syntax_end;
+ });
+
+ const latex_syntax = "$$";
+ const block_latex_syntax_start = "```math\n";
+ const block_latex_syntax_end = "\n```";
+
+ // Latex selected text
+ reset_state();
+ init_textarea("abc", {
+ start: 0,
+ end: 3,
+ text: "abc",
+ length: 3,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "");
+ assert.equal(wrap_selection_called, true);
+ assert.equal(wrap_syntax_start, latex_syntax);
+ assert.equal(wrap_syntax_end, latex_syntax);
+
+ reset_state();
+ init_textarea("Before\nBefore this should\nbe math After\nAfter", {
+ start: 14,
+ end: 33,
+ text: "this should\nbe math",
+ length: 19,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "");
+ assert.equal(wrap_selection_called, true);
+ assert.equal(wrap_syntax_start, "\n" + block_latex_syntax_start);
+ assert.equal(wrap_syntax_end, block_latex_syntax_end + "\n");
+
+ reset_state();
+ init_textarea("abc\ndef", {
+ start: 0,
+ end: 7,
+ text: "abc\ndef",
+ length: 7,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "");
+ assert.equal(wrap_selection_called, true);
+ assert.equal(wrap_syntax_start, block_latex_syntax_start);
+ assert.equal(wrap_syntax_end, block_latex_syntax_end);
+
+ // No text selected
+ reset_state();
+ init_textarea("", {
+ start: 0,
+ end: 0,
+ text: "",
+ length: 0,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "");
+ assert.equal(wrap_selection_called, true);
+
+ // Undo latex selected text, syntax not selected
+ reset_state();
+ init_textarea("$$abc$$", {
+ start: 2,
+ end: 5,
+ text: "abc",
+ length: 3,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "abc");
+ assert.equal(wrap_selection_called, false);
+
+ reset_state();
+ init_textarea("```math\nabc\ndef\n```", {
+ start: 8,
+ end: 15,
+ text: "abc\ndef",
+ length: 7,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "abc\ndef");
+ assert.equal(wrap_selection_called, false);
+
+ reset_state();
+ init_textarea("abc\n```math\nde\nf\n```\nghi", {
+ start: 12,
+ end: 16,
+ text: "de\nf",
+ length: 4,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "abc\nde\nf\nghi");
+ assert.equal(wrap_selection_called, false);
+
+ // Undo latex selected text, syntax selected
+ reset_state();
+ init_textarea("$$abc$$", {
+ start: 0,
+ end: 7,
+ text: "$$abc$$",
+ length: 7,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "abc");
+ assert.equal(wrap_selection_called, false);
+
+ reset_state();
+ init_textarea("```math\nabc\ndef\n```", {
+ start: 0,
+ end: 19,
+ text: "```math\nabc\ndef\n```",
+ length: 19,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "abc\ndef");
+ assert.equal(wrap_selection_called, false);
+
+ reset_state();
+ init_textarea("abc\n```math\nde\nf\n```\nghi", {
+ start: 4,
+ end: 20,
+ text: "```math\nde\nf\n```",
+ length: 16,
+ });
+ compose_ui.format_text($textarea, "latex");
+ assert.equal(set_text, "abc\nde\nf\nghi");
+ assert.equal(wrap_selection_called, false);
+});
+
run_test("markdown_shortcuts", ({override_rewire}) => {
let format_text_type;
override_rewire(compose_ui, "format_text", (_$textarea, type) => {