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
14 changes: 13 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@ All notable changes to this project will be documented in this file.
The format is based on http://keepachangelog.com/en/1.0.0/[Keep a Changelog]
and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioning].

// == https://github.com/robotframework/RIDE[Unreleased]
== https://github.com/robotframework/RIDE[Unreleased]

=== Added
- Added indication of *private* keywords in Grid Editor, keywords will show in _Italic_, and with error
background, when they are used outside of Keywords section, or from different files.

- Added indication of *private* keywords in Details pop-up for keywords with tag *robot:private* or name starting with underscore, *'_'* in Grid Editor.

=== Changed
- Modified the action of key TAB when selecting from auto-suggestions list in Grid Editor. Pressing TAB, selects the item and continues in cell editor.

=== Fixed
- Fix cursor position when editing cells in Grid Editor.

== https://github.com/robotframework/RIDE/blob/master/doc/releasenotes/ride-2.1.4.1.rst[2.1.4.1] - 2025-06-24

Expand Down
121 changes: 65 additions & 56 deletions src/robotide/application/CHANGELOG.html

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Added indication of <b>private</b> keywords in Grid Editor, keywords will show in <em>Italic</em>, and with error
background, when they are used outside of Keywords section, or from different files.</li>
<li>Added indication of <b>private</b> keywords in Details pop-up for keywords with tag <b>robot:private</b> or name starting
with underscore, <b>'_'</b> in Grid Editor.</li>
<li>Modified the action of key TAB when selecting from auto-suggestions list in Grid Editor. Pressing TAB, selects the
item and continues in cell editor.</li>
<li>Fix cursor position when editing cells in Grid Editor.</li>
<li>Fix broken installation of RIDE v2.1.4 by adding missing dependencies.</li>
<li>Added <b>Tools-&gt;Library Finder...</b> to install libraries and <b>Help-&gt;Open Library Documentation...</b> .
They share the same dialog, and definitions are recorded in ``settings.cfg``.</li>
Expand Down Expand Up @@ -248,7 +255,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 24/June/2025.</p>
<p>RIDE {VERSION} was released on 17/July/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
10 changes: 9 additions & 1 deletion src/robotide/controller/basecontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ def clear_namespace_update_listeners(self):
self.namespace.clear_update_listeners()

def is_user_keyword(self, datafile, value):
return self.namespace.is_user_keyword(datafile, value)
user_kw = self.namespace.is_user_keyword(datafile, value)
if user_kw:
private=self.is_private(datafile, value)
# print(f"DEBUG: basecontroller.py is_user_keyword CALLED is_private=={private} datafile={datafile.source}")
return user_kw # self.namespace.is_user_keyword(datafile, value)

def is_private(self, datafile, value):
return self.namespace.is_private(datafile, value)

def is_library_keyword(self, datafile, value):
return self.namespace.is_library_keyword(datafile, value)
Expand All @@ -115,6 +122,7 @@ def get_all_cached_library_names(self):
return self.namespace.get_all_cached_library_names()

def keyword_info(self, datafile, keyword_name):
# print(f"DEBUG: basecontroller.py WithNamespace keyword_info call find_keyword keyword_name={keyword_name}")
return self.namespace.find_keyword(datafile, keyword_name)

def is_library_import_ok(self, imp):
Expand Down
22 changes: 20 additions & 2 deletions src/robotide/controller/cellinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from enum import Enum, auto
from ..action.shortcut import localize_shortcuts
from ..utils import highlightmatcher, html_escape

CTRL_LABEL = localize_shortcuts('CtrlCmd')


class PrivateError(Enum):
no_error = auto()
invalid = auto()


class CellInfo(object):

def __init__(self, cell_content, cell_position, for_loop=False):
self._cell_content = cell_content
self._cell_position = cell_position
self.for_loop = for_loop
self._error_state = PrivateError.no_error

@property
def content_type(self):
Expand All @@ -38,12 +45,22 @@ def cell_type(self):
def source(self):
return self._cell_content.source

@property
def private(self):
return self._cell_content.private

@property
def arg_name(self):
return self._cell_position.argument_name

def has_error(self):
return self.argument_missing() or self.too_many_arguments()
return self.argument_missing() or self.too_many_arguments() or self._error_state == PrivateError.invalid

def set_or_clear_error(self, mode=False):
if mode:
self._error_state = PrivateError.invalid
else:
self._error_state = PrivateError.no_error

def argument_missing(self):
return self.cell_type == CellType.MANDATORY \
Expand Down Expand Up @@ -137,10 +154,11 @@ def __str__(self):

class CellContent(object):

def __init__(self, ctype, value, source=None):
def __init__(self, ctype, value, source=None, private=False):
self.type = ctype
self.value = value
self.source = source
self.private = private


class CellPosition(object):
Expand Down
1 change: 1 addition & 0 deletions src/robotide/controller/filecontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def is_library_keyword(self, datafile, value):

def keyword_info(self, datafile, keyword_name):
_ = datafile
# print(f"DEBUG: filecontrollers.py _DataController keyword_info call keyword_info keyword_name={keyword_name}")
return WithNamespace.keyword_info(self, self.data, keyword_name)

def mark_dirty(self):
Expand Down
8 changes: 8 additions & 0 deletions src/robotide/controller/macrocontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,16 @@ def get_cell_info(self, row, col):
steps = self.steps
if row < 0 or len(steps) <= row:
return None
# print(f"DEBUG: macrocontrollers.py WithStepsController call get_cell_info row={row} col={col}")
return steps[row].get_cell_info(col)

def get_keyword_info(self, kw_name):
# print(f"DEBUG: macrocontrollers.py WithStepsController get_keyword_info call keyword_info kw_name={kw_name}")
return self.datafile_controller.keyword_info(None, kw_name)

def is_user_keyword(self, value):
# print(f"DEBUG: macrocontrollers.py WithStepsController is_user_keyword call "
# f"DATAFILE_CONTROLER.is_user_keyword value={value}, source={self.datafile_controller.source}")
return self.datafile_controller.is_user_keyword(None, value)

def is_library_keyword(self, value):
Expand Down Expand Up @@ -499,6 +503,10 @@ def teardown(self):
def arguments(self):
return ArgumentsController(self, self.kw.args)

@property
def is_private_keyword(self):
return self.kw.is_private_keyword

def validate_keyword_name(self, name):
return self._parent.validate_name(name)

Expand Down
26 changes: 18 additions & 8 deletions src/robotide/controller/stepcontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,13 @@ def _change_last_empty_to_empty_var(args, comment):
def get_keyword_info(self, kw):
if not kw:
return None
return self.parent.get_keyword_info(kw)
# print(f"DEBUG: stepcontrollers.py StepController get_keyword_info call parent kw={kw}")
kw_info = self.parent.get_keyword_info(kw)
if kw_info: # and kw_info.type == CellType.KEYWORD:
private = kw_info.is_private_keyword
# print(f"DEBUG: stepcontrollers.py StepController get_keyword_info called parent get_keyword_info kw_info={kw_info} "
# f"kw={kw} is PRIVATE?=={private}")
return kw_info

def __eq__(self, other):
if self is other:
Expand Down Expand Up @@ -115,6 +121,7 @@ def get_value(self, col):
def get_cell_info(self, col):
position = self._get_cell_position(col)
content = self._get_content_with_type(col, position)
# print(f"DEBUG: stepcontrollers.py StepController call get_cell_info col={col} content.type={content.type}")
if content.type == ContentType.COMMENTED:
return self._build_cell_info(content, CellPosition(CellType.OPTIONAL, None))
return self._build_cell_info(content, position)
Expand All @@ -132,6 +139,7 @@ def is_assigning(self, value):
return False

def _build_cell_info(self, content, position):
# print(f"DEBUG: stepcontrollers.py StepController _build_cell_info call CellInfo content={content} position={position}")
return CellInfo(content, position)

def _get_cell_position(self, column):
Expand Down Expand Up @@ -244,13 +252,13 @@ def _get_content_with_type(self, col, position):
return CellContent(ContentType.UNKNOWN_VARIABLE, value)
return CellContent(ContentType.VARIABLE, value)
if self.is_user_keyword(value):
return CellContent(
ContentType.USER_KEYWORD, value,
self.get_keyword_info(value).source)
kw_info = self.get_keyword_info(value)
source = kw_info.source
# print(f"DEBUG: stepcontrollers.py StepController file={self.display_name} "
# f"call get_keyword_info value={value}, source={source} PRIVATE={kw_info.private}")
return CellContent(ContentType.USER_KEYWORD, value, source=source, private=kw_info.private)
if self.is_library_keyword(value):
return CellContent(
ContentType.LIBRARY_KEYWORD, value,
self.get_keyword_info(value).source)
return CellContent(ContentType.LIBRARY_KEYWORD, value, source=self.get_keyword_info(value).source)
if value == 'END': # DEBUG Don't consider start column (col == 0 and)
return CellContent(ContentType.END, value)
return CellContent(ContentType.STRING, value)
Expand Down Expand Up @@ -284,6 +292,8 @@ def is_modifiable(self):
return self.datafile_controller.is_modifiable()

def is_user_keyword(self, value):
# print(f"DEBUG: stepcontrollers.py StepController is_user_keyword CALL parent.is_user_keyword"
# f" = {self.parent.source}")
return self.parent.is_user_keyword(value)

def is_library_keyword(self, value):
Expand All @@ -297,7 +307,7 @@ def contains_variable(self, name):
return any(variablematcher.value_contains_variable(item, name)
for item in self.as_list())

def contains_variable_assignment(self, name):
def contains_variable_assignment(self, name): # TODO: Consider first cell if variable and FOR assignments
return any(variablematcher.value_contains_variable(item, "%s=" % name)
for item in self.as_list())

Expand Down
1 change: 1 addition & 0 deletions src/robotide/editor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ def on_uncomment_cells(self, event):

def on_content_assistance(self, event):
__ = event
print(f"DEBUG: init.py _EditorTab on_content_assistance event={event}")
self.editor.show_content_assist()

def save(self, message=None):
Expand Down
Loading