Module: Commiti::InteractivePrompt
- Defined in:
- lib/services/helpers/interactive_prompt.rb
Constant Summary collapse
- COMMIT_SUBJECT_MAX_LENGTH =
100- COMMIT_PREFIX =
/\A(feat|fix|chore|refactor|docs|style|test|perf|ci|build|revert)(\([^)]+\))?!?:\s+\S/i
Class Method Summary collapse
- .ask_candidate_selection(count, default: 1) ⇒ Object
- .ask_commit_action ⇒ Object
- .ask_yes_no(question, default: :no) ⇒ Object
- .code_editor_command?(exe) ⇒ Boolean
- .commit_message_errors(message) ⇒ Object
- .edit_message(initial_message) ⇒ Object
- .editor_command ⇒ Object
- .io_console_available? ⇒ Boolean
- .read_input(prompt) ⇒ Object
- .reader ⇒ Object
- .windows? ⇒ Boolean
Class Method Details
.ask_candidate_selection(count, default: 1) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/services/helpers/interactive_prompt.rb', line 37 def self.ask_candidate_selection(count, default: 1) return 0 if count <= 1 loop do input = read_input("Select candidate [1-#{count}] (default: #{default}): ") return default - 1 if input.nil? value = input.strip return default - 1 if value.empty? return value.to_i - 1 if value.match?(/\A\d+\z/) && value.to_i.between?(1, count) puts "Please type a number between 1 and #{count}." end end |
.ask_commit_action ⇒ Object
26 27 28 29 30 31 32 33 34 35 |
# File 'lib/services/helpers/interactive_prompt.rb', line 26 def self.ask_commit_action input = read_input('Commit with this message? [y/e/N] ') return :no if input.nil? value = input.strip.downcase return :yes if %w[y yes].include?(value) return :edit if %w[e edit].include?(value) :no end |
.ask_yes_no(question, default: :no) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/services/helpers/interactive_prompt.rb', line 13 def self.ask_yes_no(question, default: :no) suffix = default == :yes ? '[Y/n]' : '[y/N]' input = read_input("#{question} #{suffix} ") return default == :yes if input.nil? value = input.strip.downcase return true if %w[y yes].include?(value) return false if %w[n no].include?(value) return default == :yes if value.empty? false end |
.code_editor_command?(exe) ⇒ Boolean
99 100 101 102 |
# File 'lib/services/helpers/interactive_prompt.rb', line 99 def self.code_editor_command?(exe) name = File.basename(exe.to_s).downcase ['code', 'code.cmd', 'code.exe', 'codium', 'codium.cmd', 'codium.exe'].include?(name) end |
.commit_message_errors(message) ⇒ Object
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/services/helpers/interactive_prompt.rb', line 72 def self.() cleaned = .to_s.strip return ['Message cannot be empty.'] if cleaned.empty? first_line = cleaned.lines.first.to_s.strip errors = [] errors << 'First line must start with a conventional commit type (feat:, fix:, etc.).' unless first_line.match?(COMMIT_PREFIX) errors << "First line should be #{COMMIT_SUBJECT_MAX_LENGTH} characters or fewer." if first_line.length > COMMIT_SUBJECT_MAX_LENGTH errors end |
.edit_message(initial_message) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/services/helpers/interactive_prompt.rb', line 52 def self.() # Keep the temp file closed while the external editor runs. # On Windows, open handles can prevent editors like Notepad from # saving in place, which can make edits appear to be ignored. file = Tempfile.new(['commiti-msg', '.txt']) begin file.write("#{.to_s.rstrip}\n") file.flush file.close command = editor_command success = system(*command, file.path) return nil unless success File.read(file.path, mode: 'r:bom|utf-8').strip ensure file.unlink end end |
.editor_command ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/services/helpers/interactive_prompt.rb', line 83 def self.editor_command preferred = ENV.fetch('VISUAL', nil) preferred = ENV.fetch('EDITOR', nil) if preferred.to_s.strip.empty? if preferred.to_s.strip.empty? return ['notepad'] if windows? return ['vi'] end command = Shellwords.split(preferred) command << '--wait' if code_editor_command?(command.first) && !command.include?('--wait') command end |
.io_console_available? ⇒ Boolean
123 124 125 126 127 |
# File 'lib/services/helpers/interactive_prompt.rb', line 123 def self.io_console_available? !IO.console.nil? rescue StandardError false end |
.read_input(prompt) ⇒ Object
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/services/helpers/interactive_prompt.rb', line 108 def self.read_input(prompt) if io_console_available? reader.read_line(prompt) else print prompt $stdin.gets end rescue Interrupt nil end |
.reader ⇒ Object
119 120 121 |
# File 'lib/services/helpers/interactive_prompt.rb', line 119 def self.reader @reader ||= TTY::Reader.new end |
.windows? ⇒ Boolean
104 105 106 |
# File 'lib/services/helpers/interactive_prompt.rb', line 104 def self.windows? RUBY_PLATFORM.include?('mingw') || RUBY_PLATFORM.include?('mswin') end |