Skip to content

Commit

Permalink
Merge pull request #2921 from HelioGuilherme66/localized_bdd
Browse files Browse the repository at this point in the history
Localized bdd improvements
  • Loading branch information
HelioGuilherme66 authored Feb 16, 2025
2 parents a4de481 + 1a4184a commit 46aacd2
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 75 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni
== https://github.com/robotframework/RIDE[Unreleased]

=== Added
- Added syntax colorization for the ``GROUP`` marker. Improved colorization for multiple Gherkin words, for example in the French language.
- Added syntax colorization for the ``GROUP`` marker.

=== Changed
- Improved the recognition of BDD/Gherkin prefixes when localized in autocomplete on Grid Editor.
- Improved colorization for multiple Gherkin words, for example in the French language.

=== Fixed

Expand Down
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Likewise, the current version of wxPython, is 4.2.2, but RIDE is known to work w

`pip install -U robotframework-ride`

(3.8 <= python <= 3.13) Install current development version (**2.2dev6**) with:
(3.8 <= python <= 3.13) Install current development version (**2.2dev7**) with:

`pip install -U https://github.com/robotframework/RIDE/archive/develop.zip`

Expand Down
77 changes: 37 additions & 40 deletions src/robotide/application/CHANGELOG.html

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Improved the recognition of BDD/Gherkin prefixes when localized in autocomplete on Grid Editor.</li>
<li>Added syntax colorization for the <em>GROUP</em> marker. Improved colorization for multiple Gherkin words, for
example in the French language.</li>
<li>Fixed multiple scroll bars in Grid Editor when editing Test Cases or Keywords. This caused bad navigation on cells.</li>
Expand Down Expand Up @@ -227,7 +228,7 @@ def set_content(self, html_win, content):
<pre class="literal-block">python -m robotide.postinstall -install</pre>
<p>or</p>
<pre class="literal-block">ride_postinstall.py -install</pre>
<p>RIDE {VERSION} was released on 14/February/2025.</p>
<p>RIDE {VERSION} was released on 16/February/2025.</p>
<!-- <br/>
<h3>May The Fourth Be With You!</h3>
<h3>Celebrate the bank holiday, 10th June, Day of Portugal, Portuguese Communities and Camões!!</h3>
Expand Down
25 changes: 20 additions & 5 deletions src/robotide/controller/ctrlcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
from ..utils import variablematcher


BDD_ENGLISH = 'Given|When|Then|And|But'

def obtain_bdd_prefixes(language):
from robotide.lib.compat.parsing.language import Language
lang = Language.from_name(language[0] if isinstance(language, list) else language)
bdd_prefixes = [f"{x}|" for x in lang.bdd_prefixes]
bdd_prefixes = "".join(bdd_prefixes).strip('|')
return bdd_prefixes


class Occurrence(object):

def __init__(self, item, value):
Expand Down Expand Up @@ -279,17 +289,22 @@ class NullObserver(object):


class RenameKeywordOccurrences(_ReversibleCommand):
# TODO: Consider localization language Gherkin
_gherkin_prefix = re.compile('^(Given|When|Then|And|But) ', re.IGNORECASE)

def __init__(self, original_name, new_name, observer, keyword_info=None):
def __init__(self, original_name, new_name, observer, keyword_info=None, language='En'):
self._language = language[0] if isinstance(language, list) else language
if self._language.lower() not in ['en', 'english']:
bdd_prefix = f"{obtain_bdd_prefixes(self._language)}|{BDD_ENGLISH}"
else:
bdd_prefix = BDD_ENGLISH
self._gherkin_prefix = re.compile(f'^({bdd_prefix}) ', re.IGNORECASE)
self._original_name, self._new_name = self._check_gherkin(new_name, original_name)
self._observer = observer
self._keyword_info = keyword_info
self._occurrences = None
# print(f"DEBUG: ctrlcommands.py RenameKeywordOccurrences INIT\n"
# f"{original_name=}, {new_name=}, self._original_name={self._original_name} "
# f"self._new_name={self._new_name} self._keyword_info={self._keyword_info} ")
# f"{original_name=}, {new_name=}, self._original_name={self._original_name} "
# f"self._new_name={self._new_name} self._keyword_info={self._keyword_info}"
# f" self._gherkin_prefix={self._gherkin_prefix} ")

def _check_gherkin(self, new_name, original_name):
was_gherkin, keyword_name = self._get_gherkin(original_name)
Expand Down
17 changes: 14 additions & 3 deletions src/robotide/controller/macrocontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
TESTCASE_NAME_FIELD = 'Test Case Name'


def obtain_bdd_prefixes(language):
from robotide.lib.compat.parsing.language import Language
lang = Language.from_name(language[0] if isinstance(language, list) else language)
bdd_prefixes = lang.bdd_prefixes
return list(bdd_prefixes)

def _empty_step():
return robotapi.Step([])

Expand Down Expand Up @@ -239,10 +245,15 @@ def create_keyword(self, name, argstr):
raise ValueError(validation.error_message)
return self.datafile_controller.create_keyword(name, argstr)

@staticmethod
def _remove_bdd_prefix(name):
def _remove_bdd_prefix(self, name):
bdd_prefix = []
language = self.language[0] if isinstance(self.language, list) else self.language
if language and language.lower() not in ['en', 'english']:
bdd_prefix = [f"{x.lower()} " for x in obtain_bdd_prefixes(language)]
bdd_prefix += ['given ', 'when ', 'then ', 'and ', 'but ']
# print(f"DEBUG: macrocontrollers.py WithStepsController _remove_bdd_prefix bdd_prefix={bdd_prefix}")
matcher = name.lower()
for match in ['given ', 'when ', 'then ', 'and ', 'but ']:
for match in bdd_prefix:
if matcher.startswith(match):
return name[len(match):]
return name
Expand Down
20 changes: 19 additions & 1 deletion src/robotide/controller/stepcontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,33 @@
from ..namespace.local_namespace import local_namespace
from ..utils import variablematcher

BDD_ENGLISH = 'given|when|then|and|but'

def obtain_bdd_prefixes(language):
from robotide.lib.compat.parsing.language import Language
lang = Language.from_name(language[0] if isinstance(language, list) else language)
bdd_prefixes = [f"{x}|" for x in lang.bdd_prefixes]
bdd_prefixes = "".join(bdd_prefixes).strip('|')
return bdd_prefixes


class StepController(_BaseController):

_GIVEN_WHEN_THEN_MATCHER = re.compile(r'^(given|when|then|and|but)\s*', re.I)
indent = None

def __init__(self, parent, step):
self.continuing_kw = None
self._init(parent, step)
if hasattr(self.parent, 'language'):
self._language = self.parent.language[0] if isinstance(self.parent.language, list) else self.parent.language
else:
self._language = 'en'
if self._language and self._language.lower() not in ['en', 'english']:
bdd_prefix = f"{obtain_bdd_prefixes(self._language)}|{BDD_ENGLISH}"
else:
bdd_prefix = BDD_ENGLISH
self._GIVEN_WHEN_THEN_MATCHER = re.compile(fr'^({bdd_prefix})\s*', re.IGNORECASE)
# print(f"DEBUG: stepcontrollers.py StepController INIT {self.parent.language}")
self.step_controller_step.args = self._change_last_empty_to_empty_var(
self.step_controller_step.args, self.step_controller_step.comment)

Expand Down
32 changes: 22 additions & 10 deletions src/robotide/editor/contentassist.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@
from ..publish.messages import RideSettingsChanged


def obtain_bdd_prefixes(language):
from robotide.lib.compat.parsing.language import Language
lang = Language.from_name(language[0] if isinstance(language, list) else language)
bdd_prefixes = lang.bdd_prefixes
return list(bdd_prefixes)


_PREFERRED_POPUP_SIZE = (200, 400)
_AUTO_SUGGESTION_CFG_KEY = "enable auto suggestions"


class _ContentAssistTextCtrlBase(wx.TextCtrl):

def __init__(self, suggestion_source, **kw):
def __init__(self, suggestion_source, language='En', **kw):
super().__init__(**kw)
from ..preferences import RideSettings
_settings = RideSettings()
Expand All @@ -45,6 +52,7 @@ def __init__(self, suggestion_source, **kw):
self.color_secondary_foreground = self.general_settings['secondary foreground']
self.color_background_help = self.general_settings['background help']
self.color_foreground_text = self.general_settings['foreground text']
self.language = language
self._popup = ContentAssistPopup(self, suggestion_source)
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
self.Bind(wx.EVT_CHAR, self.on_char)
Expand Down Expand Up @@ -308,9 +316,13 @@ def _populate_content_assist(self):
(self.gherkin_prefix, value) = self._remove_bdd_prefix(value)
return self._popup.content_assist_for(value, row=self._row)

@staticmethod
def _remove_bdd_prefix(name):
for match in ['given ', 'when ', 'then ', 'and ', 'but ']:
def _remove_bdd_prefix(self, name):
bdd_prefix = []
if self.language.lower() not in ['en', 'english']:
bdd_prefix = [f"{x.lower()} " for x in obtain_bdd_prefixes(self.language)]
bdd_prefix += ['given ', 'when ', 'then ', 'and ', 'but ']
# print(f"DEBUG: contentassist.py ContentAssistTextCtrlBase _remove_bdd_prefix bdd_prefix={bdd_prefix}")
for match in bdd_prefix:
if name.lower().startswith(match):
return name[:len(match)], name[len(match):]
return '', name
Expand Down Expand Up @@ -341,11 +353,11 @@ def dismiss(self):

class ExpandingContentAssistTextCtrl(_ContentAssistTextCtrlBase, ExpandoTextCtrl):

def __init__(self, parent, plugin, controller):
def __init__(self, parent, plugin, controller, language='En'):
""" According to class MRO, super().__init__ in _ContentAssistTextCtrlBase will init ExpandoTextCtrl
instance """

_ContentAssistTextCtrlBase.__init__(self, SuggestionSource(plugin, controller),
_ContentAssistTextCtrlBase.__init__(self, SuggestionSource(plugin, controller), language=language,
parent=parent, size=wx.DefaultSize,
style=wx.WANTS_CHARS | wx.TE_NOHIDESEL)
self.SetBackgroundColour(context.POPUP_BACKGROUND)
Expand All @@ -356,8 +368,8 @@ def __init__(self, parent, plugin, controller):

class ContentAssistTextCtrl(_ContentAssistTextCtrlBase):

def __init__(self, parent, suggestion_source, size=wx.DefaultSize):
super().__init__(suggestion_source, parent=parent,
def __init__(self, parent, suggestion_source, language='En', size=wx.DefaultSize):
super().__init__(suggestion_source, language=language, parent=parent,
size=size, style=wx.WANTS_CHARS | wx.TE_NOHIDESEL)
self.SetBackgroundColour(Colour(self.color_background_help))
# self.SetOwnBackgroundColour(Colour(self.color_background_help))
Expand All @@ -367,8 +379,8 @@ def __init__(self, parent, suggestion_source, size=wx.DefaultSize):

class ContentAssistTextEditor(_ContentAssistTextCtrlBase):

def __init__(self, parent, suggestion_source, pos, size=wx.DefaultSize):
super().__init__(suggestion_source,
def __init__(self, parent, suggestion_source, pos, language='En', size=wx.DefaultSize):
super().__init__(suggestion_source, language=language,
parent=parent, id=-1, value="", pos=pos, size=size,
style=wx.WANTS_CHARS | wx.BORDER_NONE | wx.WS_EX_TRANSIENT | wx.TE_PROCESS_ENTER |
wx.TE_NOHIDESEL)
Expand Down
24 changes: 18 additions & 6 deletions src/robotide/editor/kweditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import builtins
import json
from json.decoder import JSONDecodeError
from multiprocessing import shared_memory

import wx
from wx import grid
Expand Down Expand Up @@ -74,7 +75,6 @@ def decorated_function(self, *args):

return decorated_function


class KeywordEditor(GridEditor, Plugin):
_no_cell = (-1, -1)
_popup_menu_shown = False
Expand Down Expand Up @@ -163,6 +163,17 @@ def __init__(self, parent, controller, tree):
self._cell_selected = False
self._colorizer = Colorizer(self, controller)
self._controller = controller
try:
set_lang = shared_memory.ShareableList(name="language")
self._language = [set_lang[0]]
# print(f"DEBUG: settings.py SettingEditor __init__ SHAREDMEM language={self._language}")
except AttributeError:
try:
self._language = self._controller.language
# print(f"DEBUG: settings.py SettingEditor __init__ CONTROLLER language={self._language}")
except AttributeError:
self._language = ['en']
self._language = self._language[0] if isinstance(self._language, list) else self._language
self._configure_grid()
self._updating_namespace = False
self._controller.datafile_controller.register_for_namespace_updates(
Expand Down Expand Up @@ -247,7 +258,7 @@ def _set_cells(self):

def _configure_grid(self):
self._set_cells()
self.SetDefaultEditor(ContentAssistCellEditor(self._plugin, self._controller))
self.SetDefaultEditor(ContentAssistCellEditor(self._plugin, self._controller, self._language))
self._set_fonts()
wx.CallAfter(self.SetGridCursor, (0, 0)) # To make cells colorized as soon we select keywords or tests
wx.CallAfter(self.highlight, '')
Expand Down Expand Up @@ -1150,7 +1161,7 @@ def on_rename_keyword(self, event):
new_name = wx.GetTextFromUser(_('New name'), _(REN_KW), default_value=old_name)
if new_name:
self._execute(RenameKeywordOccurrences(
old_name, new_name, RenameProgressObserver(self.GetParent())))
old_name, new_name, RenameProgressObserver(self.GetParent()), language=self._language))

# Add one new Dialog to edit pretty json String TODO: use better editor with more functions
def on_json_editor(self, event=None):
Expand Down Expand Up @@ -1205,7 +1216,7 @@ def is_json(json_str):

class ContentAssistCellEditor(GridCellEditor):

def __init__(self, plugin, controller):
def __init__(self, plugin, controller, language='En'):
self.settings = plugin.global_settings['Grid']
self.general_settings = plugin.global_settings['General']
self.filter_newlines = self.settings.get("filter newlines", True)
Expand All @@ -1214,6 +1225,7 @@ def __init__(self, plugin, controller):
GridCellEditor.__init__(self)
self._plugin = plugin
self._controller = controller
self._language = language
self._grid = None
self._original_value = None
self._value = None
Expand Down Expand Up @@ -1244,7 +1256,7 @@ def execute_sharp_uncomment(self):
self._tc.execute_sharp_uncomment()

def Create(self, parent, idd, evthandler):
self._tc = ExpandingContentAssistTextCtrl(parent, self._plugin, self._controller)
self._tc = ExpandingContentAssistTextCtrl(parent, self._plugin, self._controller, self._language)
self.SetControl(self._tc)
if evthandler:
self._tc.PushEventHandler(evthandler)
Expand Down Expand Up @@ -1316,7 +1328,7 @@ def StartingKey(self, event):
self._tc.SetInsertionPointEnd()

def Clone(self):
return ContentAssistCellEditor(self._plugin, self._controller)
return ContentAssistCellEditor(self._plugin, self._controller, self._language)


class ChooseUsageSearchStringDialog(wx.Dialog):
Expand Down
3 changes: 2 additions & 1 deletion src/robotide/lib/compat/parsing/languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,8 @@ class Fr(Language):
template_setting = 'Modèle'
timeout_setting = "Délai d'attente"
arguments_setting = 'Arguments'
given_prefixes = ['Soit', 'Sachant', 'Sachant que', "Sachant qu'", 'Étant donné', 'Étant donné que', "Etant donné qu'"]
given_prefixes = ['Soit', 'Sachant', 'Sachant que', "Sachant qu'", 'Étant donné', 'Étant donné que',
"Etant donné qu'"]
when_prefixes = ['Quand', 'Lorsque', "Lorsqu'"]
then_prefixes = ['Alors', 'Donc']
and_prefixes = ['Et', 'Et que', "Et qu'"]
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/namespace/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ def _add_resource(self, res, ctx, items):
class _Keywords(object):

new_lang = None
regexp = re.compile(r"\s*(given|when|then|and|but)\s*(.*)", re.IGNORECASE)
# Not Used? regexp = re.compile(r"\s*(given|when|then|and|but)\s*(.*)", re.IGNORECASE)

def __init__(self, keywords, caseless=True, new_lang=None):
if not self.new_lang:
Expand Down
7 changes: 3 additions & 4 deletions src/robotide/ui/treenodehandlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,10 +753,9 @@ def _add_copy_to_tree(self, parent_node, copied):
def _create_rename_command(self, new_name):
# print(f"DEBUG: treenodehandlers.py UserKeywodHandler _create_rename_command controller.name={self.controller.name}"
# f", new_name={new_name} info={self.controller.info}")
return ctrlcommands.RenameKeywordOccurrences(
self.controller.name, new_name,
RenameProgressObserver(self._tree.GetParent()),
self.controller.info)
return ctrlcommands.RenameKeywordOccurrences(self.controller.name, new_name,
RenameProgressObserver(self._tree.GetParent()),
self.controller.info, language=self.controller.language)

def on_find_usages(self, event):
Usages(self.controller, self._tree.highlight).show()
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
#
# Automatically generated by `tasks.py`.

VERSION = 'v2.2dev6'
VERSION = 'v2.2dev7'
2 changes: 2 additions & 0 deletions utest/controller/test_stepcontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import re
import unittest

from robotide.controller.stepcontrollers import StepController


class FakeStep(StepController):
_GIVEN_WHEN_THEN_MATCHER = re.compile(r'^(given|when|then|and|but)\s*', re.IGNORECASE)

def __init__(self):
pass
Expand Down

0 comments on commit 46aacd2

Please sign in to comment.