Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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