Skip to content

Commit

Permalink
feat(term): add select method
Browse files Browse the repository at this point in the history
Adds select() method to Terminal. Method behaves the same way as
TextEdit's select method.
  • Loading branch information
lihop committed Apr 28, 2024
1 parent 43303a5 commit dd118d7
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 8 deletions.
24 changes: 24 additions & 0 deletions addons/godot_xterm/native/src/terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "terminal.h"

#include <algorithm>
#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/font.hpp>
#include <godot_cpp/classes/image_texture.hpp>
Expand Down Expand Up @@ -70,6 +71,9 @@ void Terminal::_bind_methods()
ClassDB::bind_method(D_METHOD("set_blink_off_time", "time"), &Terminal::set_blink_off_time);
ClassDB::add_property("Terminal", PropertyInfo(Variant::FLOAT, "blink_off_time"), "set_blink_off_time", "get_blink_off_time");

// Selection.
ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &Terminal::select);

// Copying.
ClassDB::bind_method(D_METHOD("copy_all"), &Terminal::copy_all);
ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection);
Expand Down Expand Up @@ -657,6 +661,26 @@ String Terminal::_copy_screen(ScreenCopyFunction func) {
return data.get_string_from_utf8();
}

void Terminal::select(const int p_from_line, const int p_from_column, const int p_to_line, const int p_to_column) {
int from_line = std::clamp((int)p_from_line, 0, (int)rows);
int from_column = std::clamp((int)p_from_column, 0, (int)cols);
int to_line = std::clamp((int)p_to_line, 0, (int)rows);
int to_column = std::clamp((int)p_to_column, 0, (int)cols);

if (from_line > to_line) {
std::swap(to_line, from_line);
std::swap(to_column, from_column);
} else if ((from_line == to_line) && (from_column > to_column)) {
std::swap(to_column, from_column);
}

to_column -= 1;

tsm_screen_selection_reset(screen);
tsm_screen_selection_start(screen, from_column, from_line);
tsm_screen_selection_target(screen, to_column, to_line);
}

String Terminal::copy_all() {
return _copy_screen(&tsm_screen_copy_all);
}
Expand Down
2 changes: 2 additions & 0 deletions addons/godot_xterm/native/src/terminal.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ namespace godot

void clear();

void select(const int p_from_line, const int p_from_column, const int p_to_line, const int p_to_column);

String copy_all();
String copy_selection();
void set_copy_on_selection(const bool p_enable);
Expand Down
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/lihop/godot-xterm/compare/v2.2.0...HEAD)

### Added

- Added select() method to Terminal.

### Changed

- Custom export templates are no longer required when exporting to HTML5 from Godot v3.5.x.
Expand Down
23 changes: 15 additions & 8 deletions docs/api/terminal.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ For example if the string `"\u001b[38;2;0;255;0;mA"` was written to the terminal

## Methods

| Returns | Signature |
| ---------- | ------------------------------------------------------------------- |
| void | [clear](#mthd-clear) **( )** |
| {{String}} | [copy_all](#mthd-copy_all) **( )** |
| {{String}} | [copy_selection](#mthd-copy_selection) **( )** |
| {{int}} | [get_cols](#mthd-get_cols) **( )** |
| {{int}} | [get_rows](#mthd-get_rows) **( )** |
| void | [write](#mthd-write) **(** {{String}}\|{{PoolByteArray}} data **)** |
| Returns | Signature |
| ---------- | ------------------------------------------------------------------------------------------------------------ |
| void | [clear](#mthd-clear) **( )** |
| {{String}} | [copy_all](#mthd-copy_all) **( )** |
| {{String}} | [copy_selection](#mthd-copy_selection) **( )** |
| {{int}} | [get_cols](#mthd-get_cols) **( )** |
| {{int}} | [get_rows](#mthd-get_rows) **( )** |
| void | [select](#mthd-select) **(** {{int}} from_line, {{int}} from_column, {{int}} to_line {{int}} to_column **)** |
| void | [write](#mthd-write) **(** {{String}}\|{{PoolByteArray}} data **)** |

## Signals

Expand Down Expand Up @@ -189,6 +190,12 @@ Returns the height of the terminal in characters.
When using a monospace font, this is the number of visible characters that can fit from the top of the terminal to the bottom in a single column.
It will automatically update according to the terminal's rect_size and theme's font size.

<hr id="mthd-select" />

void **select** **(** {{int}} from_line, {{int}} from_column, {{int}} to_line, {{int}} to_column **)**

Perform selection, from line/column to line/column.

<hr id="mthd-write" />

void **write** **(** {{String}}\|{{PoolByteArray}} data **)**
Expand Down
67 changes: 67 additions & 0 deletions test/test_terminal.gd
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,70 @@ class TestClear:
var screen_after = subject.copy_all()
var expected = final_line + "\n".repeat(subject.get_rows())
assert_eq(screen_after, expected)


class TestSelect:
extends TerminalTest

# Use the behavior of TextEdit's select() method as a reference.
var text_edit: TextEdit

func assert_select_eq(argv, expected):
text_edit.callv("select", argv)
subject.callv("select", argv)
assert_eq(
expected,
text_edit.get_selected_text(),
"expected does not match reference implementation"
)
assert_eq(subject.copy_selection(), expected)

func before_each():
super.before_each()
text_edit = TextEdit.new()
text_edit.text = "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n"
add_child_autofree(text_edit)
subject.write("0123456789\r\nABCDEFGHIJ\r\n)!@#$%^&*(")

func test_select_nothing():
assert_select_eq([0, 0, 0, 0], "")

func test_select_first_character():
assert_select_eq([0, 0, 0, 1], "0")

func test_select_last_character():
assert_select_eq([2, 9, 2, 10], "(")

func test_select_reverse_column():
assert_select_eq([0, 6, 0, 1], "12345")

func test_select_preceeds_column_bounds():
assert_select_eq([0, -2, 0, -1], "")
assert_select_eq([0, -2, 0, 0], "")
assert_select_eq([0, -2, 0, 1], "0")

func test_select_exceeds_column_bounds():
assert_select_eq([0, 5, 0, 999], "56789")

func test_select_first_row():
assert_select_eq([0, 0, 0, 10], "0123456789")

func test_select_second_row():
assert_select_eq([1, 0, 1, 10], "ABCDEFGHIJ")

func test_select_multiple_rows():
assert_select_eq([0, 0, 1, 10], "0123456789\nABCDEFGHIJ")

func test_select_rows_reverse():
assert_select_eq([1, 5, 0, 0], "0123456789\nABCDE")

func test_select_preceeds_row_bounds():
assert_select_eq([-2, 0, -1, 10], "0123456789")
assert_select_eq([-2, 0, 0, 10], "0123456789")
assert_select_eq([-2, 0, 1, 10], "0123456789\nABCDEFGHIJ")

func test_select_exceeds_row_bounds():
assert_select_eq([1, 5, 999, 999], "FGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n")

func test_wide_bounds():
assert_select_eq([-999, -999, 999, 999], "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n")

0 comments on commit dd118d7

Please sign in to comment.