Skip to content

Commit

Permalink
REPL: tab/backspace aligns to 4
Browse files Browse the repository at this point in the history
* when pressing tab, compute the number of spaces to insert
  so that the cursor is aligned to a multiple of 4 chars
* when pressing backspace, delete up to 4 spaces so that
  the cursor is aligned to a multiple of 4 chars
  • Loading branch information
rfourquet committed Jul 27, 2017
1 parent 85a2555 commit 5dcb0d6
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 28 deletions.
70 changes: 42 additions & 28 deletions base/repl/LineEdit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -478,18 +478,29 @@ function edit_insert(buf::IOBuffer, c)
end
end

function edit_backspace(s::PromptState)
if edit_backspace(s.input_buffer)
function edit_backspace(s::PromptState, multispaces::Bool=true)
if edit_backspace(s.input_buffer, multispaces)
refresh_line(s)
else
beep(terminal(s))
end
end
function edit_backspace(buf::IOBuffer)
if position(buf) > 0
oldpos = position(buf)
char_move_left(buf)
splice_buffer!(buf, position(buf):oldpos-1)

function edit_backspace(buf::IOBuffer, multispaces::Bool=true)
oldpos = position(buf)
if oldpos > 0
c = char_move_left(buf)
newpos = position(buf)
if multispaces && c == ' ' # maybe delete multiple spaces
beg = rsearch(buf.data, '\n', newpos)
align = strwidth(String(buf.data[1+beg:newpos])) % 4
nonspace = findprev(c -> c != UInt8(' '), buf.data, newpos)
if newpos-align >= nonspace
newpos = newpos-align
seek(buf, newpos)
end
end
splice_buffer!(buf, newpos:oldpos-1)
return true
else
return false
Expand Down Expand Up @@ -1329,30 +1340,33 @@ function bracketed_paste(s)
return replace(input, '\t', " "^tabwidth)
end

function edit_tab(s)
buf = buffer(s)
# Yes, we are ignoring the possiblity
# the we could be in the middle of a multi-byte
# sequence, here but that's ok, since any
# whitespace we're interested in is only one byte
i = position(buf)
if i != 0
c = buf.data[i]
if c == UInt8('\n') || c == UInt8('\t') ||
# hack to allow path completion in cmds
# after a space, e.g., `cd <tab>`, while still
# allowing multiple indent levels
(c == UInt8(' ') && i > 3 && buf.data[i-1] == UInt8(' '))
beg = rsearch(buf.data, '\n', i)
align = 4 - strwidth(String(buf.data[1+beg:i])) % 4 # align to multiples of 4
return edit_insert(s, " "^align)
end
end
complete_line(s)
refresh_line(s)
end

const default_keymap =
AnyDict(
# Tab
'\t' => (s,o...)->begin
buf = buffer(s)
# Yes, we are ignoring the possiblity
# the we could be in the middle of a multi-byte
# sequence, here but that's ok, since any
# whitespace we're interested in is only one byte
i = position(buf)
if i != 0
c = buf.data[i]
if c == UInt8('\n') || c == UInt8('\t') ||
# hack to allow path completion in cmds
# after a space, e.g., `cd <tab>`, while still
# allowing multiple indent levels
(c == UInt8(' ') && i > 3 && buf.data[i-1] == UInt8(' '))
edit_insert(s, " "^4)
return
end
end
complete_line(s)
refresh_line(s)
end,
'\t' => (s,o...)->edit_tab(s),
# Enter
'\r' => (s,o...)->begin
if on_enter(s) || (eof(buffer(s)) && s.key_repeats > 1)
Expand Down
33 changes: 33 additions & 0 deletions test/lineedit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,36 @@ let
Base.LineEdit.InputAreaState(0,0), "julia> ", indent = 7)
@test s == Base.LineEdit.InputAreaState(3,1)
end

@testset "tab/backspace alignment feature" begin
term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer())
s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")]))
function bufferdata(s)
buf = LineEdit.buffer(s)
String(buf.data[1:buf.size])
end

LineEdit.edit_insert(s, "for x=1:10\n")
LineEdit.edit_tab(s)
@test bufferdata(s) == "for x=1:10\n "
LineEdit.edit_backspace(s)
@test bufferdata(s) == "for x=1:10\n"
LineEdit.edit_insert(s, " ")
LineEdit.edit_tab(s)
@test bufferdata(s) == "for x=1:10\n "
LineEdit.edit_insert(s, " ")
LineEdit.edit_backspace(s)
@test bufferdata(s) == "for x=1:10\n "
LineEdit.edit_insert(s, "éé=3 ")
LineEdit.edit_tab(s)
@test bufferdata(s) == "for x=1:10\n éé=3 "
LineEdit.edit_backspace(s)
@test bufferdata(s) == "for x=1:10\n éé=3"
LineEdit.edit_insert(s, "\n 1∉x ")
LineEdit.edit_tab(s)
@test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x "
LineEdit.edit_backspace(s, false)
@test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x "
LineEdit.edit_backspace(s)
@test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x "
end

0 comments on commit 5dcb0d6

Please sign in to comment.