linenoise 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://circleci.com/gh/kyrylo/linenoise-rb)
|
5
|
+
[](http://badge.fury.io/rb/linenoise)
|
6
|
+
[](http://inch-ci.org/github/kyrylo/linenoise)
|
7
|
+
[](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
|