doublespeak 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aea84b70ea58db091d8948dbf93a09a1a3905913
4
+ data.tar.gz: ffe214abadd1fe82486b635f063e0d6c1b6dbac7
5
+ SHA512:
6
+ metadata.gz: 1b75385fc0931283a6df62b9269673205af8c5000c2e638cdefe09d887cc5fd0a171e5346ef22c81f5ddc58a3cc74816c443382fbc18f89a20352ec1c32992af
7
+ data.tar.gz: bc1da90c2dbcddf64d8690859219b6f857bf73583eaabdea190fc8c5a412e568b2232183e850727a725afd98f64f69a3120942c192fd9aa3400634cd364fd49e
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ tmp/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.3.5
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ doublespeak (0.1.0)
5
+ pastel (~> 0.7)
6
+ tty-reader
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.3)
12
+ equatable (0.5.0)
13
+ pastel (0.7.2)
14
+ equatable (~> 0.5.0)
15
+ tty-color (~> 0.4.0)
16
+ rake (10.4.2)
17
+ rspec (3.7.0)
18
+ rspec-core (~> 3.7.0)
19
+ rspec-expectations (~> 3.7.0)
20
+ rspec-mocks (~> 3.7.0)
21
+ rspec-core (3.7.1)
22
+ rspec-support (~> 3.7.0)
23
+ rspec-expectations (3.7.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.7.0)
26
+ rspec-mocks (3.7.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.7.0)
29
+ rspec-support (3.7.1)
30
+ tty-color (0.4.2)
31
+ tty-cursor (0.5.0)
32
+ tty-reader (0.2.0)
33
+ tty-cursor (~> 0.5.0)
34
+ tty-screen (~> 0.6.4)
35
+ wisper (~> 2.0.0)
36
+ tty-screen (0.6.4)
37
+ wisper (2.0.0)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bundler (>= 1.5.0, < 2.0)
44
+ doublespeak!
45
+ rake (~> 10.0)
46
+ rspec (~> 3.7)
47
+
48
+ BUNDLED WITH
49
+ 1.16.1
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "predictive_entry"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'doublespeak/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "doublespeak"
8
+ spec.version = Doublespeak::VERSION
9
+ spec.authors = ["ccorn90"]
10
+ spec.email = [""]
11
+ spec.summary = %q{Typeahead entry for Ruby command-line applications}
12
+ spec.description = %q{Typeahead entry for Ruby command-line applications}
13
+ spec.homepage = "https://github.com/ccorn90/doublespeak"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.test_files = spec.files.grep(%r{^spec/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency 'rspec', '~> 3.7'
25
+ spec.add_runtime_dependency 'tty-reader'
26
+ spec.add_runtime_dependency 'pastel', '~> 0.7'
27
+ end
@@ -0,0 +1,10 @@
1
+ require 'doublespeak/core'
2
+ require 'doublespeak/display'
3
+ require 'doublespeak/string'
4
+ require 'doublespeak/repl'
5
+
6
+ module Doublespeak
7
+ def self.new(data_source, options = {})
8
+ Doublespeak::Repl.new(data_source, options)
9
+ end
10
+ end
@@ -0,0 +1,90 @@
1
+ module Doublespeak
2
+ class Core
3
+ attr_reader :data_source, :format_data, :display
4
+ attr_reader :query, :selected_index, :saved_candidates
5
+
6
+ def initialize(data_source, options = {})
7
+ @data_source = data_source
8
+ @format_data = options[:format_data] || ->(x) { "#{x}" }
9
+
10
+ @display = options[:display] || Display.new(options)
11
+
12
+ @query = options[:query] || ""
13
+ @selected_index = options[:selected_index] || 0
14
+ @saved_candidates = options[:saved_candidates] || []
15
+ end
16
+
17
+ def render
18
+ display.move_to_origin
19
+ display.clear_to_end_of_line
20
+
21
+ display.write (query + status_line)
22
+
23
+ display.move_to_origin
24
+ end
25
+
26
+ def finish_up
27
+ display.move_to_origin
28
+ display.clear_to_end_of_line
29
+ display.set_cursor_visible
30
+
31
+ display.write display.format_selected_result.call(format_data.call(candidate)) unless candidate.nil?
32
+ display.write "\n"
33
+ end
34
+
35
+ def increment_selection(i)
36
+ @selected_index = selected_index + i
37
+ if saved_candidates.size == 0
38
+ @selected_index = 0
39
+ elsif selected_index < 0
40
+ @selected_index = saved_candidates.size - 1
41
+ elsif selected_index >= saved_candidates.size
42
+ @selected_index = 0
43
+ end
44
+ end
45
+
46
+ def back_up
47
+ query.chop!
48
+ end
49
+
50
+ def find_candidates
51
+ @selected_index = 0
52
+ @saved_candidates = query.strip.to_s.empty? ? [] : data_source.call(query)
53
+ end
54
+
55
+ def entry(c)
56
+ if query.length < display.width
57
+ query.concat(c)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def status_line
64
+ if saved_candidates.empty?
65
+ ""
66
+ else
67
+ width = display.width - query.length + 1
68
+ display.format_result.call(candidate_text + selected_index_text).rjust_noescape(width)
69
+ end
70
+ end
71
+
72
+ def candidate_text
73
+ max_width = saved_candidates.map { |c| format_data.call(c).length }.max
74
+
75
+ format_data.call(candidate)
76
+ .ljust_noescape(max_width)
77
+ .format_substring(query, display.format_result_textmatch, downcase: true)
78
+ end
79
+
80
+ def selected_index_text
81
+ return "" if saved_candidates.length <= 1
82
+
83
+ "(#{selected_index+1}/#{saved_candidates.length})".rjust(8, " ")
84
+ end
85
+
86
+ def candidate
87
+ saved_candidates[selected_index]
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,71 @@
1
+ require 'io/console'
2
+ require 'pastel'
3
+ require 'tty/reader'
4
+
5
+ module Doublespeak
6
+ class Display
7
+ attr_reader :format_result, :format_result_textmatch, :format_selected_result,
8
+ :origin_col, :origin_row, :screen_width
9
+
10
+ def initialize(options)
11
+ @ostream = options[:ostream] || $stdout
12
+ @istream = options[:istream] || $stdin
13
+ @reader = TTY::Reader.new
14
+
15
+ colorizer = Pastel::new
16
+ @format_result = options[:format_result] || colorizer.white.dim.detach
17
+ @format_result_textmatch = options[:format_result_textmatch] || colorizer.cyan.dim.detach
18
+ @format_selected_result = options[:format_selected_result] || colorizer.green.dim.detach
19
+
20
+ c, r = *cursor_position
21
+ @origin_col = options[:origin_col] || c
22
+ @origin_row = options[:origin_row] || r
23
+ @screen_width = options[:screen_width] || IO.console.winsize[1]
24
+ end
25
+
26
+ def width
27
+ screen_width - origin_col
28
+ end
29
+
30
+ def move_to_origin
31
+ write_escaped "[#{origin_row};#{origin_col}H"
32
+ end
33
+
34
+ def clear_to_end_of_line
35
+ write_escaped "[K"
36
+ end
37
+
38
+ def set_cursor_visible(visible=true)
39
+ write_escaped(visible ? "[?25h" : "[?25l")
40
+ end
41
+
42
+ def write(str)
43
+ ostream << str
44
+ end
45
+
46
+ def read
47
+ reader.read_char
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :ostream, :istream, :reader
53
+
54
+ def write_escaped(seq)
55
+ ostream << "\e#{seq}"
56
+ ostream.flush
57
+ end
58
+
59
+ def cursor_position
60
+ res = ''
61
+ istream.raw do |stream|
62
+ write_escaped("[6n")
63
+ while (c = stream.getc) != 'R'
64
+ res << c if c
65
+ end
66
+ end
67
+ m = res.match(/(?<row>\d+);(?<column>\d+)/)
68
+ [Integer(m[:column]), Integer(m[:row])]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,43 @@
1
+ module Doublespeak
2
+ class Repl
3
+ def initialize(data_source, options = {})
4
+ raise ArgumentError.new("Must provide a data_source") if data_source.nil?
5
+ @core = Core.new(data_source, options)
6
+ end
7
+
8
+ def run
9
+ continue = true
10
+
11
+ while continue
12
+ core.render
13
+ core.display.set_cursor_visible(core.query.empty?)
14
+
15
+ char = core.display.read
16
+ case char
17
+ when "\r"
18
+ if core.saved_candidates.present?
19
+ continue = false
20
+ core.finish_up
21
+ end
22
+
23
+ when "\e[A"
24
+ core.increment_selection(-1)
25
+
26
+ when "\e[B"
27
+ core.increment_selection(+1)
28
+
29
+ when "\u007F"
30
+ core.back_up
31
+ core.find_candidates
32
+
33
+ when /^[a-zA-Z0-9 ]/
34
+ core.entry(char)
35
+ core.find_candidates
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+ attr_reader :core
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ class String
2
+ def noescape
3
+ gsub(%r{\e[^m]*m}, '')
4
+ end
5
+
6
+ def ljust_noescape(width, delim=" ")
7
+ self + delim*(width - noescape.length)
8
+ end
9
+
10
+ def rjust_noescape(width, delim=" ")
11
+ delim*(width - noescape.length) + self
12
+ end
13
+
14
+ def format_substring(substring, formatter, downcase: false)
15
+ index = downcase ? self.downcase.index(substring.downcase) : index(substring)
16
+
17
+ if index.nil?
18
+ self
19
+ else
20
+ substring = self[index, substring.length]
21
+ gsub(substring, formatter.call(substring))
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Doublespeak
2
+ VERSION="0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doublespeak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - ccorn90
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 1.5.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '10.0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '10.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: tty-reader
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: pastel
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.7'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.7'
89
+ description: Typeahead entry for Ruby command-line applications
90
+ email:
91
+ - ''
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - ".gitignore"
97
+ - ".rspec"
98
+ - ".ruby-version"
99
+ - Gemfile
100
+ - Gemfile.lock
101
+ - Rakefile
102
+ - VERSION.txt
103
+ - bin/console
104
+ - bin/setup
105
+ - doublespeak.gemspec
106
+ - lib/doublespeak.rb
107
+ - lib/doublespeak/core.rb
108
+ - lib/doublespeak/display.rb
109
+ - lib/doublespeak/repl.rb
110
+ - lib/doublespeak/string.rb
111
+ - lib/doublespeak/version.rb
112
+ - tmp/.keep
113
+ homepage: https://github.com/ccorn90/doublespeak
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.5.2.1
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Typeahead entry for Ruby command-line applications
137
+ test_files: []