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 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