howl.ui.Cursor
buffer = Buffer howl.mode.by_name 'default'
editor = Editor buffer
cursor = editor.cursor
selection = editor.selection
window = Gtk.OffscreenWindow default_width: 800, default_height: 640
window\add editor\to_gobject!
window\show_all!
howl.app\pump_mainloop!
before_each ->
cursor.pos = 1
selection.persistent = false
.at_end_of_line returns true if cursor is at the end of the line
buffer.text = 'åäö'
cursor.pos = 1
assert.is_false cursor.at_end_of_line
cursor.column = 4
assert.is_true cursor.at_end_of_line
.at_start_of_line returns true if cursor is at the start of the line
buffer.text = 'åäö'
cursor.pos = 1
assert.is_true cursor.at_start_of_line
cursor.column = 2
assert.is_false cursor.at_start_of_line
.at_end_of_file returns true if cursor is at the end of the buffer
buffer.text = 'åäö'
cursor.pos = 1
assert.is_false cursor.at_end_of_file
cursor\eof!
assert.is_true cursor.at_end_of_file
down() moves the cursor one line down, respecting the current column
buffer.text = 'hello\nmy\nworld'
cursor.pos = 4
cursor\down!
assert.equal 2, cursor.line
assert.equal 3, cursor.column
cursor\down!
assert.equal 3, cursor.line
assert.equal 4, cursor.column
up() moves the cursor one line up, respecting the current column
cursor.line = 2
cursor.column = 3
cursor\up!
assert.equal 1, cursor.line
assert.equal 3, cursor.column
right() moves the cursor one char right
buffer.text = 'åäö'
cursor.pos = 1
cursor\right!
assert.equal cursor.pos, 2
left() moves the cursor one char left
buffer.text = 'åäö'
cursor.pos = 3
cursor\left!
assert.equal cursor.pos, 2
.style
is "line" by default
assert.equal 'line', cursor.style
raises an error if set to anything else than "block" or "line"
cursor.style = 'block'
cursor.style = 'line'
assert.raises 'foo', -> cursor.style = 'foo'
.pos
before_each ->
buffer.text = 'Liñe 1 ʘf tƏxt'
reading returns the current character position in one based index
editor.view.cursor.pos = 5 -- raw aullar access, really at 'e'
assert.equal 4, cursor.pos
setting sets the current position
cursor.pos = 4
assert.equal cursor.pos, 4
setting adjusts the selection if it is persistent
selection\set 1, 2
selection.persistent = true
cursor.pos = 5
assert.equal 5, cursor.pos
assert.equals 'Liñe', selection.text
out-of-bounds values are automatically corrected
cursor.pos = 0
assert.equal 1, cursor.pos
cursor.pos = -1
assert.equal 1, cursor.pos
cursor.pos = math.huge
assert.equal #buffer + 1, cursor.pos
cursor.pos = #buffer + 2
assert.equal #buffer + 1, cursor.pos
.line
before_each ->
buffer.text = [[
Liñe 1 ʘf tƏxt
And hƏre's line twʘ
]]
returns the current line
cursor.pos = 1
assert.equal cursor.line, 1
setting moves the cursor to the first column of the specified line
cursor.line = 2
assert.equal cursor.pos, 16
assignment adjusts out-of-bounds values automatically
cursor.line = -1
assert.equal 1, cursor.pos
cursor.line = 100
assert.equal #buffer + 1, cursor.pos
assignment adjusts the selection if it is persistent
cursor.pos = 1
selection.persistent = true
cursor.line = 2
assert.equals 'Liñe 1 ʘf tƏxt\n', selection.text
.column
returns the current column
buffer.text = 'Liñe 1 ʘf tƏxt'
cursor.pos = 4
assert.equal cursor.column, 4
takes tabs into account
buffer.config.tab_width = 4
buffer.text = '\tsome text after'
cursor.pos = 2
assert.equal 5, cursor.column
.column = <nr>
moves the cursor to the specified column
buffer.text = 'Liñe 1 ʘf tƏxt'
cursor.column = 4
assert.equal 4, cursor.pos
cursor.column = 1
assert.equal 1, cursor.pos
takes tabs into account
buffer.config.tab_width = 4
buffer.text = '\tsome text after'
cursor.pos = 1
cursor.column = 5
assert.equal 2, cursor.pos
adjusts the selection if it is persistent
buffer.text = 'Liñe 1 ʘf tƏxt'
cursor.pos = 1
selection.persistent = true
cursor.column = 5
assert.equals 'Liñe', selection.text
.column_index
returns the real column index for the current line disregarding tabs
buffer.config.tab_width = 4
buffer.text = '\tsome text'
cursor.pos = 2
assert.equal 2, cursor.column_index
returns the column index as a character offset
buffer.text = 'åäö\nåäö'
cursor.pos = 6
assert.equal 2, cursor.column_index
.column_index = <nr>
before_each ->
buffer.config.tab_width = 4
buffer.text = '\tsome text after'
moves the cursor to the specified column index
cursor.column_index = 2
assert.equal 2, cursor.column_index
treats <nr> as a character offset
buffer.text = 'åäö\nåäö'
cursor.line = 2
cursor.column_index = 2
assert.equal 2, cursor.column_index
adjusts the selection if it is persistent
buffer.text = 'åäö'
cursor.pos = 1
selection.persistent = true
cursor.column_index = 3
assert.equals 'åä', selection.text
move_to(opts = {})
moves the cursor to the specified line and column if given
buffer.text = 'hello\nworld'
cursor\move_to line: 1, column: 3
assert.equal 3, cursor.pos
cursor\move_to line: 2, column: 2
assert.equal 8, cursor.pos
moves the cursor to the specified pos if given
buffer.text = 'åäö'
cursor\move_to pos: 2
assert.equal 2, cursor.pos
extends the selection if the <extend> option is truthy
buffer.text = 'hello\nworld'
cursor.pos = 1
cursor\move_to line: 1, column: 3, extend: true
assert.equal 3, cursor.pos
assert.equal 'he', selection.text
cursor\move_to pos: 8, extend: true
assert.equal 8, cursor.pos
assert.equal 'hello\nw', selection.text
considers <column> to be a virtual column
buffer.config.tab_width = 2
buffer.text = '\t23'
cursor\move_to line: 1, column: 3
assert.equal 2, cursor.pos
accepts <column_index> as well, considering that a real column
buffer.config.tab_width = 2
buffer.text = '\t23'
cursor\move_to line: 1, column_index: 3
assert.equal 3, cursor.pos
adjust out-of bound values
buffer.text = '123\n56789\n'
cursor\move_to line: 100
assert.equal 3, cursor.line
cursor\move_to line: 0
assert.equal 1, cursor.line
cursor\move_to column: 10
assert.equal 4, cursor.column
cursor\move_to column: 0
assert.equal 1, cursor.column
word_right
moves the cursor to the start of the following word
buffer.text = '12 xy 78'
cursor.pos = 1
cursor\word_right!
assert.equal 4, cursor.pos
cursor\word_right!
assert.equal 7, cursor.pos
handles punctuation
buffer.text = 'foo.bar'
cursor.pos = 1
cursor\word_right!
assert.equal 4, cursor.pos
cursor\word_right!
assert.equal 5, cursor.pos
handles tabs properly
buffer.config.tab_width = 2
buffer.text = '\t\tfoo_bar\txy'
cursor.pos = 3 -- 'f'
cursor\word_right!
assert.equal 11, cursor.pos
moves to the end of the line if no further word is available
buffer.text = '{\n34'
cursor.pos = 1
cursor\word_right!
assert.equal 2, cursor.pos
buffer.text = 'xy\n45'
cursor.pos = 1
cursor\word_right!
assert.equal 3, cursor.pos
buffer.text = '12\n45'
cursor.pos = 1
cursor\word_right!
assert.equal 3, cursor.pos
handles unicode properly
buffer.text = 'LinƏ !!'
cursor.pos = 1
cursor\word_right!
assert.equal 6, cursor.pos
cursor\word_right!
assert.equal 8, cursor.pos
when at the end of the line
moves to the first non-blank on the next line
buffer.text = '123\n 78'
cursor.pos = 4
cursor\word_right!
assert.equal 7, cursor.pos
buffer.text = '123\n56'
cursor.pos = 4
cursor\word_right!
assert.equal 5, cursor.pos
does nothing if at the end of file
buffer.text = '123'
cursor.pos = 4
cursor\word_right!
assert.equal 4, cursor.pos
leaves the cursor at end of file if no next word is present
buffer.text = '123'
cursor.pos = 2
cursor\word_right!
assert.equal 4, cursor.pos
word_right_end
moves the cursor to the end of the current word
buffer.text = 'foo bar 56 !'
cursor.pos = 1
cursor\word_right_end!
assert.equal 4, cursor.pos -- after 'foo'
cursor\word_right_end!
assert.equal 8, cursor.pos -- after 'bar'
cursor\word_right_end!
assert.equal 11, cursor.pos -- after '56'
handles punctuation
buffer.text = 'foo.bar'
cursor.pos = 1
cursor\word_right_end!
assert.equal 4, cursor.pos -- '.'
cursor\word_right_end!
assert.equal 8, cursor.pos
handles unicode properly
buffer.text = 'LinƏ !!'
cursor.pos = 1
cursor\word_right_end!
assert.equal 5, cursor.pos
cursor\word_right_end!
assert.equal 8, cursor.pos
cursor.pos = 4
cursor\word_right_end!
assert.equal 5, cursor.pos
handles single stand-alone punctuation
buffer.text = ' { foo'
cursor.pos = 1
cursor\word_right_end!
assert.equal 3, cursor.pos
handles tabs properly
buffer.config.tab_width = 2
buffer.text = '\t\tfoo_bar\txy'
cursor.pos = 3 -- 'f'
cursor\word_right_end!
assert.equal 10, cursor.pos
when at the end of the line
moves to the first non-blank on the next line
buffer.text = '123\n 78'
cursor.pos = 4
cursor\word_right_end!
assert.equal 9, cursor.pos
buffer.text = '123\n56'
cursor.pos = 4
cursor\word_right_end!
assert.equal 7, cursor.pos
does nothing if at the end of file
buffer.text = '123'
cursor.pos = 4
cursor\word_right_end!
assert.equal 4, cursor.pos
leaves the cursor at end of file if no next word is present
buffer.text = '12 '
cursor.pos = 3
cursor\word_right_end!
assert.equal 5, cursor.pos
word_left
moves the cursor to the start of the following word
buffer.text = 'a b 12 0x !!'
cursor.pos = 12
cursor\word_left!
assert.equal 11, cursor.pos -- first '!'
cursor\word_left!
assert.equal 8, cursor.pos -- '0'
cursor\word_left!
assert.equal 5, cursor.pos
cursor\word_left!
assert.equal 3, cursor.pos
handles punctuation
buffer.text = '(foo .bar'
cursor.pos = 10
cursor\word_left!
assert.equal 7, cursor.pos
cursor\word_left!
assert.equal 6, cursor.pos
cursor\word_left!
assert.equal 2, cursor.pos
cursor\word_left!
assert.equal 1, cursor.pos
handles unicode properly
buffer.text = 'LinƏ åäö '
cursor.pos = 10
cursor\word_left!
assert.equal 6, cursor.pos
cursor\word_left!
assert.equal 1, cursor.pos
handles numbers
buffer.text = ' x 2 '
cursor.pos = 5
cursor\word_left!
assert.equal 4, cursor.pos
cursor\word_left!
assert.equal 2, cursor.pos
handles tabs properly
buffer.config.tab_width = 2
buffer.text = '\t\tfoo_bar\txy'
cursor.pos = 5
cursor\word_left!
assert.equal 3, cursor.pos
(when no further word is available)
moves to the end of the previous line
buffer.text = '12\n45'
cursor.pos = 4
cursor\word_left!
assert.equal 3, cursor.pos
buffer.text = '12\n 56'
cursor.pos = 5
cursor\word_left!
assert.equal 3, cursor.pos
buffer.text = 'xy\nz'
cursor.pos = 4
cursor\word_left!
assert.equal 3, cursor.pos
does nothing if at the start of file
buffer.text = '123'
cursor.pos = 1
cursor\word_left!
assert.equal 1, cursor.pos
moves to the start of the file if no previous line is available
buffer.text = ' 34'
cursor.pos = 3
cursor\word_left!
assert.equal 1, cursor.pos
word_left_end
moves the cursor to the end of the previous word
buffer.text = 'foo bar 56 !'
cursor.pos = 13
cursor\word_left_end!
assert.equal 11, cursor.pos -- end of '56'
cursor\word_left_end!
assert.equal 8, cursor.pos -- end of 'bar'
cursor\word_left_end!
assert.equal 4, cursor.pos -- end of 'foo'
handles punctuation
buffer.text = 'foo.bar'
cursor.pos = 8
cursor\word_left_end!
assert.equal 5, cursor.pos -- after '.'
cursor\word_left_end!
assert.equal 4, cursor.pos -- after 'foo'
handles single stand-alone punctuation
buffer.text = ' { foo'
cursor.pos = 7
cursor\word_left_end!
assert.equal 3, cursor.pos
handles unicode properly
buffer.text = 'LiñƏ åäö x'
cursor.pos = 10
cursor\word_left_end!
assert.equal 9, cursor.pos
cursor\word_left_end!
assert.equal 5, cursor.pos
cursor\word_left_end!
assert.equal 1, cursor.pos
handles tabs properly
buffer.config.tab_width = 2
buffer.text = 'foo\tbar'
cursor.pos = 5
cursor\word_left_end!
assert.equal 4, cursor.pos
(when no further word is available)
moves to the end of the previous line
buffer.text = '12\n45'
cursor.pos = 4
cursor\word_left_end!
assert.equal 3, cursor.pos
buffer.text = '12\n 56'
cursor.pos = 5
cursor\word_left_end!
assert.equal 3, cursor.pos
buffer.text = 'xy\nz'
cursor.pos = 4
cursor\word_left_end!
assert.equal 3, cursor.pos
does nothing if at the start of file
buffer.text = '123'
cursor.pos = 1
cursor\word_left_end!
assert.equal 1, cursor.pos
moves to the start of the file if no previous line is available
buffer.text = ' 34'
cursor.pos = 3
cursor\word_left_end!
assert.equal 1, cursor.pos
para_up
(when between paragraphs)
moves to the first previous blank line before the above paragraph
buffer.text = '12\n\n5\n7\n'
cursor.pos = 9
cursor\para_up!
assert.equal 2, cursor.line
moves to the start of the buffer if no previous paragraph exists
buffer.text = '12\n\n'
cursor.pos = 5
cursor\para_up!
assert.equal 1, cursor.pos
(when in the middle of a paragraph)
moves to the first previous blank line
buffer.text = '12\n\n56\n89'
cursor.pos = 9
cursor\para_up!
assert.equal 2, cursor.line
moves to the start of the buffer if no previous paragraph exists
buffer.text = '12\n45'
cursor.pos = 5
cursor\para_up!
assert.equal 1, cursor.pos
para_down
(when between paragraphs)
moves to the next blank line after the above paragraph
buffer.text = '12\n\n5\n7\n'
cursor.pos = 4
cursor\para_down!
assert.equal 5, cursor.line
moves to the end of the buffer if no subsequent paragraph exists
buffer.text = '12\n\n5'
cursor.pos = 4
cursor\para_down!
assert.equal 6, cursor.pos
(when in the middle of a paragraph)
moves to the next blank line
buffer.text = '12\n45\n\n89'
cursor.pos = 1
cursor\para_down!
assert.equal 3, cursor.line
moves to the end of the buffer if no subsequent paragraph exists
buffer.text = '12\n45'
cursor.pos = 1
cursor\para_down!
assert.equal 6, cursor.pos
home_indent
moves the cursor to the first non-blank column
buffer.text = ' 345'
cursor.pos = 5
cursor\home_indent!
assert.equal 3, cursor.pos
does nothing for an empty line
buffer.text = '\nsecond'
cursor.pos = 1
cursor\home_indent!
assert.equal 1, cursor.pos
handles tabs correctly
buffer.text = '\t 345'
cursor.pos = 5
cursor\home_indent!
assert.equal 3, cursor.pos
home_indent_auto
toggles the cursor between the first and the first non-blank column
buffer.text = ' 345'
cursor.pos = 5
cursor\home_indent_auto!
assert.equal 3, cursor.pos
cursor\home_indent_auto!
assert.equal 1, cursor.pos
cursor\home_indent_auto!
assert.equal 3, cursor.pos
(when passing true for extended_selection to movement commands)
before_each ->
buffer.text = [[
Liñe 1 ʘf tƏxt
And hƏre's line twʘ
]]
the selection is extended along with moving the cursor
sel = editor.selection
cursor.pos = 1
cursor\right true
assert.equal 'L', sel.text
cursor\down true
assert.equals 'Liñe 1 ʘf tƏxt\nA', sel.text
cursor\left true
assert.equals 'Liñe 1 ʘf tƏxt\n', sel.text
cursor\up true
assert.is_true sel.empty
(when the editor selection is marked as persistent)
the selection is extended along with moving the cursor
sel = editor.selection
sel.persistent = true
cursor.pos = 1
cursor\right!
assert.equal 'L', sel.text
cursor.line = 2
assert.equals 'Liñe 1 ʘf tƏxt\nA', sel.text