tty-editor 0.3.0 → 0.6.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
- SHA1:
3
- metadata.gz: 0b9918234c5fc0e4eabd6a59d859fc16a2827c88
4
- data.tar.gz: e609ed53f8aa25a9590405d924ac27a35f8fb47c
2
+ SHA256:
3
+ metadata.gz: 43382f86c223e305662a67b003a4337ce5946adaee09dcd48879714a91e45eae
4
+ data.tar.gz: fd58b6084c4cbedd605c09ce30fca817ea7f4d467e32ddfd558e3c7eb972f0a4
5
5
  SHA512:
6
- metadata.gz: bea5f34a6cd157e0804107ffa1c6fec9ade2a12bebe6506843a56f212bb2a93af37050ed5e5ed7bd359e3a1e76bd8a0a11efc3b9e8261f37093bf962e852df27
7
- data.tar.gz: 21dd8869bcba744dc42759e70f70db5e653e597eee5b13b1cb258f55e5b8965502ca2a37d5a7582d9d6fae318c6c2d8213232a54bcfe53cc62b7a2ccf8671f71
6
+ metadata.gz: 31385f2ece1195d0c3801e495830c182f6808050c510ae83f3c78f48117a6a6a088ffce3177ea5283507a7e67cc235f6e580261e73ce03d0916044eeaeed0209
7
+ data.tar.gz: ddf5f4e74879e30a641ca4d3c7af06ffb0b7e36f8dd989915d599b283855e1fee77843d4788dd60eb7be93b1ef47b77285abb4a06b60528a36aefae75717dead
@@ -1,5 +1,48 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.6.0] - 2020-09-22
4
+
5
+ ### Added
6
+ * Add ability to edit multiple files
7
+ * Add ability to configure input and output
8
+ * Add :raise_on_failure configuration option to control editor failure to run
9
+ * Add :show_menu configuration option to disable editor menu choice
10
+ * Add :prompt to configure an editor choice menu prompt
11
+
12
+ ### Changed
13
+ * Change Editor#exist? to use direct path env var search
14
+ * Change Editor#new to stop accepting filename and text arguments
15
+ * Change Editor#new to select available text editors
16
+ * Change Editor#open to accept keyword arguments
17
+ * Change to stop raising when editor command cannot be run and return false instead
18
+ * Remove tty-which dependency
19
+ * Update tty-prompt dependency
20
+
21
+ ### Fixed
22
+ * Fix to allow setting editor commands with flags
23
+
24
+ ## [v0.5.1] - 2019-08-06
25
+
26
+ ### Changed
27
+ * Change to update tty-prompt dependency
28
+ * Change to relax bundler & rake version requirement
29
+
30
+ ## [v0.5.0] - 2018-12-18
31
+
32
+ ### Changed
33
+ * Change to update and relax tty-prompt & tty-which constraints
34
+
35
+ ## [v0.4.1] - 2018-08-29
36
+
37
+ ### Changed
38
+ * Update tty-prompt dependency
39
+
40
+ ## [v0.4.0] - 2018-04-14
41
+
42
+ ### Changed
43
+ * Update tty-prompt dependency
44
+ * Change to freeze all strings
45
+
3
46
  ## [v0.3.0] - 2018-01-06
4
47
 
5
48
  ### Changed
@@ -41,6 +84,11 @@
41
84
 
42
85
  * Initial implementation and release
43
86
 
87
+ [v0.6.0]: https://github.com/piotrmurach/tty-editor/compare/v0.5.1...v0.6.0
88
+ [v0.5.1]: https://github.com/piotrmurach/tty-editor/compare/v0.5.0...v0.5.1
89
+ [v0.5.0]: https://github.com/piotrmurach/tty-editor/compare/v0.4.1...v0.5.0
90
+ [v0.4.1]: https://github.com/piotrmurach/tty-editor/compare/v0.4.0...v0.4.1
91
+ [v0.4.0]: https://github.com/piotrmurach/tty-editor/compare/v0.3.0...v0.4.0
44
92
  [v0.3.0]: https://github.com/piotrmurach/tty-editor/compare/v0.2.1...v0.3.0
45
93
  [v0.2.1]: https://github.com/piotrmurach/tty-editor/compare/v0.2.0...v0.2.1
46
94
  [v0.2.0]: https://github.com/piotrmurach/tty-editor/compare/v0.1.2...v0.2.0
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <div align="center">
2
+ <a href="https://ttytoolkit.org" target="_blank"><img width="130" src="https://github.com/piotrmurach/tty/raw/master/images/tty.png" alt="TTY Toolkit logo" /></a>
3
+ </div>
4
+
1
5
  # TTY::Editor [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
2
6
 
3
7
  [![Gem Version](https://badge.fury.io/rb/tty-editor.svg)][gem]
@@ -24,7 +28,7 @@
24
28
  Add this line to your application's Gemfile:
25
29
 
26
30
  ```ruby
27
- gem 'tty-editor'
31
+ gem "tty-editor"
28
32
  ```
29
33
 
30
34
  And then execute:
@@ -35,70 +39,206 @@ Or install it yourself as:
35
39
 
36
40
  $ gem install tty-editor
37
41
 
38
- ## Usage
42
+ ## Contents
43
+
44
+ * [1. Usage](#1-usage)
45
+ * [2. API](#2-api)
46
+ * [2.1 new](#21-new)
47
+ * [2.1.1 :command](#211-command)
48
+ * [2.1.2 :env](#212-env)
49
+ * [2.1.3 :raise_on_failure](#213-raise_on_failure)
50
+ * [2.1.4 :prompt](#214-prompt)
51
+ * [2.2 open](#22-open)
52
+
53
+ ## 1. Usage
54
+
55
+ To edit a file in a default text editor do:
56
+
57
+ ```ruby
58
+ TTY::Editor.open("/path/to/file")
59
+ ```
39
60
 
40
- To edit a file in default editor:
61
+ To edit text in a default editor:
41
62
 
42
63
  ```ruby
43
- TTY::Editor.open('hello.rb')
64
+ TTY::Editor.open(text: "Some text")
44
65
  ```
45
66
 
46
- To edit content in a default editor:
67
+ You can also open multiple existing and/or new files:
47
68
 
48
69
  ```ruby
49
- TTY::Editor.open(content: "some text")
70
+ TTY::Editor.open("file_1", "file_2", "new_file_3")
50
71
  ```
51
72
 
52
- You can also set your preferred editor command:
73
+ Note that the `VISUAL` or `EDITOR` shell environment variables take precedence when auto detecting available editors.
74
+
75
+ You can also set your preferred editor command(s) and ignore `VISUAL` and `EDITOR` as well as other user preferences:
53
76
 
54
77
  ```ruby
55
- TTY::Editor.open('hello.rb', command: :vim)
78
+ TTY::Editor.open("/path/to/file", command: "vim -f")
56
79
  ```
57
80
 
58
- Also, the `VISUAL` or `EDITOR` shell environment variables take precedencee when auto detecting available editors.
81
+ When `VISUAL` or `EDITOR` are not specified, a selection menu will be presented to the user.
59
82
 
60
- ## Interface
83
+ For example, if an user has `vim`, `emacs` and `code` editors available on their system, they will see the following menu:
61
84
 
62
- ### open
85
+ ```
86
+ # Select an editor?
87
+ # 1) vim
88
+ # 2) emacs
89
+ # 3) code
90
+ # Choose 1-2 [1]:
91
+ ```
63
92
 
64
- If you wish to open editor with no file or content do:
93
+ You can further customise this behaviour with [:prompt](#214-prompt).
94
+
95
+ ## 2. API
96
+
97
+ ### 2.1 new
98
+
99
+ Instantiation of an editor will trigger automatic search for available command-line editors:
65
100
 
66
101
  ```ruby
67
- TTY::Editor.open
102
+ editor = TTY::Editor.new
68
103
  ```
69
104
 
70
- When editor successfully opens file or content then `true` is returned.
105
+ You can change default search with the `:command` keyword argument.
71
106
 
72
- If the editor cannot be opened, a `TTY::Editor::CommandInvocation` error is raised.
107
+ #### 2.1.1 :command
73
108
 
74
- In order to open text content inside an editor do:
109
+ You can force to always use a specific editor by passing `:command` option:
75
110
 
76
111
  ```ruby
77
- TTY::Editor.open(content: 'text')
112
+ editor = TTY::Editor.new(command: "vim")
78
113
  ```
79
114
 
80
- You can also provide filename that will be created with specified content before editor is opened:
115
+ Or you can specify multiple commands and give a user a choice:
116
+
117
+ ```ruby
118
+ editor = TTY::Editor.new(command: ["vim", "emacs"])
119
+ ```
120
+
121
+ The class-level `open` method accepts the same parameters:
81
122
 
82
123
  ```ruby
83
- TTY::Editor.open('new.rb', content: 'text')
124
+ TTY::Editor.open("/path/to/file", command: "vim")
84
125
  ```
85
126
 
86
- If you open a filename with already existing content then new content gets appended at the end of the file.
127
+ #### 2.1.2 :env
87
128
 
88
- ### :env
129
+ Use `:env` key to forward environment variables to the text editor launch command:
89
130
 
90
- Use `:env` key to forward environment variables to the editor.
131
+ ```ruby
132
+ TTY::Editor.new(env: {"FOO" => "bar"})
133
+ ```
134
+
135
+ The class-level `open` method accepts the same parameters:
91
136
 
92
137
  ```ruby
93
- TTY::Editor.open('hello.rb', env: {"FOO" => "bar"})
138
+ TTY::Editor.open("/path/to/file", env: {"FOO" => "bar"})
94
139
  ```
95
140
 
96
- ### :command
141
+ #### 2.1.3 :raise_on_failure
97
142
 
98
- You can force to always use a specific editor by passing `:command` option:
143
+ By default when editor fails to open a `false` status is returned:
144
+
145
+ ```ruby
146
+ TTY::Editor.open("/path/to/unknown/file") # => false
147
+ ```
148
+
149
+ Alternatively, you can use `:raise_on_failure` to raise an error on failure to open a file.
150
+
151
+ The `TTY::Editor::CommandInvocationError` will be raised anytime an editor fails to open a file:
152
+
153
+ ```ruby
154
+ editor = TTY::Editor.new(raise_on_failure: true)
155
+ editor.open("/path/to/unknown/file")
156
+ # => raises TTY::Editor::ComandInvocationError
157
+ ```
158
+
159
+ #### 2.1.4 :prompt
160
+
161
+ When more than one editor is available and user hasn't specified their preferred choice via `VISUAL` or `EDITOR` variables, a selection menu is presented.
162
+
163
+ For example, when `vim`, `emacs` and `code` executable exists on the system, the following menu will be displayed:
164
+
165
+ ```
166
+ # Select an editor?
167
+ # 1) vim
168
+ # 2) emacs
169
+ # 3) code
170
+ # Choose 1-2 [1]:
171
+ ```
172
+
173
+ If you would like to change the menu prompt use `:prompt` keyword:
174
+
175
+ ```ruby
176
+ editor = TTY::Editor.new(prompt: "Which one do you fancy?")
177
+ editor.open("/path/to/file")
178
+ ```
179
+
180
+ This may produce the following in the terminal:
181
+
182
+ ```
183
+ # Which one do you fancy?
184
+ # 1) vim
185
+ # 2) emacs
186
+ # 3) code
187
+ # Choose 1-2 [1]:
188
+ ```
189
+
190
+ ### 2.2 open
191
+
192
+ There is a class-level and instance-level `open` method. These are equivalent:
193
+
194
+ ```ruby
195
+ editor = TTY::Editor.new
196
+ editor.open(...)
197
+ # or
198
+ TTY::Editor.open(...)
199
+ ```
200
+
201
+ Creating `TTY::Editor` instance means that the search for a command editor will be performed only once. Then the editor command will be shared between invocations of `open` call.
202
+
203
+ Conversely, the class-level `open` method will search for an editor each time it is invoked.
204
+
205
+ The following examples of using the `open` method apply to both the instance and class level invocations.
206
+
207
+ If you wish to open an editor without giving a file or content do:
208
+
209
+ ```ruby
210
+ TTY::Editor.open
211
+ ```
212
+
213
+ To open a file, pass a path as an argument to `open`:
214
+
215
+ ```ruby
216
+ TTY::Editor.open("../README.md")
217
+ # => true
218
+ ```
219
+
220
+ When editor successfully opens a file or content then `true` is returned, `false` otherwise.
221
+
222
+ You can change this with `:raise_on_failure` keyword to raise a `TTY::Editor::CommandInvocation` error when an editor cannot be opened.
223
+
224
+ In order to open text content inside an editor use `:text` keyword like so:
225
+
226
+ ```ruby
227
+ TTY::Editor.open(text: "Some text")
228
+ ```
229
+
230
+ You can also provide filename that will be created with specified content before editor is opened:
231
+
232
+ ```ruby
233
+ TTY::Editor.open("/path/to/new-file", text: "Some text")
234
+ ```
235
+
236
+ If you open a filename with already existing content then the new content will be appended at the end of the file.
237
+
238
+ You can also open multiple existing and non-existing files providing them as consecutive arguments:
99
239
 
100
240
  ```ruby
101
- TTY::Editor.open('hello.rb', command: :vim)
241
+ TTY::Editor.open("file_1", "file_2", "new_file_3")
102
242
  ```
103
243
 
104
244
  ## Development
@@ -117,4 +257,4 @@ The gem is available as open source under the terms of the [MIT License](http://
117
257
 
118
258
  ## Copyright
119
259
 
120
- Copyright (c) 2017-2018 Piotr Murach. See LICENSE for further details.
260
+ Copyright (c) 2017 Piotr Murach. See LICENSE for further details.
@@ -1,3 +1 @@
1
- # encoding: utf-8
2
-
3
- require_relative 'tty/editor'
1
+ require_relative "tty/editor"
@@ -1,40 +1,53 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'tty-prompt'
4
- require 'tty-which'
5
- require 'tempfile'
6
- require 'fileutils'
7
- require 'shellwords'
3
+ require "fileutils"
4
+ require "shellwords"
5
+ require "tempfile"
6
+ require "tty-prompt"
8
7
 
9
- require_relative 'editor/version'
8
+ require_relative "editor/version"
10
9
 
11
10
  module TTY
12
11
  # A class responsible for launching an editor
13
12
  #
14
13
  # @api public
15
14
  class Editor
15
+ Error = Class.new(StandardError)
16
+
17
+ # Raised when user provides unnexpected or incorrect argument
18
+ InvalidArgumentError = Class.new(Error)
19
+
16
20
  # Raised when command cannot be invoked
17
21
  class CommandInvocationError < RuntimeError; end
18
22
 
19
23
  # Raised when editor cannot be found
20
24
  class EditorNotFoundError < RuntimeError; end
21
25
 
22
- # Check if editor exists
26
+ # List possible command line text editors
23
27
  #
24
- # @return [Boolean]
28
+ # @return [Array[String]]
25
29
  #
26
- # @api private
27
- def self.exist?(cmd)
28
- TTY::Which.exist?(cmd)
29
- end
30
+ # @api public
31
+ EXECUTABLES = [
32
+ "nano -w", "notepad", "vim", "vi", "emacs",
33
+ "code", "subl -n -w", "mate -w", "atom",
34
+ "pico", "qe", "mg", "jed"
35
+ ].freeze
30
36
 
31
- # Check if Windowz
37
+ # Check if editor command exists
38
+ #
39
+ # @example
40
+ # exist?("vim") # => true
32
41
  #
33
42
  # @return [Boolean]
34
43
  #
35
- # @api public
36
- def self.windows?
37
- ::File::ALT_SEPARATOR == "\\"
44
+ # @api private
45
+ def self.exist?(command)
46
+ exts = ENV.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR)
47
+ ENV.fetch("PATH", "").split(::File::PATH_SEPARATOR).any? do |dir|
48
+ file = ::File.join(dir, command)
49
+ ::File.exist?(file) || exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
50
+ end
38
51
  end
39
52
 
40
53
  # Check editor from environment variables
@@ -43,90 +56,98 @@ module TTY
43
56
  #
44
57
  # @api public
45
58
  def self.from_env
46
- [ENV['VISUAL'], ENV['EDITOR']].compact
47
- end
48
-
49
- # List possible executable for editor command
50
- #
51
- # @return [Array[String]]
52
- #
53
- # @api public
54
- def self.executables
55
- ['vim', 'vi', 'emacs', 'nano', 'nano-tiny', 'pico', 'mate -w']
59
+ [ENV["VISUAL"], ENV["EDITOR"]].compact
56
60
  end
57
61
 
58
- # Find available command
62
+ # Find available text editors
59
63
  #
60
64
  # @param [Array[String]] commands
61
65
  # the commands to use intstead of defaults
62
66
  #
63
67
  # @return [Array[String]]
68
+ # the existing editor commands
64
69
  #
65
70
  # @api public
66
71
  def self.available(*commands)
67
- return commands unless commands.empty?
68
-
69
- if !from_env.all?(&:empty?)
70
- [from_env.find { |e| !e.empty? }]
71
- elsif windows?
72
- ['notepad']
73
- else
74
- executables.uniq.select(&method(:exist?))
75
- end
72
+ execs = if !commands.empty?
73
+ commands.map(&:to_s)
74
+ elsif from_env.any?
75
+ [from_env.first]
76
+ else
77
+ EXECUTABLES
78
+ end
79
+ execs.compact.map(&:strip).reject(&:empty?).uniq
80
+ .select { |exec| exist?(exec.split.first) }
76
81
  end
77
82
 
78
83
  # Open file in system editor
79
84
  #
80
85
  # @example
81
- # TTY::Editor.open('filename.rb')
86
+ # TTY::Editor.open("/path/to/filename")
82
87
  #
83
- # @param [String] file
84
- # the name of the file
88
+ # @example
89
+ # TTY::Editor.open("file1", "file2", "file3")
90
+ #
91
+ # @example
92
+ # TTY::Editor.open(text: "Some text")
93
+ #
94
+ # @param [Array<String>] files
95
+ # the files to open in an editor
96
+ # @param [String] :command
97
+ # the editor command to use, by default auto detects
98
+ # @param [String] :text
99
+ # the text to edit in an editor
100
+ # @param [Hash] :env
101
+ # environment variables to forward to the editor
85
102
  #
86
103
  # @return [Object]
87
104
  #
88
105
  # @api public
89
- def self.open(*args)
90
- editor = new(*args)
91
-
92
- yield(editor) if block_given?
93
-
94
- editor.open
106
+ def self.open(*files, text: nil, **options, &block)
107
+ editor = new(**options, &block)
108
+ editor.open(*files, text: text)
95
109
  end
96
110
 
97
111
  # Initialize an Editor
98
112
  #
99
- # @param [String] file
100
- # @param [Hash[Symbol]] options
101
- # @option options [Hash] :command
113
+ # @param [String] :command
102
114
  # the editor command to use, by default auto detects
103
- # @option options [Hash] :env
115
+ # @param [Hash] :env
104
116
  # environment variables to forward to the editor
117
+ # @param [IO] :input
118
+ # the standard input
119
+ # @param [IO] :output
120
+ # the standard output
121
+ # @param [Boolean] :raise_on_failure
122
+ # whether or not raise on command failure, false by default
123
+ # @param [Boolean] :show_menu
124
+ # whether or not show commands menu, true by default
105
125
  #
106
126
  # @api public
107
- def initialize(*args, **options)
108
- @filename = args.unshift.first
109
- @env = options.fetch(:env) { {} }
110
- @command = options[:command]
111
- if @filename
112
- if ::File.exist?(@filename) && !::FileTest.file?(@filename)
113
- raise ArgumentError, "Don't know how to handle `#{@filename}`. " \
114
- "Please provida a file path or content"
115
- elsif ::File.exist?(@filename) && !options[:content].to_s.empty?
116
- ::File.open(@filename, 'a') { |f| f.write(options[:content]) }
117
- elsif !::File.exist?(@filename)
118
- ::File.write(@filename, options[:content])
119
- end
120
- elsif options[:content]
121
- @filename = tempfile_path(options[:content])
122
- end
127
+ def initialize(command: nil, raise_on_failure: false, show_menu: true,
128
+ prompt: "Select an editor?", env: {},
129
+ input: $stdin, output: $stdout, &block)
130
+ @env = env
131
+ @command = nil
132
+ @input = input
133
+ @output = output
134
+ @raise_on_failure = raise_on_failure
135
+ @show_menu = show_menu
136
+ @prompt = prompt
137
+
138
+ block.(self) if block
139
+
140
+ command(*Array(command))
123
141
  end
124
142
 
125
143
  # Read or update environment vars
126
144
  #
145
+ # @return [Hash]
146
+ #
127
147
  # @api public
128
148
  def env(value = (not_set = true))
129
149
  return @env if not_set
150
+
130
151
  @env = value
131
152
  end
132
153
 
@@ -146,64 +167,116 @@ module TTY
146
167
  execs = self.class.available(*commands)
147
168
  if execs.empty?
148
169
  raise EditorNotFoundError,
149
- 'Could not find editor to use. Please specify $VISUAL or $EDITOR'
170
+ "could not find a text editor to use. Please specify $VISUAL or "\
171
+ "$EDITOR or install one of the following editors: " \
172
+ "#{EXECUTABLES.map { |ed| ed.split.first }.join(", ")}."
150
173
  end
151
- exec = choose_exec_from(execs)
152
- @command = TTY::Which.which(exec.to_s)
174
+ @command = choose_exec_from(execs)
153
175
  end
154
176
 
177
+ # Run editor command in a shell
178
+ #
179
+ # @param [Array<String>] files
180
+ # the files to open in an editor
181
+ # @param [String] :text
182
+ # the text to edit in an editor
183
+ #
184
+ # @raise [TTY::CommandInvocationError]
185
+ #
186
+ # @return [Boolean]
187
+ # whether editor command suceeded or not
188
+ #
155
189
  # @api private
156
- def choose_exec_from(execs)
157
- if execs.size > 1
158
- prompt = TTY::Prompt.new
159
- prompt.enum_select('Select an editor?', execs)
160
- else
161
- execs[0]
190
+ def open(*files, text: nil)
191
+ validate_arguments(files, text)
192
+ text_written = false
193
+
194
+ filepaths = files.reduce([]) do |paths, filename|
195
+ if !::File.exist?(filename)
196
+ ::File.write(filename, text || "")
197
+ text_written = true
198
+ end
199
+ paths + [filename]
200
+ end
201
+
202
+ if !text.nil? && !text_written
203
+ tempfile = create_tempfile(text)
204
+ filepaths << tempfile.path
162
205
  end
206
+
207
+ run(filepaths)
208
+ ensure
209
+ tempfile.unlink if tempfile
163
210
  end
164
211
 
165
- # Escape file path
212
+ private
213
+
214
+ # Run editor command with file arguments
215
+ #
216
+ # @param [Array<String>] filepaths
217
+ # the file paths to open in an editor
218
+ #
219
+ # @return [Boolean]
220
+ # whether command succeeded or not
166
221
  #
167
222
  # @api private
168
- def escape_file
169
- Shellwords.shellescape(@filename)
223
+ def run(filepaths)
224
+ command_path = "#{command} #{filepaths.shelljoin}"
225
+ status = system(env, *Shellwords.split(command_path))
226
+ if @raise_on_failure && !status
227
+ raise CommandInvocationError,
228
+ "`#{command_path}` failed with status: #{$? ? $?.exitstatus : nil}"
229
+ end
230
+ !!status
170
231
  end
171
232
 
172
- # Build command path to invoke
233
+ # Check if filename and text arguments are valid
173
234
  #
174
- # @return [String]
235
+ # @raise [InvalidArgumentError]
175
236
  #
176
237
  # @api private
177
- def command_path
178
- "#{command} #{escape_file}"
238
+ def validate_arguments(files, text)
239
+ return if files.empty?
240
+
241
+ if files.all? { |file| ::File.exist?(file) } && !text.nil?
242
+ raise InvalidArgumentError,
243
+ "cannot give a path to an existing file and text at the same time."
244
+ elsif filename = files.find { |file| ::File.exist?(file) && !::FileTest.file?(file) }
245
+ raise InvalidArgumentError, "don't know how to handle `#{filename}`. " \
246
+ "Please provide a file path or text"
247
+ end
179
248
  end
180
249
 
181
- # Create tempfile with content
250
+ # Create tempfile with text
182
251
  #
183
- # @param [String] content
252
+ # @param [String] text
253
+ #
254
+ # @return [Tempfile]
184
255
  #
185
- # @return [String]
186
256
  # @api private
187
- def tempfile_path(content)
188
- tempfile = Tempfile.new('tty-editor')
189
- tempfile << content
257
+ def create_tempfile(text)
258
+ tempfile = Tempfile.new("tty-editor")
259
+ tempfile << text
190
260
  tempfile.flush
191
- unless tempfile.nil?
192
- tempfile.close
193
- end
194
- tempfile.path
261
+ tempfile.close
262
+ tempfile
195
263
  end
196
264
 
197
- # Inovke editor command in a shell
265
+ # Render an editor selection prompt to the terminal
198
266
  #
199
- # @raise [TTY::CommandInvocationError]
267
+ # @return [String]
268
+ # the chosen editor
200
269
  #
201
270
  # @api private
202
- def open
203
- status = system(env, *Shellwords.split(command_path))
204
- return status if status
205
- fail CommandInvocationError,
206
- "`#{command_path}` failed with status: #{$? ? $?.exitstatus : nil}"
271
+ def choose_exec_from(execs)
272
+ if @show_menu && execs.size > 1
273
+ prompt = TTY::Prompt.new(input: @input, output: @output, env: @env)
274
+ exec = prompt.enum_select(@prompt, execs)
275
+ @output.print(prompt.cursor.up + prompt.cursor.clear_line)
276
+ exec
277
+ else
278
+ execs[0]
279
+ end
207
280
  end
208
281
  end # Editor
209
282
  end # TTY
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TTY
2
4
  class Editor
3
- VERSION = '0.3.0'.freeze
5
+ VERSION = "0.6.0"
4
6
  end # Editor
5
7
  end # TTY
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-editor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-06 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-prompt
@@ -16,62 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.14.0
19
+ version: '0.22'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.14.0
26
+ version: '0.22'
27
27
  - !ruby/object:Gem::Dependency
28
- name: tty-which
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 0.3.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 0.3.0
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
28
+ name: rake
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - ">="
46
32
  - !ruby/object:Gem::Version
47
- version: 1.5.0
48
- - - "<"
49
- - !ruby/object:Gem::Version
50
- version: '2.0'
33
+ version: '0'
51
34
  type: :development
52
35
  prerelease: false
53
36
  version_requirements: !ruby/object:Gem::Requirement
54
37
  requirements:
55
38
  - - ">="
56
39
  - !ruby/object:Gem::Version
57
- version: 1.5.0
58
- - - "<"
59
- - !ruby/object:Gem::Version
60
- version: '2.0'
61
- - !ruby/object:Gem::Dependency
62
- name: rake
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '10.0'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '10.0'
40
+ version: '0'
75
41
  - !ruby/object:Gem::Dependency
76
42
  name: rspec
77
43
  requirement: !ruby/object:Gem::Requirement
@@ -86,41 +52,32 @@ dependencies:
86
52
  - - "~>"
87
53
  - !ruby/object:Gem::Version
88
54
  version: '3.0'
89
- description: Opens a file or text in the user's preferred editor.
55
+ description: Open a file or text in a terminal text editor.
90
56
  email:
91
- - ''
57
+ - piotr@piotrmurach.com
92
58
  executables: []
93
59
  extensions: []
94
- extra_rdoc_files: []
60
+ extra_rdoc_files:
61
+ - README.md
62
+ - CHANGELOG.md
63
+ - LICENSE.txt
95
64
  files:
96
- - ".gitignore"
97
- - ".rspec"
98
- - ".travis.yml"
99
65
  - CHANGELOG.md
100
- - CODE_OF_CONDUCT.md
101
- - Gemfile
102
66
  - LICENSE.txt
103
67
  - README.md
104
- - Rakefile
105
- - appveyor.yml
106
- - bin/console
107
- - bin/setup
108
- - examples/basic.rb
109
- - examples/choices.rb
110
- - examples/empty.rb
111
- - examples/env.rb
112
- - examples/tempfile.rb
113
68
  - lib/tty-editor.rb
114
69
  - lib/tty/editor.rb
115
70
  - lib/tty/editor/version.rb
116
- - tasks/console.rake
117
- - tasks/coverage.rake
118
- - tasks/spec.rake
119
- - tty-editor.gemspec
120
- homepage: https://piotrmurach.github.io/tty
71
+ homepage: https://ttytoolkit.org
121
72
  licenses:
122
73
  - MIT
123
- metadata: {}
74
+ metadata:
75
+ allowed_push_host: https://rubygems.org
76
+ bug_tracker_uri: https://github.com/piotrmurach/tty-editor/issues
77
+ changelog_uri: https://github.com/piotrmurach/tty-editor/blob/master/CHANGELOG.md
78
+ documentation_uri: https://www.rubydoc.info/gems/tty-editor
79
+ homepage_uri: https://ttytoolkit.org
80
+ source_code_uri: https://github.com/piotrmurach/tty-editor
124
81
  post_install_message:
125
82
  rdoc_options: []
126
83
  require_paths:
@@ -136,9 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
93
  - !ruby/object:Gem::Version
137
94
  version: '0'
138
95
  requirements: []
139
- rubyforge_project:
140
- rubygems_version: 2.5.1
96
+ rubygems_version: 3.1.2
141
97
  signing_key:
142
98
  specification_version: 4
143
- summary: Opens a file or text in the user's preferred editor.
99
+ summary: Open a file or text in a terminal text editor.
144
100
  test_files: []
data/.gitignore DELETED
@@ -1,9 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --require spec_helper
2
- --color
3
- --warnings
@@ -1,24 +0,0 @@
1
- ---
2
- language: ruby
3
- sudo: false
4
- cache: bundler
5
- script: "bundle exec rake ci"
6
- rvm:
7
- - 2.0.0
8
- - 2.1.10
9
- - 2.2.8
10
- - 2.3.6
11
- - 2.4.3
12
- - ruby-head
13
- - jruby-9.1.7.0
14
- - jruby-head
15
- matrix:
16
- allow_failures:
17
- - rvm: ruby-head
18
- - rvm: jruby-head
19
- - rvm: jruby-9.1.7.0
20
- fast_finish: true
21
- branches:
22
- only: master
23
- notifications:
24
- email: false
@@ -1,49 +0,0 @@
1
- # Contributor Code of Conduct
2
-
3
- As contributors and maintainers of this project, and in the interest of
4
- fostering an open and welcoming community, we pledge to respect all people who
5
- contribute through reporting issues, posting feature requests, updating
6
- documentation, submitting pull requests or patches, and other activities.
7
-
8
- We are committed to making participation in this project a harassment-free
9
- experience for everyone, regardless of level of experience, gender, gender
10
- identity and expression, sexual orientation, disability, personal appearance,
11
- body size, race, ethnicity, age, religion, or nationality.
12
-
13
- Examples of unacceptable behavior by participants include:
14
-
15
- * The use of sexualized language or imagery
16
- * Personal attacks
17
- * Trolling or insulting/derogatory comments
18
- * Public or private harassment
19
- * Publishing other's private information, such as physical or electronic
20
- addresses, without explicit permission
21
- * Other unethical or unprofessional conduct
22
-
23
- Project maintainers have the right and responsibility to remove, edit, or
24
- reject comments, commits, code, wiki edits, issues, and other contributions
25
- that are not aligned to this Code of Conduct, or to ban temporarily or
26
- permanently any contributor for other behaviors that they deem inappropriate,
27
- threatening, offensive, or harmful.
28
-
29
- By adopting this Code of Conduct, project maintainers commit themselves to
30
- fairly and consistently applying these principles to every aspect of managing
31
- this project. Project maintainers who do not follow or enforce the Code of
32
- Conduct may be permanently removed from the project team.
33
-
34
- This code of conduct applies both within project spaces and in public spaces
35
- when an individual is representing the project or its community.
36
-
37
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
- reported by contacting a project maintainer at [email]. All
39
- complaints will be reviewed and investigated and will result in a response that
40
- is deemed necessary and appropriate to the circumstances. Maintainers are
41
- obligated to maintain confidentiality with regard to the reporter of an
42
- incident.
43
-
44
- This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
- version 1.3.0, available at
46
- [http://contributor-covenant.org/version/1/3/0/][version]
47
-
48
- [homepage]: http://contributor-covenant.org
49
- [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile DELETED
@@ -1,8 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :test do
6
- gem 'simplecov', '~> 0.12.0'
7
- gem 'coveralls', '~> 0.8.0'
8
- end
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require "bundler/gem_tasks"
4
-
5
- FileList['tasks/**/*.rake'].each(&method(:import))
6
-
7
- task default: :spec
8
-
9
- desc 'Run all specs'
10
- task ci: %w[ spec ]
@@ -1,23 +0,0 @@
1
- ---
2
- install:
3
- - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
- - ruby --version
5
- - gem --version
6
- - bundle install
7
- build: off
8
- test_script:
9
- - bundle exec rake ci
10
- environment:
11
- matrix:
12
- - ruby_version: "193"
13
- - ruby_version: "200"
14
- - ruby_version: "200-x64"
15
- - ruby_version: "21"
16
- - ruby_version: "21-x64"
17
- - ruby_version: "22"
18
- - ruby_version: "22-x64"
19
- - ruby_version: "23"
20
- - ruby_version: "23-x64"
21
- matrix:
22
- allow_failures:
23
- - ruby_version: "193"
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "tty/editor"
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
data/bin/setup DELETED
@@ -1,8 +0,0 @@
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
@@ -1,7 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tty-editor'
4
-
5
- path = File.join(File.expand_path(File.dirname(__FILE__)), '../README.md')
6
-
7
- TTY::Editor.open(path)
@@ -1,9 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tty-editor'
4
-
5
- path = File.join(File.expand_path(File.dirname(__FILE__)), '../README.md')
6
-
7
- TTY::Editor.open(path) do |editor|
8
- editor.command :vim, :emacs
9
- end
@@ -1,5 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tty-editor'
4
-
5
- TTY::Editor.open
@@ -1,7 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tty-editor'
4
-
5
- path = File.join(File.expand_path(File.dirname(__FILE__)), '../README.md')
6
-
7
- TTY::Editor.open(path, env: {"FOO" => "bar"})
@@ -1,7 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tty-editor'
4
-
5
- content = "Human madness is oftentimes a cunning and most feline thing.\n When you think it fled, it may have but become transfigured into some still subtler form."
6
-
7
- TTY::Editor.open(content)
@@ -1,10 +0,0 @@
1
- # encoding: utf-8
2
-
3
- desc 'Load gem inside irb console'
4
- task :console do
5
- require 'irb'
6
- require 'irb/completion'
7
- require File.join(__FILE__, '../../lib/tty-editor')
8
- ARGV.clear
9
- IRB.start
10
- end
@@ -1,11 +0,0 @@
1
- # encoding: utf-8
2
-
3
- desc 'Measure code coverage'
4
- task :coverage do
5
- begin
6
- original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true'
7
- Rake::Task['spec'].invoke
8
- ensure
9
- ENV['COVERAGE'] = original
10
- end
11
- end
@@ -1,29 +0,0 @@
1
- # encoding: utf-8
2
-
3
- begin
4
- require 'rspec/core/rake_task'
5
-
6
- desc 'Run all specs'
7
- RSpec::Core::RakeTask.new(:spec) do |task|
8
- task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
9
- end
10
-
11
- namespace :spec do
12
- desc 'Run unit specs'
13
- RSpec::Core::RakeTask.new(:unit) do |task|
14
- task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
15
- end
16
-
17
- desc 'Run integration specs'
18
- RSpec::Core::RakeTask.new(:integration) do |task|
19
- task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
20
- end
21
- end
22
-
23
- rescue LoadError
24
- %w[spec spec:unit spec:integration].each do |name|
25
- task name do
26
- $stderr.puts "In order to run #{name}, do `gem install rspec`"
27
- end
28
- end
29
- end
@@ -1,30 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'tty/editor/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "tty-editor"
8
- spec.version = TTY::Editor::VERSION
9
- spec.authors = ["Piotr Murach"]
10
- spec.email = [""]
11
-
12
- spec.summary = %q{Opens a file or text in the user's preferred editor.}
13
- spec.description = %q{Opens a file or text in the user's preferred editor.}
14
- spec.homepage = "https://piotrmurach.github.io/tty"
15
- spec.license = "MIT"
16
-
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
19
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
21
-
22
- spec.required_ruby_version = '>= 2.0.0'
23
-
24
- spec.add_dependency 'tty-prompt', '~> 0.14.0'
25
- spec.add_dependency 'tty-which', '~> 0.3.0'
26
-
27
- spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
28
- spec.add_development_dependency 'rake', '~> 10.0'
29
- spec.add_development_dependency 'rspec', '~> 3.0'
30
- end