fir-repl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []