linenoise 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +100 -9
- data/ext/linenoise/line_noise.c +41 -2
- data/ext/linenoise/line_noise.h +4 -0
- data/ext/linenoise/linenoise.c +581 -16
- data/lib/linenoise/version.rb +1 -1
- data/spec/linenoise_history_spec.rb +71 -0
- data/spec/linenoise_spec.rb +57 -2
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4cd7f0921e5e628c64d106d5468fbb058392d0c3
|
4
|
+
data.tar.gz: f45df4beb22b1553294cf811aaecebb51b4dc094
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4615c8507956123c6f1e3c2120906954d15e6e79c42477385938c2e26d7674b5e74fa84fc585baef5851e0c53744039e77ddc80b6413d3542d6538d8c727449d
|
7
|
+
data.tar.gz: 6bd64f537d2b48b05a00a82bebff9736dee05278fd6b73325e3fdc49f149eaa384b29218059ffd896cbda63cfb4026d73f13b52325e1faf091f182aa424d1006
|
data/CHANGELOG.md
CHANGED
@@ -3,8 +3,13 @@ Linenoise Ruby Changelog
|
|
3
3
|
|
4
4
|
### master
|
5
5
|
|
6
|
+
### [v1.1.0][v1.1.0] (December 30, 2018)
|
7
|
+
|
8
|
+
* Implemented all Linenoise features (previous release had none)
|
9
|
+
|
6
10
|
### [v1.0.0][v1.0.0] (November 25, 2018)
|
7
11
|
|
8
12
|
* Initial release
|
9
13
|
|
10
14
|
[v1.0.0]: https://github.com/kyrylo/linenoise-rb/releases/tag/v1.0.0
|
15
|
+
[v1.1.0]: https://github.com/kyrylo/linenoise-rb/releases/tag/v1.1.0
|
data/README.md
CHANGED
@@ -1,33 +1,122 @@
|
|
1
|
-
Linenoise
|
2
|
-
|
1
|
+
Linenoise Ruby
|
2
|
+
==============
|
3
|
+
|
4
|
+
[![CircleCI](https://circleci.com/gh/kyrylo/linenoise-rb.svg?style=svg)](https://circleci.com/gh/kyrylo/linenoise-rb)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/linenoise.svg)](http://badge.fury.io/rb/linenoise)
|
6
|
+
[![Documentation Status](http://inch-ci.org/github/kyrylo/linenoise.svg?branch=master)](http://inch-ci.org/github/kyrylo/linenoise)
|
7
|
+
[![Downloads](https://img.shields.io/gem/dt/linenoise.svg?style=flat)](https://rubygems.org/gems/linenoise)
|
8
|
+
|
9
|
+
* [Documentation][documentation]
|
3
10
|
|
4
11
|
Introduction
|
5
12
|
------------
|
6
13
|
|
7
|
-
|
14
|
+
The Linenoise gem is a wrapper around the small self-contained alternative to
|
15
|
+
Readline and Libedit called [Linenoise](https://github.com/antirez/linenoise).
|
16
|
+
|
17
|
+
Features
|
18
|
+
--------
|
19
|
+
|
20
|
+
* History support
|
21
|
+
* Completion
|
22
|
+
* Hints (suggestions at the right of the prompt as you type)
|
23
|
+
* Single and multiline editing mode with the usual key bindings
|
8
24
|
|
9
25
|
Installation
|
10
26
|
------------
|
11
27
|
|
12
|
-
|
28
|
+
### Bundler
|
29
|
+
|
30
|
+
Add the Linenoise gem to your Gemfile:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
gem 'linenoise', '~> 1.1'
|
34
|
+
```
|
35
|
+
|
36
|
+
### Manual
|
37
|
+
|
38
|
+
Invoke the following command from your terminal:
|
39
|
+
|
40
|
+
```sh
|
41
|
+
gem install linenoise
|
42
|
+
```
|
13
43
|
|
14
44
|
Examples
|
15
45
|
--------
|
16
46
|
|
17
|
-
|
47
|
+
### Basic example
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require 'linenoise'
|
51
|
+
|
52
|
+
while buf = Linenoise.linenoise('> ')
|
53
|
+
p buf
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
### Completion
|
18
58
|
|
19
|
-
|
20
|
-
|
59
|
+
```ruby
|
60
|
+
require 'linenoise'
|
21
61
|
|
22
|
-
|
62
|
+
LIST = %w[
|
63
|
+
search download open help history quit url next clear prev past
|
64
|
+
].freeze
|
23
65
|
|
66
|
+
Linenoise.completion_proc = proc do |input|
|
67
|
+
LIST.grep(/\A#{Regexp.escape(input)}/)
|
68
|
+
end
|
69
|
+
|
70
|
+
while line = Linenoise.linenoise('> ')
|
71
|
+
p line
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### Hints
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
require 'linenoise'
|
79
|
+
|
80
|
+
Linenoise.hint_proc = proc do |input|
|
81
|
+
case input
|
82
|
+
when /git show/
|
83
|
+
' [<options>] [<object>...]'
|
84
|
+
when /git log/
|
85
|
+
' [<options>] [<revision range>]'
|
86
|
+
else
|
87
|
+
' --help'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
while line = Linenoise.linenoise('> ')
|
92
|
+
p line
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
More examples and full API explanation is available on the
|
97
|
+
[documentation][documentation] page.
|
98
|
+
|
99
|
+
Development
|
100
|
+
-----------
|
101
|
+
|
102
|
+
### Running tests
|
103
|
+
|
104
|
+
```sh
|
105
|
+
bundle exec rake compile spec
|
106
|
+
```
|
107
|
+
|
108
|
+
### Launching development console
|
109
|
+
|
110
|
+
```
|
111
|
+
bundle exec rake compile console
|
112
|
+
```
|
24
113
|
|
25
114
|
Contact
|
26
115
|
-------
|
27
116
|
|
28
117
|
In case you have a problem, question or a bug report, feel free to:
|
29
118
|
|
30
|
-
* [file an issue](https://github.com/kyrylo/linenoise/issues)
|
119
|
+
* [file an issue](https://github.com/kyrylo/linenoise-rb/issues)
|
31
120
|
* send me an email (see https://github.com/kyrylo)
|
32
121
|
* [tweet at me](https://twitter.com/kyrylosilin)
|
33
122
|
|
@@ -35,3 +124,5 @@ License
|
|
35
124
|
-------
|
36
125
|
|
37
126
|
The project uses the MIT License. See LICENSE.md for details.
|
127
|
+
|
128
|
+
[documentation]: https://www.rubydoc.info/github/kyrylo/linenoise-rb/Linenoise
|
data/ext/linenoise/line_noise.c
CHANGED
@@ -1086,6 +1086,14 @@ static void freeHistory(void) {
|
|
1086
1086
|
}
|
1087
1087
|
}
|
1088
1088
|
|
1089
|
+
// Allocate fresh memory for history and reset history length.
|
1090
|
+
static void resetHistory(void) {
|
1091
|
+
history = malloc(sizeof(char*)*history_max_len);
|
1092
|
+
if (history == NULL) return;
|
1093
|
+
memset(history, 0, (sizeof(char*)*history_max_len));
|
1094
|
+
history_len = 0;
|
1095
|
+
}
|
1096
|
+
|
1089
1097
|
/* At exit we'll try to fix the terminal to the initial conditions. */
|
1090
1098
|
static void linenoiseAtExit(void) {
|
1091
1099
|
disableRawMode(STDIN_FILENO);
|
@@ -1106,9 +1114,8 @@ int linenoiseHistoryAdd(const char *line) {
|
|
1106
1114
|
|
1107
1115
|
/* Initialization on first call. */
|
1108
1116
|
if (history == NULL) {
|
1109
|
-
|
1117
|
+
resetHistory();
|
1110
1118
|
if (history == NULL) return 0;
|
1111
|
-
memset(history,0,(sizeof(char*)*history_max_len));
|
1112
1119
|
}
|
1113
1120
|
|
1114
1121
|
/* Don't add duplicated lines. */
|
@@ -1199,3 +1206,35 @@ int linenoiseHistoryLoad(const char *filename) {
|
|
1199
1206
|
fclose(fp);
|
1200
1207
|
return 0;
|
1201
1208
|
}
|
1209
|
+
|
1210
|
+
int linenoiseHistorySize(void) {
|
1211
|
+
return history_len;
|
1212
|
+
}
|
1213
|
+
|
1214
|
+
char *linenoiseHistoryGet(int index) {
|
1215
|
+
if (index < 0 || index+1 > history_len)
|
1216
|
+
return NULL;
|
1217
|
+
return history[index];
|
1218
|
+
}
|
1219
|
+
|
1220
|
+
char *linenoiseHistoryReplaceLine(int index, char *line) {
|
1221
|
+
char *linecopy, *old_line;
|
1222
|
+
|
1223
|
+
if (index < 0 || index+1 > history_len)
|
1224
|
+
return NULL;
|
1225
|
+
|
1226
|
+
/* Allocate memory for this new line so it can be freed */
|
1227
|
+
linecopy = strdup(line);
|
1228
|
+
if (!linecopy)
|
1229
|
+
return NULL;
|
1230
|
+
|
1231
|
+
old_line = history[index];
|
1232
|
+
history[index] = linecopy;
|
1233
|
+
|
1234
|
+
return old_line;
|
1235
|
+
}
|
1236
|
+
|
1237
|
+
void linenoiseHistoryClear(void) {
|
1238
|
+
freeHistory();
|
1239
|
+
resetHistory();
|
1240
|
+
}
|
data/ext/linenoise/line_noise.h
CHANGED
@@ -62,6 +62,10 @@ int linenoiseHistoryAdd(const char *line);
|
|
62
62
|
int linenoiseHistorySetMaxLen(int len);
|
63
63
|
int linenoiseHistorySave(const char *filename);
|
64
64
|
int linenoiseHistoryLoad(const char *filename);
|
65
|
+
int linenoiseHistorySize();
|
66
|
+
char *linenoiseHistoryGet(int index);
|
67
|
+
char *linenoiseHistoryReplaceLine(int index, char *line);
|
68
|
+
void linenoiseHistoryClear();
|
65
69
|
void linenoiseClearScreen(void);
|
66
70
|
void linenoiseSetMultiLine(int ml);
|
67
71
|
void linenoisePrintKeyCodes(void);
|
data/ext/linenoise/linenoise.c
CHANGED
@@ -1,31 +1,596 @@
|
|
1
1
|
#include <ruby.h>
|
2
|
+
#include <ruby/io.h>
|
3
|
+
#include <string.h>
|
2
4
|
#include "line_noise.h"
|
3
5
|
|
6
|
+
static VALUE mLinenoise;
|
7
|
+
static ID id_call, id_multiline, id_hint_bold, id_hint_color, completion_proc,
|
8
|
+
hint_proc;
|
9
|
+
static VALUE hint_boldness;
|
10
|
+
static int hint_color;
|
11
|
+
|
12
|
+
#define COMPLETION_PROC "completion_proc"
|
13
|
+
#define HINT_PROC "hint_proc"
|
14
|
+
|
15
|
+
/*
|
16
|
+
* Document-module: Linenoise
|
17
|
+
*
|
18
|
+
* The Linenoise module provides interface for the Linenoise library, which is a
|
19
|
+
* Readline replacement used in Redis, MongoDB, and Android.
|
20
|
+
*
|
21
|
+
* This module defines a number of methods to facilitate completion and accesses
|
22
|
+
* input history from the Ruby interpreter.
|
23
|
+
*
|
24
|
+
* Linenoise:: https://github.com/antirez/linenoise
|
25
|
+
*
|
26
|
+
* Reads one inputted line with line edit via the {Linenoise.linenoise} method.
|
27
|
+
*
|
28
|
+
* require 'linenoise'
|
29
|
+
*
|
30
|
+
* while buf = Linenoise.linenoise('> ')
|
31
|
+
* p buf
|
32
|
+
* end
|
33
|
+
*
|
34
|
+
* User input can be persisted via the history feature. The history can be
|
35
|
+
* accessed through the {Linenoise::HISTORY} constant.
|
36
|
+
*
|
37
|
+
* require 'linenoise'
|
38
|
+
*
|
39
|
+
* while buf = Linenoise.linenoise('> ')
|
40
|
+
* p Linenoise::HISTORY.to_a
|
41
|
+
* print('-> ', buf, '\n')
|
42
|
+
* end
|
43
|
+
*
|
44
|
+
* == Using history
|
45
|
+
*
|
46
|
+
* History can be accessed through {Linenoise::HISTORY}. It can be saved to a
|
47
|
+
* file, or loaded from a file.
|
48
|
+
*
|
49
|
+
* === Adding lines to the history
|
50
|
+
*
|
51
|
+
* Linenoise::HISTORY << '1 + 1'
|
52
|
+
*
|
53
|
+
* # Or push multiple items.
|
54
|
+
* Linenoise::HISTORY.push('2', '3')
|
55
|
+
* Linenoise::HISTORY.size
|
56
|
+
* #=> 3
|
57
|
+
*
|
58
|
+
* === Iterating lines & accessing individual entries
|
59
|
+
*
|
60
|
+
* # Read a line at given index.
|
61
|
+
* Linenoise::HISTORY[0]
|
62
|
+
* #=> '1 + 1'
|
63
|
+
*
|
64
|
+
* # Replace a line in the history with another one.
|
65
|
+
* Linenoise::HISTORY[0] = 'begin'
|
66
|
+
* Linenoise::HISTORY[0]
|
67
|
+
* #=> 'begin'
|
68
|
+
*
|
69
|
+
* # Iterate over lines like an array (HISTORY is enumerable).
|
70
|
+
* Linenoise::HISTORY.each { |line| puts line }
|
71
|
+
*
|
72
|
+
* === Saving & loading
|
73
|
+
*
|
74
|
+
* # Save to file.
|
75
|
+
* Linenoise::HISTORY.save('linenoise_history')
|
76
|
+
*
|
77
|
+
* # Load from file.
|
78
|
+
* Linenoise::HISTORY.load('linenoise_history')
|
79
|
+
*
|
80
|
+
* # Wipe out current history (doesn't delete the file).
|
81
|
+
* Linenoise::HISTORY.clear
|
82
|
+
* Linenoise::HISTORY.size
|
83
|
+
* #=> 0
|
84
|
+
*
|
85
|
+
* === Setting maximum size of history
|
86
|
+
*
|
87
|
+
* # The cap sets how many entries history can hold. When the capacity is
|
88
|
+
* # exceeded, older entries are removed.
|
89
|
+
* Linenoise::HISTORY.max_size = 3
|
90
|
+
*/
|
91
|
+
|
92
|
+
/* Hint colors */
|
93
|
+
enum {red = 31, green, yellow, blue, magenta, cyan, white};
|
94
|
+
|
95
|
+
static void
|
96
|
+
mustbe_callable(VALUE proc)
|
97
|
+
{
|
98
|
+
if (!NIL_P(proc) && !rb_respond_to(proc, id_call))
|
99
|
+
rb_raise(rb_eArgError, "argument must respond to `call'");
|
100
|
+
}
|
101
|
+
|
102
|
+
/*
|
103
|
+
* call-seq:
|
104
|
+
* Linenoise.linenoise(prompt) -> string or nil
|
105
|
+
*
|
106
|
+
* Shows the +prompt+ and reads the inputted line with line editing.
|
107
|
+
*
|
108
|
+
* Returns nil when the inputted line is empty and user inputs EOF
|
109
|
+
* (Presses ^D on UNIX).
|
110
|
+
*
|
111
|
+
* Aliased as +readline+ for easier integration with Readline-enabled apps.
|
112
|
+
*/
|
4
113
|
static VALUE
|
5
114
|
linenoise_linenoise(VALUE self, VALUE prompt)
|
6
115
|
{
|
7
|
-
|
8
|
-
|
116
|
+
VALUE result;
|
117
|
+
char *line;
|
118
|
+
|
119
|
+
line = linenoise(StringValueCStr(prompt));
|
120
|
+
if (line) {
|
121
|
+
result = rb_locale_str_new_cstr(line);
|
122
|
+
}
|
123
|
+
else
|
124
|
+
result = Qnil;
|
125
|
+
if (line) free(line);
|
126
|
+
|
127
|
+
return result;
|
128
|
+
}
|
129
|
+
|
130
|
+
static void
|
131
|
+
linenoise_attempted_completion_function(const char *buf, struct linenoiseCompletions *lc)
|
132
|
+
{
|
133
|
+
VALUE proc, ary, str;
|
134
|
+
long i, matches;
|
135
|
+
rb_encoding *enc;
|
136
|
+
VALUE encobj;
|
137
|
+
|
138
|
+
proc = rb_attr_get(mLinenoise, completion_proc);
|
139
|
+
if (NIL_P(proc))
|
140
|
+
return;
|
141
|
+
|
142
|
+
ary = rb_funcall(proc, id_call, 1, rb_locale_str_new_cstr(buf));
|
143
|
+
if (!RB_TYPE_P(ary, T_ARRAY))
|
144
|
+
ary = rb_Array(ary);
|
145
|
+
|
146
|
+
matches = RARRAY_LEN(ary);
|
147
|
+
if (matches == 0)
|
148
|
+
return;
|
149
|
+
|
150
|
+
enc = rb_locale_encoding();
|
151
|
+
encobj = rb_enc_from_encoding(enc);
|
152
|
+
for (i = 0; i < matches; i++) {
|
153
|
+
str = rb_obj_as_string(RARRAY_AREF(ary, i));
|
154
|
+
StringValueCStr(str);
|
155
|
+
rb_enc_check(encobj, str);
|
156
|
+
linenoiseAddCompletion(lc, RSTRING_PTR(str));
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
/*
|
161
|
+
* call-seq:
|
162
|
+
* Linenoise.completion_proc = proc
|
163
|
+
*
|
164
|
+
* Specifies a Proc object +proc+ to determine completion behavior. It should
|
165
|
+
* take input string and return an array of completion candidates.
|
166
|
+
*
|
167
|
+
* require 'linenoise'
|
168
|
+
*
|
169
|
+
* LIST = %w[
|
170
|
+
* search download open help history quit url next clear prev past
|
171
|
+
* ].freeze
|
172
|
+
*
|
173
|
+
* Linenoise.completion_proc = proc do |input|
|
174
|
+
* LIST.grep(/\A#{Regexp.escape(input)}/)
|
175
|
+
* end
|
176
|
+
*
|
177
|
+
* while line = Linenoise.linenoise('> ')
|
178
|
+
* p line
|
179
|
+
* end
|
180
|
+
*
|
181
|
+
* @raise ArgumentError if +proc+ is not a Proc
|
182
|
+
*/
|
183
|
+
static VALUE
|
184
|
+
linenoise_set_completion_proc(VALUE self, VALUE proc)
|
185
|
+
{
|
186
|
+
mustbe_callable(proc);
|
187
|
+
linenoiseSetCompletionCallback(linenoise_attempted_completion_function);
|
188
|
+
return rb_ivar_set(mLinenoise, completion_proc, proc);
|
189
|
+
}
|
190
|
+
|
191
|
+
/*
|
192
|
+
* call-seq:
|
193
|
+
* Linenoise.completion_proc -> proc
|
194
|
+
*
|
195
|
+
* Returns the completion Proc object.
|
196
|
+
*/
|
197
|
+
static VALUE
|
198
|
+
linenoise_get_completion_proc(VALUE self)
|
199
|
+
{
|
200
|
+
return rb_attr_get(mLinenoise, completion_proc);
|
201
|
+
}
|
202
|
+
|
203
|
+
/*
|
204
|
+
* call-seq:
|
205
|
+
* Linenoise.multiline = bool -> bool
|
206
|
+
*
|
207
|
+
* Specifies multiline mode. By default, Linenoise uses single line editing,
|
208
|
+
* that is, a single row on the screen will be used, and as the user types more,
|
209
|
+
* the text will scroll towards left to make room.
|
210
|
+
*/
|
211
|
+
static VALUE
|
212
|
+
linenoise_set_multiline(VALUE self, VALUE vbool)
|
213
|
+
{
|
214
|
+
int i = RTEST(vbool) ? 1 : 0;
|
215
|
+
rb_ivar_set(mLinenoise, id_multiline, vbool);
|
216
|
+
linenoiseSetMultiLine(i);
|
217
|
+
return vbool;
|
218
|
+
}
|
219
|
+
|
220
|
+
/*
|
221
|
+
* call-seq:
|
222
|
+
* Linenoise.multiline?
|
223
|
+
*
|
224
|
+
* Checks if multiline mode is enabled.
|
225
|
+
*/
|
226
|
+
static VALUE
|
227
|
+
linenoise_get_multiline(VALUE self)
|
228
|
+
{
|
229
|
+
return rb_attr_get(mLinenoise, id_multiline);
|
230
|
+
}
|
231
|
+
|
232
|
+
static char *
|
233
|
+
linenoise_attempted_hint_function(const char *buf, int *color, int *bold)
|
234
|
+
{
|
235
|
+
VALUE proc, str, encobj;
|
236
|
+
rb_encoding *enc;
|
237
|
+
|
238
|
+
*bold = RTEST(hint_boldness) ? 1 : 0;
|
239
|
+
*color = hint_color;
|
240
|
+
|
241
|
+
proc = rb_attr_get(mLinenoise, hint_proc);
|
242
|
+
if (NIL_P(proc))
|
243
|
+
return NULL;
|
244
|
+
|
245
|
+
str = rb_funcall(proc, id_call, 1, rb_locale_str_new_cstr(buf));
|
246
|
+
enc = rb_locale_encoding();
|
247
|
+
encobj = rb_enc_from_encoding(enc);
|
248
|
+
StringValueCStr(str);
|
249
|
+
rb_enc_check(encobj, str);
|
250
|
+
|
251
|
+
return RSTRING_PTR(str);
|
252
|
+
}
|
253
|
+
|
254
|
+
/*
|
255
|
+
* call-seq:
|
256
|
+
* Linenoise.hint_proc = proc
|
257
|
+
*
|
258
|
+
* Specifies a Proc object +proc+ to determine hint behavior. It should take
|
259
|
+
* input string and return the completion according to the input.
|
260
|
+
*
|
261
|
+
* require 'linenoise'
|
262
|
+
*
|
263
|
+
* Linenoise.hint_proc = proc do |input|
|
264
|
+
* case input
|
265
|
+
* when /git show/
|
266
|
+
* ' [<options>] [<object>...]'
|
267
|
+
* when /git log/
|
268
|
+
* ' [<options>] [<revision range>]'
|
269
|
+
* else
|
270
|
+
* ' --help'
|
271
|
+
* end
|
272
|
+
* end
|
273
|
+
*
|
274
|
+
* while line = Linenoise.linenoise('> ')
|
275
|
+
* p line
|
276
|
+
* end
|
277
|
+
*
|
278
|
+
* @raise ArgumentError if +proc+ is not a Proc
|
279
|
+
*/
|
280
|
+
static VALUE
|
281
|
+
linenoise_set_hint_proc(VALUE self, VALUE proc)
|
282
|
+
{
|
283
|
+
mustbe_callable(proc);
|
284
|
+
linenoiseSetHintsCallback(linenoise_attempted_hint_function);
|
285
|
+
return rb_ivar_set(mLinenoise, hint_proc, proc);
|
286
|
+
}
|
287
|
+
|
288
|
+
/*
|
289
|
+
* call-seq:
|
290
|
+
* Linenoise.hint_proc -> proc
|
291
|
+
*
|
292
|
+
* Returns the hint Proc object.
|
293
|
+
*/
|
294
|
+
static VALUE
|
295
|
+
linenoise_get_hint_proc(VALUE self)
|
296
|
+
{
|
297
|
+
return rb_attr_get(mLinenoise, hint_proc);
|
298
|
+
}
|
299
|
+
|
300
|
+
/*
|
301
|
+
* call-seq:
|
302
|
+
* Linenoise.hint_color = Integer -> Integer
|
303
|
+
*
|
304
|
+
* Sets the hint color. Allowed values are in a range from 31 to 37. Setting
|
305
|
+
* this option to 0 removes the color and uses the default font color.
|
306
|
+
*
|
307
|
+
* There are convenience constants for setting colors:
|
308
|
+
*
|
309
|
+
* # Make the hint red.
|
310
|
+
* Linenoise.hint_color = Linenoise::RED
|
311
|
+
*
|
312
|
+
* # Remove the color.
|
313
|
+
* Linenoise.hint_color = Linenoise::DEFAULT
|
314
|
+
*
|
315
|
+
* @raise TypeError if +color+ is not an Integer
|
316
|
+
* @raise ArgumentError if +color+ is not 0 or in range of 31-37
|
317
|
+
*/
|
318
|
+
static VALUE
|
319
|
+
linenoise_set_hint_color(VALUE self, VALUE color)
|
320
|
+
{
|
321
|
+
int c = 0;
|
322
|
+
|
323
|
+
switch (TYPE(color)) {
|
324
|
+
case T_NIL:
|
325
|
+
break;
|
326
|
+
case T_FIXNUM:
|
327
|
+
c = NUM2INT(color);
|
328
|
+
break;
|
329
|
+
default:
|
330
|
+
rb_raise(rb_eTypeError, "hint color is not an Integer");
|
331
|
+
}
|
332
|
+
|
333
|
+
if (c == 0 || (c >= 31 && c <= 37)) {
|
334
|
+
hint_color = c;
|
335
|
+
}
|
336
|
+
else
|
337
|
+
rb_raise(rb_eArgError, "color '%d' is not in range (31-37)", c);
|
338
|
+
|
339
|
+
return rb_ivar_set(mLinenoise, id_hint_color, color);
|
340
|
+
}
|
341
|
+
|
342
|
+
/*
|
343
|
+
* call-seq:
|
344
|
+
* Linenoise.hint_color -> Integer
|
345
|
+
*
|
346
|
+
* Checks hint font color.
|
347
|
+
*/
|
348
|
+
static VALUE
|
349
|
+
linenoise_get_hint_color(VALUE self)
|
350
|
+
{
|
351
|
+
return rb_attr_get(mLinenoise, id_hint_color);
|
352
|
+
}
|
353
|
+
|
354
|
+
/*
|
355
|
+
* call-seq:
|
356
|
+
* Linenoise.hint_bold = bool -> bool
|
357
|
+
*
|
358
|
+
* Sets hint boldness. +false+ means normal text, +true+ means bold. Defults to
|
359
|
+
* +false+.
|
360
|
+
*
|
361
|
+
* Linenoise.hint_bold = true
|
362
|
+
*/
|
363
|
+
static VALUE
|
364
|
+
linenoise_set_hint_boldness(VALUE self, VALUE boldness)
|
365
|
+
{
|
366
|
+
hint_boldness = boldness;
|
367
|
+
return rb_ivar_set(mLinenoise, id_hint_bold, boldness);
|
368
|
+
}
|
369
|
+
|
370
|
+
/*
|
371
|
+
* call-seq:
|
372
|
+
* Linenoise.hint_bold? -> bool
|
373
|
+
*
|
374
|
+
* Checks if the hint font is bold.
|
375
|
+
*/
|
376
|
+
static VALUE
|
377
|
+
linenoise_get_hint_boldness(VALUE self)
|
378
|
+
{
|
379
|
+
return rb_attr_get(mLinenoise, id_hint_bold);
|
380
|
+
}
|
381
|
+
|
382
|
+
/*
|
383
|
+
* call-seq:
|
384
|
+
* Linenoise.clear_screen -> self
|
385
|
+
*
|
386
|
+
* Clears screen from characters.
|
387
|
+
*/
|
388
|
+
static VALUE
|
389
|
+
linenoise_clear_screen(VALUE self)
|
390
|
+
{
|
391
|
+
linenoiseClearScreen();
|
392
|
+
return self;
|
393
|
+
}
|
394
|
+
|
395
|
+
static VALUE
|
396
|
+
hist_set_max_len(VALUE self, VALUE len)
|
397
|
+
{
|
398
|
+
linenoiseHistorySetMaxLen(NUM2INT(len));
|
399
|
+
return len;
|
400
|
+
}
|
9
401
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
402
|
+
static VALUE
|
403
|
+
hist_push(VALUE self, VALUE str)
|
404
|
+
{
|
405
|
+
linenoiseHistoryAdd(StringValueCStr(str));
|
406
|
+
return self;
|
407
|
+
}
|
408
|
+
|
409
|
+
static VALUE
|
410
|
+
hist_push_method(int argc, VALUE *argv, VALUE self)
|
411
|
+
{
|
412
|
+
VALUE str;
|
413
|
+
|
414
|
+
while (argc--) {
|
415
|
+
str = *argv++;
|
416
|
+
linenoiseHistoryAdd(StringValueCStr(str));
|
417
|
+
}
|
418
|
+
return self;
|
419
|
+
}
|
420
|
+
|
421
|
+
static VALUE
|
422
|
+
hist_save(VALUE self, VALUE filename)
|
423
|
+
{
|
424
|
+
char *file = StringValueCStr(filename);
|
425
|
+
|
426
|
+
if (linenoiseHistorySave(file) == -1) {
|
427
|
+
rb_raise(rb_eArgError,
|
428
|
+
"couldn't save Linenoise history to file '%s'", file);
|
429
|
+
}
|
430
|
+
return filename;
|
431
|
+
}
|
432
|
+
|
433
|
+
|
434
|
+
static VALUE
|
435
|
+
hist_load(VALUE self, VALUE filename)
|
436
|
+
{
|
437
|
+
char *file = StringValueCStr(filename);
|
17
438
|
|
18
|
-
|
439
|
+
if (linenoiseHistoryLoad(file) == -1) {
|
440
|
+
rb_raise(rb_eArgError,
|
441
|
+
"couldn't load Linenoise history from file '%s'", file);
|
442
|
+
}
|
443
|
+
return filename;
|
444
|
+
}
|
445
|
+
|
446
|
+
static VALUE
|
447
|
+
hist_length(VALUE self)
|
448
|
+
{
|
449
|
+
return INT2NUM(linenoiseHistorySize());
|
450
|
+
}
|
451
|
+
|
452
|
+
static VALUE
|
453
|
+
hist_each(VALUE self)
|
454
|
+
{
|
455
|
+
char *line;
|
456
|
+
int i;
|
457
|
+
|
458
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
459
|
+
|
460
|
+
for (i = 0; i < linenoiseHistorySize(); i++) {
|
461
|
+
line = linenoiseHistoryGet(i);
|
462
|
+
if (line == NULL)
|
463
|
+
break;
|
464
|
+
rb_yield(rb_locale_str_new_cstr(line));
|
465
|
+
}
|
466
|
+
return self;
|
467
|
+
}
|
468
|
+
|
469
|
+
static VALUE
|
470
|
+
hist_get(VALUE self, VALUE index)
|
471
|
+
{
|
472
|
+
char *line = NULL;
|
473
|
+
int i;
|
474
|
+
|
475
|
+
i = NUM2INT(index);
|
476
|
+
if (i < 0) {
|
477
|
+
i += linenoiseHistorySize();
|
478
|
+
}
|
479
|
+
if (i >= 0) {
|
480
|
+
line = linenoiseHistoryGet(i);
|
481
|
+
}
|
482
|
+
if (line == NULL) {
|
483
|
+
rb_raise(rb_eIndexError, "invalid index");
|
484
|
+
}
|
485
|
+
return rb_locale_str_new_cstr(line);
|
486
|
+
}
|
487
|
+
|
488
|
+
static VALUE
|
489
|
+
hist_set(VALUE self, VALUE index, VALUE str)
|
490
|
+
{
|
491
|
+
char *old_line = NULL;
|
492
|
+
int i;
|
493
|
+
|
494
|
+
i = NUM2INT(index);
|
495
|
+
StringValueCStr(str);
|
496
|
+
if (i < 0) {
|
497
|
+
i += linenoiseHistorySize();
|
498
|
+
}
|
499
|
+
if (i >= 0) {
|
500
|
+
old_line = linenoiseHistoryReplaceLine(i, RSTRING_PTR(str));
|
501
|
+
}
|
502
|
+
if (old_line == NULL) {
|
503
|
+
rb_raise(rb_eIndexError, "invalid index");
|
504
|
+
}
|
505
|
+
return str;
|
506
|
+
}
|
507
|
+
|
508
|
+
/*
|
509
|
+
* call-seq:
|
510
|
+
* Linenoise.hist_clear -> self
|
511
|
+
*
|
512
|
+
* Clears input history.
|
513
|
+
*/
|
514
|
+
static VALUE
|
515
|
+
hist_clear(VALUE self)
|
516
|
+
{
|
517
|
+
linenoiseHistoryClear();
|
518
|
+
return self;
|
19
519
|
}
|
20
520
|
|
21
521
|
void
|
22
522
|
Init_linenoise(void)
|
23
523
|
{
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
524
|
+
VALUE history;
|
525
|
+
|
526
|
+
id_call = rb_intern("call");
|
527
|
+
id_multiline = rb_intern("multiline");
|
528
|
+
id_hint_bold = rb_intern("hint_bold");
|
529
|
+
id_hint_color = rb_intern("hint_color");
|
530
|
+
|
531
|
+
completion_proc = rb_intern(COMPLETION_PROC);
|
532
|
+
hint_proc = rb_intern(HINT_PROC);
|
533
|
+
|
534
|
+
mLinenoise = rb_define_module("Linenoise");
|
535
|
+
/* Version string of Linenoise. */
|
536
|
+
rb_define_const(mLinenoise, "VERSION", rb_str_new_cstr("1.0"));
|
537
|
+
rb_define_module_function(mLinenoise, "linenoise",
|
538
|
+
linenoise_linenoise, 1);
|
539
|
+
rb_define_alias(rb_singleton_class(mLinenoise), "readline", "linenoise");
|
540
|
+
rb_define_singleton_method(mLinenoise, "completion_proc=",
|
541
|
+
linenoise_set_completion_proc, 1);
|
542
|
+
rb_define_singleton_method(mLinenoise, "completion_proc",
|
543
|
+
linenoise_get_completion_proc, 0);
|
544
|
+
rb_define_singleton_method(mLinenoise, "multiline=",
|
545
|
+
linenoise_set_multiline, 1);
|
546
|
+
rb_define_singleton_method(mLinenoise, "multiline?",
|
547
|
+
linenoise_get_multiline, 0);
|
548
|
+
rb_define_singleton_method(mLinenoise, "hint_proc=",
|
549
|
+
linenoise_set_hint_proc, 1);
|
550
|
+
rb_define_singleton_method(mLinenoise, "hint_proc",
|
551
|
+
linenoise_get_hint_proc, 0);
|
552
|
+
rb_define_singleton_method(mLinenoise, "hint_color=",
|
553
|
+
linenoise_set_hint_color, 1);
|
554
|
+
rb_define_singleton_method(mLinenoise, "hint_color",
|
555
|
+
linenoise_get_hint_color, 0);
|
556
|
+
rb_define_singleton_method(mLinenoise, "hint_bold=",
|
557
|
+
linenoise_set_hint_boldness, 1);
|
558
|
+
rb_define_singleton_method(mLinenoise, "hint_bold?",
|
559
|
+
linenoise_get_hint_boldness, 0);
|
560
|
+
rb_define_singleton_method(mLinenoise, "clear_screen",
|
561
|
+
linenoise_clear_screen, 0);
|
562
|
+
|
563
|
+
history = rb_obj_alloc(rb_cObject);
|
564
|
+
rb_extend_object(history, rb_mEnumerable);
|
565
|
+
rb_define_singleton_method(history, "max_size=", hist_set_max_len, 1);
|
566
|
+
rb_define_singleton_method(history, "<<", hist_push, 1);
|
567
|
+
rb_define_singleton_method(history, "push", hist_push_method, -1);
|
568
|
+
rb_define_singleton_method(history, "save", hist_save, 1);
|
569
|
+
rb_define_singleton_method(history, "load", hist_load, 1);
|
570
|
+
rb_define_singleton_method(history, "size", hist_length, 0);
|
571
|
+
rb_define_singleton_method(history, "clear", hist_clear, 0);
|
572
|
+
rb_define_singleton_method(history, "each", hist_each, 0);
|
573
|
+
rb_define_singleton_method(history, "[]", hist_get, 1);
|
574
|
+
rb_define_singleton_method(history, "[]=", hist_set, 2);
|
575
|
+
|
576
|
+
/*
|
577
|
+
* The history buffer. It extends Enumerable module, so it behaves just like
|
578
|
+
* an array. For example, gets the fifth content that the user input by
|
579
|
+
* HISTORY[4].
|
580
|
+
*/
|
581
|
+
rb_define_const(mLinenoise, "HISTORY", history);
|
582
|
+
|
583
|
+
/* Hint color helpers */
|
584
|
+
rb_define_const(mLinenoise, "DEFAULT", Qnil);
|
585
|
+
rb_define_const(mLinenoise, "RED", INT2NUM(red));
|
586
|
+
rb_define_const(mLinenoise, "GREEN", INT2NUM(green));
|
587
|
+
rb_define_const(mLinenoise, "YELLOW", INT2NUM(yellow));
|
588
|
+
rb_define_const(mLinenoise, "BLUE", INT2NUM(blue));
|
589
|
+
rb_define_const(mLinenoise, "MAGENTA", INT2NUM(magenta));
|
590
|
+
rb_define_const(mLinenoise, "CYAN", INT2NUM(cyan));
|
591
|
+
rb_define_const(mLinenoise, "WHITE", INT2NUM(white));
|
28
592
|
|
29
|
-
|
30
|
-
|
593
|
+
rb_funcall(mLinenoise, rb_intern("multiline="), 1, Qtrue);
|
594
|
+
rb_funcall(mLinenoise, rb_intern("hint_color="), 1, Qnil);
|
595
|
+
rb_funcall(mLinenoise, rb_intern("hint_bold="), 1, Qfalse);
|
31
596
|
}
|
data/lib/linenoise/version.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
RSpec.describe Linenoise::HISTORY do
|
2
|
+
after { subject.clear }
|
3
|
+
|
4
|
+
describe "#push" do
|
5
|
+
it "appends to history" do
|
6
|
+
subject.max_size = 3
|
7
|
+
|
8
|
+
subject << "123"
|
9
|
+
expect(subject.size).to eq(1)
|
10
|
+
|
11
|
+
subject.push("1", "2")
|
12
|
+
expect(subject.size).to eq(3)
|
13
|
+
|
14
|
+
subject.push("3", "4")
|
15
|
+
expect(subject.size).to eq(3)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#save" do
|
20
|
+
let(:filename) { 'history_file' }
|
21
|
+
|
22
|
+
after { File.delete(filename) }
|
23
|
+
|
24
|
+
it "saves history" do
|
25
|
+
expect(File.exist?(filename)).not_to be_truthy
|
26
|
+
subject.save(filename)
|
27
|
+
expect(File.exist?(filename)).to be_truthy
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#load" do
|
32
|
+
let(:filename) { 'history_file' }
|
33
|
+
|
34
|
+
before do
|
35
|
+
File.open(filename, 'w+') { |f| f.puts("1\n2\n") }
|
36
|
+
end
|
37
|
+
|
38
|
+
after { File.delete(filename) }
|
39
|
+
|
40
|
+
it "loads history" do
|
41
|
+
subject.load(filename)
|
42
|
+
expect(subject.size).to eq(2)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#[]=" do
|
47
|
+
before { subject.push('1', '2', '3') }
|
48
|
+
|
49
|
+
it "replaces a line in history" do
|
50
|
+
subject[0] = 'begin'
|
51
|
+
subject[-1] = 'end'
|
52
|
+
|
53
|
+
expect(subject[0]).to eq('begin')
|
54
|
+
expect(subject[1]).to eq('2')
|
55
|
+
expect(subject[2]).to eq('end')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "raises error when index is out of boundary" do
|
59
|
+
expect { subject[100] = 'begin' }
|
60
|
+
.to raise_error(IndexError, 'invalid index')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#each" do
|
65
|
+
before { subject.push('1', '2', '3') }
|
66
|
+
|
67
|
+
it "iterates over history lines" do
|
68
|
+
expect(subject.each.to_a.join).to eq('123')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/linenoise_spec.rb
CHANGED
@@ -1,9 +1,64 @@
|
|
1
1
|
RSpec.describe Linenoise do
|
2
2
|
it "has a version number" do
|
3
|
-
expect(Linenoise::VERSION).
|
3
|
+
expect(Linenoise::VERSION).to be_a(String)
|
4
4
|
end
|
5
5
|
|
6
6
|
it "has a gem version number" do
|
7
|
-
expect(Linenoise::GEM_VERSION).
|
7
|
+
expect(Linenoise::GEM_VERSION).to be_a(String)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#completion_proc=" do
|
11
|
+
it "raises error when passed value doesn't implement #call" do
|
12
|
+
expect { described_class.completion_proc = 1 }
|
13
|
+
.to raise_error(ArgumentError, "argument must respond to `call'")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#hint_proc=" do
|
18
|
+
it "raises error when passed value doesn't implement #call" do
|
19
|
+
expect { described_class.hint_proc = 1 }
|
20
|
+
.to raise_error(ArgumentError, "argument must respond to `call'")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#hint_color=" do
|
25
|
+
after { Linenoise.hint_color = Linenoise::DEFAULT }
|
26
|
+
|
27
|
+
it "sets hint color" do
|
28
|
+
Linenoise.hint_color = Linenoise::RED
|
29
|
+
expect(Linenoise.hint_color).to eq(Linenoise::RED)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "raises error if color is not in range" do
|
33
|
+
expect { Linenoise.hint_color = -1 }
|
34
|
+
.to raise_error(ArgumentError, "color '-1' is not in range (31-37)")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "raises error if color is not an integer" do
|
38
|
+
expect { Linenoise.hint_color = 'salamat' }
|
39
|
+
.to raise_error(TypeError, 'hint color is not an Integer')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#hint_bold=" do
|
44
|
+
after { Linenoise.hint_bold = false }
|
45
|
+
|
46
|
+
it "sets hint color" do
|
47
|
+
Linenoise.hint_bold = true
|
48
|
+
expect(Linenoise.hint_bold?).to be_truthy
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#multiline?" do
|
53
|
+
after { Linenoise.multiline = true }
|
54
|
+
|
55
|
+
it "is `true` by default" do
|
56
|
+
expect(Linenoise).to be_multiline
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can be set to `false`" do
|
60
|
+
Linenoise.multiline = false
|
61
|
+
expect(Linenoise).not_to be_multiline
|
62
|
+
end
|
8
63
|
end
|
9
64
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linenoise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyrylo Silin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.9'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.9'
|
55
83
|
description: ''
|
56
84
|
email:
|
57
85
|
- silin@kyrylo.org
|
@@ -69,6 +97,7 @@ files:
|
|
69
97
|
- ext/linenoise/linenoise.c
|
70
98
|
- lib/linenoise.rb
|
71
99
|
- lib/linenoise/version.rb
|
100
|
+
- spec/linenoise_history_spec.rb
|
72
101
|
- spec/linenoise_spec.rb
|
73
102
|
- spec/spec_helper.rb
|
74
103
|
homepage: https://github.com/kyrylo/linenoise
|
@@ -99,5 +128,6 @@ signing_key:
|
|
99
128
|
specification_version: 4
|
100
129
|
summary: ''
|
101
130
|
test_files:
|
131
|
+
- spec/linenoise_history_spec.rb
|
102
132
|
- spec/spec_helper.rb
|
103
133
|
- spec/linenoise_spec.rb
|