From fafd3d693d41e18d51e9da9dd227d8789df06197 Mon Sep 17 00:00:00 2001 From: Sullivan Ford Date: Wed, 12 Jan 2022 09:37:19 +0000 Subject: [PATCH 1/6] Add Robot syntax highlighting --- demo/kitchen-sink/docs/robot.robot | 209 ++++++++++++++++++++++++++ lib/ace/ext/modelist.js | 1 + lib/ace/mode/robot.js | 51 +++++++ lib/ace/mode/robot_highlight_rules.js | 157 +++++++++++++++++++ 4 files changed, 418 insertions(+) create mode 100644 demo/kitchen-sink/docs/robot.robot create mode 100644 lib/ace/mode/robot.js create mode 100644 lib/ace/mode/robot_highlight_rules.js diff --git a/demo/kitchen-sink/docs/robot.robot b/demo/kitchen-sink/docs/robot.robot new file mode 100644 index 00000000000..0fac30e212d --- /dev/null +++ b/demo/kitchen-sink/docs/robot.robot @@ -0,0 +1,209 @@ +*** Settings *** +Documentation Robot Framework 4 syntax recipes cheat sheet robot. +... Demonstrates Robot Framework syntax in a concise format. +Library MyLibrary +Library MyLibrary WITH NAME HelloLibrary +Library MyLibrary greeting=Howdy! WITH NAME HowdyLibrary +Resource keywords.robot +Suite Setup Log Suite Setup! +Suite Teardown Log Suite Teardown! +Test Setup Log Test Setup! +Test Teardown Log Test Teardown! +Test Timeout 2 minutes +Variables variables.py + +*** Variables *** +${STRING}= cat +${NUMBER}= ${1} +@{LIST}= one two three +&{DICTIONARY}= string=${STRING} number=${NUMBER} list=@{LIST} +${ENVIRONMENT_VARIABLE}= %{PATH=Default value} + +*** Keywords *** +A keyword without arguments + Log No arguments. + +A keyword with a required argument + [Arguments] ${argument} + Log Required argument: ${argument} + +A keyword with an optional argument + [Arguments] ${argument}=Default value + Log Optional argument: ${argument} + ... Another arg + +A keyword with any number of arguments + [Arguments] @{varargs} + Log Any number of arguments: @{varargs} + +A keyword with one or more arguments + [Arguments] ${argument} @{varargs} + Log One or more arguments: ${argument} @{varargs} + +A keyword that returns a value + [Return] Return value + +A keyword with documentation + [Documentation] This is keyword documentation. + No Operation + +*** Test Cases *** +Call keywords with a varying number of arguments + [Tags] Test Another Tag + A keyword without arguments + A keyword with a required argument Argument + A keyword with a required argument argument=Argument + A keyword with an optional argument + A keyword with an optional argument Argument + A keyword with an optional argument argument=Argument + A keyword with any number of arguments + A keyword with any number of arguments arg1 arg2 arg3 arg4 arg5 + A keyword with one or more arguments arg1 + A keyword with one or more arguments arg1 arg2 arg3 + +Call a keyword that returns a value + ${value}= A keyword that returns a value + Log ${value} # Return value + +Do conditional IF - ELSE IF - ELSE execution + IF ${NUMBER} > 1 + Log Greater than one. + ELSE IF "${STRING}" == "dog" + Log It's a dog! + ELSE + Log Probably a cat. 🤔 + END + +Loop a list + Log ${LIST} # ['one', 'two', 'three'] + FOR ${item} IN @{LIST} + Log ${item} # one, two, three + END + FOR ${item} IN one two three + Log ${item} # one, two, three + END + +Loop a dictionary + Log ${DICTIONARY} + # {'string': 'cat', 'number': 1, 'list': ['one', 'two', 'three']} + FOR ${key_value_tuple} IN &{DICTIONARY} + Log ${key_value_tuple} + # ('string', 'cat'), ('number', 1), ('list', ['one', 'two', 'three']) + END + FOR ${key} IN @{DICTIONARY} + Log ${key}=${DICTIONARY}[${key}] + # string=cat, number=1, list=['one', 'two', 'three'] + END + +Loop a range from 0 to end index + FOR ${index} IN RANGE 10 + Log ${index} # 0-9 + END + +Loop a range from start to end index + FOR ${index} IN RANGE 1 10 + Log ${index} # 1-9 + END + +Loop a range from start to end index with steps + FOR ${index} IN RANGE 0 10 2 + Log ${index} # 0, 2, 4, 6, 8 + END + +Nest loops + @{alphabets}= Create List a b c + Log ${alphabets} # ['a', 'b', 'c'] + @{numbers}= Create List ${1} ${2} ${3} + Log ${numbers} # [1, 2, 3] + FOR ${alphabet} IN @{alphabets} + FOR ${number} IN @{numbers} + Log ${alphabet}${number} + # a1, a2, a3, b1, b2, b3, c1, c2, c3 + END + END + +Exit a loop on condition + FOR ${i} IN RANGE 5 + Exit For Loop If ${i} == 2 + Log ${i} # 0, 1 + END + +Continue a loop from the next iteration on condition + FOR ${i} IN RANGE 3 + Continue For Loop If ${i} == 1 + Log ${i} # 0, 2 + END + +Create a scalar variable + ${animal}= Set Variable dog + Log ${animal} # dog + Log ${animal}[0] # d + Log ${animal}[-1] # g + +Create a number variable + ${π}= Set Variable ${3.14} + Log ${π} # 3.14 + +Create a list variable + @{animals}= Create List dog cat bear + Log ${animals} # ['dog', 'cat', 'bear'] + Log ${animals}[0] # dog + Log ${animals}[-1] # bear + +Create a dictionary variable + &{dictionary}= Create Dictionary key1=value1 key2=value2 + Log ${dictionary} # {'key1': 'value1', 'key2': 'value2'} + Log ${dictionary}[key1] # value1 + Log ${dictionary.key2} # value2 + +Access the items in a sequence (list, string) + ${string}= Set Variable Hello world! + Log ${string}[0] # H + Log ${string}[:5] # Hello + Log ${string}[6:] # world! + Log ${string}[-1] # ! + @{list}= Create List one two three four five + Log ${list} # ['one', 'two', 'three', 'four', 'five'] + Log ${list}[0:6:2] # ['one', 'three', 'five'] + +Call a custom Python library + ${greeting}= MyLibrary.Get Greeting + Log ${greeting} # Hello! + ${greeting}= HelloLibrary.Get Greeting + Log ${greeting} # Hello! + ${greeting}= HowdyLibrary.Get Greeting + Log ${greeting} # Howdy! + +Call a keyword from a separate resource file + My keyword in a separate resource file + +Access a variable in a separate variable file + Log ${MY_VARIABLE_FROM_A_SEPARATE_VARIABLE_FILE} + +Split arguments to multiple lines + A keyword with any number of arguments + ... arg1 + ... arg2 + ... arg3 + +Log available variables + Log Variables + # ${/} = / + # &{DICTIONARY} = { string=cat | number=1 | list=['one', 'two', 'three'] } + # ${OUTPUT_DIR} = /Users//... + # ... + +Evaluate Python expressions + ${path}= Evaluate os.environ.get("PATH") + ${path}= Set Variable ${{os.environ.get("PATH")}} + +Use special variables + Log ${EMPTY} # Like the ${SPACE}, but without the space. + Log ${False} # Boolean False. + Log ${None} # Python None + Log ${null} # Java null. + Log ${SPACE} # ASCII space (\x20). + Log ${SPACE * 4} # Four spaces. + Log "${SPACE}" # Quoted space (" "). + Log ${True} # Boolean True. + LOG ${CURDIR} # Current directory diff --git a/lib/ace/ext/modelist.js b/lib/ace/ext/modelist.js index 296919256f4..63f8f6dd229 100644 --- a/lib/ace/ext/modelist.js +++ b/lib/ace/ext/modelist.js @@ -168,6 +168,7 @@ var supportedModes = { RDoc: ["Rd"], Red: ["red|reds"], RHTML: ["Rhtml"], + Robot: ["robot|resource"], RST: ["rst"], Ruby: ["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"], Rust: ["rs"], diff --git a/lib/ace/mode/robot.js b/lib/ace/mode/robot.js new file mode 100644 index 00000000000..5cb53cd4352 --- /dev/null +++ b/lib/ace/mode/robot.js @@ -0,0 +1,51 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextMode = require("./text").Mode; +var RobotHighlightRules = require("./robot_highlight_rules").RobotHighlightRules; +var FoldMode = require("./folding/pythonic").FoldMode; + +var Mode = function() { + this.HighlightRules = RobotHighlightRules; + this.foldingRules = new FoldMode(); +}; +oop.inherits(Mode, TextMode); + +(function() { + this.lineCommentStart = "#"; + this.$id = "ace/mode/robot"; +}).call(Mode.prototype); + +exports.Mode = Mode; +}); diff --git a/lib/ace/mode/robot_highlight_rules.js b/lib/ace/mode/robot_highlight_rules.js new file mode 100644 index 00000000000..090e98c0ff9 --- /dev/null +++ b/lib/ace/mode/robot_highlight_rules.js @@ -0,0 +1,157 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2012, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +'use strict'; + +var oop = require("../lib/oop"); +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + +var RobotHighlightRules = function() { + var builtinConstantsRegex = new RegExp( + /\$\{CURDIR\}|\$\{TEMPDIR\}|\$\{EXECDIR\}|\$\{\/\}|\$\{\:\}|\$\{\\n\}|\$\{true\}|\$\{false\}|\$\{none\}|\$\{null\}|\$\{space(?:\s*\*\s+[0-9]+)?\}|\$\{empty\}|&\{empty\}|@\{empty\}|\$\{TEST NAME\}|@\{TEST[\s_]TAGS\}|\$\{TEST[\s_]DOCUMENTATION\}|\$\{TEST[\s_]STATUS\}|\$\{TEST[\s_]MESSAGE\}|\$\{PREV[\s_]TEST[\s_]NAME\}|\$\{PREV[\s_]TEST[\s_]STATUS\}|\$\{PREV[\s_]TEST[\s_]MESSAGE\}|\$\{SUITE[\s_]NAME\}|\$\{SUITE[\s_]SOURCE\}|\$\{SUITE[\s_]DOCUMENTATION\}|&\{SUITE[\s_]METADATA\}|\$\{SUITE[\s_]STATUS\}|\$\{SUITE[\s_]MESSAGE\}|\$\{KEYWORD[\s_]STATUS\}|\$\{KEYWORD[\s_]MESSAGE\}|\$\{LOG[\s_]LEVEL\}|\$\{OUTPUT[\s_]FILE\}|\$\{LOG[\s_]FILE\}|\$\{REPORT[\s_]FILE\}|\$\{DEBUG[\s_]FILE\}|\$\{OUTPUT[\s_]DIR\}/ + ); + + this.$rules = { + "start" : [ { + token: "string.robot.header", + regex: /^\*{3}\s+(?:settings?|metadata|(?:user )?keywords?|test ?cases?|tasks?|variables?)/, + caseInsensitive: true, + push: [{ + token: "string.robot.header", + regex: /$/, + next: "pop" + }, { + defaultToken: "string.robot.header" + }], + comment: "start of a table" + }, { + token: "comment.robot", + regex: /(?:^| {2,}| |\| {1,})(? Date: Tue, 18 Jan 2022 09:23:34 +0000 Subject: [PATCH 2/6] Remove lookbehinds in highlighter --- lib/ace/mode/robot_highlight_rules.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/ace/mode/robot_highlight_rules.js b/lib/ace/mode/robot_highlight_rules.js index 090e98c0ff9..0059de27210 100644 --- a/lib/ace/mode/robot_highlight_rules.js +++ b/lib/ace/mode/robot_highlight_rules.js @@ -54,7 +54,7 @@ var RobotHighlightRules = function() { comment: "start of a table" }, { token: "comment.robot", - regex: /(?:^| {2,}| |\| {1,})(? Date: Tue, 18 Jan 2022 10:10:37 +0000 Subject: [PATCH 3/6] Add some snippets --- lib/ace/mode/robot.js | 2 ++ lib/ace/snippets/robot.js | 7 +++++++ lib/ace/snippets/robot.snippets | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 lib/ace/snippets/robot.js create mode 100644 lib/ace/snippets/robot.snippets diff --git a/lib/ace/mode/robot.js b/lib/ace/mode/robot.js index 5cb53cd4352..b5a54e7edde 100644 --- a/lib/ace/mode/robot.js +++ b/lib/ace/mode/robot.js @@ -39,12 +39,14 @@ var FoldMode = require("./folding/pythonic").FoldMode; var Mode = function() { this.HighlightRules = RobotHighlightRules; this.foldingRules = new FoldMode(); + this.$behaviour = this.$defaultBehaviour; }; oop.inherits(Mode, TextMode); (function() { this.lineCommentStart = "#"; this.$id = "ace/mode/robot"; + this.snippetFileId = "ace/snippets/robot"; }).call(Mode.prototype); exports.Mode = Mode; diff --git a/lib/ace/snippets/robot.js b/lib/ace/snippets/robot.js new file mode 100644 index 00000000000..0a4de68756b --- /dev/null +++ b/lib/ace/snippets/robot.js @@ -0,0 +1,7 @@ +define(function(require, exports, module) { +"use strict"; + +exports.snippetText = require("../requirejs/text!./robot.snippets"); +exports.scope = "robot"; + +}); diff --git a/lib/ace/snippets/robot.snippets b/lib/ace/snippets/robot.snippets new file mode 100644 index 00000000000..fe5c23a7404 --- /dev/null +++ b/lib/ace/snippets/robot.snippets @@ -0,0 +1,29 @@ +snippet settings +description Settings section +scope robot + *** Settings *** + ${1:Library} ${2} +snippet keywords +description Keywords section +scope robot + *** Keywords *** +snippet for +description For Loop +scope robot + FOR ${1:\$\{item\}} IN RANGE ${2:10} + Log ${1:\$\{item\}} + END +snippet if +description If Statement +scope robot + IF ${1:condition} + ${2:Do something} + END +snippet elif +description Else-If Statement +scope robot + IF ${1:condition} + ${2:Do something} + ELSE IF ${3:condition} + ${4:Do something else} + END From e91705e41cb32deab6d827386b1c3905d4e007c2 Mon Sep 17 00:00:00 2001 From: Sullivan Ford Date: Thu, 5 May 2022 12:06:37 +0100 Subject: [PATCH 4/6] Fix indentation and add extra snippets --- lib/ace/snippets/robot.snippets | 90 ++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/lib/ace/snippets/robot.snippets b/lib/ace/snippets/robot.snippets index fe5c23a7404..2cb13946ec1 100644 --- a/lib/ace/snippets/robot.snippets +++ b/lib/ace/snippets/robot.snippets @@ -1,29 +1,69 @@ -snippet settings -description Settings section -scope robot - *** Settings *** - ${1:Library} ${2} -snippet keywords -description Keywords section -scope robot - *** Keywords *** -snippet for -description For Loop -scope robot - FOR ${1:\$\{item\}} IN RANGE ${2:10} - Log ${1:\$\{item\}} - END +# scope: robot +### Sections +snippet settingssection +description *** Settings *** section + *** Settings *** + Library ${1} + +snippet keywordssection +description *** Keywords *** section + *** Keywords *** + ${1:Keyword Name} + [Arguments] \${${2:Example Arg 1}} + +snippet testcasessection +description *** Test Cases *** section + *** Test Cases *** + ${1:First Test Case} + ${2:Log Example Arg} + +snippet variablessection +description *** Variables *** section + *** Variables *** + \${${1:Variable Name}}= ${2:Variable Value} + +### Helpful keywords +snippet testcase +description A test case + ${1:Test Case Name} + ${2:Log Example log message} + +snippet keyword +description A keyword + ${1:Example Keyword} + [Arguments] \${${2:Example Arg 1}} + +### Built Ins +snippet forinr +description For In Range Loop + FOR \${${1:Index}} IN RANGE \${${2:10}} + Log \${${1:Index}} + END + +snippet forin +description For In Loop + FOR \${${1:Item}} IN @{${2:List Variable}} + Log \${${1:Item}} + END + snippet if description If Statement -scope robot - IF ${1:condition} - ${2:Do something} - END + IF ${1:condition} + ${2:Do something} + END + +snippet else +description If Statement + IF ${1:Condition} + ${2:Do something} + ELSE + ${3:Otherwise do this} + END + snippet elif description Else-If Statement -scope robot - IF ${1:condition} - ${2:Do something} - ELSE IF ${3:condition} - ${4:Do something else} - END + IF ${1:Condition 1} + ${2:Do something} + ELSE IF ${3:Condition 2} + ${4:Do something else} + END From 461c020561064cc18bce044aea407c927c976d84 Mon Sep 17 00:00:00 2001 From: Sullivan Ford Date: Thu, 5 May 2022 12:16:37 +0100 Subject: [PATCH 5/6] Fix keywords highlighting in the middle of words --- lib/ace/mode/robot_highlight_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mode/robot_highlight_rules.js b/lib/ace/mode/robot_highlight_rules.js index 0059de27210..785d913601e 100644 --- a/lib/ace/mode/robot_highlight_rules.js +++ b/lib/ace/mode/robot_highlight_rules.js @@ -127,7 +127,7 @@ var RobotHighlightRules = function() { regex: /\b[0-9]+(?:\.[0-9]+)?\b/ }, { token: "keyword", - regex: /\s{2,}(for|in range|in|end|else if|if|else|with name)/, + regex: /\s{2,}(for|in range|in|end|else if|if|else|with name)(\s{2,}|$)/, caseInsensitive: true }, { token: "storage.type.function", From 843c017298270f401060486b82644eba3561a5c2 Mon Sep 17 00:00:00 2001 From: Sullivan Ford Date: Thu, 5 May 2022 16:50:48 +0100 Subject: [PATCH 6/6] Remove unnecessary escape chars --- lib/ace/mode/robot_highlight_rules.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/mode/robot_highlight_rules.js b/lib/ace/mode/robot_highlight_rules.js index 785d913601e..ed274de7dec 100644 --- a/lib/ace/mode/robot_highlight_rules.js +++ b/lib/ace/mode/robot_highlight_rules.js @@ -114,7 +114,7 @@ var RobotHighlightRules = function() { }] }, { token: "keyword.control.robot", - regex: /^[^\s\t\*$\|]+|(?=^\|)\s+[^\s\t\*$\|]+/, + regex: /^[^\s\t*$|]+|(?=^\|)\s+[^\s\t*$|]+/, push: [{ token: "keyword.control.robot", regex: /(?=\s{2})|\t|$|\s+(?=\|)/, @@ -131,7 +131,7 @@ var RobotHighlightRules = function() { caseInsensitive: true }, { token: "storage.type.function", - regex: /^(?:\s{2,}\s+)[^ \t\*$@&%\[.\|]+/, + regex: /^(?:\s{2,}\s+)[^ \t*$@&%[.|]+/, push: [{ token: "storage.type.function", regex: /(?=\s{2})|\t|$|\s+(?=\|)/,