Feature #11084 » rb-readline.patch
| /dev/null | ||
|---|---|---|
|
The Readline module provides interface for GNU Readline.
|
||
|
This module defines a number of methods to facilitate completion
|
||
|
and accesses input history from the Ruby interpreter.
|
||
|
This module supported Edit Line(libedit) too.
|
||
|
libedit is compatible with GNU Readline.
|
||
|
GNU Readline:: http://www.gnu.org/directory/readline.html
|
||
|
libedit:: http://www.thrysoee.dk/editline/
|
||
|
See RDoc for Readline module.
|
||
| /dev/null | ||
|---|---|---|
|
GNU Readline によるコマンドライン入力インタフェースを提供するモジュール
|
||
|
です。GNU Readline の互換ライブラリのひとつである Edit Line(libedit) も
|
||
|
サポートしています。
|
||
|
GNU Readline:: http://www.gnu.org/directory/readline.html
|
||
|
libedit:: http://www.thrysoee.dk/editline/
|
||
|
Readline.readline を使用してユーザからの入力を取得できます。このとき、
|
||
|
GNU Readline のように入力の補完やEmacs のようなキー操作などができます。
|
||
|
require "readline"
|
||
|
while buf = Readline.readline("> ", true)
|
||
|
p buf
|
||
|
end
|
||
|
ユーザが入力した内容を履歴(以下、ヒストリ)として記録することができます。
|
||
|
定数 Readline::HISTORY を使用してヒストリにアクセスできます。
|
||
|
require "readline"
|
||
|
while buf = Readline.readline("> ", true)
|
||
|
p Readline::HISTORY.to_a
|
||
|
print("-> ", buf, "\n")
|
||
|
end
|
||
|
使用するライブラリにより、いくつかのメソッドで例外 NotImplementedError
|
||
|
が発生します。
|
||
|
== Readline モジュール
|
||
|
=== モジュール関数
|
||
|
readline([prompt, [add_hist]]) -> String | nil
|
||
|
prompt を出力し、ユーザからのキー入力を待ちます。
|
||
|
エンターキーの押下などでユーザが文字列を入力し終えると、
|
||
|
入力した文字列を返します。
|
||
|
このとき、add_hist が true であれば、入力した文字列をヒストリに追加します。
|
||
|
何も入力していない状態で EOF(UNIX では ^D) を入力するなどで、
|
||
|
ユーザからの入力がない場合は nil を返します。
|
||
|
次の条件を全て満たす場合、例外 IOError が発生します。
|
||
|
1. 標準入力が tty でない。
|
||
|
2. 標準入力をクローズしている。(isatty(2) の errno が EBADF である。)
|
||
|
本メソッドはスレッドに対応しています。
|
||
|
入力待ち状態のときはスレッドコンテキストの切替えが発生します。
|
||
|
入力時には行内編集が可能で、vi モードと Emacs モードが用意されています。
|
||
|
デフォルトは Emacs モードです。
|
||
|
本メソッドには注意事項があります。
|
||
|
入力待ちの状態で ^C すると ruby インタプリタが終了し、端末状態を復帰しません。
|
||
|
これを回避するための例を3つ挙げます。
|
||
|
* ^CによるInterrupt例外を補足して、端末状態を復帰します:
|
||
|
require "readline"
|
||
|
stty_save = `stty -g`.chomp
|
||
|
begin
|
||
|
while buf = Readline.readline
|
||
|
p buf
|
||
|
end
|
||
|
rescue Interrupt
|
||
|
system("stty", stty_save)
|
||
|
exit
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
* INTシグナルを補足して、端末状態を復帰します:
|
||
|
require "readline"
|
||
|
stty_save = `stty -g`.chomp
|
||
|
trap("INT") { system "stty", stty_save; exit }
|
||
|
while buf = Readline.readline
|
||
|
p buf
|
||
|
end
|
||
|
* 単に ^C を無視する方法もあります:
|
||
|
require "readline"
|
||
|
trap("INT", "SIG_IGN")
|
||
|
while buf = Readline.readline
|
||
|
p buf
|
||
|
end
|
||
|
入力履歴 Readline::HISTORY を使用して、空行や直前の入力と同じ内容は入力
|
||
|
履歴に残さないということもできます。
|
||
|
require "readline"
|
||
|
while buf = Readline.readline("> ", true)
|
||
|
# p Readline::HISTORY.to_a
|
||
|
Readline::HISTORY.pop if /^\s*$/ =~ buf
|
||
|
begin
|
||
|
if Readline::HISTORY[Readline::HISTORY.length-2] == buf
|
||
|
Readline::HISTORY.pop
|
||
|
end
|
||
|
rescue IndexError
|
||
|
end
|
||
|
# p Readline::HISTORY.to_a
|
||
|
print "-> ", buf, "\n"
|
||
|
end
|
||
|
=== クラスメソッド
|
||
|
Readline.input = input
|
||
|
Readline.readline メソッドで使用する入力用の File オブジェクト input
|
||
|
を指定します。
|
||
|
Readline.output = output
|
||
|
Readline.readline メソッドで使用する出力用の File オブジェクト
|
||
|
output を指定します。
|
||
|
Readline.completion_proc = proc
|
||
|
ユーザからの入力を補完する時の候補を取得する Proc オブジェクト proc を
|
||
|
指定します。proc は、次のものを想定しています。
|
||
|
1. call メソッドを持ちます。
|
||
|
call メソッドを持たない場合、例外 ArgumentError が発生します。
|
||
|
2. 引数にユーザからの入力文字列(注1)を取ります。
|
||
|
3. 候補の文字列の配列を返します。
|
||
|
注1:「/var/lib /v」の後で補完を行うと、
|
||
|
デフォルトでは proc の引数に「/v」が渡されます。
|
||
|
このように、ユーザが入力した文字列を
|
||
|
Readline.completer_word_break_characters に含まれる文字で区切ったも
|
||
|
のを単語とすると、カーソルがある単語の最初の文字から現在のカーソル位
|
||
|
置までの文字列が proc の引数に渡されます。
|
||
|
Readline.completion_proc -> proc
|
||
|
ユーザからの入力を補完する時の候補を取得する Proc オブジェクト proc
|
||
|
を取得します。
|
||
|
Readline.completion_case_fold = bool
|
||
|
ユーザの入力を補完する際、大文字と小文字を区別する/しないを指定します。
|
||
|
bool が真ならば区別しません。bool が偽ならば区別します。
|
||
|
Readline.completion_case_fold -> bool
|
||
|
ユーザの入力を補完する際、大文字と小文字を区別する/しないを取得します。
|
||
|
bool が真ならば区別しません。bool が偽ならば区別します。
|
||
|
なお、Readline.completion_case_fold= メソッドで指定したオブジェクトを
|
||
|
そのまま取得するので、次のような動作をします。
|
||
|
require "readline"
|
||
|
Readline.completion_case_fold = "This is a String."
|
||
|
p Readline.completion_case_fold # => "This is a String."
|
||
|
Readline.line_buffer -> string
|
||
|
入力中の行全体を返します。complete_proc の中で使用することを想定し
|
||
|
ています。Readline.line_buffer の長さは GNU Readline の rl_end 変数の
|
||
|
値と一致します。
|
||
|
Readline.point -> int
|
||
|
現在のカーソルの位置を返します。
|
||
|
Readline モジュールは補完対象の単語の開始位置の情報を提供していません。
|
||
|
しかしながら、 completion_proc の中で入力した単語 text と
|
||
|
Readline.point を使用することで開始位置を導くことができます。
|
||
|
開始位置 = 入力した単語の長さ - Readline.point
|
||
|
Readline.vi_editing_mode -> nil
|
||
|
編集モードを vi モードにします。
|
||
|
vi モードの詳細は、GNU Readline のマニュアルを参照してください。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.vi_editing_mode? -> bool
|
||
|
編集モードが vi モードの場合、true を返します。そうでなければ false
|
||
|
を返します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.emacs_editing_mode -> nil
|
||
|
編集モードを Emacs モードにします。
|
||
|
デフォルトは Emacs モードです。
|
||
|
Emacs モードの詳細は、GNU Readline のマニュアルを参照してください。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.emacs_editing_mode? -> bool
|
||
|
編集モードが Emacs モードの場合、true を返します。そうでなければ false
|
||
|
を返します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.completion_append_character = char
|
||
|
ユーザの入力の補完が完了した場合に、最後に付加する文字 char を指定し
|
||
|
ます。半角スペース「" "」などの単語を区切る文字を指定すれば、連続して
|
||
|
入力する際に便利です。
|
||
|
使用例:
|
||
|
require "readline"
|
||
|
Readline.readline("> ", true)
|
||
|
Readline.completion_append_character = " "
|
||
|
実行例:
|
||
|
>
|
||
|
ここで "/var/li" を入力します。
|
||
|
> /var/li
|
||
|
ここで TAB キーを入力します。
|
||
|
> /var/lib
|
||
|
"b" が補完され、最後に " " が追加されるので、"/usr" を連続して入力できます。
|
||
|
> /var/lib /usr
|
||
|
なお、1文字しか指定することはできません。
|
||
|
例えば、"string"を指定した場合は最初の文字である"s"だけを使用します。
|
||
|
require "readline"
|
||
|
Readline.completion_append_character = "string"
|
||
|
p Readline.completion_append_character # => "s"
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.completion_append_character -> char
|
||
|
ユーザの入力の補完が完了した場合に、最後に付加する文字を取得します。
|
||
|
デフォルトは空白 (" ") です。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.basic_word_break_characters = string
|
||
|
ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成される
|
||
|
文字列 string を指定します。
|
||
|
GNU Readline のデフォルトの値は、Bash の補完処理で使用している文字列
|
||
|
" \t\n\"\\'`@$><=;|&{(" (スペースを含む) になっています。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.basic_word_break_characters -> string
|
||
|
ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成される
|
||
|
文字列を取得します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.completer_word_break_characters = string
|
||
|
ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成される
|
||
|
文字列 string を指定します。
|
||
|
Readline.basic_word_break_characters= との違いは、
|
||
|
GNU Readline の rl_complete_internal 関数で使用されることです。
|
||
|
GNU Readline のデフォルトの値は、
|
||
|
Readline.basic_word_break_characters と同じです。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.completer_word_break_characters -> string
|
||
|
ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成された
|
||
|
文字列を取得します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.basic_quote_characters = string
|
||
|
スペースなどの単語の区切りをクオートするための複数の文字で構成される
|
||
|
文字列 string を指定します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.basic_quote_characters -> string
|
||
|
スペースなどの単語の区切りをクオートするための複数の文字で構成される
|
||
|
文字列を取得します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.completer_quote_characters = string
|
||
|
ユーザの入力の補完を行う際、スペースなどの単語の区切りを
|
||
|
クオートするための複数の文字で構成される文字列 string を指定します。
|
||
|
指定した文字の間では、Readline.completer_word_break_characters=
|
||
|
で指定した文字列に含まれる文字も、普通の文字列として扱われます。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.completer_quote_characters -> string
|
||
|
ユーザの入力の補完を行う際、スペースなどの単語の区切りを
|
||
|
クオートするための複数の文字で構成される文字列を取得します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.filename_quote_characters = string
|
||
|
ユーザの入力時にファイル名の補完を行う際、スペースなどの単語の区切りを
|
||
|
クオートするための複数の文字で構成される文字列 string を指定します。
|
||
|
GNU Readline のデフォルト値は nil です。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
Readline.filename_quote_characters -> string
|
||
|
ユーザの入力時にファイル名の補完を行う際、スペースなどの単語の区切りを
|
||
|
クオートするための複数の文字で構成される文字列を取得します。
|
||
|
サポートしていない環境では、例外 NotImplementedError が発生します。
|
||
|
=== クラス定数
|
||
|
HISTORY
|
||
|
定数 HISTORY を使用してヒストリにアクセスできます。
|
||
|
Enumerable モジュールを extend しており、
|
||
|
配列のように振る舞うことができます。
|
||
|
例えば、HISTORY[4] により 5 番目に入力した内容を取り出すことができます。
|
||
|
require "readline"
|
||
|
Readline::HISTORY.push("a", "b", "c", "d", "e")
|
||
|
p Readline::HISTORY[4] # => "e"
|
||
|
実装しているメソッドを次に挙げます。
|
||
|
* HISTORY.to_s -> "HISTORY"
|
||
|
* HISTORY[index] -> string
|
||
|
* HISTORY[index] = string
|
||
|
* HISTORY.push(string[, string, ...]) -> self
|
||
|
* HISTORY << string -> self
|
||
|
* HISTORY.pop -> string
|
||
|
* HISTORY.shift -> string
|
||
|
* HISTORY.each -> Enumerator
|
||
|
* HISTORY.each { |i| } -> [string]
|
||
|
* HISTORY.length -> Integer
|
||
|
* HISTORY.empty? -> true or false
|
||
|
* HISTORY.delete_at(index) -> string
|
||
|
* HISTORY.clear -> self
|
||
|
サポートしていない環境では、次のメソッドで例外 NotImplementedError が
|
||
|
発生します。
|
||
|
* HISTORY[index] = string
|
||
|
* HISTORY.pop -> string
|
||
|
* HISTORY.shift -> string
|
||
|
* HISTORY.delete_at(index) -> string
|
||
|
* HISTORY.clear -> self
|
||
|
FILENAME_COMPLETION_PROC
|
||
|
ファイル名の補完を行う call メソッドを持つオブジェクトです。
|
||
|
Readline.completion_proc= により、ユーザの入力時にファイル名の補完を
|
||
|
行うように設定するために使用することを想定してます。
|
||
|
USERNAME_COMPLETION_PROC
|
||
|
ユーザ名の補完を行う call メソッドを持つオブジェクトです。
|
||
|
Readline.completion_proc= により、ユーザの入力時にユーザ名の補完を行
|
||
|
うように設定するために使用することを想定してます。
|
||
|
VERSION
|
||
|
使用している GNU Readline または libedit のバージョンです。
|
||
| /dev/null | ||
|---|---|---|
|
# AUTOGENERATED DEPENDENCIES START
|
||
|
readline.o: $(RUBY_EXTCONF_H)
|
||
|
readline.o: $(arch_hdrdir)/ruby/config.h
|
||
|
readline.o: $(hdrdir)/ruby/defines.h
|
||
|
readline.o: $(hdrdir)/ruby/encoding.h
|
||
|
readline.o: $(hdrdir)/ruby/intern.h
|
||
|
readline.o: $(hdrdir)/ruby/io.h
|
||
|
readline.o: $(hdrdir)/ruby/missing.h
|
||
|
readline.o: $(hdrdir)/ruby/oniguruma.h
|
||
|
readline.o: $(hdrdir)/ruby/ruby.h
|
||
|
readline.o: $(hdrdir)/ruby/st.h
|
||
|
readline.o: $(hdrdir)/ruby/subst.h
|
||
|
readline.o: $(hdrdir)/ruby/thread.h
|
||
|
readline.o: $(top_srcdir)/include/ruby.h
|
||
|
readline.o: $(top_srcdir)/internal.h
|
||
|
readline.o: readline.c
|
||
|
# AUTOGENERATED DEPENDENCIES END
|
||
| /dev/null | ||
|---|---|---|
|
require "mkmf"
|
||
|
readline = Struct.new(:headers, :extra_check).new(["stdio.h"])
|
||
|
def readline.have_header(header)
|
||
|
if super(header, &extra_check)
|
||
|
headers.push(header)
|
||
|
return true
|
||
|
else
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
def readline.have_var(var)
|
||
|
return super(var, headers)
|
||
|
end
|
||
|
def readline.have_func(func)
|
||
|
return super(func, headers)
|
||
|
end
|
||
|
def readline.have_type(type)
|
||
|
return super(type, headers)
|
||
|
end
|
||
|
dir_config('curses')
|
||
|
dir_config('ncurses')
|
||
|
dir_config('termcap')
|
||
|
dir_config("readline")
|
||
|
enable_libedit = enable_config("libedit")
|
||
|
have_library("user32", nil) if /cygwin/ === RUBY_PLATFORM
|
||
|
have_library("ncurses", "tgetnum") ||
|
||
|
have_library("termcap", "tgetnum") ||
|
||
|
have_library("curses", "tgetnum")
|
||
|
case enable_libedit
|
||
|
when true
|
||
|
# --enable-libedit
|
||
|
unless (readline.have_header("editline/readline.h") ||
|
||
|
readline.have_header("readline/readline.h")) &&
|
||
|
have_library("edit", "readline")
|
||
|
raise "libedit not found"
|
||
|
end
|
||
|
when false
|
||
|
# --disable-libedit
|
||
|
unless ((readline.have_header("readline/readline.h") &&
|
||
|
readline.have_header("readline/history.h")) &&
|
||
|
have_library("readline", "readline"))
|
||
|
raise "readline not found"
|
||
|
end
|
||
|
else
|
||
|
# does not specify
|
||
|
unless ((readline.have_header("readline/readline.h") &&
|
||
|
readline.have_header("readline/history.h")) &&
|
||
|
(have_library("readline", "readline") ||
|
||
|
have_library("edit", "readline"))) ||
|
||
|
(readline.have_header("editline/readline.h") &&
|
||
|
have_library("edit", "readline"))
|
||
|
raise "readline nor libedit not found"
|
||
|
end
|
||
|
end
|
||
|
readline.have_func("rl_getc")
|
||
|
readline.have_func("rl_getc_function")
|
||
|
readline.have_func("rl_filename_completion_function")
|
||
|
readline.have_func("rl_username_completion_function")
|
||
|
readline.have_func("rl_completion_matches")
|
||
|
readline.have_func("rl_refresh_line")
|
||
|
readline.have_var("rl_deprep_term_function")
|
||
|
readline.have_var("rl_completion_append_character")
|
||
|
readline.have_var("rl_basic_word_break_characters")
|
||
|
readline.have_var("rl_completer_word_break_characters")
|
||
|
readline.have_var("rl_basic_quote_characters")
|
||
|
readline.have_var("rl_completer_quote_characters")
|
||
|
readline.have_var("rl_filename_quote_characters")
|
||
|
readline.have_var("rl_attempted_completion_over")
|
||
|
readline.have_var("rl_library_version")
|
||
|
readline.have_var("rl_editing_mode")
|
||
|
readline.have_var("rl_line_buffer")
|
||
|
readline.have_var("rl_point")
|
||
|
# workaround for native windows.
|
||
|
/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_event_hook")
|
||
|
/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_sigwinch")
|
||
|
/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_signals")
|
||
|
readline.have_var("rl_pre_input_hook")
|
||
|
readline.have_var("rl_special_prefixes")
|
||
|
readline.have_func("rl_cleanup_after_signal")
|
||
|
readline.have_func("rl_free_line_state")
|
||
|
readline.have_func("rl_clear_signals")
|
||
|
readline.have_func("rl_set_screen_size")
|
||
|
readline.have_func("rl_get_screen_size")
|
||
|
readline.have_func("rl_vi_editing_mode")
|
||
|
readline.have_func("rl_emacs_editing_mode")
|
||
|
readline.have_func("replace_history_entry")
|
||
|
readline.have_func("remove_history")
|
||
|
readline.have_func("clear_history")
|
||
|
readline.have_func("rl_redisplay")
|
||
|
readline.have_func("rl_insert_text")
|
||
|
readline.have_func("rl_delete_text")
|
||
|
unless readline.have_type("rl_hook_func_t*")
|
||
|
# rl_hook_func_t is available since readline-4.2 (2001).
|
||
|
# Function is removed at readline-6.3 (2014).
|
||
|
# However, editline (NetBSD 6.1.3, 2014) doesn't have rl_hook_func_t.
|
||
|
$defs << "-Drl_hook_func_t=Function"
|
||
|
end
|
||
|
$INCFLAGS << " -I$(top_srcdir)"
|
||
|
create_makefile("readline")
|
||
| /dev/null | ||
|---|---|---|
|
/************************************************
|
||
|
readline.c - GNU Readline module
|
||
|
$Author$
|
||
|
created at: Wed Jan 20 13:59:32 JST 1999
|
||
|
Copyright (C) 1997-2008 Shugo Maeda
|
||
|
Copyright (C) 2008-2013 Kouji Takao
|
||
|
$Id$
|
||
|
Contact:
|
||
|
- Kouji Takao <kouji dot takao at gmail dot com> (current maintainer)
|
||
|
************************************************/
|
||
|
#ifdef RUBY_EXTCONF_H
|
||
|
#include RUBY_EXTCONF_H
|
||
|
#endif
|
||
|
#include "ruby/config.h"
|
||
|
#include <errno.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#ifdef HAVE_READLINE_READLINE_H
|
||
|
#include <readline/readline.h>
|
||
|
#endif
|
||
|
#ifdef HAVE_READLINE_HISTORY_H
|
||
|
#include <readline/history.h>
|
||
|
#endif
|
||
|
#ifdef HAVE_EDITLINE_READLINE_H
|
||
|
#include <editline/readline.h>
|
||
|
#endif
|
||
|
#include "internal.h"
|
||
|
#include "ruby/io.h"
|
||
|
#include "ruby/thread.h"
|
||
|
#ifdef HAVE_UNISTD_H
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
#ifdef HAVE_SYS_STAT_H
|
||
|
#include <sys/stat.h>
|
||
|
#endif
|
||
|
static VALUE mReadline;
|
||
|
#define EDIT_LINE_LIBRARY_VERSION "EditLine wrapper"
|
||
|
#ifndef USE_INSERT_IGNORE_ESCAPE
|
||
|
# if !defined(HAVE_EDITLINE_READLINE_H) && defined(RL_PROMPT_START_IGNORE) && defined(RL_PROMPT_END_IGNORE)
|
||
|
# define USE_INSERT_IGNORE_ESCAPE 1
|
||
|
# else
|
||
|
# define USE_INSERT_IGNORE_ESCAPE 0
|
||
|
# endif
|
||
|
#endif
|
||
|
#define COMPLETION_PROC "completion_proc"
|
||
|
#define COMPLETION_CASE_FOLD "completion_case_fold"
|
||
|
static ID completion_proc, completion_case_fold;
|
||
|
#if USE_INSERT_IGNORE_ESCAPE
|
||
|
static ID id_orig_prompt, id_last_prompt;
|
||
|
#endif
|
||
|
#if defined(HAVE_RL_PRE_INPUT_HOOK)
|
||
|
static ID id_pre_input_hook;
|
||
|
#endif
|
||
|
#if defined(HAVE_RL_SPECIAL_PREFIXES)
|
||
|
static ID id_special_prefixes;
|
||
|
#endif
|
||
|
#ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION
|
||
|
# define rl_filename_completion_function filename_completion_function
|
||
|
#endif
|
||
|
#ifndef HAVE_RL_USERNAME_COMPLETION_FUNCTION
|
||
|
# define rl_username_completion_function username_completion_function
|
||
|
#endif
|
||
|
#ifndef HAVE_RL_COMPLETION_MATCHES
|
||
|
# define rl_completion_matches completion_matches
|
||
|
#endif
|
||
|
static int (*history_get_offset_func)(int);
|
||
|
static int (*history_replace_offset_func)(int);
|
||
|
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
|
||
|
static int readline_completion_append_character;
|
||
|
#endif
|
||
|
static char **readline_attempted_completion_function(const char *text,
|
||
|
int start, int end);
|
||
|
#define OutputStringValue(str) do {\
|
||
|
SafeStringValue(str);\
|
||
|
(str) = rb_str_conv_enc((str), rb_enc_get(str), rb_locale_encoding());\
|
||
|
} while (0)\
|
||
|
/*
|
||
|
* Document-class: Readline
|
||
|
*
|
||
|
* The Readline module provides interface for GNU Readline.
|
||
|
* This module defines a number of methods to facilitate completion
|
||
|
* and accesses input history from the Ruby interpreter.
|
||
|
* This module supported Edit Line(libedit) too.
|
||
|
* libedit is compatible with GNU Readline.
|
||
|
*
|
||
|
* GNU Readline:: http://www.gnu.org/directory/readline.html
|
||
|
* libedit:: http://www.thrysoee.dk/editline/
|
||
|
*
|
||
|
* Reads one inputted line with line edit by Readline.readline method.
|
||
|
* At this time, the facilitatation completion and the key
|
||
|
* bind like Emacs can be operated like GNU Readline.
|
||
|
*
|
||
|
* require "readline"
|
||
|
* while buf = Readline.readline("> ", true)
|
||
|
* p buf
|
||
|
* end
|
||
|
*
|
||
|
* The content that the user input can be recorded to the history.
|
||
|
* The history can be accessed by Readline::HISTORY constant.
|
||
|
*
|
||
|
* require "readline"
|
||
|
* while buf = Readline.readline("> ", true)
|
||
|
* p Readline::HISTORY.to_a
|
||
|
* print("-> ", buf, "\n")
|
||
|
* end
|
||
|
*
|
||
|
* Documented by Kouji Takao <kouji dot takao at gmail dot com>.
|
||
|
*/
|
||
|
static VALUE readline_instream;
|
||
|
static VALUE readline_outstream;
|
||
|
static FILE *readline_rl_instream;
|
||
|
static FILE *readline_rl_outstream;
|
||
|
#if defined HAVE_RL_GETC_FUNCTION
|
||
|
#ifndef HAVE_RL_GETC
|
||
|
#define rl_getc(f) EOF
|
||
|
#endif
|
||
|
struct getc_struct {
|
||
|
FILE *input;
|
||
|
int fd;
|
||
|
int ret;
|
||
|
int err;
|
||
|
};
|
||
|
static int
|
||
|
getc_body(struct getc_struct *p)
|
||
|
{
|
||
|
char ch;
|
||
|
ssize_t ss;
|
||
|
#if defined(_WIN32)
|
||
|
{
|
||
|
INPUT_RECORD ir;
|
||
|
int n;
|
||
|
static int prior_key = '0';
|
||
|
for (;;) {
|
||
|
if (prior_key > 0xff) {
|
||
|
prior_key = rl_getc(p->input);
|
||
|
return prior_key;
|
||
|
}
|
||
|
if (PeekConsoleInput((HANDLE)_get_osfhandle(p->fd), &ir, 1, &n)) {
|
||
|
if (n == 1) {
|
||
|
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) {
|
||
|
prior_key = rl_getc(p->input);
|
||
|
return prior_key;
|
||
|
} else {
|
||
|
ReadConsoleInput((HANDLE)_get_osfhandle(p->fd), &ir, 1, &n);
|
||
|
}
|
||
|
} else {
|
||
|
HANDLE h = (HANDLE)_get_osfhandle(p->fd);
|
||
|
rb_w32_wait_events(&h, 1, INFINITE);
|
||
|
}
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
ss = read(p->fd, &ch, 1);
|
||
|
if (ss == 0) {
|
||
|
errno = 0;
|
||
|
return EOF;
|
||
|
}
|
||
|
if (ss != 1)
|
||
|
return EOF;
|
||
|
return (unsigned char)ch;
|
||
|
}
|
||
|
static void *
|
||
|
getc_func(void *data1)
|
||
|
{
|
||
|
struct getc_struct *p = data1;
|
||
|
errno = 0;
|
||
|
p->ret = getc_body(p);
|
||
|
p->err = errno;
|
||
|
return NULL;
|
||
|
}
|
||
|
static int
|
||
|
readline_getc(FILE *input)
|
||
|
{
|
||
|
struct getc_struct data;
|
||
|
if (input == NULL) /* editline may give NULL as input. */
|
||
|
input = stdin;
|
||
|
data.input = input;
|
||
|
data.fd = fileno(input);
|
||
|
again:
|
||
|
data.ret = EOF;
|
||
|
data.err = EINTR; /* getc_func is not called if already interrupted. */
|
||
|
rb_thread_call_without_gvl2(getc_func, &data, RUBY_UBF_IO, NULL);
|
||
|
if (data.ret == EOF) {
|
||
|
if (data.err == 0) {
|
||
|
return EOF;
|
||
|
}
|
||
|
if (data.err == EINTR) {
|
||
|
rb_thread_check_ints();
|
||
|
goto again;
|
||
|
}
|
||
|
if (data.err == EWOULDBLOCK || data.err == EAGAIN) {
|
||
|
int ret;
|
||
|
if (fileno(input) != data.fd)
|
||
|
rb_bug("readline_getc: input closed unexpectedly or memory corrupted");
|
||
|
ret = rb_wait_for_single_fd(data.fd, RB_WAITFD_IN, NULL);
|
||
|
if (ret != -1 || errno == EINTR)
|
||
|
goto again;
|
||
|
rb_sys_fail("rb_wait_for_single_fd");
|
||
|
}
|
||
|
errno = data.err;
|
||
|
rb_sys_fail("read");
|
||
|
}
|
||
|
return data.ret;
|
||
|
}
|
||
|
#elif defined HAVE_RL_EVENT_HOOK
|
||
|
#define BUSY_WAIT 0
|
||
|
static int readline_event(void);
|
||
|
static int
|
||
|
readline_event(void)
|
||
|
{
|
||
|
#if BUSY_WAIT
|
||
|
rb_thread_schedule();
|
||
|
#else
|
||
|
rb_wait_for_single_fd(fileno(rl_instream), RB_WAITFD_IN, NULL);
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
#if USE_INSERT_IGNORE_ESCAPE
|
||
|
static VALUE
|
||
|
insert_ignore_escape(VALUE self, VALUE prompt)
|
||
|
{
|
||
|
VALUE last_prompt, orig_prompt = rb_attr_get(self, id_orig_prompt);
|
||
|
int ignoring = 0;
|
||
|
const char *s0, *s, *e;
|
||
|
long len;
|
||
|
static const char ignore_code[2] = {RL_PROMPT_START_IGNORE, RL_PROMPT_END_IGNORE};
|
||
|
prompt = rb_str_new_shared(prompt);
|
||
|
last_prompt = rb_attr_get(self, id_last_prompt);
|
||
|
if (orig_prompt == prompt) return last_prompt;
|
||
|
len = RSTRING_LEN(prompt);
|
||
|
if (NIL_P(last_prompt)) {
|
||
|
last_prompt = rb_str_tmp_new(len);
|
||
|
}
|
||
|
s = s0 = RSTRING_PTR(prompt);
|
||
|
e = s0 + len;
|
||
|
rb_str_set_len(last_prompt, 0);
|
||
|
while (s < e && *s) {
|
||
|
switch (*s) {
|
||
|
case RL_PROMPT_START_IGNORE:
|
||
|
ignoring = -1;
|
||
|
rb_str_cat(last_prompt, s0, ++s - s0);
|
||
|
s0 = s;
|
||
|
break;
|
||
|
case RL_PROMPT_END_IGNORE:
|
||
|
ignoring = 0;
|
||
|
rb_str_cat(last_prompt, s0, ++s - s0);
|
||
|
s0 = s;
|
||
|
break;
|
||
|
case '\033':
|
||
|
if (++s < e && *s == '[') {
|
||
|
rb_str_cat(last_prompt, s0, s - s0 - 1);
|
||
|
s0 = s - 1;
|
||
|
while (++s < e && *s) {
|
||
|
if (ISALPHA(*(unsigned char *)s)) {
|
||
|
if (!ignoring) {
|
||
|
ignoring = 1;
|
||
|
rb_str_cat(last_prompt, ignore_code+0, 1);
|
||
|
}
|
||
|
rb_str_cat(last_prompt, s0, ++s - s0);
|
||
|
s0 = s;
|
||
|
break;
|
||
|
}
|
||
|
else if (!(('0' <= *s && *s <= '9') || *s == ';')) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (ignoring > 0) {
|
||
|
ignoring = 0;
|
||
|
rb_str_cat(last_prompt, ignore_code+1, 1);
|
||
|
}
|
||
|
s++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (ignoring > 0) {
|
||
|
ignoring = 0;
|
||
|
rb_str_cat(last_prompt, ignore_code+1, 1);
|
||
|
}
|
||
|
rb_str_cat(last_prompt, s0, s - s0);
|
||
|
rb_ivar_set(self, id_orig_prompt, prompt);
|
||
|
rb_ivar_set(self, id_last_prompt, last_prompt);
|
||
|
return last_prompt;
|
||
|
}
|
||
|
#endif
|
||
|
static VALUE
|
||
|
readline_get(VALUE prompt)
|
||
|
{
|
||
|
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
|
||
|
readline_completion_append_character = rl_completion_append_character;
|
||
|
#endif
|
||
|
return (VALUE)readline((char *)prompt);
|
||
|
}
|
||
|
static void
|
||
|
clear_rl_instream(void)
|
||
|
{
|
||
|
if (readline_rl_instream) {
|
||
|
fclose(readline_rl_instream);
|
||
|
if (rl_instream == readline_rl_instream)
|
||
|
rl_instream = NULL;
|
||
|
readline_rl_instream = NULL;
|
||
|
}
|
||
|
readline_instream = Qfalse;
|
||
|
}
|
||
|
static void
|
||
|
clear_rl_outstream(void)
|
||
|
{
|
||
|
if (readline_rl_outstream) {
|
||
|
fclose(readline_rl_outstream);
|
||
|
if (rl_outstream == readline_rl_outstream)
|
||
|
rl_outstream = NULL;
|
||
|
readline_rl_outstream = NULL;
|
||
|
}
|
||
|
readline_outstream = Qfalse;
|
||
|
}
|
||
|
static void
|
||
|
prepare_readline(void)
|
||
|
{
|
||
|
static int initialized = 0;
|
||
|
if (!initialized) {
|
||
|
rl_initialize();
|
||
|
initialized = 1;
|
||
|
}
|
||
|
if (readline_instream) {
|
||
|
rb_io_t *ifp;
|
||
|
rb_io_check_initialized(ifp = RFILE(rb_io_taint_check(readline_instream))->fptr);
|
||
|
if (ifp->fd < 0) {
|
||
|
clear_rl_instream();
|
||
|
rb_raise(rb_eIOError, "closed readline input");
|
||
|
}
|
||
|
}
|
||
|
if (readline_outstream) {
|
||
|
rb_io_t *ofp;
|
||
|
rb_io_check_initialized(ofp = RFILE(rb_io_taint_check(readline_outstream))->fptr);
|
||
|
if (ofp->fd < 0) {
|
||
|
clear_rl_outstream();
|
||
|
rb_raise(rb_eIOError, "closed readline output");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.readline(prompt = "", add_hist = false) -> string or nil
|
||
|
*
|
||
|
* Shows the +prompt+ and reads the inputted line with line editing.
|
||
|
* The inputted line is added to the history if +add_hist+ is true.
|
||
|
*
|
||
|
* Returns nil when the inputted line is empty and user inputs EOF
|
||
|
* (Presses ^D on UNIX).
|
||
|
*
|
||
|
* Raises IOError exception if one of below conditions are satisfied.
|
||
|
* 1. stdin was closed.
|
||
|
* 2. stdout was closed.
|
||
|
*
|
||
|
* This method supports thread. Switches the thread context when waits
|
||
|
* inputting line.
|
||
|
*
|
||
|
* Supports line edit when inputs line. Provides VI and Emacs editing mode.
|
||
|
* Default is Emacs editing mode.
|
||
|
*
|
||
|
* NOTE: Terminates ruby interpreter and does not return the terminal
|
||
|
* status after user pressed '^C' when wait inputting line.
|
||
|
* Give 3 examples that avoid it.
|
||
|
*
|
||
|
* * Catches the Interrupt exception by pressed ^C after returns
|
||
|
* terminal status:
|
||
|
*
|
||
|
* require "readline"
|
||
|
*
|
||
|
* stty_save = `stty -g`.chomp
|
||
|
* begin
|
||
|
* while buf = Readline.readline
|
||
|
* p buf
|
||
|
* end
|
||
|
* rescue Interrupt
|
||
|
* system("stty", stty_save)
|
||
|
* exit
|
||
|
* end
|
||
|
* end
|
||
|
* end
|
||
|
*
|
||
|
* * Catches the INT signal by pressed ^C after returns terminal
|
||
|
* status:
|
||
|
*
|
||
|
* require "readline"
|
||
|
*
|
||
|
* stty_save = `stty -g`.chomp
|
||
|
* trap("INT") { system "stty", stty_save; exit }
|
||
|
*
|
||
|
* while buf = Readline.readline
|
||
|
* p buf
|
||
|
* end
|
||
|
*
|
||
|
* * Ignores pressing ^C:
|
||
|
*
|
||
|
* require "readline"
|
||
|
*
|
||
|
* trap("INT", "SIG_IGN")
|
||
|
*
|
||
|
* while buf = Readline.readline
|
||
|
* p buf
|
||
|
* end
|
||
|
*
|
||
|
* Can make as follows with Readline::HISTORY constant.
|
||
|
* It does not record to the history if the inputted line is empty or
|
||
|
* the same it as last one.
|
||
|
*
|
||
|
* require "readline"
|
||
|
*
|
||
|
* while buf = Readline.readline("> ", true)
|
||
|
* # p Readline::HISTORY.to_a
|
||
|
* Readline::HISTORY.pop if /^\s*$/ =~ buf
|
||
|
*
|
||
|
* begin
|
||
|
* if Readline::HISTORY[Readline::HISTORY.length-2] == buf
|
||
|
* Readline::HISTORY.pop
|
||
|
* end
|
||
|
* rescue IndexError
|
||
|
* end
|
||
|
*
|
||
|
* # p Readline::HISTORY.to_a
|
||
|
* print "-> ", buf, "\n"
|
||
|
* end
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_readline(int argc, VALUE *argv, VALUE self)
|
||
|
{
|
||
|
VALUE tmp, add_hist, result;
|
||
|
char *prompt = NULL;
|
||
|
char *buff;
|
||
|
int status;
|
||
|
if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) {
|
||
|
OutputStringValue(tmp);
|
||
|
#if USE_INSERT_IGNORE_ESCAPE
|
||
|
tmp = insert_ignore_escape(self, tmp);
|
||
|
rb_str_locktmp(tmp);
|
||
|
#endif
|
||
|
prompt = RSTRING_PTR(tmp);
|
||
|
}
|
||
|
prepare_readline();
|
||
|
#ifdef _WIN32
|
||
|
rl_prep_terminal(1);
|
||
|
#endif
|
||
|
buff = (char*)rb_protect(readline_get, (VALUE)prompt, &status);
|
||
|
#if USE_INSERT_IGNORE_ESCAPE
|
||
|
if (prompt) {
|
||
|
rb_str_unlocktmp(tmp);
|
||
|
}
|
||
|
#endif
|
||
|
if (status) {
|
||
|
#if defined HAVE_RL_CLEANUP_AFTER_SIGNAL
|
||
|
/* restore terminal mode and signal handler*/
|
||
|
#if defined HAVE_RL_FREE_LINE_STATE
|
||
|
rl_free_line_state();
|
||
|
#endif
|
||
|
rl_cleanup_after_signal();
|
||
|
#elif defined HAVE_RL_DEPREP_TERM_FUNCTION
|
||
|
/* restore terminal mode */
|
||
|
if (rl_deprep_term_function != NULL) /* NULL in libedit. [ruby-dev:29116] */
|
||
|
(*rl_deprep_term_function)();
|
||
|
else
|
||
|
#else
|
||
|
rl_deprep_terminal();
|
||
|
#endif
|
||
|
rb_jump_tag(status);
|
||
|
}
|
||
|
if (RTEST(add_hist) && buff) {
|
||
|
add_history(buff);
|
||
|
}
|
||
|
if (buff) {
|
||
|
result = rb_locale_str_new_cstr(buff);
|
||
|
}
|
||
|
else
|
||
|
result = Qnil;
|
||
|
if (buff) free(buff);
|
||
|
return result;
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.input = input
|
||
|
*
|
||
|
* Specifies a File object +input+ that is input stream for
|
||
|
* Readline.readline method.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_set_input(VALUE self, VALUE input)
|
||
|
{
|
||
|
rb_io_t *ifp;
|
||
|
int fd;
|
||
|
FILE *f;
|
||
|
if (NIL_P(input)) {
|
||
|
clear_rl_instream();
|
||
|
}
|
||
|
else {
|
||
|
Check_Type(input, T_FILE);
|
||
|
GetOpenFile(input, ifp);
|
||
|
clear_rl_instream();
|
||
|
fd = rb_cloexec_dup(ifp->fd);
|
||
|
if (fd == -1)
|
||
|
rb_sys_fail("dup");
|
||
|
f = fdopen(fd, "r");
|
||
|
if (f == NULL) {
|
||
|
int save_errno = errno;
|
||
|
close(fd);
|
||
|
errno = save_errno;
|
||
|
rb_sys_fail("fdopen");
|
||
|
}
|
||
|
rl_instream = readline_rl_instream = f;
|
||
|
readline_instream = input;
|
||
|
}
|
||
|
return input;
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.output = output
|
||
|
*
|
||
|
* Specifies a File object +output+ that is output stream for
|
||
|
* Readline.readline method.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_set_output(VALUE self, VALUE output)
|
||
|
{
|
||
|
rb_io_t *ofp;
|
||
|
int fd;
|
||
|
FILE *f;
|
||
|
if (NIL_P(output)) {
|
||
|
clear_rl_outstream();
|
||
|
}
|
||
|
else {
|
||
|
Check_Type(output, T_FILE);
|
||
|
GetOpenFile(output, ofp);
|
||
|
clear_rl_outstream();
|
||
|
fd = rb_cloexec_dup(ofp->fd);
|
||
|
if (fd == -1)
|
||
|
rb_sys_fail("dup");
|
||
|
f = fdopen(fd, "w");
|
||
|
if (f == NULL) {
|
||
|
int save_errno = errno;
|
||
|
close(fd);
|
||
|
errno = save_errno;
|
||
|
rb_sys_fail("fdopen");
|
||
|
}
|
||
|
rl_outstream = readline_rl_outstream = f;
|
||
|
readline_outstream = output;
|
||
|
}
|
||
|
return output;
|
||
|
}
|
||
|
#if defined(HAVE_RL_PRE_INPUT_HOOK)
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.pre_input_hook = proc
|
||
|
*
|
||
|
* Specifies a Proc object +proc+ to call after the first prompt has
|
||
|
* been printed and just before readline starts reading input
|
||
|
* characters.
|
||
|
*
|
||
|
* See GNU Readline's rl_pre_input_hook variable.
|
||
|
*
|
||
|
* Raises ArgumentError if +proc+ does not respond to the call method.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_set_pre_input_hook(VALUE self, VALUE proc)
|
||
|
{
|
||
|
if (!NIL_P(proc) && !rb_respond_to(proc, rb_intern("call")))
|
||
|
rb_raise(rb_eArgError, "argument must respond to `call'");
|
||
|
return rb_ivar_set(mReadline, id_pre_input_hook, proc);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.pre_input_hook -> proc
|
||
|
*
|
||
|
* Returns a Proc object +proc+ to call after the first prompt has
|
||
|
* been printed and just before readline starts reading input
|
||
|
* characters. The default is nil.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_get_pre_input_hook(VALUE self)
|
||
|
{
|
||
|
return rb_attr_get(mReadline, id_pre_input_hook);
|
||
|
}
|
||
|
static int
|
||
|
readline_pre_input_hook(void)
|
||
|
{
|
||
|
VALUE proc;
|
||
|
proc = rb_attr_get(mReadline, id_pre_input_hook);
|
||
|
if (!NIL_P(proc))
|
||
|
rb_funcall(proc, rb_intern("call"), 0);
|
||
|
return 0;
|
||
|
}
|
||
|
#else
|
||
|
#define readline_s_set_pre_input_hook rb_f_notimplement
|
||
|
#define readline_s_get_pre_input_hook rb_f_notimplement
|
||
|
#endif
|
||
|
#if defined(HAVE_RL_INSERT_TEXT)
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.insert_text(string) -> self
|
||
|
*
|
||
|
* Insert text into the line at the current cursor position.
|
||
|
*
|
||
|
* See GNU Readline's rl_insert_text function.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_insert_text(VALUE self, VALUE str)
|
||
|
{
|
||
|
OutputStringValue(str);
|
||
|
rl_insert_text(RSTRING_PTR(str));
|
||
|
return self;
|
||
|
}
|
||
|
#else
|
||
|
#define readline_s_insert_text rb_f_notimplement
|
||
|
#endif
|
||
|
#if defined(HAVE_RL_DELETE_TEXT)
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.delete_text([start[, length]]) -> self
|
||
|
* Readline.delete_text(start..end) -> self
|
||
|
* Readline.delete_text() -> self
|
||
|
*
|
||
|
* Delete text between start and end in the current line.
|
||
|
*
|
||
|
* See GNU Readline's rl_delete_text function.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_delete_text(int argc, VALUE *argv, VALUE self)
|
||
|
{
|
||
|
rb_check_arity(argc, 0, 2);
|
||
|
if (rl_line_buffer) {
|
||
|
char *p, *ptr = rl_line_buffer;
|
||
|
long beg = 0, len = strlen(rl_line_buffer);
|
||
|
struct RString fakestr;
|
||
|
VALUE str = rb_setup_fake_str(&fakestr, ptr, len, rb_locale_encoding());
|
||
|
OBJ_FREEZE(str);
|
||
|
if (argc == 2) {
|
||
|
beg = NUM2LONG(argv[0]);
|
||
|
len = NUM2LONG(argv[1]);
|
||
|
num_pos:
|
||
|
p = rb_str_subpos(str, beg, &len);
|
||
|
if (!p) rb_raise(rb_eArgError, "invalid index");
|
||
|
beg = p - ptr;
|
||
|
}
|
||
|
else if (argc == 1) {
|
||
|
len = rb_str_strlen(str);
|
||
|
if (!rb_range_beg_len(argv[0], &beg, &len, len, 1)) {
|
||
|
beg = NUM2LONG(argv[0]);
|
||
|
goto num_pos;
|
||
|
}
|
||
|
}
|
||
|
rl_delete_text(rb_long2int(beg), rb_long2int(beg + len));
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
#else
|
||
|
#define readline_s_delete_text rb_f_notimplement
|
||
|
#endif
|
||
|
#if defined(HAVE_RL_REDISPLAY)
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.redisplay -> self
|
||
|
*
|
||
|
* Change what's displayed on the screen to reflect the current
|
||
|
* contents.
|
||
|
*
|
||
|
* See GNU Readline's rl_redisplay function.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_redisplay(VALUE self)
|
||
|
{
|
||
|
rl_redisplay();
|
||
|
return self;
|
||
|
}
|
||
|
#else
|
||
|
#define readline_s_redisplay rb_f_notimplement
|
||
|
#endif
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.completion_proc = proc
|
||
|
*
|
||
|
* Specifies a Proc object +proc+ to determine completion behavior. It
|
||
|
* should take input string and return an array of completion candidates.
|
||
|
*
|
||
|
* The default completion is used if +proc+ is nil.
|
||
|
*
|
||
|
* The String that is passed to the Proc depends on the
|
||
|
* Readline.completer_word_break_characters property. By default the word
|
||
|
* under the cursor is passed to the Proc. For example, if the input is "foo
|
||
|
* bar" then only "bar" would be passed to the completion Proc.
|
||
|
*
|
||
|
* Upon successful completion the Readline.completion_append_character will be
|
||
|
* appended to the input so the user can start working on their next argument.
|
||
|
*
|
||
|
* = Examples
|
||
|
*
|
||
|
* == Completion for a Static List
|
||
|
*
|
||
|
* require 'readline'
|
||
|
*
|
||
|
* LIST = [
|
||
|
* 'search', 'download', 'open',
|
||
|
* 'help', 'history', 'quit',
|
||
|
* 'url', 'next', 'clear',
|
||
|
* 'prev', 'past'
|
||
|
* ].sort
|
||
|
*
|
||
|
* comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }
|
||
|
*
|
||
|
* Readline.completion_append_character = " "
|
||
|
* Readline.completion_proc = comp
|
||
|
*
|
||
|
* while line = Readline.readline('> ', true)
|
||
|
* p line
|
||
|
* end
|
||
|
*
|
||
|
* == Completion For Directory Contents
|
||
|
*
|
||
|
* require 'readline'
|
||
|
*
|
||
|
* Readline.completion_append_character = " "
|
||
|
* Readline.completion_proc = Proc.new do |str|
|
||
|
* Dir[str+'*'].grep(/^#{Regexp.escape(str)}/)
|
||
|
* end
|
||
|
*
|
||
|
* while line = Readline.readline('> ', true)
|
||
|
* p line
|
||
|
* end
|
||
|
*
|
||
|
* = Autocomplete strategies
|
||
|
*
|
||
|
* When working with auto-complete there are some strategies that work well.
|
||
|
* To get some ideas you can take a look at the
|
||
|
* completion.rb[http://svn.ruby-lang.org/repos/ruby/trunk/lib/irb/completion.rb]
|
||
|
* file for irb.
|
||
|
*
|
||
|
* The common strategy is to take a list of possible completions and filter it
|
||
|
* down to those completions that start with the user input. In the above
|
||
|
* examples Enumerator.grep is used. The input is escaped to prevent Regexp
|
||
|
* special characters from interfering with the matching.
|
||
|
*
|
||
|
* It may also be helpful to use the Abbrev library to generate completions.
|
||
|
*
|
||
|
* Raises ArgumentError if +proc+ does not respond to the call method.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_set_completion_proc(VALUE self, VALUE proc)
|
||
|
{
|
||
|
if (!NIL_P(proc) && !rb_respond_to(proc, rb_intern("call")))
|
||
|
rb_raise(rb_eArgError, "argument must respond to `call'");
|
||
|
return rb_ivar_set(mReadline, completion_proc, proc);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.completion_proc -> proc
|
||
|
*
|
||
|
* Returns the completion Proc object.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_get_completion_proc(VALUE self)
|
||
|
{
|
||
|
return rb_attr_get(mReadline, completion_proc);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.completion_case_fold = bool
|
||
|
*
|
||
|
* Sets whether or not to ignore case on completion.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_set_completion_case_fold(VALUE self, VALUE val)
|
||
|
{
|
||
|
return rb_ivar_set(mReadline, completion_case_fold, val);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.completion_case_fold -> bool
|
||
|
*
|
||
|
* Returns true if completion ignores case. If no, returns false.
|
||
|
*
|
||
|
* NOTE: Returns the same object that is specified by
|
||
|
* Readline.completion_case_fold= method.
|
||
|
*
|
||
|
* require "readline"
|
||
|
*
|
||
|
* Readline.completion_case_fold = "This is a String."
|
||
|
* p Readline.completion_case_fold # => "This is a String."
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_get_completion_case_fold(VALUE self)
|
||
|
{
|
||
|
return rb_attr_get(mReadline, completion_case_fold);
|
||
|
}
|
||
|
#ifdef HAVE_RL_LINE_BUFFER
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.line_buffer -> string
|
||
|
*
|
||
|
* Returns the full line that is being edited. This is useful from
|
||
|
* within the complete_proc for determining the context of the
|
||
|
* completion request.
|
||
|
*
|
||
|
* The length of +Readline.line_buffer+ and GNU Readline's rl_end are
|
||
|
* same.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_get_line_buffer(VALUE self)
|
||
|
{
|
||
|
if (rl_line_buffer == NULL)
|
||
|
return Qnil;
|
||
|
return rb_locale_str_new_cstr(rl_line_buffer);
|
||
|
}
|
||
|
#else
|
||
|
#define readline_s_get_line_buffer rb_f_notimplement
|
||
|
#endif
|
||
|
#ifdef HAVE_RL_POINT
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.point -> int
|
||
|
*
|
||
|
* Returns the index of the current cursor position in
|
||
|
* +Readline.line_buffer+.
|
||
|
*
|
||
|
* The index in +Readline.line_buffer+ which matches the start of
|
||
|
* input-string passed to completion_proc is computed by subtracting
|
||
|
* the length of input-string from +Readline.point+.
|
||
|
*
|
||
|
* start = (the length of input-string) - Readline.point
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_get_point(VALUE self)
|
||
|
{
|
||
|
return INT2NUM(rl_point);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* Readline.point = int
|
||
|
*
|
||
|
* Set the index of the current cursor position in
|
||
|
* +Readline.line_buffer+.
|
||
|
*
|
||
|
* Raises NotImplementedError if the using readline library does not support.
|
||
|
*
|
||
|
* See +Readline.point+.
|
||
|
*/
|
||
|
static VALUE
|
||
|
readline_s_set_point(VALUE self, VALUE pos)
|
||
|
{
|
||
|
rl_point = NUM2INT(pos);
|
||
|
return pos;
|
||
|
}
|
||
|
#else
|
||
|
#define readline_s_get_point rb_f_notimplement
|
||
|
#define readline_s_set_point rb_f_notimplement
|
||
|
#endif
|
||
|
static char **
|
||
|
readline_attempted_completion_function(const char *text, int start, int end)
|
||
|
{
|
||
|
VALUE proc, ary, temp;
|
||
|
char **result;
|
||
|
int case_fold;
|
||
|
long i, matches;
|
||
|
rb_encoding *enc;
|
||
|
VALUE encobj;
|
||
|
proc = rb_attr_get(mReadline, completion_proc);
|
||