doublespeak 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,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: []