Skip to content

Commit

Permalink
Add Completions for storing and navigating completion suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrmurach committed Jul 15, 2021
1 parent ccb1869 commit aa4606e
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/tty/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require "tty-screen"
require "wisper"

require_relative "reader/completions"
require_relative "reader/history"
require_relative "reader/line"
require_relative "reader/key_event"
Expand Down
107 changes: 107 additions & 0 deletions lib/tty/reader/completions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# frozen_string_literal: true

require "forwardable"

module TTY
class Reader
# Responsible for storing and navigating completion suggestions
#
# @api private
class Completions
include Enumerable
extend Forwardable

def_delegators :@completions, :size, :empty?

# Create a Completions collection
#
# @api public
def initialize
@completions = []
@index = 0
end

# Clear current completions
#
# @api public
def clear
@completions.clear
@index = 0
end

# Check whether the current index is at the first completion or not
#
# @return [Boolean]
#
# @api public
def first?
@index.zero?
end

# Check whether the current index is at the last completion or not
#
# @return [Boolean]
#
# @api public
def last?
@index == size - 1
end

# Add completion suggestions
#
# @param [Array<String>] suggestions
# the suggestions to add
#
# @api public
def concat(suggestions)
suggestions.each { |suggestion| @completions << suggestion.dup }
end

# Iterate over all completions
#
# @api public
def each(&block)
if block_given?
@completions.each(&block)
else
@completions.to_enum
end
end

# Retrieve completion at the current index
#
# @return [String]
#
# @api public
def get
@completions[@index]
end

# Move index to the next completion
#
# @api public
def next
return if size.zero?

if @index == size - 1
@index = 0
else
@index += 1
end
end

# Move index to the previous completion
#
# @api public
def previous
return if size.zero?

if @index.zero?
@index = size - 1
else
@index -= 1
end
end
end # Completions
end # Reader
end # TTY
100 changes: 100 additions & 0 deletions spec/unit/completions_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

RSpec.describe TTY::Reader::Completions do
it "has no suggestions by default" do
completions = described_class.new

expect(completions.empty?).to eq(true)
expect(completions.size).to eq(0)
end

it "adds suggestions making copies of each one" do
completions = described_class.new
suggestions = %w[aa ab ac]

completions.concat(suggestions)

suggestions[0] = "ax"

expect(completions.to_a).to eq(%w[aa ab ac])
expect(completions.to_a).to_not eq(suggestions)
end

it "clears all suggestions" do
completions = described_class.new
suggestions = %w[aa ab ac]

completions.concat(suggestions)

expect(completions.empty?).to eq(false)
expect(completions.size).to eq(3)

completions.clear

expect(completions.empty?).to eq(true)
expect(completions.size).to eq(0)
end

it "navigates to the next completion" do
completions = described_class.new
completions.concat(%w[aa ab ac])

expect(completions.get).to eq("aa")

completions.next
expect(completions.get).to eq("ab")
end

it "navigates to the previous completion" do
completions = described_class.new
completions.concat(%w[aa ab ac])

completions.next
expect(completions.get).to eq("ab")

completions.previous
expect(completions.get).to eq("aa")
end

it "cycles through completions forward" do
completions = described_class.new
suggestions = %w[aa ab ac]
completions.concat(suggestions)

suggestions.size.times { completions.next }

expect(completions.get).to eq("aa")
end

it "cycles through completions backward" do
completions = described_class.new
suggestions = %w[aa ab ac]
completions.concat(suggestions)

suggestions.size.times { completions.previous }

expect(completions.get).to eq("aa")
end

it "checks whether index is at the first completion" do
completions = described_class.new
suggestions = %w[aa ab ac]
completions.concat(suggestions)

expect(completions.first?).to eq(true)

completions.next
expect(completions.first?).to eq(false)
end

it "checks whether index is at the last completion" do
completions = described_class.new
suggestions = %w[aa ab ac]
completions.concat(suggestions)

expect(completions.last?).to eq(false)

2.times { completions.next }
expect(completions.last?).to eq(true)
end
end

0 comments on commit aa4606e

Please sign in to comment.