fir-repl 0.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.
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'screen_helper'
5
+
6
+ class Fir
7
+ class Renderer
8
+ include ScreenHelper
9
+ attr_reader :output
10
+
11
+ def initialize(output)
12
+ @output = output
13
+ output.syswrite(line_prompt)
14
+ end
15
+
16
+ def perform(state)
17
+ output.syswrite(rendered_lines(state))
18
+ output.syswrite(rendered_cursor(state))
19
+ output.syswrite(rendered_suggestion(state)) if state.suggestion
20
+ end
21
+
22
+ def rendered_suggestion(state)
23
+ return unless state.suggestion.length.positive?
24
+ "#{state.suggestion}#{cursor_back(state.suggestion.length)}"
25
+ end
26
+
27
+ def rendered_cursor(state)
28
+ cursor = ''
29
+ if state.cursor.x != state.current_line.length
30
+ cursor = "#{cursor}#{cursor_back(state.current_line.length - state.cursor.x)}"
31
+ end
32
+ cursor
33
+ end
34
+
35
+ def rendered_lines(state)
36
+ lines_with_prompt(state)
37
+ .map(&:join)
38
+ .join("\n#{horizontal_absolute(1)}")
39
+ .concat(result_prompt(state))
40
+ end
41
+
42
+ def lines_with_prompt(state)
43
+ lines_to_render(state).map.with_index do |line, i|
44
+ prompt = state.indents[i].zero? ? '>' : '*'
45
+ [line_prompt(prompt), (' ' * state.indents[i]), line.join]
46
+ end
47
+ end
48
+
49
+ def lines_to_render(state)
50
+ if state.executable?
51
+ state.lines[0...-1]
52
+ else
53
+ state.lines
54
+ end
55
+ end
56
+
57
+ def result_prompt(state)
58
+ if state.executable?
59
+ "\n=> "
60
+ else
61
+ ''
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'lines'
5
+ require_relative 'cursor'
6
+ require_relative 'indent'
7
+ require_relative 'history'
8
+ require_relative 'suggestion'
9
+
10
+ class Fir
11
+ class ReplState
12
+ attr_accessor :lines, :cursor
13
+ attr_reader :indent, :repl_binding, :history
14
+
15
+ def self.blank
16
+ new(Lines.blank, Cursor.blank)
17
+ end
18
+
19
+ def initialize(
20
+ lines,
21
+ cursor,
22
+ repl_binding = TOPLEVEL_BINDING,
23
+ history = Fir::History.new
24
+ )
25
+ @lines = lines
26
+ @cursor = cursor
27
+ @repl_binding = repl_binding
28
+ @history = history
29
+ set_indent
30
+ end
31
+
32
+ def transition(command)
33
+ new_state = command.execute
34
+ new_state.set_indent
35
+ yield new_state if block_given?
36
+ return blank if new_state.executable?
37
+ new_state
38
+ end
39
+
40
+ def clone
41
+ self.class.new(
42
+ lines.clone,
43
+ cursor.clone,
44
+ repl_binding,
45
+ history
46
+ )
47
+ end
48
+
49
+ def blank
50
+ self.class.new(
51
+ Lines.blank,
52
+ Cursor.blank,
53
+ repl_binding,
54
+ history
55
+ )
56
+ end
57
+
58
+ def blank?
59
+ lines.blank? && cursor.blank?
60
+ end
61
+
62
+ def ==(other)
63
+ lines == other.lines && cursor == other.cursor
64
+ end
65
+
66
+ def current_line=(new_line)
67
+ lines[cursor.y] = new_line
68
+ end
69
+
70
+ def current_line
71
+ lines[cursor.y]
72
+ end
73
+
74
+ def indents
75
+ indent.indents
76
+ end
77
+
78
+ def executable?
79
+ indent.executable?
80
+ end
81
+
82
+ def suggestion
83
+ history.suggestion(current_line.join)
84
+ end
85
+
86
+ def commit_current_line_to_history
87
+ Fir::History.add_line_to_history_file(
88
+ current_line.join
89
+ )
90
+ end
91
+
92
+ protected
93
+
94
+ def set_indent
95
+ @indent = indent!
96
+ end
97
+
98
+ def indent!
99
+ Fir::Indent.new(lines.map(&:join)).generate
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'eraser'
5
+ require_relative 'evaluater'
6
+ require_relative 'renderer'
7
+
8
+ class Fir
9
+ class Screen
10
+ attr_reader :eraser
11
+ attr_reader :renderer
12
+ attr_reader :evaluater
13
+
14
+ def initialize(output, error)
15
+ @eraser = Eraser.new(output)
16
+ @renderer = Renderer.new(output)
17
+ @evaluater = Evaluater.new(output, error)
18
+ end
19
+
20
+ def update(state, new_state)
21
+ eraser.perform(state)
22
+ renderer.perform(new_state)
23
+ evaluater.perform(new_state)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+
4
+ class Fir
5
+ module ScreenHelper
6
+ def previous_line(n)
7
+ "\e[#{n}F"
8
+ end
9
+
10
+ def next_line(n)
11
+ "\e[#{n}E"
12
+ end
13
+
14
+ def horizontal_absolute(n)
15
+ "\e[#{n}G"
16
+ end
17
+
18
+ def clear(n)
19
+ "\e[#{n}K"
20
+ end
21
+
22
+ def cursor_back(n)
23
+ "\e[#{n}D"
24
+ end
25
+
26
+ def line_prompt(prompt = '>')
27
+ "(fir)#{prompt} "
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+
4
+ class Fir
5
+ class Suggestion
6
+ attr_reader :line, :history
7
+
8
+ def initialize(line, history)
9
+ @line = line
10
+ @history = history
11
+ end
12
+
13
+ def generate(i)
14
+ word = suggestion(line, i)
15
+ return unless word
16
+ word[(line.length)..(word.length)]
17
+ end
18
+
19
+ def suggestion(str, i)
20
+ if str == '' && i.positive?
21
+ history[-i]
22
+ elsif str != ''
23
+ filtered_history(str)[-i]
24
+ end
25
+ end
26
+
27
+ def filtered_history(str)
28
+ history.grep(/^#{Regexp.escape(str)}/).reverse
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+
4
+ class Fir
5
+ VERSION = '0.1.0'
6
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fir-repl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nassredean Nasseri
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A Ruby REPL inspired by the Fish shell
14
+ email: dean@nasseri.io
15
+ executables:
16
+ - fir
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - bin/fir
23
+ - lib/fir.rb
24
+ - lib/fir/cursor.rb
25
+ - lib/fir/eraser.rb
26
+ - lib/fir/evaluater.rb
27
+ - lib/fir/history.rb
28
+ - lib/fir/indent.rb
29
+ - lib/fir/key.rb
30
+ - lib/fir/key_command/backspace_command.rb
31
+ - lib/fir/key_command/ctrl_a_command.rb
32
+ - lib/fir/key_command/ctrl_c_command.rb
33
+ - lib/fir/key_command/ctrl_d_command.rb
34
+ - lib/fir/key_command/ctrl_e_command.rb
35
+ - lib/fir/key_command/ctrl_v_command.rb
36
+ - lib/fir/key_command/ctrl_z_command.rb
37
+ - lib/fir/key_command/down_arrow_command.rb
38
+ - lib/fir/key_command/enter_command.rb
39
+ - lib/fir/key_command/key_command.rb
40
+ - lib/fir/key_command/left_arrow_command.rb
41
+ - lib/fir/key_command/right_arrow_command.rb
42
+ - lib/fir/key_command/single_key_command.rb
43
+ - lib/fir/key_command/tab_command.rb
44
+ - lib/fir/key_command/up_arrow_command.rb
45
+ - lib/fir/lines.rb
46
+ - lib/fir/renderer.rb
47
+ - lib/fir/repl_state.rb
48
+ - lib/fir/screen.rb
49
+ - lib/fir/screen_helper.rb
50
+ - lib/fir/suggestion.rb
51
+ - lib/fir/version.rb
52
+ homepage:
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.6.14
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: A Ruby REPL inspired by the Fish shell
76
+ test_files: []