vtparser 0.1.0 → 0.2.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
  SHA256:
3
- metadata.gz: d622edd5fbb0b4f47cb9831699c466aeabc3da9c6bed99370cc7d99d169c8c02
4
- data.tar.gz: 33eccc5b843d8c5d734a06d5b2b3d389462c23ea568bf9456ed55a38d6f1a50a
3
+ metadata.gz: b142e3d52113ff288e56ef70f3a948e71233e3dbf6dd52078c168b205c0f5c69
4
+ data.tar.gz: 5a76607662cc257ad2b963d2ea8d52d86c3f7e1e5176e9b289966c611d649cb0
5
5
  SHA512:
6
- metadata.gz: 0e5c6dd30582e24e0ad63b142446280b1be31d51cbae543b78c0019bf626ce2d477ea7bf002ab3707c7dd8056df886d33304eaa2a9509c30a9cac7ac6148e4b9
7
- data.tar.gz: ab1f60a0cdd8e079274bb8a7d6a285287b81a3fe1304462086ccdb71910fe09451faadaa4f3ce8935b6a63fdc579a856e069558e7183c8a7c659924e7ad32417
6
+ metadata.gz: e2861798a37ed651916d3bdd2c5a8c5e990ffc66a92c1e2b006bcfd75fe80e38288af7ca1118681a805573d2b9fb87f01bd1842f6e5232d7e599abc61d0c7255
7
+ data.tar.gz: 2f1b3fa73f48e839c818c0d433d2d42edc9caa1a9afff6eb4ea6e8e13fd0453405cf364a404b9605346e47a8a625f3c7bd38babe4c9434b629714aca8d331e9b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2024-10-03
4
+
5
+ - Add keyevent handling
6
+
3
7
  ## [0.1.0] - 2024-10-01
4
8
 
5
9
  - Initial release
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # VT 100 Parser Gem
2
2
 
3
- This gem is a parser for VT100 terminal escape sequences. It is based on the C code from https://github.com/haberman/vtparse/.
3
+ This gem is a parser for VT100 terminal escape sequences. It is based on the C code from https://github.com/haberman/vtparse/ and implements the statemachine from https://www.vt100.net/emu/dec_ansi_parser.
4
+
5
+ The purpose of this Gem is to have a relatively easy way to filter/modify the output of child/sub-processes (for instance launched via `PTY::spawn`) which use animation or colors.
6
+
7
+ Uses keyboard mapping logic from https://github.com/vidarh/keyboard_map/
4
8
 
5
9
  ## Background on VT100 Escape Sequences
6
10
 
@@ -22,26 +26,39 @@ gem install vtparser
22
26
 
23
27
  ## Basic Usage
24
28
 
25
- See the minimal example below and the [`examples`](https://github.com/coezbek/vtparser/examples) directory for more examples.
29
+ See the minimal example below:
26
30
 
27
31
  ```ruby
28
- require 'vtparser'
32
+ require_relative '../lib/vtparser'
29
33
 
30
34
  # Instantiate the parser with a block to handle actions
31
35
  parser = VTParser.new do |action, ch, intermediate_chars, params|
32
36
 
33
37
  # For this minimal example, we'll just turn everything back strings to print
34
- print VtParser::to_ansi(action, ch, intermediate_chars, params)
38
+ print VTParser::to_ansi(action, ch, intermediate_chars, params)
35
39
 
36
40
  end
37
41
 
38
- # Sample input containing ANSI escape sequences
39
- input = "\e[31mHello, \e[1mWorld!\e[0m"
42
+ # Sample input containing ANSI escape sequences (red text, bold text)
43
+ input = "\e[31mHello, \e[1mWorld!\e[0m\n"
40
44
 
41
45
  # Parse the input
42
46
  parser.parse(input)
43
47
  ```
44
48
 
49
+ Further samples in the [`examples directory`](https://github.com/coezbek/vtparser/tree/main/examples):
50
+
51
+ - [`echo_keys.rb`](https://github.com/coezbek/vtparser/tree/main/examples/echo_keys.rb): Echoes the keys pressed by the user
52
+ - [`indent_cli.rb`](https://github.com/coezbek/vtparser/tree/main/examples/indent_cli.rb): Indents the output of simple command line tools
53
+
54
+ ## Limitations
55
+
56
+ - The parser is based on the implementation https://github.com/haberman/vtparse/ and based on a state machine which precedes Unicode. As such it does not have state transitions for Unicode characters. Rather, it will output them as `:ignore` actions. In case unicode characters are used inside escape sequences, the parser will likely not be able to handle them correctly.
57
+
58
+ - The state machine does not expose all input characters to the implementation in relationship to the `DSC` (Device Control String) sequences. In particular the "Final Character" is swallowed by the statemachine from https://www.vt100.net/emu/dec_ansi_parser. To circumvent this limitation, I have modified the parser to expose the final character as intermediate_chars to the `:hook` action.
59
+
60
+ - The parser only outputs full `actions`. So triggering an event for the `ESC` key doesn't work (as expected).
61
+
45
62
  ## Development
46
63
 
47
64
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,27 @@
1
+ require 'io/console'
2
+ require_relative '../lib/vtparser'
3
+
4
+ #
5
+ # Example for how to switch to raw mode to output infos about each keypress
6
+ #
7
+ STDIN.raw do |io|
8
+
9
+ parser = VTParser.new do |action, ch, intermediate_chars, params|
10
+
11
+ puts " New VTParser action: #{action}, ch: #{ch.inspect}, ch0x: #{ch.ord.to_s(16)}, intermediate_chars: #{intermediate_chars}, params: #{params}\r\n"
12
+
13
+ parser.to_key(action, ch, intermediate_chars, params) do |event|
14
+
15
+ puts " Keyevent: #{event.to_sym.inspect} #{event.inspect}\r\n"
16
+ exit(1) if event.to_sym == :ctrl_c
17
+
18
+ end
19
+ end
20
+
21
+ loop do
22
+ ch = $stdin.getch
23
+
24
+ puts "Getch: #{ch.inspect}\r\n"
25
+ parser.parse ch
26
+ end
27
+ end
@@ -4,6 +4,19 @@ require 'rainbow/refinement' # for colorizing output
4
4
  using Rainbow
5
5
  require_relative '../lib/vtparser'
6
6
 
7
+ #
8
+ # 'indent_cli.rb' - Example for vtparser
9
+ #
10
+ # This example demonstrates how to use the VTParser to indent the output of simple (!) tty programs
11
+ # with colorized or animated output.
12
+ #
13
+ # Run with `ruby indent_cli.rb <command>`` where <command> is the command you want to run.
14
+ #
15
+ # Two simple examples are included:
16
+ # - A simple spinner animation is included in `examples/spinner.rb`: `ruby indent_cli.rb 'ruby spinner.rb'`
17
+ # - A simple progress bar animation is included in `examples/progress.rb`: `ruby indent_cli.rb 'ruby progress.rb'`
18
+ #
19
+
7
20
  # Get the command from ARGV
8
21
  command = ARGV.join(' ')
9
22
  if command.empty?
@@ -12,13 +25,22 @@ if command.empty?
12
25
  end
13
26
 
14
27
  line_indent = ' ▐ '.yellow
28
+ line_indent_length = 6
29
+
30
+ #
31
+ # Use VTParser to process the VT100 escape sequences outputted by nested program and prepend the line_indent text.
32
+ #
15
33
  first_line = true
16
34
  parser = VTParser.new do |action, ch, intermediate_chars, params|
17
35
  print line_indent if first_line
18
36
  first_line = false
19
37
 
38
+ if $DEBUG && (action != :print || !(ch =~ /\P{Cc}/))
39
+ puts "action: #{action}, ch: #{ch.inspect}, ch0x: 0x#{ "%02x" % ch.ord}, intermediate_chars: #{intermediate_chars}, params: #{params}"
40
+ end
20
41
  to_output = VTParser::to_ansi(action, ch, intermediate_chars, params)
21
42
 
43
+ # Handle newlines, carriage returns, and cursor movement
22
44
  case action
23
45
  when :print, :execute, :put, :osc_put
24
46
  if ch == "\n" || ch == "\r"
@@ -27,16 +49,13 @@ parser = VTParser.new do |action, ch, intermediate_chars, params|
27
49
  next
28
50
  end
29
51
  when :csi_dispatch
30
- if to_output == "\e[2K"
52
+ if to_output == "\e[2K" # Clear line
31
53
  print "\e[2K"
32
54
  print line_indent
33
55
  next
34
56
  else
35
- if ch == 'G'
36
- # puts "to_output: #{to_output.inspect} action: #{action} ch: #{ch.inspect}"
37
- # && parser.params.size == 1
38
- print "\e[#{parser.params[0] + 6}G"
39
-
57
+ if ch == 'G' # Cursor movement to column
58
+ print "\e[#{parser.params[0] + line_indent_length}G"
40
59
  next
41
60
  end
42
61
  end
@@ -45,9 +64,13 @@ parser = VTParser.new do |action, ch, intermediate_chars, params|
45
64
  print to_output
46
65
  end
47
66
 
67
+ #
68
+ # Spawn the given command using PTY::spawn, and connect pipes.
69
+ #
48
70
  begin
49
71
  PTY.spawn(command) do |stdout_and_stderr, stdin, pid|
50
72
 
73
+ # Start separate thread to pipe stdin to the child process
51
74
  Thread.new do
52
75
  while pid != nil
53
76
  stdin.write(STDIN.readpartial(1024)) # Requires user to press enter!
@@ -57,8 +80,12 @@ begin
57
80
  exit(0)
58
81
  end
59
82
 
83
+ # Pipe stdout and stderr to the parser
60
84
  begin
61
- stdout_and_stderr.winsize = $stdout.winsize
85
+ # Ensure the child process has the proper window size, because
86
+ # - tools such as yarn use it to identify tty mode
87
+ # - some tools use it to determine the width of the terminal for formatting
88
+ stdout_and_stderr.winsize = [$stdout.winsize.first, $stdout.winsize.last - line_indent_length]
62
89
 
63
90
  stdout_and_stderr.each_char do |char|
64
91
 
@@ -73,7 +100,7 @@ begin
73
100
  Process.wait(pid)
74
101
  pid = nil
75
102
  exit_status = $?.exitstatus
76
- result = exit_status == 0
103
+ # result = exit_status == 0
77
104
 
78
105
  # Clear the line, reset the cursor to the start of the line
79
106
  print "\e[2K\e[1G"
@@ -0,0 +1,15 @@
1
+ require_relative '../lib/vtparser'
2
+
3
+ # Instantiate the parser with a block to handle actions
4
+ parser = VTParser.new do |action, ch, intermediate_chars, params|
5
+
6
+ # For this minimal example, we'll just turn everything back strings to print
7
+ print VTParser::to_ansi(action, ch, intermediate_chars, params)
8
+
9
+ end
10
+
11
+ # Sample input containing ANSI escape sequences (red text, bold text)
12
+ input = "\e[31mHello, \e[1mWorld!\e[0m\n"
13
+
14
+ # Parse the input
15
+ parser.parse(input)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Helper script to be used to demonstrate `indent_cli.rb`. It displays a full-width animated progress bar.
5
+ #
6
+
7
+ require 'ruby-progressbar'
8
+
9
+ loop do
10
+ progressbar = ProgressBar.create
11
+ 99.times {
12
+ progressbar.increment
13
+ sleep 0.02
14
+ }
15
+ print "\r"
16
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Helper script to be used to demonstrate `indent_cli.rb`
5
+ #
6
+
7
+ # See: https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json
8
+ frames = [
9
+ "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"
10
+ ]
11
+ interval = 0.08 # 80 milliseconds
12
+
13
+ # Infinite loop to display the animation until interrupted
14
+ loop do
15
+ frames.each do |frame|
16
+ print "\r #{frame} " # "\r" moves the cursor back to the start of the line
17
+ sleep(interval)
18
+ end
19
+ end
@@ -0,0 +1,177 @@
1
+ require 'set'
2
+
3
+ #
4
+ # Logic taken from vidarh/keyboard_map gem
5
+ #
6
+ # https://github.com/vidarh/keyboard_map
7
+ #
8
+ # See examples/keymap.rb for usage
9
+ #
10
+
11
+ class KeyEvent
12
+ attr_reader :modifiers, :key, :args
13
+
14
+ def initialize(key, *modifiers, args: nil)
15
+ @key = key
16
+ @args = args
17
+ @modifiers = modifiers.map(&:to_sym).to_set
18
+ end
19
+
20
+ def to_s
21
+ (modifiers.to_a.sort << key).join('_')
22
+ end
23
+
24
+ def to_sym
25
+ to_s.to_sym
26
+ end
27
+
28
+ def ==(other)
29
+ case other
30
+ when KeyEvent
31
+ self.modifiers == other.modifiers && self.key == other.key
32
+ when Symbol
33
+ self.to_sym == other
34
+ else
35
+ self.to_s == other
36
+ end
37
+ end
38
+ end
39
+
40
+ module Keymap
41
+
42
+ SINGLE_KEY_EVENT = {
43
+ "\t" => :tab,
44
+ "\r" => :enter,
45
+ "\n" => :enter,
46
+ "\u007F" => :backspace
47
+ }.freeze
48
+
49
+ CSI_BASIC_MAP = {
50
+ "A" => :up,
51
+ "B" => :down,
52
+ "C" => :right,
53
+ "D" => :left,
54
+ "E" => :keypad_5,
55
+ "F" => :end,
56
+ "H" => :home,
57
+ }.freeze
58
+
59
+ CSI_TILDE_MAP = {
60
+ "1" => :home,
61
+ "2" => :insert,
62
+ "3" => :delete,
63
+ "4" => :end,
64
+ "5" => :page_up,
65
+ "6" => :page_down,
66
+ "15" => :f5,
67
+ "17" => :f6,
68
+ "18" => :f7,
69
+ "19" => :f8,
70
+ "20" => :f9,
71
+ "21" => :f10,
72
+ "23" => :f11,
73
+ "24" => :f12,
74
+ }.freeze
75
+
76
+ SS3_KEY_MAP = {
77
+ "P" => :f1,
78
+ "Q" => :f2,
79
+ "R" => :f3,
80
+ "S" => :f4,
81
+ }.freeze
82
+
83
+ def map_modifiers(mod)
84
+ return [] if mod.nil? || mod < 2
85
+ modifiers = []
86
+ mod = mod - 1 # Subtract 1 to align with modifier bits
87
+ modifiers << :shift if mod & 1 != 0
88
+ modifiers << :alt if mod & 2 != 0
89
+ modifiers << :ctrl if mod & 4 != 0
90
+ modifiers
91
+ end
92
+
93
+ def to_key(action, ch, intermediate_chars, params, &block)
94
+
95
+ case action
96
+ when :execute, :print, :ignore
97
+ # Control characters (e.g., Ctrl+C)
98
+ key = map_control_character(ch)
99
+ yield key if key
100
+ #when :print, :ignore
101
+ # Regular printable characters
102
+ #yield KeyboardEvent.new(ch)
103
+ when :esc_dispatch
104
+ # ESC sequences without intermediates
105
+ if intermediate_chars == ''
106
+ key = process_esc_sequence(ch)
107
+ yield key if key
108
+ else
109
+ # Handle other ESC sequences if necessary
110
+ end
111
+ when :csi_dispatch
112
+ key = process_csi_sequence(params, intermediate_chars, ch)
113
+ yield key if key
114
+ when :collect, :param, :clear
115
+ # Handled internally; no action needed here
116
+ else
117
+ # Handle other actions if necessary
118
+ end
119
+ end
120
+
121
+ def map_control_character(ch)
122
+ if SINGLE_KEY_EVENT.key?(ch)
123
+ return KeyboardEvent.new(SINGLE_KEY_EVENT[ch])
124
+ elsif ch.ord.between?(0x01, 0x1A)
125
+ # Ctrl+A to Ctrl+Z
126
+ key = (ch.ord + 96).chr
127
+ return KeyboardEvent.new(key, :ctrl)
128
+ else
129
+ return KeyboardEvent.new(ch)
130
+ end
131
+ end
132
+
133
+ def process_esc_sequence(final_char)
134
+ case final_char
135
+ when 'Z'
136
+ # Shift+Tab
137
+ return KeyboardEvent.new(:tab, :shift)
138
+ when "\e"
139
+ # Double ESC
140
+ return KeyboardEvent.new(:esc)
141
+ else
142
+ # Meta key (Alt) combinations
143
+ if final_char.ord.between?(0x20, 0x7E)
144
+ return KeyboardEvent.new(final_char, :meta)
145
+ else
146
+ # Handle other ESC sequences if necessary
147
+ end
148
+ end
149
+ end
150
+
151
+ def process_csi_sequence(params, intermediate_chars, final_char)
152
+ key = nil
153
+ modifiers = []
154
+ params = params.map(&:to_i)
155
+
156
+ if intermediate_chars == ''
157
+ if final_char == '~'
158
+ # Sequences like ESC [ 1 ~
159
+ key = CSI_TILDE_MAP[params[0].to_s]
160
+ modifiers = map_modifiers(params[1]) if params.size > 1
161
+ else
162
+ # Sequences like ESC [ A
163
+ key = CSI_BASIC_MAP[final_char]
164
+ modifiers = map_modifiers(params[0]) if params.size > 0
165
+ end
166
+ else
167
+ # Handle intermediates if necessary
168
+ end
169
+
170
+ if key
171
+ return KeyboardEvent.new(key, *modifiers)
172
+ else
173
+ # Handle unrecognized sequences
174
+ end
175
+ end
176
+
177
+ end
@@ -1,6 +1,10 @@
1
+ require_relative "keymap"
2
+
1
3
  class VTParser
2
4
  attr_reader :intermediate_chars, :params
3
5
 
6
+ include Keymap
7
+
4
8
  def initialize(&block)
5
9
  @callback = block
6
10
  @state = :GROUND
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vtparser
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vtparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Oezbek
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-01 00:00:00.000000000 Z
11
+ date: 2024-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-prompt
@@ -58,14 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.40.0
61
+ version: '0.40'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.40.0
68
+ version: '0.40'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-progressbar
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  description: A pure Ruby VT100 parser that can be used to parse ANSI escape sequences.
70
84
  email:
71
85
  - c.oezbek@gmail.com
@@ -73,12 +87,16 @@ executables: []
73
87
  extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
- - ".bash_history"
77
90
  - CHANGELOG.md
78
91
  - README.md
79
92
  - Rakefile
93
+ - examples/echo_keys.rb
80
94
  - examples/indent_cli.rb
95
+ - examples/minimal.rb
96
+ - examples/progress.rb
97
+ - examples/spinner.rb
81
98
  - lib/vtparser.rb
99
+ - lib/vtparser/keymap.rb
82
100
  - lib/vtparser/parser.rb
83
101
  - lib/vtparser/version.rb
84
102
  homepage: https://github.com/coezbek/vtparser
@@ -103,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
121
  - !ruby/object:Gem::Version
104
122
  version: '0'
105
123
  requirements: []
106
- rubygems_version: 3.4.19
124
+ rubygems_version: 3.5.20
107
125
  signing_key:
108
126
  specification_version: 4
109
127
  summary: Pure Ruby VT100 parser
data/.bash_history DELETED
@@ -1,117 +0,0 @@
1
- rspec
2
- rspec
3
- rspec
4
- rspec
5
- rspec
6
- rspec
7
- rspec
8
- rspec
9
- rspec
10
- rspec
11
- rspec ./spec/vtparser_spec.rb:153
12
- rspec ./spec/vtparser_spec.rb:153
13
- rspec ./spec/vtparser_spec.rb:153
14
- rspec ./spec/vtparser_spec.rb:153
15
- rspec ./spec/vtparser_spec.rb:153
16
- rspec ./spec/vtparser_spec.rb:153
17
- rspec
18
- rspec
19
- rspec
20
- rspec ./spec/vtparser_spec.rb:37
21
- rspec ./spec/vtparser_spec.rb:37
22
- rspec
23
- rspec
24
- rspec expect(output).to eq(input)
25
- rspec ./spec/vtparser_spec.rb:156
26
- rspec ./spec/vtparser_spec.rb:156
27
- rspec ./spec/vtparser_spec.rb:156
28
- rspec ./spec/vtparser_spec.rb:156
29
- rspec
30
- rspec
31
- D
32
- rspec ./spec/vtparser_spec.rb
33
- rspec ./spec/vtparser_spec.rb:137
34
- rspec ./spec/vtparser_spec.rb:137
35
- rspec ./spec/vtparser_spec.rb:137
36
- rspec ./spec/vtparser_spec.rb:137
37
- rspec ./spec/vtparser_spec.rb:137
38
- rspec ./spec/vtparser_spec.rb:137
39
- rspec ./spec/vtparser_spec.rb:137
40
- rspec ./spec/vtparser_spec.rb:137
41
- rspec ./spec/vtparser_spec.rb
42
- rspec ./spec/vtparser_spec.rb
43
- rspec ./spec/vtparser_spec.rb
44
- bundle
45
- rspec ./spec/vtparser_spec.rb
46
- rspec ./spec/vtparser_spec.rb
47
- ruby lib/vtparser.rb 'yarn add --dev esbuild from "."'
48
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
49
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
50
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
51
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
52
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
53
- bundle
54
- bundle
55
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
56
- irb
57
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
58
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
59
- ruby lib/vtparser/parser.rb 'yarn add --dev esbuild from "."'
60
- ruby lib/vtparser/parser.rb 'joe'
61
- joe
62
- ruby lib/vtparser/parser.rb 'vim'
63
- vim
64
- ruby example/indent_cli.rb 'vim'
65
- ruby examples/indent_cli.rb 'vim'
66
- ruby examples/indent_cli.rb 'vim'
67
- ruby examples/indent_cli.rb 'vim'
68
- ruby examples/indent_cli.rb 'joe'
69
- ruby examples/indent_cli.rb 'joe'
70
- ruby examples/indent_cli.rb 'joe'
71
- lsss
72
- less
73
- less README.md
74
- ls
75
- exit
76
- ls
77
- ruby examples/indent_cli.rb 'less'
78
- ruby examples/indent_cli.rb 'less README.md'
79
- ruby examples/indent_cli.rb 'less README.md'
80
- ruby examples/indent_cli.rb 'less README.md'
81
- ruby examples/indent_cli.rb 'less README.md'
82
- ruby examples/indent_cli.rb 'less README.md'
83
- ruby examples/indent_cli.rb 'less README.md'
84
- ruby examples/indent_cli.rb 'less README.md'
85
- ruby examples/indent_cli.rb 'less README.md'
86
- ruby examples/indent_cli.rb 'less README.md'
87
- ruby examples/indent_cli.rb 'less README.md'
88
- ruby examples/indent_cli.rb 'less README.md'
89
- ruby examples/indent_cli.rb 'less README.md'
90
- ruby examples/indent_cli.rb 'less README.md'
91
- ruby examples/indent_cli.rb 'less README.md'
92
- less README.md
93
- ruby examples/indent_cli.rb 'less README.md'
94
- ruby examples/indent_cli.rb 'less README.md'
95
- ruby examples/indent_cli.rb 'less README.md'
96
- ruby examples/indent_cli.rb 'less README.md'
97
- ruby examples/indent_cli.rb 'less README.md'
98
- ruby examples/indent_cli.rb 'less README.md'
99
- ruby examples/indent_cli.rb 'less README.md'
100
- ruby examples/indent_cli.rb 'less README.md'
101
- ruby examples/indent_cli.rb 'less README.md'
102
- ruby examples/indent_cli.rb 'less README.md'
103
- ruby examples/test.rb
104
- kkllllijjaaa
105
- ruby examples/test.rb
106
- ruby examples/test.rb
107
- ruby examples/test.rb
108
- ruby examples/test.rb
109
- ruby examples/test.rb
110
- ruby examples/test.rb
111
- ruby examples/test.rb
112
- ruby examples/test.rb
113
- ruby examples/test.rb
114
- ruby examples/test.rb
115
- ruby examples/indent_cli.rb
116
- ruby examples/indent_cli.rb 'bundle gem test17'
117
- ruby examples/indent_cli.rb 'bundle gem test18'