howl.ui.SearchView
local command_line, search_view, list_widget, editor, buffer, finish_result, search, preview_buffer, error_message, info_message, find_iter_count keypress = (keystroke) -> search_view.keymap[keystroke] search_view howl.app\pump_mainloop! -- allow all timer.asap functions to run keypress_binding_for = (cmd) -> search_view.keymap.binding_for[cmd] search_view howl.app\pump_mainloop! -- allow all timer.asap functions to run set_text = (sv, text) -> command_line.text = text sv\on_text_changed text if sv.searcher.running howl.dispatch.wait sv.searcher.running howl.app\pump_mainloop! -- allow all timer.asap functions to run list_widget_items = -> list_widget.list._items list_widget_height = -> list_widget.height found_count = -> return unless info_message _, _, count = info_message\find '(%d+) match' tonumber count before_each -> preview_buffer = nil editor = { preview: spy.new (b) => preview_buffer = b cancel_preview: spy.new => line_at_top: 1 line_at_bottom: 1 } command_line = add_widget: spy.new (name, w) => list_widget = w if name == 'matches' notification: NotificationWidget! finish: spy.new (r) => finish_result = r info_message = '<not-set>' error_message = '<not-set>' command_line.notification.error = spy.new (self, msg) -> error_message = msg command_line.notification.info = spy.new (self, msg) -> info_message = msg command_line.notification.clear = spy.new (self, msg) -> info_message = '' error_message = '' buffer = howl.Buffer! editor.buffer = buffer search = spy.new (query) -> return if query.is_empty start = 1 find_iter_count = 0 text = buffer.text -> start_pos, end_pos = text\ufind query, start, true return unless start_pos start = end_pos + 1 find_iter_count += 1 buffer\chunk start_pos, end_pos
(on initialization)
calls add_widget on the passed command line, adding a list widget
search_view = SearchView editor: editor buffer: buffer :search search_view\init command_line, max_height: 100 assert.spy(command_line.add_widget).was_called 1 assert.same "ListWidget", typeof(list_widget)
displays the specified prompt and title
search_view = SearchView editor: editor buffer: buffer :search prompt: 'hello>' title: '#hello' search_view\init command_line, max_height: 100 assert.same 'hello>', command_line.prompt assert.same '#hello', command_line.title
displays no info message
buffer.text = 'content' search_view = SearchView editor: editor buffer: buffer :search search_view\init command_line, max_height: 100 set_text search_view, '' assert.is_nil found_count!
displays all lines of buffer
buffer.text = 'hello1\nhello2\nhello3' search_view = SearchView editor: editor buffer: buffer :search search_view\init command_line, max_height: 100 set_text search_view, '' items = list_widget_items! assert.same {'hello1', 'hello2', 'hello3'}, [item[2].text for item in *items] assert.same {}, [item.item_highlights for item in *items]
does not call search for empty query
search = spy.new -> search_view = SearchView editor: editor buffer: buffer :search search_view\init command_line, max_height: 100 set_text search_view, '' assert.spy(search).was_not_called!
(searching)
calls search(query) iterator when text is updated
buffer.text = 'buffer-content' search_view = SearchView :editor :buffer :search search_view\init command_line, max_height: 100 set_text search_view, 'query-target' assert.spy(search).was_called_with 'query-target'
(.. when query text is set)
before_each -> buffer.text = 'cöntent-line1\ncöntent-line12\ncontent-line3' search_view = SearchView :editor :buffer :search search_view\init command_line, max_height: 100
iterates over search(query)
set_text search_view, 'line1' assert.spy(search).was_called 1 assert.spy(search).was_called_with 'line1' assert.same 2, find_iter_count
displays the matching lines, at most one match per line
set_text search_view, 'line1' items = list_widget_items! assert.same {1, 2}, [item[1] for item in *items] assert.same {'cöntent-line1', 'cöntent-line12'}, [item[2].text for item in *items] set_text search_view, 'line3' items = list_widget_items! assert.same {3}, [item[1] for item in *items] assert.same {'content-line3'}, [item[2].text for item in *items]
displays an info message with match count
set_text search_view, 'line1' assert.same 2, found_count! set_text search_view, 'line12' assert.same 1, found_count! set_text search_view, 'line-' assert.same 0, found_count!
resets info message when query text is cleared
set_text search_view, 'line1' assert.same 2, found_count! set_text search_view, '' assert.is_nil found_count!
does not shrink diplayed list widget size
set_text search_view, 'li' height = list_widget_height! set_text search_view, 'line1' assert.same height, list_widget_height!
returns selected match on pressing enter
set_text search_view, 'line1' assert.spy(command_line.finish).was_not_called! keypress_binding_for 'cursor-down' keypress 'enter' assert.spy(command_line.finish).was_called 1 assert.same 23, finish_result.chunk.start_pos assert.same 27, finish_result.chunk.end_pos
displays error message on pressing enter when no selection
set_text search_view, 'xxxxxxxxxxxxx' keypress 'enter' assert.spy(command_line.finish).was_not_called! assert.spy(command_line.notification.error).was_called 1 assert.same error_message, 'No selection'
(.. selected line)
is centered
set_text search_view, 'line1' assert.same 1, editor.line_at_center
is changed by keypresses
set_text search_view, 'line1' keypress_binding_for 'cursor-down' assert.same 2, editor.line_at_center keypress_binding_for 'cursor-up' assert.same 1, editor.line_at_center
(.. when search raises an error)
before_each -> search_view = SearchView :editor :buffer search: -> error 'search-error', 0 search_view\init command_line, max_height: 100
displays the error message
set_text search_view, 'query' assert.same 'search-error', error_message
(.. when limit is set)
before_each -> buffer.text = 'a a a a a a a a' search_view = SearchView :editor :buffer :search limit: 3 search_view\init command_line, max_height: 100
does not iterate once limit matches are found
set_text search_view, 'a' assert.same 3, find_iter_count
(replacing)
local replace before_each -> replace = spy.new (_, _, _, replacement)-> replacement search = spy.new (query) -> return if query.is_empty start = 1 find_iter_count = 0 text = buffer.text -> start_pos, end_pos = text\ufind query, start, true return unless start_pos start = end_pos + 1 find_iter_count += 1 buffer\chunk(start_pos, end_pos), 'match-info'
parses query and calls search(query) with the search part
buffer.text = 'buffer-content' search_view = SearchView :editor :buffer :search :replace search_view\init command_line, max_height: 100 set_text search_view, '/search' assert.spy(search).was_called_with 'search'
displays all matches
buffer.text = 'cöntent-line1\ncöntent-line2\n' search_view = SearchView :editor :buffer :search :replace search_view\init command_line, max_height: 100 set_text search_view, '/t' assert.same {'cöntent-line1', 'cöntent-line1', 'cöntent-line2', 'cöntent-line2'}, [item[2].text for item in *list_widget_items!]
(.. when the query includes a replacement)
before_each -> buffer.text = 'content-line1\ncontent-line2\n' search_view = SearchView :editor :buffer :search :replace search_view\init command_line, max_height: 100
calls replace for each match
set_text search_view, '/content/<new>' assert.spy(replace).was_called 2 assert.spy(replace).was_called_with match._, 'match-info', match._, '<new>'
displays a preview with replacements applied
editor.line_at_top = 1 editor.line_at_bottom = 2 set_text search_view, '/line/<new>' assert.spy(editor.preview).was_called 1 assert.same 'content-<new>1\ncontent-<new>2\n', preview_buffer.text assert.same {'search'}, howl.ui.highlight.at_pos preview_buffer, 9
returns text with replacements applied as previewed
set_text search_view, '/line/<new>' text = preview_buffer.text keypress 'enter' assert.same text, finish_result.replacement_text assert.same 2, finish_result.replacement_count
(.. if no replacement text)
previews deletions using strikeout style
editor.line_at_top = 1 editor.line_at_bottom = 2 set_text search_view, '/line/' text = preview_buffer.text assert.same buffer.text, text assert.same {'replace_strikeout', 'search'}, howl.ui.highlight.at_pos preview_buffer, 9 assert.same {'replace_strikeout', 'search'}, howl.ui.highlight.at_pos preview_buffer, 10
returns text with deletions applied
set_text search_view, '/line/' keypress 'enter' assert.same 'content-1\ncontent-2\n', finish_result.replacement_text
(.. if no trailing delimiter)
previews existing buffer
set_text search_view, '/line' assert.equal preview_buffer, buffer
returns nil
set_text search_view, '/line' keypress 'enter' assert.is_nil finish_result