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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21125585819cb1e4b7334c6451cedf89075e8371
4
- data.tar.gz: 3e135e25334f937331e1ad76152ebe23e9c8035b
3
+ metadata.gz: 4cd7f0921e5e628c64d106d5468fbb058392d0c3
4
+ data.tar.gz: f45df4beb22b1553294cf811aaecebb51b4dc094
5
5
  SHA512:
6
- metadata.gz: 61dff245d9241f8f264060f8084a4cf0ab9e5a4d05f7eb0ceca5f82d741cbde7cbd6ee5b21d3cebea51e9c218de6542333c5bf312c0d985bbb7149802de64629
7
- data.tar.gz: 5c29070bd3157b220d1763203884c7ca6f6dbc05735f8d12e5bf635fee98eee716a6dba751ce4fa78d7b4b235ace6d17b16735771447e3875a574e36ce9aa9fb
6
+ metadata.gz: 4615c8507956123c6f1e3c2120906954d15e6e79c42477385938c2e26d7674b5e74fa84fc585baef5851e0c53744039e77ddc80b6413d3542d6538d8c727449d
7
+ data.tar.gz: 6bd64f537d2b48b05a00a82bebff9736dee05278fd6b73325e3fdc49f149eaa384b29218059ffd896cbda63cfb4026d73f13b52325e1faf091f182aa424d1006
@@ -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
- TBA
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
- TBA
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
- TBA
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
- API
20
- ---
59
+ ```ruby
60
+ require 'linenoise'
21
61
 
22
- TBA
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
@@ -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
- history = malloc(sizeof(char*)*history_max_len);
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
+ }
@@ -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);
@@ -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
- VALUE result;
8
- char *line;
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
- line = linenoise(StringValueCStr(prompt));
11
- if (line) {
12
- result = rb_locale_str_new_cstr(line);
13
- }
14
- else
15
- result = Qnil;
16
- if (line) free(line);
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
- return result;
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
- VALUE mLinenoise = rb_define_module("Linenoise");
25
- rb_define_module_function(mLinenoise, "linenoise",
26
- linenoise_linenoise, 1);
27
- rb_define_alias(rb_singleton_class(mLinenoise), "readline", "linenoise");
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
- /* Version string of Linenoise. */
30
- rb_define_const(mLinenoise, "VERSION", rb_str_new_cstr("1.0"));
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
  }
@@ -1,3 +1,3 @@
1
1
  module Linenoise
2
- GEM_VERSION = '1.0.0'.freeze
2
+ GEM_VERSION = '1.1.0'.freeze
3
3
  end
@@ -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
@@ -1,9 +1,64 @@
1
1
  RSpec.describe Linenoise do
2
2
  it "has a version number" do
3
- expect(Linenoise::VERSION).not_to be_a(String)
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).not_to be_a(String)
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.0.0
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-24 00:00:00.000000000 Z
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