howl.breadcrumbs
local editor, cursor, old_tolerance buffer = (text) -> with Buffer {} .text = text setup -> app.window = Window! editor = app\new_editor! cursor = editor.cursor app.editor = editor breadcrumbs.init! old_tolerance = config.breadcrumb_tolerance teardown -> config.breadcrumb_tolerance = old_tolerance app.editor = nil app.window\destroy! before_each -> app.editor.buffer = buffer '' config.breadcrumb_tolerance = 0 after_each -> breadcrumbs.clear!
drop(opts)
accepts a file and a pos
File.with_tmpfile (file) -> breadcrumbs.drop :file, pos: 3 assert.same {:file, pos: 3}, breadcrumbs.previous
accepts a file path and a pos
File.with_tmpfile (file) -> breadcrumbs.drop :file, pos: 3 assert.same {file: File(file), pos: 3}, breadcrumbs.previous
accepts a buffer and a pos
File.with_tmpfile (file) -> file.contents = '123456789\nabcdefgh' b = buffer '' b.file = file breadcrumbs.drop buffer: b, pos: 3 assert.equals b.file, breadcrumbs.previous.file assert.equals 3, breadcrumbs.previous.pos assert.not_nil breadcrumbs.previous.buffer_marker
(when a buffer is present)
sets a marker in the buffer pointing to the crumb
b = buffer '123456789\nabcdefgh' breadcrumbs.drop buffer: b, pos: 3 assert.equals 3, breadcrumbs.previous.pos assert.not_nil breadcrumbs.previous.buffer_marker m = breadcrumbs.previous.buffer_marker marker_buffer = m.buffer markers = marker_buffer.markers\find name: m.name assert.equals 1, #markers assert.equals 3, markers[1].start_offset assert.equals 3, markers[1].end_offset
(when opts is missing)
adds a crumb for the current buffer and position
File.with_tmpfile (file) -> file.contents = '1234\n6789' editor.buffer.file = file cursor.pos = 7 breadcrumbs.drop! crumb = breadcrumbs.previous assert.equals 7, crumb.pos assert.equals editor.buffer, crumb.buffer_marker.buffer assert.equals file, crumb.file
(when forward crumbs exists)
invalidates all such crumbs and buffer markers
b = buffer '123456789\nabcdefgh' editor.buffer = b breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 6 breadcrumbs.drop buffer: b, pos: 9 cursor.pos = 10 breadcrumbs.go_back! -- loc 3 assert.equals 4, #breadcrumbs.trail breadcrumbs.go_back! -- loc 2 assert.equals 4, #breadcrumbs.trail assert.is_not_nil breadcrumbs.trail[2] assert.is_not_nil breadcrumbs.trail[3] assert.is_not_nil breadcrumbs.trail[4] assert.equals 2, breadcrumbs.location breadcrumbs.drop buffer: b, pos: 4 assert.equals 4, breadcrumbs.trail[2].pos assert.is_nil breadcrumbs.trail[3] markers = [m.start_offset for m in *b.markers\find({})] table.sort markers assert.same { 3, 4 }, markers
( context '(tolerance handling)', )
merges crumbs when their distance is within the tolerance
buf = editor.buffer buf.text = string.rep('123456789\n', 10) config.breadcrumb_tolerance = 5 breadcrumbs.drop buffer: buf, pos: 1 breadcrumbs.drop buffer: buf, pos: 6 assert.equals 1, #breadcrumbs.trail assert.equals 6, breadcrumbs.trail[1].pos markers = [m.start_offset for m in *buf.markers\find({})] assert.same { 6 }, markers breadcrumbs.drop buffer: buf, pos: 12 assert.equals 2, #breadcrumbs.trail assert.equals 12, breadcrumbs.trail[2].pos markers = [m.start_offset for m in *buf.markers\find({})] assert.same { 6, 12 }, markers
(house cleaning according to breadcrumb_limit)
local old_limit before_each -> old_limit = config.breadcrumb_limit config.breadcrumb_limit = 2 after_each -> config.breadcrumb_limit = old_limit
purges old crumbs according to breadcrumb_limit
b = buffer '123456789\nabcdefgh' breadcrumbs.drop buffer: b, pos: 1 breadcrumbs.drop buffer: b, pos: 2 breadcrumbs.drop buffer: b, pos: 3 assert.equals 2, #breadcrumbs.trail assert.equals 3, breadcrumbs.location assert.same {2, 3}, [c.pos for c in *breadcrumbs.trail]
crumb cleaning
removes duplicate crumbs
b = buffer '123456789' breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 3 assert.equals 1, #breadcrumbs.trail markers = b.markers\find {} assert.equals 1, #markers
reduces unnecessary loops
b = buffer '123456789' breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 6 breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 6 assert.equals 2, #breadcrumbs.trail assert.equals 3, breadcrumbs.location
clear
invalidates any existing crumbs (buffer markers and crumbs)
b = buffer '123456789\nabcdefgh' breadcrumbs.drop buffer: b, pos: 3 crumb = breadcrumbs.previous breadcrumbs.clear! assert.equals 1, breadcrumbs.location assert.same {}, b.markers\find name: crumb.buffer_marker.name assert.is_nil breadcrumbs.trail[1]
go_back
inserts a crumb if needed before going back
b = buffer '123456789' editor.buffer = b cursor.pos = 2 breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 5 cursor.pos = 7 breadcrumbs.go_back! assert.equals 5, cursor.pos -- at pos 5 assert.equals 2, breadcrumbs.location -- at breadcrumbs location 2 assert.equals 3, #breadcrumbs.trail -- with two forward crumbs assert.equals 5, breadcrumbs.trail[2].pos -- the old one assert.equals 7, breadcrumbs.trail[3].pos -- and the newly inserted breadcrumbs.go_back! assert.equals 3, cursor.pos -- at pos 3 assert.equals 1, breadcrumbs.location -- at breadcrumbs location 1 assert.equals 3, #breadcrumbs.trail -- only three forward crumbs assert.same {3, 5, 7}, [c.pos for c in *breadcrumbs.trail]
(with a buffer and pos available)
opens the buffer and set the current position
b = buffer '123456789\nabcdefgh' breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.go_back! assert.equals 3, cursor.pos assert.equals b, editor.buffer assert.equals 1, breadcrumbs.location
uses a buffer marker for positioning to account for updates
b = buffer '123456789' breadcrumbs.drop buffer: b, pos: 6 b\insert 'xx', 2 breadcrumbs.go_back! assert.equals 8, cursor.pos
(with a file and pos available)
opens the file and sets the current position
File.with_tmpfile (file) -> file.contents = '123456789\nabcdefgh' breadcrumbs.drop file: file, pos: 3 breadcrumbs.go_back! assert.equals 3, cursor.pos assert.equals file, editor.buffer.file
(when the buffer has been collected)
falls back to the file when available
File.with_tmpfile (file) -> file.contents = '1234\n6789' b = buffer '' b.file = file breadcrumbs.drop file: file, buffer: b, pos: 3 b = nil collectgarbage! breadcrumbs.go_back! assert.equals 3, cursor.pos assert.equals file, editor.buffer.file
moves to the crumb before if present
b1 = buffer 'buffer1' breadcrumbs.drop buffer: b1, pos: 3 b2 = buffer 'buffer2' breadcrumbs.drop buffer: b2, pos: 5 b2 = nil collectgarbage! breadcrumbs.go_back! assert.equals 3, cursor.pos assert.equals b1, editor.buffer assert.equals 1, breadcrumbs.location
(tolerance handling)
moves beyond the previous crumb if it is within the distance
b = editor.buffer b.text = string.rep('1234567890', 2) config.breadcrumb_tolerance = 2 breadcrumbs.drop buffer: b, pos: 1 breadcrumbs.drop buffer: b, pos: 4 breadcrumbs.drop buffer: b, pos: 7 breadcrumbs.drop buffer: b, pos: 10 cursor.pos = 12 breadcrumbs.go_back! assert.equals 7, cursor.pos breadcrumbs.go_back! assert.equals 4, cursor.pos
go_forward
inserts a crumb if needed before going forward
b = buffer '123456789' editor.buffer = b breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 5 breadcrumbs.drop buffer: b, pos: 7 cursor.pos = 7 breadcrumbs.go_back! breadcrumbs.go_back! breadcrumbs.go_back! assert.equals 3, cursor.pos assert.equals 3, #breadcrumbs.trail -- with three forward crumbs assert.equals 1, breadcrumbs.location -- at breadcrumbs location 1 cursor.pos = 4 breadcrumbs.go_forward! assert.equals 5, cursor.pos -- at pos 5 assert.equals 4, #breadcrumbs.trail -- with two forward crumbs assert.equals 3, breadcrumbs.location -- with location thus = 3 assert.equals 3, breadcrumbs.trail[1].pos -- old back crumb assert.equals 4, breadcrumbs.trail[2].pos -- newly inserted assert.equals 5, breadcrumbs.trail[3].pos -- old forward crumb assert.equals 7, breadcrumbs.trail[4].pos -- old forward crumb breadcrumbs.go_forward! assert.equals 7, cursor.pos -- at pos 5 assert.equals 4, #breadcrumbs.trail assert.equals 4, breadcrumbs.location
(with a buffer and pos available)
opens the buffer and set the current position
b = buffer '123456789\nabcdefgh' breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop buffer: b, pos: 7 breadcrumbs.go_back! breadcrumbs.go_back! breadcrumbs.go_forward! assert.equals 7, cursor.pos assert.equals b, editor.buffer
uses a buffer marker for positioning to account for updates
b = buffer '123456789' breadcrumbs.drop buffer: b, pos: 1 breadcrumbs.drop buffer: b, pos: 6 breadcrumbs.go_back! breadcrumbs.go_back! b\insert 'xx', 2 breadcrumbs.go_forward! assert.equals 8, cursor.pos
(with a file and pos available)
opens the file and sets the current position
File.with_tmpfile (file) -> file.contents = '123456789\nabcdefgh' breadcrumbs.drop file: file, pos: 3 breadcrumbs.drop file: file, pos: 7 breadcrumbs.go_back! breadcrumbs.go_back! breadcrumbs.go_forward! assert.equals 7, cursor.pos assert.equals file, editor.buffer.file
(when the buffer has been collected)
falls back to the file when available
File.with_tmpfile (file) -> file.contents = '1234\n6789' b = buffer '' b.file = file breadcrumbs.drop file: file, buffer: b, pos: 3 breadcrumbs.drop file: file, buffer: b, pos: 7 b = nil breadcrumbs.go_back! breadcrumbs.go_back! collectgarbage! breadcrumbs.go_forward! assert.equals 7, cursor.pos assert.equals file, editor.buffer.file
moves to the crumb after if present
b1 = buffer 'buffer1' breadcrumbs.drop buffer: b1, pos: 3 breadcrumbs.drop buffer: b1, pos: 4 b2 = buffer 'buffer2' breadcrumbs.drop buffer: b2, pos: 5 breadcrumbs.go_back! breadcrumbs.go_back! b1 = nil collectgarbage! breadcrumbs.go_forward! assert.equals 5, cursor.pos assert.equals b2, editor.buffer
(tolerance handling)
moves beyond the next crumb if it is within the distance
b = editor.buffer b.text = string.rep('1234567890', 2) config.breadcrumb_tolerance = 2 breadcrumbs.drop buffer: b, pos: 1 breadcrumbs.drop buffer: b, pos: 4 breadcrumbs.drop buffer: b, pos: 7 breadcrumbs.go_back! breadcrumbs.go_back! breadcrumbs.go_back! cursor.pos = 2 breadcrumbs.go_forward! assert.equals 7, cursor.pos
location
points to the current crumb position
b = editor.buffer b.text = string.rep('1234567890', 2) assert.equals 1, breadcrumbs.location breadcrumbs.drop buffer: b, pos: 1 assert.equals 2, breadcrumbs.location breadcrumbs.drop buffer: b, pos: 4 assert.equals 3, breadcrumbs.location
can be assigned to move to a specific location
b = editor.buffer b.text = string.rep('1234567890', 2) breadcrumbs.drop buffer: b, pos: 1 breadcrumbs.drop buffer: b, pos: 4 breadcrumbs.drop buffer: b, pos: 8 breadcrumbs.location = 2 assert.equals 2, breadcrumbs.location assert.equals 4, cursor.pos
.trail
contains a list of breadcrumbs
b = buffer '123456789' File.with_tmpfile (file) -> breadcrumbs.drop buffer: b, pos: 3 breadcrumbs.drop :file, pos: 6 crumbs = breadcrumbs.trail assert.equals 3, crumbs[1].pos assert.equals b, crumbs[1].buffer_marker.buffer assert.same {:file, pos: 6}, crumbs[2]
is automatically cleaned
b1 = buffer '123456789' b2 = buffer '123456789' breadcrumbs.drop buffer: b1, pos: 3 breadcrumbs.drop buffer: b2, pos: 6 b1 = nil collectgarbage! crumbs = breadcrumbs.trail assert.equals 1, #crumbs assert.equals 6, crumbs[1].pos
(when a buffer is closed)
removes any crumbs missing a file reference
b1 = buffer '123456789' b2 = app\new_buffer! b2.text = '123456789' b2.modified = false breadcrumbs.drop buffer: b1, pos: 3 breadcrumbs.drop buffer: b2, pos: 5 breadcrumbs.drop buffer: b1, pos: 7 assert.equals 3, #breadcrumbs.trail assert.equals 4, breadcrumbs.location app\close_buffer b2 assert.equals 2, #breadcrumbs.trail assert.equals 3, breadcrumbs.location assert.same {3, 7}, [c.pos for c in *breadcrumbs.trail]
clears any buffer references for crumbs with a file reference
File.with_tmpfile (file) -> file.contents = '123456789' b1 = buffer '123456789' b2 = app\new_buffer! b2.file = file breadcrumbs.drop buffer: b1, pos: 3 breadcrumbs.drop buffer: b2, pos: 5 breadcrumbs.drop buffer: b1, pos: 7 assert.equals 3, #breadcrumbs.trail assert.equals 4, breadcrumbs.location app\close_buffer b2 assert.is_nil breadcrumbs.trail[2].buffer_marker assert.equals 3, #breadcrumbs.trail assert.equals 4, breadcrumbs.location
moves the current location down as necessary
File.with_tmpfile (file) -> file.contents = '123456789' b1 = buffer '123456789' b2 = app\new_buffer! b2.file = file breadcrumbs.drop buffer: b1, pos: 3 breadcrumbs.drop buffer: b2, pos: 5 breadcrumbs.drop buffer: b2, pos: 7 assert.equals 4, breadcrumbs.location app\close_buffer b2 assert.equals 1, breadcrumbs.location
(memory management)
keeps weak references to buffers
holder = setmetatable { buffer: buffer '123456789\nabcdefgh' }, __mode: 'v' breadcrumbs.drop buffer: holder.buffer, pos: 3 collectgarbage! assert.is_nil holder.buffer
