class Reply::ExpressionEditor

Overview

The ExpressionEditor allows to edit and display an expression.

Its main task is to provide the display of the prompt and a multiline expression within the term bounds, and ensure the correspondence between the cursor on screen and the cursor on the expression.

Usage example:

# new editor:
@editor = ExpressionEditor.new(
  prompt: ->(expr_line_number : Int32) { "prompt>" }
)

# edit some code:
@editor.update do
  @editor << %(puts "World")

  insert_new_line(indent: 1)
  @editor << %(puts "!")
end

# move cursor:
@editor.move_cursor_up
4.times { @editor.move_cursor_left }

# edit:
@editor.update do
  @editor << "Hello "
end

@editor.end_editing

@editor.expression # => %(puts "Hello World"\n  puts "!")
puts "=> ok"

# clear and restart edition:
@editor.prompt_next

The above displays:

prompt>puts "Hello World"
prompt>  puts "!"
=> ok
prompt>

Methods that modify the expression should be placed inside an #update so the screen can be refreshed taking in account the adding or removing of lines, and doesn't boilerplate the display.

Defined in:

expression_editor.cr

Constructors

Class Method Summary

Instance Method Summary

Constructor Detail

def self.new(&prompt : Int32, Bool -> String) #

Creates a new ExpressionEditor with the given prompt.


[View source]

Class Method Detail

def self.parts_from_colorized(line, width, prompt_size = 0) #

Splits the given line (colorized) into parts delimited by wrapping.

Because line is colorized, it's hard to know when it's wrap based on its size (colors sequence might appear anywhere in the string) Here we does the following:

  • Create a String::Builder for the first part (part_builder)
  • Iterate over the line, parsing the color sequence
  • Count cursor #x for each char unless color sequences
  • If count goes over term width: reset #x to 0, and create a new String::Builder for next part.

[View source]

Instance Method Detail

def <<(char : Char) : self #

Should be called inside an #update.

If char is \n or \r, inserts a new line with indent 0. Does nothing if the char is an ascii_control?.


[View source]
def <<(str : String) : self #

Should be called inside an #update.


[View source]
def back #

Should be called inside an #update.


[View source]
def clear_expression #

Should be called inside an #update.


[View source]
def color=(color : Bool) #

[View source]
def color? : Bool #

[View source]
def current_line #

[View source]
def current_line=(line) #

Should be called inside an #update.


[View source]
def current_word #

Returns the word under the cursor following #word_delimiters.


[View source]
def current_word=(replacement) #

Replaces the word under the cursor by replacement, then moves cursor at the end of replacement. Should be called inside an #update.


[View source]
def current_word_begin_end #

Returns begin and end position of #current_word.


[View source]
def cursor_on_last_line? #

[View source]
def delete #

Should be called inside an #update.


[View source]
def delete_after_cursor #

Should be called inside an #update.


[View source]
def delete_before_cursor #

Should be called inside an #update.


[View source]
def delete_line(y) #

Should be called inside an #update.


[View source]
def delete_word #

Should be called inside an #update.


[View source]
def empty? #

[View source]
def end_editing(replacement : Array(String) | Nil = nil) #

Prints the full expression (without view bounds), and eventually replace it by replacement.


[View source]
def expression : String | Nil #

[View source]
def expression_before_cursor(x = @x, y = @y) #

[View source]
def expression_height : Int32 | Nil #

[View source]
def height #

The editor height, if not set (nil), equal to term height.


[View source]
def height=(height : Int32 | Nil) #

The editor height, if not set (nil), equal to term height.


[View source]
def insert_new_line(indent) #

Should be called inside an #update.


[View source]
def lines : Array(String) #

[View source]
def move_cursor_down(allow_scrolling = true) #

[View source]
def move_cursor_left(allow_scrolling = true) #

[View source]
def move_cursor_right(allow_scrolling = true) #

[View source]
def move_cursor_to(x, y, allow_scrolling = true) #

[View source]
def move_cursor_to_begin(allow_scrolling = true) #

[View source]
def move_cursor_to_end(allow_scrolling = true) #

[View source]
def move_cursor_to_end_of_line(y = @y, allow_scrolling = true) #

[View source]
def move_cursor_up(allow_scrolling = true) #

[View source]
def move_word_backward #

[View source]
def move_word_forward #

[View source]
def next_line=(line) #

Should be called inside an #update.


[View source]
def next_line? #

[View source]
def output : IO #

[View source]
def output=(output : IO) #

[View source]
def previous_line=(line) #

Should be called inside an #update.


[View source]
def previous_line? #

[View source]
def prompt_next #

Clears the expression and start a new prompt on a next line.


[View source]
def replace(lines : Array(String)) #

[View source]
def scroll_down #

[View source]
def scroll_up #

[View source]
def set_footer(&footer : IO, Int32 -> Int32) #

Sets a Proc allowing to display a footer under the prompt. (used by search)

io: The IO in which the footer should be displayed. previous_height: Previous footer height. Should returns the exact height printed in the io.


[View source]
def set_header(&header : IO, Int32 -> Int32) #

Sets a Proc allowing to display a header above the prompt. (used by auto-completion)

io: The IO in which the header should be displayed. previous_height: Previous header height, useful to keep a header size constant. Should returns the exact height printed in the io.


[View source]
def set_highlight(&highlight : String -> String) #

Sets the Proc to highlight the expression.


[View source]
def update(force_full_view = false, &) #

Refresh the screen.

It clears the display of the current expression, then yields for modifications, and displays the new expression.

if force_full_view is true, whole expression is displayed, even if it overflow the term width, otherwise the expression is bound and can be scrolled.


[View source]
def update(force_full_view = false) #

[View source]
def width #

The editor width, if not set (nil), equal to term width.


[View source]
def width=(width : Int32 | Nil) #

The editor width, if not set (nil), equal to term width.


[View source]
def word_back #

Should be called inside an #update.


[View source]
def word_delimiters : Array(Char) #

The list of characters delimiting words.

default: \n\t+-*/,;@&%<>"'^\\[](){}|.~:=!?


[View source]
def word_delimiters=(word_delimiters : Array(Char)) #

The list of characters delimiting words.

default: \n\t+-*/,;@&%<>"'^\\[](){}|.~:=!?


[View source]
def x : Int32 #

Tracks the cursor position relatively to the expression's lines, (y=0 corresponds to the first line and x=0 the first char) This position is independent of text wrapping so its position will not match to real cursor on screen.

| : cursor position

prompt>def very_looo
ooo|ng_name            <= wrapping
prompt>  bar
prompt>end

For example here the cursor position is x=16, y=0, but real cursor is at x=3,y=1 from the beginning of expression.


[View source]
def y : Int32 #

[View source]