tty-editor 0.5.1 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +155 -33
- data/lib/tty-editor.rb +1 -1
- data/lib/tty/editor.rb +171 -98
- data/lib/tty/editor/version.rb +1 -1
- metadata +20 -58
- data/Rakefile +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/examples/basic.rb +0 -7
- data/examples/choices.rb +0 -9
- data/examples/empty.rb +0 -5
- data/examples/env.rb +0 -7
- data/examples/tempfile.rb +0 -7
- data/spec/spec_helper.rb +0 -56
- data/spec/unit/available_spec.rb +0 -52
- data/spec/unit/command_path_spec.rb +0 -25
- data/spec/unit/command_spec.rb +0 -55
- data/spec/unit/executables_spec.rb +0 -16
- data/spec/unit/open_spec.rb +0 -103
- data/spec/unit/tempfile_path_spec.rb +0 -18
- data/tasks/console.rake +0 -12
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-editor.gemspec +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43382f86c223e305662a67b003a4337ce5946adaee09dcd48879714a91e45eae
|
4
|
+
data.tar.gz: fd58b6084c4cbedd605c09ce30fca817ea7f4d467e32ddfd558e3c7eb972f0a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31385f2ece1195d0c3801e495830c182f6808050c510ae83f3c78f48117a6a6a088ffce3177ea5283507a7e67cc235f6e580261e73ce03d0916044eeaeed0209
|
7
|
+
data.tar.gz: ddf5f4e74879e30a641ca4d3c7af06ffb0b7e36f8dd989915d599b283855e1fee77843d4788dd60eb7be93b1ef47b77285abb4a06b60528a36aefae75717dead
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
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
|
+
|
3
24
|
## [v0.5.1] - 2019-08-06
|
4
25
|
|
5
26
|
### Changed
|
@@ -63,6 +84,7 @@
|
|
63
84
|
|
64
85
|
* Initial implementation and release
|
65
86
|
|
87
|
+
[v0.6.0]: https://github.com/piotrmurach/tty-editor/compare/v0.5.1...v0.6.0
|
66
88
|
[v0.5.1]: https://github.com/piotrmurach/tty-editor/compare/v0.5.0...v0.5.1
|
67
89
|
[v0.5.0]: https://github.com/piotrmurach/tty-editor/compare/v0.4.1...v0.5.0
|
68
90
|
[v0.4.1]: https://github.com/piotrmurach/tty-editor/compare/v0.4.0...v0.4.1
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<div align="center">
|
2
|
-
<a href="https://
|
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
3
|
</div>
|
4
4
|
|
5
5
|
# TTY::Editor [][gitter]
|
@@ -28,7 +28,7 @@
|
|
28
28
|
Add this line to your application's Gemfile:
|
29
29
|
|
30
30
|
```ruby
|
31
|
-
gem
|
31
|
+
gem "tty-editor"
|
32
32
|
```
|
33
33
|
|
34
34
|
And then execute:
|
@@ -39,84 +39,206 @@ Or install it yourself as:
|
|
39
39
|
|
40
40
|
$ gem install tty-editor
|
41
41
|
|
42
|
-
##
|
42
|
+
## Contents
|
43
43
|
|
44
|
-
|
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:
|
45
56
|
|
46
57
|
```ruby
|
47
|
-
TTY::Editor.open(
|
58
|
+
TTY::Editor.open("/path/to/file")
|
48
59
|
```
|
49
60
|
|
50
|
-
To edit
|
61
|
+
To edit text in a default editor:
|
51
62
|
|
52
63
|
```ruby
|
53
|
-
TTY::Editor.open(
|
64
|
+
TTY::Editor.open(text: "Some text")
|
54
65
|
```
|
55
66
|
|
56
|
-
You can also
|
67
|
+
You can also open multiple existing and/or new files:
|
57
68
|
|
58
69
|
```ruby
|
59
|
-
TTY::Editor.open(
|
70
|
+
TTY::Editor.open("file_1", "file_2", "new_file_3")
|
60
71
|
```
|
61
72
|
|
62
|
-
|
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:
|
63
76
|
|
64
|
-
|
77
|
+
```ruby
|
78
|
+
TTY::Editor.open("/path/to/file", command: "vim -f")
|
79
|
+
```
|
65
80
|
|
66
|
-
|
81
|
+
When `VISUAL` or `EDITOR` are not specified, a selection menu will be presented to the user.
|
82
|
+
|
83
|
+
For example, if an user has `vim`, `emacs` and `code` editors available on their system, they will see the following menu:
|
84
|
+
|
85
|
+
```
|
86
|
+
# Select an editor?
|
87
|
+
# 1) vim
|
88
|
+
# 2) emacs
|
89
|
+
# 3) code
|
90
|
+
# Choose 1-2 [1]:
|
91
|
+
```
|
67
92
|
|
68
|
-
|
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:
|
69
100
|
|
70
101
|
```ruby
|
71
|
-
TTY::Editor.
|
102
|
+
editor = TTY::Editor.new
|
103
|
+
```
|
104
|
+
|
105
|
+
You can change default search with the `:command` keyword argument.
|
106
|
+
|
107
|
+
#### 2.1.1 :command
|
108
|
+
|
109
|
+
You can force to always use a specific editor by passing `:command` option:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
editor = TTY::Editor.new(command: "vim")
|
72
113
|
```
|
73
114
|
|
74
|
-
|
115
|
+
Or you can specify multiple commands and give a user a choice:
|
75
116
|
|
76
117
|
```ruby
|
77
|
-
TTY::Editor.
|
118
|
+
editor = TTY::Editor.new(command: ["vim", "emacs"])
|
78
119
|
```
|
79
120
|
|
80
|
-
|
121
|
+
The class-level `open` method accepts the same parameters:
|
81
122
|
|
82
|
-
|
123
|
+
```ruby
|
124
|
+
TTY::Editor.open("/path/to/file", command: "vim")
|
125
|
+
```
|
83
126
|
|
84
|
-
|
127
|
+
#### 2.1.2 :env
|
128
|
+
|
129
|
+
Use `:env` key to forward environment variables to the text editor launch command:
|
85
130
|
|
86
131
|
```ruby
|
87
|
-
TTY::Editor.
|
132
|
+
TTY::Editor.new(env: {"FOO" => "bar"})
|
88
133
|
```
|
89
134
|
|
90
|
-
|
135
|
+
The class-level `open` method accepts the same parameters:
|
91
136
|
|
92
137
|
```ruby
|
93
|
-
TTY::Editor.open(
|
138
|
+
TTY::Editor.open("/path/to/file", env: {"FOO" => "bar"})
|
94
139
|
```
|
95
140
|
|
96
|
-
|
141
|
+
#### 2.1.3 :raise_on_failure
|
97
142
|
|
98
|
-
|
143
|
+
By default when editor fails to open a `false` status is returned:
|
99
144
|
|
100
|
-
|
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:
|
101
152
|
|
102
153
|
```ruby
|
103
|
-
TTY::Editor.
|
154
|
+
editor = TTY::Editor.new(raise_on_failure: true)
|
155
|
+
editor.open("/path/to/unknown/file")
|
156
|
+
# => raises TTY::Editor::ComandInvocationError
|
104
157
|
```
|
105
158
|
|
106
|
-
|
159
|
+
#### 2.1.4 :prompt
|
107
160
|
|
108
|
-
|
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:
|
109
174
|
|
110
175
|
```ruby
|
111
|
-
TTY::Editor.
|
176
|
+
editor = TTY::Editor.new(prompt: "Which one do you fancy?")
|
177
|
+
editor.open("/path/to/file")
|
112
178
|
```
|
113
179
|
|
114
|
-
|
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:
|
115
239
|
|
116
240
|
```ruby
|
117
|
-
TTY::Editor.open(
|
118
|
-
editor.command :vim, :emacs
|
119
|
-
end
|
241
|
+
TTY::Editor.open("file_1", "file_2", "new_file_3")
|
120
242
|
```
|
121
243
|
|
122
244
|
## Development
|
data/lib/tty-editor.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "tty/editor"
|
data/lib/tty/editor.rb
CHANGED
@@ -1,40 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require 'shellwords'
|
3
|
+
require "fileutils"
|
4
|
+
require "shellwords"
|
5
|
+
require "tempfile"
|
6
|
+
require "tty-prompt"
|
8
7
|
|
9
|
-
require_relative
|
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
|
-
#
|
26
|
+
# List possible command line text editors
|
23
27
|
#
|
24
|
-
# @return [
|
28
|
+
# @return [Array[String]]
|
25
29
|
#
|
26
|
-
# @api
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
37
|
+
# Check if editor command exists
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# exist?("vim") # => true
|
32
41
|
#
|
33
42
|
# @return [Boolean]
|
34
43
|
#
|
35
|
-
# @api
|
36
|
-
def self.
|
37
|
-
|
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[
|
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
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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(
|
86
|
+
# TTY::Editor.open("/path/to/filename")
|
82
87
|
#
|
83
|
-
# @
|
84
|
-
#
|
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(*
|
90
|
-
editor = new(
|
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]
|
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
|
-
# @
|
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(
|
108
|
-
|
109
|
-
|
110
|
-
@
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
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
|
169
|
-
|
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
|
-
#
|
233
|
+
# Check if filename and text arguments are valid
|
173
234
|
#
|
174
|
-
# @
|
235
|
+
# @raise [InvalidArgumentError]
|
175
236
|
#
|
176
237
|
# @api private
|
177
|
-
def
|
178
|
-
|
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
|
250
|
+
# Create tempfile with text
|
182
251
|
#
|
183
|
-
# @param [String]
|
252
|
+
# @param [String] text
|
253
|
+
#
|
254
|
+
# @return [Tempfile]
|
184
255
|
#
|
185
|
-
# @return [String]
|
186
256
|
# @api private
|
187
|
-
def
|
188
|
-
tempfile = Tempfile.new(
|
189
|
-
tempfile <<
|
257
|
+
def create_tempfile(text)
|
258
|
+
tempfile = Tempfile.new("tty-editor")
|
259
|
+
tempfile << text
|
190
260
|
tempfile.flush
|
191
|
-
|
192
|
-
|
193
|
-
end
|
194
|
-
tempfile.path
|
261
|
+
tempfile.close
|
262
|
+
tempfile
|
195
263
|
end
|
196
264
|
|
197
|
-
#
|
265
|
+
# Render an editor selection prompt to the terminal
|
198
266
|
#
|
199
|
-
# @
|
267
|
+
# @return [String]
|
268
|
+
# the chosen editor
|
200
269
|
#
|
201
270
|
# @api private
|
202
|
-
def
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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
|