Skip to content

Commit

Permalink
Misc fixes (#2620)
Browse files Browse the repository at this point in the history
* Fix position of cursor in Text Editor auto-complete when line contains multibyte characters

* Add unit tests to get_selected_or_near_text
  • Loading branch information
HelioGuilherme66 authored Aug 15, 2023
1 parent 6b58c7f commit f30fb0d
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 60 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni

=== Fixed

-
- Position of cursor in Text Editor auto-suggestions when line contains multibyte characters

=== Changed

Expand Down
80 changes: 45 additions & 35 deletions src/robotide/editor/texteditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def __init__(self, parent, title, data_validator):
self._create_ui(title)
self._data = None
self._dirty = 0 # 0 is False and 1 is True, when changed on this editor
self._position = None
self._position = 0 # Start at 0 if first time access
self._showing_list = False
self._tab_open = None
self._controller_for_context = None
Expand Down Expand Up @@ -503,7 +503,7 @@ def show_help_dialog(event):
HtmlDialog("Getting syntax colorization", content).Show()

def store_position(self, force=False):
if self.source_editor and self.datafile_controller:
if self.source_editor: # We don't necessarily need a data controller, was: "and self.datafile_controller:"
cur_pos = self.source_editor.GetCurrentPos()
if cur_pos > 0: # Cheating because it always goes to zero
self._position = cur_pos
Expand Down Expand Up @@ -1550,6 +1550,10 @@ def on_key_pressed(self, event):
self.AutoCompSetIgnoreCase(True)
self.AutoCompSetSeparator(ord(';'))
self.AutoCompShow(0, ";".join(sugs))
else:
self.SetSelectionStart(pos)
self.SetSelectionEnd(pos)
self.SetInsertionPoint(pos)
else:
event.Skip()

Expand Down Expand Up @@ -1608,44 +1612,50 @@ def get_selected_or_near_text(self):
line = self.GetCurrentLine()
line_end = self.GetLineEndPosition(line)
size = self.GetLineLength(line)
min_pos = line_end - size
star = self.GetLineRaw(line)
sz_star = len(star) - 1
min_pos = line_end - sz_star
pos_in_line = start_pos - min_pos
if pos_in_line > 0:
start_chr = end_chr = None
for i in range(pos_in_line, 1, -1):
if text[i] == ' ' and text[i-1] == ' ':
start_chr = i + 1
break
# print(f"DEBUG: line={text}\nstar={star}\nmin_pos={min_pos} start_pos={start_pos}"
# f" line_end={line_end}\nline={line}"
# f" pos_in_line={pos_in_line} size={size} sz_star={sz_star} lentext={len(text)}")
# if pos_in_line > 0:
start_chr = end_chr = None
for i in range(max(1, min(size, pos_in_line-1)), 1, -1):
if text[i] == ' ' and text[i-1] == ' ':
start_chr = i + 1
break
if pos_in_line >= 0:
for i in range(pos_in_line, size):
if text[i] == ' ' and text[i+1] == ' ':
end_chr = i
break
value = None
if start_chr is not None:
if end_chr is not None:
value = text[start_chr:end_chr]
else:
value = text[start_chr:].strip()
elif end_chr is not None:
value = text[pos_in_line:end_chr]
if value:
# self.SetInsertionPoint(self.GetSelectionStart())
if start_chr:
start_pos = min_pos + start_chr
else:
start_pos = min_pos + pos_in_line
if value.endswith('.'): # Special cases for libraries prefix
self.SetInsertionPoint(start_pos + len(value))
elif len(value.split('.')) > 1:
parts = value.split('.')
self.SetSelectionStart(start_pos + len(parts[0]) + 1)
self.SetSelectionEnd(start_pos + len(value))
self.SetInsertionPoint(start_pos + len(parts[0]) + 1)
else:
self.SetSelectionStart(start_pos)
self.SetSelectionEnd(start_pos + len(value))
self.SetInsertionPoint(start_pos)
content.add(value)
value = None
if start_chr is not None:
if end_chr is not None:
value = text[start_chr:end_chr]
else:
value = text[start_chr:].strip()
elif end_chr is not None:
value = text[pos_in_line:end_chr]
if value:
# self.SetInsertionPoint(self.GetSelectionStart())
if start_chr:
start_pos = min_pos + start_chr
else:
start_pos = min_pos + pos_in_line
if value.endswith('.'): # Special cases for libraries prefix
self.SetInsertionPoint(start_pos + len(value))
elif len(value.split('.')) > 1:
parts = value.split('.')
self.SetSelectionStart(start_pos + len(parts[0]) + 1)
self.SetSelectionEnd(start_pos + len(value))
self.SetInsertionPoint(start_pos + len(parts[0]) + 1)
else:
self.SetSelectionStart(start_pos)
self.SetSelectionEnd(start_pos + len(value))
self.SetInsertionPoint(start_pos)
content.add(value)
return content if content else ['']

def on_update_ui(self, evt):
Expand Down
143 changes: 119 additions & 24 deletions utest/editor/test_texteditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,10 @@
from robotide.editor import texteditor
from robotide.namespace.namespace import Namespace

"""
th.FakeDirectorySuiteHandler = th.FakeUserKeywordHandler = \
th.FakeSuiteHandler = th.FakeTestCaseHandler = \
th.FakeResourceHandler = th.TestDataDirectoryHandler
st.Editor = lambda *args: FakeEditor()
"""
"""
DEBUG:
Tree._show_correct_editor = lambda self, x: None
Tree.get_active_datafile = lambda self: None
Tree._select = lambda self, node: self.SelectItem(node)
Tree.get_selected_datafile_controller = lambda self, node: self.SelectItem(node)
"""

app = wx.App()
nb_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_WINDOWLIST_BUTTON | aui.AUI_NB_TAB_EXTERNAL_MOVE \
| aui.AUI_NB_SUB_NOTEBOOK | aui.AUI_NB_SMART_TABS
MYTESTOVERRIDE = 'My Overriding Test Teardown'


class MainFrame(wx.Frame):
Expand Down Expand Up @@ -153,7 +140,7 @@ def setUp(self):
self.plugin._open_tree_selection_in_editor()
self.app.frame.SetStatusText("File:" + self.app.project.data.source)
# Uncomment next line (and MainLoop in tests) if you want to see the app
# self.frame.Show()
self.frame.Show()

def tearDown(self):
self.plugin.unsubscribe_all()
Expand Down Expand Up @@ -351,7 +338,7 @@ def test_execute_sharp_comment_two_lines(self):
def test_execute_sharp_comment_no_selection(self):
spaces = ' ' * self.plugin._editor_component.tab_size
content = [spaces + '1 - Line one' + spaces + 'with cells' + spaces + 'last text\n']
pos = len(spaces + '1 - Line one' + spaces+ 'with cells')
pos = len(spaces + '1 - Line one' + spaces + 'with cells')
self.plugin._editor_component.source_editor.set_text(''.join(content))
self.plugin._editor_component.source_editor.SetAnchor(pos)
self.plugin._editor_component.source_editor.SetSelection(pos, pos)
Expand All @@ -367,7 +354,7 @@ def test_execute_sharp_comment_no_selection(self):
def test_execute_sharp_comment_with_selection(self):
spaces = ' ' * self.plugin._editor_component.tab_size
content = [spaces + '1 - Line one' + spaces + 'with cells' + spaces + 'last text\n']
pos = len(spaces + '1 - Line one' + spaces+ 'with cells')
pos = len(spaces + '1 - Line one' + spaces + 'with cells')
self.plugin._editor_component.source_editor.set_text(''.join(content))
self.plugin._editor_component.source_editor.SetAnchor(pos)
self.plugin._editor_component.source_editor.SetSelection(pos - len('with cells'), pos)
Expand Down Expand Up @@ -399,7 +386,7 @@ def test_execute_sharp_uncomment_two_lines(self):
def test_execute_sharp_uncomment_no_selection(self):
spaces = ' ' * self.plugin._editor_component.tab_size
content = [spaces + '# ' + '1 - Line one' + spaces + 'with cells' + spaces + 'last text\n']
pos = len(spaces + '1 - Line one' + spaces+ 'with cells')
pos = len(spaces + '1 - Line one' + spaces + 'with cells')
self.plugin._editor_component.source_editor.set_text(''.join(content))
self.plugin._editor_component.source_editor.SetAnchor(pos)
self.plugin._editor_component.source_editor.SetSelection(pos, pos)
Expand Down Expand Up @@ -428,10 +415,10 @@ def test_execute_sharp_uncomment_with_selection(self):
# self.app.MainLoop()

def test_check_variables_section(self):
pos = len('1 - Line one\n')
spaces = ' ' * self.plugin._editor_component.tab_size
# pos = len('1 - Line one\n')
# spaces = ' ' * self.plugin._editor_component.tab_size
text = "${ARG} value" # Text to find (last item in Variable section)
with open(datafilereader.TESTCASEFILE_WITH_EVERYTHING, "r") as fp: #, MessageRecordingLoadObserver())
with open(datafilereader.TESTCASEFILE_WITH_EVERYTHING, "r") as fp: # MessageRecordingLoadObserver())
content = fp.readlines()
content = "".join(content)
# self.plugin.on_open(None)
Expand All @@ -451,9 +438,9 @@ def test_check_variables_section(self):
self.plugin._editor_component._show_search_results(position, text)
position = self.plugin._editor_component.source_editor.GetCurrentPos()
self.plugin._editor_component.source_editor.InsertText(position, "123\n\n")
fulltext = self.plugin._editor_component.source_editor.GetText()
# fulltext = self.plugin._editor_component.source_editor.GetText()
# print(f"DEBUG: fulltext:\n{fulltext}")
# Activate Apply to cleanup text
# Activate Apply to clean-up text
self.plugin._editor_component._dirty = True
# self.plugin._apply_txt_changes_to_model()
# DEBUG: THIS IS THE TEST, IT FAILS BECAUSE WE DON'T HAVE data and controller
Expand All @@ -466,7 +453,115 @@ def test_check_variables_section(self):
# wx.CallLater(5000, self.app.ExitMainLoop)
# self.app.MainLoop()

def test_get_selected_or_near_text(self):
with open(datafilereader.TESTCASEFILE_WITH_EVERYTHING, "r") as fp:
content = fp.readlines()
content = "".join(content)
self.plugin._editor_component.source_editor.set_text(content)
self.plugin._editor_component.source_editor.SetInsertionPoint(0)
self.plugin._editor_component.store_position(True)
self.plugin._editor_component.set_editor_caret_position()
# Should return first line content
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
assert result == {'*** Setting ***'}
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 0

# Different positions in content: My Overriding Test Teardown
# X marks cursor position, XMy Overriding Test Teardown
# Should return My Overriding Test Teardown
position = 1565
self.plugin._editor_component.source_editor.SetAnchor(position)
self.plugin._editor_component.source_editor.SetSelection(position, position)
self.plugin._editor_component.source_editor.SetInsertionPoint(position)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = list(result)
assert result == [MYTESTOVERRIDE]
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 1565
# X marks cursor position, My XOverriding Test Teardown
# Should return My Overriding Test Teardown
position = 1568
self.plugin._editor_component.source_editor.SetAnchor(position)
self.plugin._editor_component.source_editor.SetSelection(position, position)
self.plugin._editor_component.source_editor.SetInsertionPoint(position)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = list(result)
assert result == [MYTESTOVERRIDE]
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 1565
# X marks cursor position, My OverrXiding Test Teardown
# Should return My Overriding Test Teardown
position = 1573
self.plugin._editor_component.source_editor.SetAnchor(position)
self.plugin._editor_component.source_editor.SetSelection(position, position)
self.plugin._editor_component.source_editor.SetInsertionPoint(position)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = list(result)
assert result == [MYTESTOVERRIDE]
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 1565
# X marks cursor position, My Overriding TestX Teardown
# Selected 'Test'
# Should return My Overriding Test Teardown, Test
position = 1583
self.plugin._editor_component.source_editor.SetAnchor(position)
self.plugin._editor_component.source_editor.SetSelection(position-len('Test'), position)
self.plugin._editor_component.source_editor.SetInsertionPoint(position)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = sorted(result)
assert result == [MYTESTOVERRIDE, 'Test']
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 1565
# X marks cursor position, My Overriding TestX Teardown
# Should return My Overriding Test Teardown
position = 1583
self.plugin._editor_component.source_editor.SetAnchor(position)
self.plugin._editor_component.source_editor.SetSelection(position, position)
self.plugin._editor_component.source_editor.SetInsertionPoint(position)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = sorted(result)
assert result == [MYTESTOVERRIDE]
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 1565
# X marks cursor position, My Overriding Test TeardownX
# Should return My Overriding Test Teardown
position = 1592
self.plugin._editor_component.source_editor.SetAnchor(position)
self.plugin._editor_component.source_editor.SetSelection(position, position)
self.plugin._editor_component.source_editor.SetInsertionPoint(position)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = sorted(result)
print(f"DEBUG: check position len={position}\n{result}")
assert result == [MYTESTOVERRIDE]
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == 1565

# Should return [Timeout]
text_length = self.plugin._editor_component.source_editor.GetTextLength() - 1
self.plugin._editor_component.source_editor.SetAnchor(text_length)
self.plugin._editor_component.source_editor.SetSelection(text_length, text_length)
self.plugin._editor_component.source_editor.SetInsertionPoint(text_length)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
result = list(result)
assert result == ['[Timeout]']
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == text_length - len(result[0])

# Should return empty value
text_length = self.plugin._editor_component.source_editor.GetTextLength()
self.plugin._editor_component.source_editor.SetAnchor(text_length)
self.plugin._editor_component.source_editor.SetSelection(text_length, text_length)
self.plugin._editor_component.source_editor.SetInsertionPoint(text_length)
result = self.plugin._editor_component.source_editor.get_selected_or_near_text()
assert result == ['']
position = self.plugin._editor_component.source_editor.GetCurrentPos()
assert position == text_length

# Uncomment next lines if you want to see the app
wx.CallLater(5000, self.app.ExitMainLoop)
self.app.MainLoop()


if __name__ == '__main__':
unittest.main()

0 comments on commit f30fb0d

Please sign in to comment.