From a9efe8fd3a35bfd4dbe49c790c65942962fbeee1 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 1 Jan 2019 17:55:28 -0700 Subject: [PATCH] Add plaintext quote marker to blockquote lines in one pass --- test/js/composer-page-state-test.vala | 55 ----------------- ui/composer-web-view.js | 89 ++++++--------------------- 2 files changed, 19 insertions(+), 125 deletions(-) diff --git a/test/js/composer-page-state-test.vala b/test/js/composer-page-state-test.vala index 94611932..ad8098fd 100644 --- a/test/js/composer-page-state-test.vala +++ b/test/js/composer-page-state-test.vala @@ -23,8 +23,6 @@ class ComposerPageStateTest : ClientWebViewTestCase { add_test("get_text_with_nested_quote", get_text_with_nested_quote); add_test("contains_keywords", contains_keywords); - add_test("quote_lines", quote_lines); - add_test("resolve_nesting", resolve_nesting); add_test("replace_non_breaking_space", replace_non_breaking_space); try { @@ -271,59 +269,6 @@ unknown://example6.com } } - public void resolve_nesting() throws Error { - load_body_fixture(); - unichar q_marker = Geary.RFC822.Utils.QUOTE_MARKER; - unichar q_start = '‘'; - unichar q_end = '’'; - string js_no_quote = "foo"; - string js_spaced_quote = @"foo $(q_start)0$(q_end) bar"; - string js_leading_quote = @"$(q_start)0$(q_end) bar"; - string js_hanging_quote = @"foo $(q_start)0$(q_end)"; - string js_cosy_quote1 = @"foo$(q_start)0$(q_end)bar"; - string js_cosy_quote2 = @"foo$(q_start)0$(q_end)$(q_start)1$(q_end)bar"; - string js_values = "['quote1','quote2']"; - try { - assert(WebKitUtil.to_string(run_javascript(@"ComposerPageState.resolveNesting('$(js_no_quote)', $(js_values));")) == - @"foo"); - assert(WebKitUtil.to_string(run_javascript(@"ComposerPageState.resolveNesting('$(js_spaced_quote)', $(js_values));")) == - @"foo $(q_marker)quote1 bar"); - assert(WebKitUtil.to_string(run_javascript(@"ComposerPageState.resolveNesting('$(js_leading_quote)', $(js_values));")) == - @"$(q_marker)quote1 bar"); - assert(WebKitUtil.to_string(run_javascript(@"ComposerPageState.resolveNesting('$(js_hanging_quote)', $(js_values));")) == - @"foo $(q_marker)quote1"); - assert(WebKitUtil.to_string(run_javascript(@"ComposerPageState.resolveNesting('$(js_cosy_quote1)', $(js_values));")) == - @"foo$(q_marker)quote1bar"); - assert(WebKitUtil.to_string(run_javascript(@"ComposerPageState.resolveNesting('$(js_cosy_quote2)', $(js_values));")) == - @"foo$(q_marker)quote1$(q_marker)quote2bar"); - } catch (Geary.JS.Error err) { - print("Geary.JS.Error: %s\n", err.message); - assert_not_reached(); - } catch (Error err) { - print("WKError: %s\n", err.message); - assert_not_reached(); - } - } - - public void quote_lines() throws Error { - load_body_fixture(); - unichar q_marker = Geary.RFC822.Utils.QUOTE_MARKER; - try { - assert(WebKitUtil.to_string(run_javascript("ComposerPageState.quoteLines('');")) == - @"$(q_marker)"); - assert(WebKitUtil.to_string(run_javascript("ComposerPageState.quoteLines('line1');")) == - @"$(q_marker)line1"); - assert(WebKitUtil.to_string(run_javascript("ComposerPageState.quoteLines('line1\\nline2');")) == - @"$(q_marker)line1\n$(q_marker)line2"); - } catch (Geary.JS.Error err) { - print("Geary.JS.Error: %s\n", err.message); - assert_not_reached(); - } catch (Error err) { - print("WKError: %s\n", err.message); - assert_not_reached(); - } - } - public void replace_non_breaking_space() throws Error { load_body_fixture(); string single_nbsp = "a b"; diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js index 84bfa2d3..6425cb61 100644 --- a/ui/composer-web-view.js +++ b/ui/composer-web-view.js @@ -13,8 +13,6 @@ let ComposerPageState = function() { this.init.apply(this, arguments); }; ComposerPageState.KEYWORD_SPLIT_REGEX = /[\s]+/g; -ComposerPageState.QUOTE_START = "\x91"; // private use one -ComposerPageState.QUOTE_END = "\x92"; // private use two ComposerPageState.QUOTE_MARKER = "\x7f"; // delete ComposerPageState.PROTOCOL_REGEX = /^(aim|apt|bitcoin|cvs|ed2k|ftp|file|finger|git|gtalk|http|https|irc|ircs|irc6|lastfm|ldap|ldaps|magnet|news|nntp|rsync|sftp|skype|smb|sms|svn|telnet|tftp|ssh|webcal|xmpp):/i; // Taken from Geary.HTML.URL_REGEX, without the inline modifier (?x) @@ -330,7 +328,8 @@ ComposerPageState.prototype = { return parent.innerHTML; }, getText: function() { - return ComposerPageState.htmlToQuotedText(document.body); + let text = ComposerPageState.htmlToText(document.body); + return ComposerPageState.replaceNonBreakingSpace(text); }, setRichText: function(enabled) { if (enabled) { @@ -440,25 +439,6 @@ ComposerPageState.cleanPart = function(part, removeIfEmpty) { return part; }; -/** - * Convert a HTML DOM tree to plain text with delineated quotes. - * - * Lines are delinated using LF. Quoted lines are prefixed with - * `ComposerPageState.QUOTE_MARKER`, where the number of markers - * indicates the depth of nesting of the quote. - */ -ComposerPageState.htmlToQuotedText = function(root) { - let bqTexts = []; - - text = ComposerPageState.htmlToTextAndQuotes(root, bqTexts); - - // Reassemble plain text out of parts, and replace non-breaking - // space with regular space. - text = ComposerPageState.resolveNesting(text, bqTexts); - - return ComposerPageState.replaceNonBreakingSpace(text); -}; - /** * Gets plain text that adequately represents the information in the HTML * @@ -466,10 +446,11 @@ ComposerPageState.htmlToQuotedText = function(root) { * underscores around underlined text. Link URLs are inserted after the link * text. * - * Blockquotes are extracted and replaced with tokens deliminated with the - * characters QUOTE_START and QUOTE_END (from a unicode private use block). + * Each line of a blockquote is prefixed with + * `ComposerPageState.QUOTE_MARKER`, where the number of markers indicates + * the depth of nesting of the quote. */ -ComposerPageState.htmlToTextAndQuotes = function(root, bqTexts) { +ComposerPageState.htmlToText = function(root) { let parentStyle = window.getComputedStyle(root); let text = ""; @@ -509,37 +490,38 @@ ComposerPageState.htmlToTextAndQuotes = function(root, bqTexts) { if (node.textContent == node.href) { text += "<" + node.href + ">"; } else { - text += ComposerPageState.htmlToTextAndQuotes(node, bqTexts); + text += ComposerPageState.htmlToText(node); text += " <" + node.href + ">"; } break; case "b": case "strong": - text += "*" + ComposerPageState.htmlToTextAndQuotes(node, bqTexts) + "*"; + text += "*" + ComposerPageState.htmlToText(node) + "*"; break; case "blockquote": - let bqText = ComposerPageState.htmlToTextAndQuotes(node, bqTexts); - text += ( - ComposerPageState.QUOTE_START - + bqTexts.length.toString() - + ComposerPageState.QUOTE_END - ); - bqTexts.push(bqText); + let bqText = ComposerPageState.htmlToText(node); + // If there is a newline at the end of the quote, remove it + // After this switch we ensure that there is a newline after the quote + bqText = bqText.replace(/\n$/, ""); + let lines = bqText.split("\n"); + for (let i = 0; i < lines.length; i++) + lines[i] = ComposerPageState.QUOTE_MARKER + lines[i]; + text += lines.join("\n"); break; case "br": text += "\n"; break; case "i": case "em": - text += "/" + ComposerPageState.htmlToTextAndQuotes(node, bqTexts) + "/"; + text += "/" + ComposerPageState.htmlToText(node) + "/"; break; case "u": - text += "_" + ComposerPageState.htmlToTextAndQuotes(node, bqTexts) + "_"; + text += "_" + ComposerPageState.htmlToText(node) + "_"; break; case "#comment": break; default: - text += ComposerPageState.htmlToTextAndQuotes(node, bqTexts); + text += ComposerPageState.htmlToText(node); break; } if (isBlock) { @@ -607,39 +589,6 @@ ComposerPageState.linkify = function(node) { } }; -ComposerPageState.resolveNesting = function(text, values) { - let tokenregex = new RegExp( - ComposerPageState.QUOTE_START - + "([0-9]+)" - + ComposerPageState.QUOTE_END, "g" - ); - return text.replace(tokenregex, function(match, p1, offset, str) { - let key = new Number(p1); - - let value = ""; - if (key >= 0 && key < values.length) { - let nested = ComposerPageState.resolveNesting(values[key], values); - // If there is a newline at the end of the quote, remove it - // htmltoTextandQuotes already ensured that there is a newline after the quote - nested = nested.replace(/\n$/, ""); - value = ComposerPageState.quoteLines(nested); - } else { - console.error("Regex error in denesting blockquotes: Invalid key"); - } - return value; - }); -}; - -/** - * Prefixes each NL-delineated line with `ComposerPageState.QUOTE_MARKER`. - */ -ComposerPageState.quoteLines = function(text) { - let lines = text.split("\n"); - for (let i = 0; i < lines.length; i++) - lines[i] = ComposerPageState.QUOTE_MARKER + lines[i]; - return lines.join("\n"); -}; - /** * Converts all non-breaking space chars to plain spaces. */