## special shortcuts for manipulating the editor # Some keys on the keyboard generate unicode characters, others generate # terminfo key codes. We need to modify different places in the two cases. # tab - insert two spaces scenario editor-inserts-two-spaces-on-tab [ local-scope assume-screen 10/width, 5/height s:text <- new [ab cd] e:&:editor <- new-editor s, 0/left, 5/right editor-render screen, e $clear-trace assume-console [ press tab ] run [ editor-event-loop screen, console, e ] screen-should-contain [ . . . ab . .cd . ] # we render at most two editor rows worth (one row for each space) check-trace-count-for-label-lesser-than 10, [print-character] ] scenario editor-inserts-two-spaces-and-wraps-line-on-tab [ local-scope assume-screen 10/width, 5/height e:&:editor <- new-editor [abcd], 0/left, 5/right editor-render screen, e $clear-trace assume-console [ press tab ] run [ editor-event-loop screen, console, e ] screen-should-contain [ . . . ab↩ . .cd . ] # we re-render the whole editor check-trace-count-for-label-greater-than 10, [print-character] ] after [ { tab?:bool <- equal c, 9/tab break-unless tab? # todo: decompose insert-at-cursor into editor update and screen update, # so that 'tab' doesn't render the current line multiple times insert-at-cursor editor, 32/space, screen go-render? <- insert-at-cursor editor, 32/space, screen return } ] # backspace - delete character before cursor scenario editor-handles-backspace-key [ local-scope assume-screen 10/width, 5/height e:&:editor <- new-editor [abc], 0/left, 10/right editor-render screen, e $clear-trace assume-console [ left-click 1, 1 press backspace ] run [ editor-event-loop screen, console, e 4:num/raw <- get *e, cursor-row:offset 5:num/raw <- get *e, cursor-column:offset ] screen-should-contain [ . . .bc . .┈┈┈┈┈┈┈┈┈┈. . . ] memory-should-contain [ 4 <- 1 5 <- 0 ] check-trace-count-for-label 3, [print-character] # length of original line to overwrite ] after [ { delete-previous-character?:bool <- equal c, 8/backspace break-unless delete-previous-character? go-render?:bool, backspaced-cell:&:duplex-list:char <- delete-before-cursor editor, screen return } ] # return values: # go-render? - whether caller needs to update the screen # backspaced-cell - value deleted (or 0 if nothing was deleted) so we can save it for undo, etc. def delete-before-cursor editor:&:editor, screen:&:screen -> go-render?:bool, backspaced-cell:&:duplex-list:char, editor:&:editor, screen:&:screen [ local-scope load-inputs before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset data:&:duplex-list:char <- get *editor, data:offset # if at start of text (before-cursor at § sentinel), return prev:&:duplex-list:char <- prev before-cursor return-unless prev, false/no-more-render, null/nothing-deleted trace 10, [app], [delete-before-cursor] original-row:num <- get *editor, cursor-row:offset move-cursor-coordinates-left editor backspaced-cell:&:duplex-list:char <- copy before-cursor data <- remove before-cursor, data # will also neatly trim next/prev pointers in backspaced-cell/before-cursor before-cursor <- copy prev *editor <- put *editor, before-cursor:offset, before-cursor screen-width:num <- screen-width screen cursor-row:num <- get *editor, cursor-row:offset cursor-column:num <- get *editor, cursor-column:offset # did we just backspace over a newline? same-row?:bool <- equal cursor-row, original-row return-unless same-row?, true/go-render left:num <- get *editor, left:offset right:num <- get *editor, right:offset curr:&:duplex-list:char <- next before-cursor screen <- move-cursor screen, cursor-row, cursor-column curr-column:num <- copy cursor-column { # hit right margin? give up and let caller render at-right?:bool <- greater-or-equal curr-column, right return-if at-right?, true/go-render break-unless curr # newline? done. currc:char <- get *curr, value:offset at-newline?:bool <- equal currc, 10/newline break-if at-newline? screen <- print screen, currc curr-column <- add curr-column, 1 curr <- next curr loop } # we're guaranteed not to be at the right margin space:char <- copy 32/space screen <- print screen, space go-render? <- copy false ] def move-cursor-coordinates-left editor:&:editor -> editor:&:editor [ local-scope load-inputs before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset cursor-row:num <- get *editor, cursor-row:offset cursor-column:num <- get *editor, cursor-column:offset left:num <- get *editor, left:offset # if not at left margin, move one character left { at-left-margin?:bool <- equal cursor-column, left break-if at-left-margin? trace 10, [app], [decrementing cursor column] cursor-column <- subtract cursor-column, 1 *editor <- put *editor, cursor-column:offset, cursor-column return } # if at left margin, we must move to previous row: top-of-screen?:bool <- equal cursor-row, 1 # exclude menu bar { break-if top-of-screen? cursor-row <- subtract cursor-row, 1 *editor <- put *editor, cursor-row:offset, cursor-row } { break-unless top-of-screen? # no scroll, so do nothing } { # case 1: if previous character was newline, figure out how long the previous line is previous-character:char <- get *before-cursor, value:offset previous-character-is-newline?:bool <- equal previous-character, 10/newline break-unless previous-character-is-newline? # compute length of previous line trace 10, [app], [switching to previous line] d:&:duplex-list:char <- get *editor, data:offset end-of-line:num <- previous-line-length before-cursor, d right:num <- get *editor, right:offset width:num <- subtract right, left wrap?:bool <- greater-than end-of-line, width { break-unless wrap? _, column-offset:num <- divide-with-remainder end-of-line, width cursor-column <- add left, column-offset *editor <- put *editor, cursor-column:offset, cursor-column } { break-if wrap? cursor-column <- add left, end-of-line *editor <- put *editor, cursor-column:offset, cursor-column } return } # case 2: if previous-character was not newline, we're just at a wrapped line trace 10, [app], [wrapping to previous line] right:num <- get *editor, right:offset cursor-column <- subtract right, 1 # leave room for wrap icon *editor <- put *editor, cursor-column:offset, cursor-column ] # takes a pointer 'curr' into the doubly-linked list and its sentinel, counts # the length of the previous line before the 'curr' pointer. def previous-line-length curr:&:duplex-list:char, start:&:duplex-list:char -> result:num [ local-scope load-inputs result:num <- copy 0 return-unless curr at-start?:bool <- equal curr, start return-if at-start? { curr <- prev curr break-unless curr at-start?:bool <- equal curr, start break-if at-start? c:char <- get *curr, value:offset at-newline?:bool <- equal c, 10/newline break-if at-newline? result <- add result, 1 loop } ] scenario editor-clears-last-line-on-backspace [ local-scope assume-screen 10/width, 5/height s:text <- new [ab cd] e:&:editor <- new-editor s, 0/left, 10/right assume-console [ left-click 2, 0 press backspace ] run [ editor-event-loop screen, console, e 4:num/raw <- get *e, cursor-row:offset 5:num/raw <- get *e, cursor-column:offset ] screen-should-contain [ . . .abcd . .┈┈┈┈┈┈┈┈┈┈. . . ] memory-should-contain [ 4 <- 1 5 <- 2 ] ] scenario editor-joins-and-wraps-lines-on-backspace [ local-scope assume-screen 10/width, 5/height # initialize editor with two long-ish but non-wrapping lines s:text <- new [abc def ghi jkl] e:&:editor <- new-editor s, 0/left, 10/right editor-render screen, e $clear-trace # position the cursor at the start of the second and hit backspace assume-console [ left-click 2, 0 press backspace ] run [ editor-event-loop screen, console, e ] # resulting single line should wrap correctly screen-should-contain [ . . .abc defgh↩. .i jkl . .┈┈┈┈┈┈┈┈┈┈. . . ] ] scenario editor-wraps-long-lines-on-backspace [ local-scope assume-screen 10/width, 5/height # initialize editor in part of the screen with a long line e:&:editor <- new-editor [abc def ghij], 0/left, 8/right editor-render screen, e # confirm that it wraps screen-should-contain [ . . .abc def↩ . . ghij . .┈┈┈┈┈┈┈┈ . ] $clear-trace # position the cursor somewhere in the middle of the top screen line and hit backspace assume-console [ left-click 1, 4 press backspace ] run [ editor-event-loop screen, console, e ] # resulting single line should wrap correctly and not overflow its bounds screen-should-contain [ . . .abcdef ↩ . .ghij . .┈┈┈┈┈┈┈┈ . . . ] ] # delete - delete character at cursor scenario editor-handles-delete-key [ local-scope assume-screen 10/width, 5/height e:&:editor <- new-editor [abc], 0/left, 10/right editor-render screen, e $clear-trace assume-console [ press delete ] run [ editor-event-loop screen, console, e ] screen-should-contain [ . . .bc . .┈┈┈┈┈┈┈┈┈┈. . . ] check-trace-count-for-label 3, [print-character] # length of original line to overwrite $clear-trace assume-console [ press delete ] run [ editor-event-loop screen, console, e ] screen-should-contain [ . . .c . .┈┈┈┈┈┈┈┈┈┈. . . ] check-trace-count-for-label 2, [print-character] # new length to overwrite ] after [ { delete-next-character?:bool <- equal k, 65522/delete break-unless delete-next-character? go-render?:bool
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module ranger.container.commandlist</title>
</head><body bgcolor="#f0f0f8">

<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="ranger.html"><font color="#ffffff">ranger</font></a>.<a href="ranger.container.html"><font color="#ffffff">container</font></a>.commandlist</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/hut/ranger/ranger/container/commandlist.py">/home/hut/ranger/ranger/container/commandlist.py</a></font></td></tr></table>
    <p><tt>#&nbsp;Copyright&nbsp;(C)&nbsp;2009,&nbsp;2010&nbsp;&nbsp;Roman&nbsp;Zimbelmann&nbsp;&lt;romanz@lavabit.com&gt;<br>
#<br>
#&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software:&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and/or&nbsp;modify<br>
#&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;as&nbsp;published&nbsp;by<br>
#&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation,&nbsp;either&nbsp;version&nbsp;3&nbsp;of&nbsp;the&nbsp;License,&nbsp;or<br>
#&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.<br>
#<br>
#&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,<br>
#&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of<br>
#&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the<br>
#&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.<br>
#<br>
#&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License<br>
#&nbsp;along&nbsp;with&nbsp;this&nbsp;program.&nbsp;&nbsp;If&nbsp;not,&nbsp;see&nbsp;&lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
    
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="ranger.container.commandlist.html#Command">Command</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="ranger.container.commandlist.html#AliasedCommand">AliasedCommand</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="ranger.container.commandlist.html#CommandArgument">CommandArgument</a>
</font></dt><dt><font face="helvetica, arial"><a href="ranger.container.commandlist.html#CommandList">CommandList</a>
</font></dt><dt><font face="helvetica, arial"><a href="ranger.container.commandlist.html#Show">Show</a>
</font></dt></dl>
</dd>
</dl>
 <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="AliasedCommand">class <strong>AliasedCommand</strong></a>(<a href="ranger.container.commandlist.html#Command">Command</a>)</font></td></tr>
    
<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="ranger.container.commandlist.html#AliasedCommand">AliasedCommand</a></dd>
<dd><a href="ranger.container.commandlist.html#Command">Command</a></dd>
<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="AliasedCommand-__init__"><strong>__init__</strong></a>(self, getter, keys)</dt></dl>

<dl><dt><a name="AliasedCommand-get_execute"><strong>get_execute</strong></a>(self)</dt></dl>

<hr>
Data descriptors defined here:<br>
<dl><dt><strong>execute</strong></dt>
</dl>
<hr>
Methods inherited from <a href="ranger.container.commandlist.html#Command">Command</a>:<br>
<dl><dt><a name="AliasedCommand-execute_wrap"><strong>execute_wrap</strong></a>(self, displayable)</dt></dl>

<hr>
Data descriptors inherited from <a href="ranger.container.commandlist.html#Command">Command</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<hr>
Data and other attributes inherited from <a href="ranger.container.commandlist.html#Command">Command</a>:<br>
<dl><dt><strong>keys</strong> = []</dl>

</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="Command">class <strong>Command</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
    
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt><a href="#Command">Command</a>&nbsp;objects&nbsp;store&nbsp;information&nbsp;about&nbsp;a&nbsp;command<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="Command-__init__"><strong>__init__</strong></a>(self, fnc, keys)</dt></dl>

<dl><dt><a name="Command-execute"><strong>execute</strong></a>(self, *args)</dt><dd><tt>Execute&nbsp;the&nbsp;command</tt></dd></dl>

<dl><dt><a name="Command-execute_wrap"><strong>execute_wrap</strong></a>(self, displayable)</dt></dl>

<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<hr>
Data and other attributes defined here:<br>
<dl><dt><strong>keys</strong> = []</dl>

</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="CommandArgument">class <strong>CommandArgument</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
    
<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="CommandArgument-__init__"><strong>__init__</strong></a>(self, fm, displayable, keybuffer)</dt></dl>

<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="CommandList">class <strong>CommandList</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
    
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>CommandLists&nbsp;are&nbsp;dictionary-like&nbsp;objects&nbsp;which&nbsp;give&nbsp;you&nbsp;a&nbsp;command<br>
for&nbsp;a&nbsp;given&nbsp;key&nbsp;combination.&nbsp;&nbsp;CommandLists&nbsp;must&nbsp;be&nbsp;filled&nbsp;before&nbsp;use.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="CommandList-__call__"><strong>__call__</strong></a>(self, *args, **keywords)</dt></dl>

<dl><dt><a name="CommandList-__getitem__"><strong>__getitem__</strong></a>(self, key)</dt><dd><tt>Returns&nbsp;the&nbsp;command&nbsp;with&nbsp;the&nbsp;given&nbsp;key&nbsp;combination</tt></dd></dl>

<dl><dt><a name="CommandList-__init__"><strong>__init__</strong></a>(self)</dt></dl>

<dl><dt><a name="CommandList-alias"><strong>alias</strong></a>(self, existing, *new)</dt><dd><tt>bind&nbsp;the&nbsp;&lt;new&gt;&nbsp;keys&nbsp;to&nbsp;the&nbsp;command&nbsp;of&nbsp;the&nbsp;&lt;existing&gt;&nbsp;key</tt></dd></dl>

<dl><dt><a name="CommandList-bind"><strong>bind</strong></a>(self, fnc, *keys)</dt><dd><tt>create&nbsp;a&nbsp;<a href="#Command">Command</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;and&nbsp;assign&nbsp;it&nbsp;to&nbsp;the&nbsp;given&nbsp;key&nbsp;combinations.</tt></dd></dl>

<dl><dt><a name="CommandList-clear"><strong>clear</strong></a>(self)</dt><dd><tt>remove&nbsp;all&nbsp;bindings</tt></dd></