From 34750bccd8a6a7e1513b7b0d494df5a3ca7afbeb Mon Sep 17 00:00:00 2001 From: Tim Dawborn Date: Sat, 21 Jan 2017 11:08:27 +1100 Subject: [PATCH] New blocks text_count, text_replace, and text_reverse (#830) Includes generators for all languages and units tests on those generators. --- blocks/text.js | 87 ++++++ generators/dart/text.js | 50 +++ generators/javascript/text.js | 48 +++ generators/lua/text.js | 67 ++++ generators/php/text.js | 29 ++ generators/python/text.js | 27 ++ msg/json/en.json | 11 +- msg/json/qqq.json | 16 +- msg/messages.js | 24 ++ tests/generators/index.html | 3 + tests/generators/text.xml | 558 +++++++++++++++++++++++++++++++++- tests/playground.html | 24 ++ 12 files changed, 935 insertions(+), 9 deletions(-) diff --git a/blocks/text.js b/blocks/text.js index f3c8def6a..afe8f253b 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -689,3 +689,90 @@ Blockly.Blocks['text_prompt'] = { mutationToDom: Blockly.Blocks['text_prompt_ext'].mutationToDom, domToMutation: Blockly.Blocks['text_prompt_ext'].domToMutation }; + +Blockly.Blocks['text_count'] = { + /** + * Block for counting how many times one string appears within another string. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.TEXT_COUNT_MESSAGE0, + "args0": [ + { + "type": "input_value", + "name": "SUB", + "check": "String" + }, + { + "type": "input_value", + "name": "TEXT", + "check": "String" + } + ], + "output": "Number", + "inputsInline": true, + "colour": Blockly.Blocks.math.HUE, + "tooltip": Blockly.Msg.TEXT_COUNT_TOOLTIP, + "helpUrl": Blockly.Msg.TEXT_COUNT_HELPURL + }); + } +}; + +Blockly.Blocks['text_replace'] = { + /** + * Block for replacing one string with another in the text. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.TEXT_REPLACE_MESSAGE0, + "args0": [ + { + "type": "input_value", + "name": "FROM", + "check": "String" + }, + { + "type": "input_value", + "name": "TO", + "check": "String" + }, + { + "type": "input_value", + "name": "TEXT", + "check": "String" + }, + ], + "output": "String", + "inputsInline": true, + "colour": Blockly.Blocks.texts.HUE, + "tooltip": Blockly.Msg.TEXT_REPLACE_TOOLTIP, + "helpUrl": Blockly.Msg.TEXT_REPLACE_HELPURL + }); + } +}; + +Blockly.Blocks['text_reverse'] = { + /** + * Block for reversing a string. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.TEXT_REVERSE_MESSAGE0, + "args0": [ + { + "type": "input_value", + "name": "TEXT", + "check": "String" + }, + ], + "output": "String", + "inputsInline": true, + "colour": Blockly.Blocks.texts.HUE, + "tooltip": Blockly.Msg.TEXT_REVERSE_TOOLTIP, + "helpUrl": Blockly.Msg.TEXT_REVERSE_HELPURL + }); + } +}; diff --git a/generators/dart/text.js b/generators/dart/text.js index 47660d3bd..94c60f74b 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.js @@ -295,3 +295,53 @@ Blockly.Dart['text_prompt_ext'] = function(block) { }; Blockly.Dart['text_prompt'] = Blockly.Dart['text_prompt_ext']; + +Blockly.Dart['text_count'] = function(block) { + var text = Blockly.Dart.valueToCode(block, 'TEXT', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; + var sub = Blockly.Dart.valueToCode(block, 'SUB', + Blockly.Dart.ORDER_NONE) || '\'\''; + // Substring count is not a native Dart function. Define one. + var functionName = Blockly.Dart.provideFunction_( + 'text_count', + ['int ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(String haystack, String needle) {', + ' if (needle.length == 0) {', + ' return haystack.length + 1;', + ' }', + ' int index = 0;', + ' int count = 0;', + ' while (index != -1) {', + ' index = haystack.indexOf(needle, index);', + ' if (index != -1) {', + ' count++;', + ' index += needle.length;', + ' }', + ' }', + ' return count;', + '}']); + var code = functionName + '(' + text + ', ' + sub + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; +}; + +Blockly.Dart['text_replace'] = function(block) { + var text = Blockly.Dart.valueToCode(block, 'TEXT', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; + var from = Blockly.Dart.valueToCode(block, 'FROM', + Blockly.Dart.ORDER_NONE) || '\'\''; + var to = Blockly.Dart.valueToCode(block, 'TO', + Blockly.Dart.ORDER_NONE) || '\'\''; + var code = text + '.replaceAll(' + from + ', ' + to + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; +}; + +Blockly.Dart['text_reverse'] = function(block) { + // There isn't a sensible way to do this in Dart. See: + // http://stackoverflow.com/a/21613700/3529104 + // Implementing something is possibly better than not implementing anything? + var text = Blockly.Dart.valueToCode(block, 'TEXT', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; + var code = 'new String.fromCharCodes(' + text + '.runes.toList().reversed)'; + // XXX What should the operator precedence be for a `new`? + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; +}; diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 3a229dc00..9e2a375a2 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -302,3 +302,51 @@ Blockly.JavaScript['text_prompt_ext'] = function(block) { }; Blockly.JavaScript['text_prompt'] = Blockly.JavaScript['text_prompt_ext']; + +Blockly.JavaScript['text_count'] = function(block) { + var text = Blockly.JavaScript.valueToCode(block, 'TEXT', + Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + var sub = Blockly.JavaScript.valueToCode(block, 'SUB', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var functionName = Blockly.JavaScript.provideFunction_( + 'textCount', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(haystack, needle) {', + ' if (needle.length === 0) {', + ' return haystack.length + 1;', + ' } else {', + ' return haystack.split(needle).length - 1;', + ' }', + '}']); + var code = functionName + '(' + text + ', ' + sub + ')'; + return [code, Blockly.JavaScript.ORDER_SUBTRACTION]; +}; + +Blockly.JavaScript['text_replace'] = function(block) { + var text = Blockly.JavaScript.valueToCode(block, 'TEXT', + Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + var from = Blockly.JavaScript.valueToCode(block, 'FROM', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var to = Blockly.JavaScript.valueToCode(block, 'TO', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + // The regex escaping code below is taken from the implementation of + // goog.string.regExpEscape. + var functionName = Blockly.JavaScript.provideFunction_( + 'textReplace', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(haystack, needle, replacement) {', + ' needle = ' + + 'needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#", - "lastupdated": "2017-01-12 13:53:04.889198", + "lastupdated": "2017-01-18 10:58:30.631169", "locale": "en", "messagedocumentation" : "qqq" }, @@ -253,6 +253,15 @@ "TEXT_PROMPT_TYPE_NUMBER": "prompt for number with message", "TEXT_PROMPT_TOOLTIP_NUMBER": "Prompt for user for a number.", "TEXT_PROMPT_TOOLTIP_TEXT": "Prompt for user for some text.", + "TEXT_COUNT_MESSAGE0": "count %1 in %2", + "TEXT_COUNT_HELPURL": "", + "TEXT_COUNT_TOOLTIP": "Count how many times a string occurs in another string.", + "TEXT_REPLACE_MESSAGE0": "replace %1 with %2 in %3", + "TEXT_REPLACE_HELPURL": "", + "TEXT_REPLACE_TOOLTIP": "Replace a string within another string.", + "TEXT_REVERSE_MESSAGE0": "reverse %1", + "TEXT_REVERSE_HELPURL": "", + "TEXT_REVERSE_TOOLTIP": "Reverses the characters in a string.", "LISTS_CREATE_EMPTY_HELPURL": "https://github.com/google/blockly/wiki/Lists#create-empty-list", "LISTS_CREATE_EMPTY_TITLE": "create empty list", "LISTS_CREATE_EMPTY_TOOLTIP": "Returns a list, of length 0, containing no data records", diff --git a/msg/json/qqq.json b/msg/json/qqq.json index dbb69c7cb..d37228ff9 100644 --- a/msg/json/qqq.json +++ b/msg/json/qqq.json @@ -1,11 +1,4 @@ { - "@metadata": { - "authors": [ - "Espertus", - "Liuxinyu970226", - "Shirayuki" - ] - }, "VARIABLES_DEFAULT_NAME": "default name - A simple, general default name for a variable, preferably short. For more context, see [[Translating:Blockly#infrequent_message_types]].\n{{Identical|Item}}", "TODAY": "button text - Button that sets a calendar to today's date.\n{{Identical|Today}}", "DUPLICATE_BLOCK": "context menu - Make a copy of the selected block (and any blocks it contains).\n{{Identical|Duplicate}}", @@ -254,6 +247,15 @@ "TEXT_PROMPT_TYPE_NUMBER": "dropdown - Specifies that a number should be requested from the user with the following message. See [https://github.com/google/blockly/wiki/Text#printing-text https://github.com/google/blockly/wiki/Text#printing-text].", "TEXT_PROMPT_TOOLTIP_NUMBER": "dropdown - Precedes the message with which the user should be prompted for a number. See [https://github.com/google/blockly/wiki/Text#printing-text https://github.com/google/blockly/wiki/Text#printing-text].", "TEXT_PROMPT_TOOLTIP_TEXT": "dropdown - Precedes the message with which the user should be prompted for some text. See [https://github.com/google/blockly/wiki/Text#printing-text https://github.com/google/blockly/wiki/Text#printing-text].", + "TEXT_COUNT_MESSAGE0": "message0 interpolation string", + "TEXT_COUNT_HELPURL": "url - Information about counting how many times a string appears in another string.", + "TEXT_COUNT_TOOLTIP": "tooltip - See [https://github.com/google/blockly/wiki/Text#printing-text https://github.com/google/blockly/wiki/Text#printing-text].", + "TEXT_REPLACE_MESSAGE0": "message0 interpolation string", + "TEXT_REPLACE_HELPURL": "url - Information about replacing a string within another string.", + "TEXT_REPLACE_TOOLTIP": "tooltip - See [https://github.com/google/blockly/wiki/Text#printing-text https://github.com/google/blockly/wiki/Text#printing-text].", + "TEXT_REVERSE_MESSAGE0": "message0 interpolation string", + "TEXT_REVERSE_HELPURL": "url - Information about reversing a string.", + "TEXT_REVERSE_TOOLTIP": "tooltip - See [https://github.com/google/blockly/wiki/Text#printing-text https://github.com/google/blockly/wiki/Text#printing-text].", "LISTS_CREATE_EMPTY_HELPURL": "url - Information on empty lists.", "LISTS_CREATE_EMPTY_TITLE": "block text - See [https://github.com/google/blockly/wiki/Lists#create-empty-list https://github.com/google/blockly/wiki/Lists#create-empty-list].", "LISTS_CREATE_EMPTY_TOOLTIP": "block text - See [https://github.com/google/blockly/wiki/Lists#create-empty-list https://github.com/google/blockly/wiki/Lists#create-empty-list].", diff --git a/msg/messages.js b/msg/messages.js index 24fc1fa14..a11c62d6c 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -766,6 +766,30 @@ Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER = 'Prompt for user for a number.'; /// https://github.com/google/blockly/wiki/Text#printing-text]. Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT = 'Prompt for user for some text.'; +/// block text - Title of a block that counts the number of instances of +/// a smaller pattern (%1) inside a longer string (%2). +Blockly.Msg.TEXT_COUNT_MESSAGE0 = 'count %1 in %2'; +/// url - Information about counting how many times a string appears in another string. +Blockly.Msg.TEXT_COUNT_HELPURL = 'https://github.com/google/blockly/wiki/Text#counting-substrings'; +/// tooltip - Short description of a block that counts how many times some text occurs within some other text. +Blockly.Msg.TEXT_COUNT_TOOLTIP = 'Count how many times some text occurs within some other text.'; + +/// block text - Title of a block that returns a copy of text (%3) with all +/// instances of some smaller text (%1) replaced with other text (%2). +Blockly.Msg.TEXT_REPLACE_MESSAGE0 = 'replace %1 with %2 in %3'; +/// url - Information about replacing each copy text (or string, in computer lingo) with other text. +Blockly.Msg.TEXT_REPLACE_HELPURL = 'https://github.com/google/blockly/wiki/Text#replacing-substrings'; +/// tooltip - Short description of a block that replaces copies of text in a large text with other text. +Blockly.Msg.TEXT_REPLACE_TOOLTIP = 'Replace all occurances of some text within some other text.'; + +/// block text - Title of block that returns a copy of text (%1) with the order +/// of letters and characters reversed. +Blockly.Msg.TEXT_REVERSE_MESSAGE0 = 'reverse %1'; +/// url - Information about reversing a letters/characters in text. +Blockly.Msg.TEXT_REVERSE_HELPURL = 'https://github.com/google/blockly/wiki/Text#reversing-text'; +/// tooltip - See [https://github.com/google/blockly/wiki/Text]. +Blockly.Msg.TEXT_REVERSE_TOOLTIP = 'Reverses the order of the characters in the text.'; + // Lists Blocks. /// url - Information on empty lists. Blockly.Msg.LISTS_CREATE_EMPTY_HELPURL = 'https://github.com/google/blockly/wiki/Lists#create-empty-list'; diff --git a/tests/generators/index.html b/tests/generators/index.html index 3f905b698..af802f2a9 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -280,6 +280,9 @@ h1 { + + + diff --git a/tests/generators/text.xml b/tests/generators/text.xml index baf62d80f..32887ec93 100644 --- a/tests/generators/text.xml +++ b/tests/generators/text.xml @@ -1,5 +1,5 @@ - + @@ -36,6 +36,21 @@ + + + + + + + + + + + + + + + @@ -4091,4 +4106,545 @@ + + test count + Tests the "trim" block. + + + text + + + woolloomooloo + + + + + + + len 1 + + + + + + + o + + + + + text + + + + + + + 8 + + + + + + + len 2 + + + + + + + oo + + + + + text + + + + + + + 4 + + + + + + + len 3 + + + + + + + loo + + + + + text + + + + + + + 2 + + + + + + + start + + + + + + + wool + + + + + text + + + + + + + 1 + + + + + + + missing + + + + + + + chicken + + + + + text + + + + + + + 0 + + + + + + + empty needle + + + + + + + + + + + + text + + + + + + + 14 + + + + + + + empty source + + + + + + + chicken + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + test reverse + Tests the "trim" block. + + + + + empty string + + + + + + + + + + + + + + + + + + + + + len 1 + + + + + + + a + + + + + + + a + + + + + + + len 2 + + + + + + + ab + + + + + + + ba + + + + + + + longer + + + + + + + woolloomooloo + + + + + + + ooloomoolloow + + + + + + + + + + + + + test replace + Tests the "trim" block. + + + + + replace all instances 1 + + + + + + + oo + + + + + 123 + + + + + woolloomooloo + + + + + + + w123ll123m123l123 + + + + + + + literal string replacement + + + + + + + .oo + + + + + X + + + + + woolloomooloo + + + + + + + woolloomooloo + + + + + + + not found + + + + + + + abc + + + + + X + + + + + woolloomooloo + + + + + + + woolloomooloo + + + + + + + empty replacement 1 + + + + + + + o + + + + + + + + + + woolloomooloo + + + + + + + wllml + + + + + + + empty replacement 2 + + + + + + + aaaaa + + + + + + + + + + aaaaa + + + + + + + + + + + + + + empty replacement 3 + + + + + + + a + + + + + + + + + + aaaaa + + + + + + + + + + + + + + empty source + + + + + + + a + + + + + chicken + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/playground.html b/tests/playground.html index c728879e7..c05c25b81 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -591,6 +591,30 @@ h1 { + + + + + + + + + + + + + + + + + + + + + + + +