appear 1.1.1 → 1.2.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/.doc-coverage +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +1 -1
- data/bin/appear +6 -0
- data/lib/appear/command.rb +28 -0
- data/lib/appear/config.rb +7 -0
- data/lib/appear/constants.rb +1 -4
- data/lib/appear/editor/nvim.rb +251 -0
- data/lib/appear/editor.rb +262 -0
- data/lib/appear/instance.rb +4 -0
- data/lib/appear/lsof.rb +84 -51
- data/lib/appear/mac_os.rb +12 -2
- data/lib/appear/output.rb +16 -0
- data/lib/appear/processes.rb +4 -6
- data/lib/appear/revealers.rb +65 -62
- data/lib/appear/runner.rb +25 -5
- data/lib/appear/terminal.rb +127 -0
- data/lib/appear/tmux.rb +285 -39
- data/lib/appear/util/command_builder.rb +148 -0
- data/lib/appear/util/join.rb +144 -0
- data/lib/appear/util/memoizer.rb +83 -0
- data/lib/appear/util/value_class.rb +57 -0
- data/lib/appear/util.rb +6 -0
- data/lib/appear.rb +55 -1
- data/scripts/console +9 -1
- data/tools/macOS-helper.js +24 -16
- data/tools/unix-dropper.applescript +167 -0
- metadata +11 -3
- data/lib/appear/join.rb +0 -134
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
module Appear
|
|
2
|
+
module Util
|
|
3
|
+
# Class for joining objects based on hash value or method value.
|
|
4
|
+
# @see Join.join
|
|
5
|
+
class Join
|
|
6
|
+
# Join objects or hashes together where thier field values match. This
|
|
7
|
+
# method is analogous to a JOIN in SQL, althought the behavior is not
|
|
8
|
+
# exactly the same.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# foos = many_foos
|
|
12
|
+
# bars = many_bars
|
|
13
|
+
# foo_bars = Join.join(:common_attribute, foos, bars)
|
|
14
|
+
#
|
|
15
|
+
# # can still access all the properties on either a foo or a bar
|
|
16
|
+
# foo_bars.first.common_attribute
|
|
17
|
+
#
|
|
18
|
+
# # can access attributes by symbol, too
|
|
19
|
+
# foo_bars.first[:something_else]
|
|
20
|
+
#
|
|
21
|
+
# foo_bars is an array of Join instances. Reads from a foo_bar will read
|
|
22
|
+
# first from the foo, and then from the bar - this is based on the order of
|
|
23
|
+
# "tables" passed to Join.join().
|
|
24
|
+
#
|
|
25
|
+
# @param field [Symbol] the method or hash field name to join on.
|
|
26
|
+
# @param tables [Array<Any>] arrays of any sort of object, so long as it is
|
|
27
|
+
# either a hash, or has a method named `field`.
|
|
28
|
+
# @return [Array<Join>]
|
|
29
|
+
def self.join(field, *tables)
|
|
30
|
+
by_field = Hash.new { |h, k| h[k] = self.new }
|
|
31
|
+
|
|
32
|
+
tables.each do |table|
|
|
33
|
+
table.each do |row|
|
|
34
|
+
field_value = access(row, field)
|
|
35
|
+
joined = by_field[field_value]
|
|
36
|
+
joined.push!(row)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
by_field.values.select do |joined|
|
|
41
|
+
joined.joined_count >= tables.length
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# True if we can access the given field on an object, either by calling
|
|
46
|
+
# that method on the object, or by accessing using []
|
|
47
|
+
#
|
|
48
|
+
# @param obj [Any]
|
|
49
|
+
# @param field [Symbol, String]
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
def self.can_access?(obj, field)
|
|
52
|
+
if obj.respond_to?(field)
|
|
53
|
+
return true
|
|
54
|
+
elsif obj.respond_to?(:[])
|
|
55
|
+
return true
|
|
56
|
+
end
|
|
57
|
+
return false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Access the given field on an object.
|
|
61
|
+
# Raises an error if the field cannot be accessed.
|
|
62
|
+
#
|
|
63
|
+
# @param obj [Any]
|
|
64
|
+
# @param field [Symbol, String]
|
|
65
|
+
# @return [Any] the value at that field
|
|
66
|
+
def self.access(obj, field)
|
|
67
|
+
if obj.respond_to?(field)
|
|
68
|
+
obj.send(field)
|
|
69
|
+
elsif obj.respond_to?(:[])
|
|
70
|
+
obj[field]
|
|
71
|
+
else
|
|
72
|
+
raise "cannot access #{field.inspect} on #{obj.inspect}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# A Join is a union of data objects. You can use a Join to group objects of
|
|
77
|
+
# different types, so that you may read from whichever has a given field.
|
|
78
|
+
#
|
|
79
|
+
# It is more useful to use self.join to perform a join operation on
|
|
80
|
+
# collections than to create Join objects directly.
|
|
81
|
+
def initialize(*objs)
|
|
82
|
+
@objs = objs
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# add another data object to this join.
|
|
86
|
+
#
|
|
87
|
+
# @param obj [Any]
|
|
88
|
+
def push!(obj)
|
|
89
|
+
@objs << obj
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# get the number of objects in this join
|
|
93
|
+
#
|
|
94
|
+
# @return [Fixnum]
|
|
95
|
+
def joined_count
|
|
96
|
+
@objs.length
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Return the first member in the join that matches the given block.
|
|
100
|
+
# @yield [Object] join member.
|
|
101
|
+
def unjoin(&block)
|
|
102
|
+
@objs.find(&block)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# read a field from the join. Returns the first non-nil value we can read.
|
|
106
|
+
# @see self.access for information about how fields are accessed.
|
|
107
|
+
#
|
|
108
|
+
# @param sym [String, Symbol] the field name
|
|
109
|
+
# @return [Any, nil]
|
|
110
|
+
def [](sym)
|
|
111
|
+
result = nil
|
|
112
|
+
|
|
113
|
+
@objs.each do |obj|
|
|
114
|
+
if self.class.can_access?(obj, sym)
|
|
115
|
+
result = self.class.access(obj, sym)
|
|
116
|
+
end
|
|
117
|
+
break unless result.nil?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
result
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# the {#method_missing} implementation on a Join allows you to access valid
|
|
124
|
+
# fields with regular accessors.
|
|
125
|
+
#
|
|
126
|
+
# @param method [String, Symbol]
|
|
127
|
+
# @param args [Array<Any>] should have none
|
|
128
|
+
# @param block [Proc] should have none
|
|
129
|
+
def method_missing(method, *args, &block)
|
|
130
|
+
raise NoMethodError.new("Cannot access #{method.inspect}") unless respond_to?(method)
|
|
131
|
+
raise ArgumentError.new("Passed args to accessor") if args.length > 0
|
|
132
|
+
raise ArgumentError.new("Passed block to accessor") if block
|
|
133
|
+
self[method]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @param sym [String, Symbol] name of the method
|
|
137
|
+
# @param priv [Boolean] default false
|
|
138
|
+
# @return [Boolean] true if we can respond to the given method name
|
|
139
|
+
def respond_to?(sym, priv = false)
|
|
140
|
+
super(sym, priv) || (@objs.any? { |o| self.class.can_access?(o, sym) })
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module Appear
|
|
4
|
+
module Util
|
|
5
|
+
# A Memoizer memoizes calls to a block, skipping repeated work when the
|
|
6
|
+
# arguments are the same. Memoization is thread-safe, so it's safe to
|
|
7
|
+
# memoize pure computations that occur on different threads.
|
|
8
|
+
#
|
|
9
|
+
# @example memoize a method
|
|
10
|
+
# class Example
|
|
11
|
+
# def initialize
|
|
12
|
+
# @memo = Memoizer.new
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# def foo(a, b)
|
|
16
|
+
# @memo.call(a, b) do
|
|
17
|
+
# expensive_computaion(a, b)
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example memoize part of a computation
|
|
23
|
+
# class Example
|
|
24
|
+
# def initialize
|
|
25
|
+
# @memo = Memoizer.new
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# def foo(a, b)
|
|
29
|
+
# state = get_state(a, b)
|
|
30
|
+
# d = memo.call(state) { expensive_pure_computation(state) }
|
|
31
|
+
# [a, d]
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
class Memoizer
|
|
35
|
+
def initialize
|
|
36
|
+
@cache = {}
|
|
37
|
+
@cache_mutex = Mutex.new
|
|
38
|
+
@disable = false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Memoize the call to a block. Any arguments given to this method will be
|
|
42
|
+
# passed to the given block.
|
|
43
|
+
#
|
|
44
|
+
# @param args [Array<Any>] memoization key
|
|
45
|
+
# @return [Any] result of the block
|
|
46
|
+
def call(*args)
|
|
47
|
+
raise ArgumentError.new('no block given') unless block_given?
|
|
48
|
+
|
|
49
|
+
if @disable
|
|
50
|
+
return yield
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@cache_mutex.synchronize do
|
|
54
|
+
return @cache[args] if @cache.key?(args)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
result = yield
|
|
58
|
+
@cache_mutex.synchronize do
|
|
59
|
+
@cache[args] = result
|
|
60
|
+
end
|
|
61
|
+
result
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Evict the cache
|
|
65
|
+
#
|
|
66
|
+
# @return [self]
|
|
67
|
+
def clear!
|
|
68
|
+
@cache_mutex.synchronize do
|
|
69
|
+
@cache = {}
|
|
70
|
+
end
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Disable memoization permanently on this instance.
|
|
75
|
+
#
|
|
76
|
+
# @return [self]
|
|
77
|
+
def disable!
|
|
78
|
+
@disable = true
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'appear/constants'
|
|
2
|
+
|
|
3
|
+
module Appear
|
|
4
|
+
module Util
|
|
5
|
+
|
|
6
|
+
# An immutable value type, similar to Struct, but with annotated
|
|
7
|
+
# attr_readers, that can be easily created from a Hash with the necessary
|
|
8
|
+
# fields.
|
|
9
|
+
class ValueClass
|
|
10
|
+
# Thrown if a value is not supplied for an attribute
|
|
11
|
+
class MissingValueError < ::Appear::Error; end
|
|
12
|
+
|
|
13
|
+
# @param data [Hash]
|
|
14
|
+
def initialize(data)
|
|
15
|
+
self.class.properties.each do |val|
|
|
16
|
+
begin
|
|
17
|
+
instance_variable_set("@#{val}", data.fetch(val))
|
|
18
|
+
rescue KeyError
|
|
19
|
+
raise MissingValueError.new("#{self.class.name}: no value for attribute #{val.inspect}")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Define an attribute reader that can be populated by the constructor.
|
|
25
|
+
#
|
|
26
|
+
# @param name [Symbol] define an attr_reader with this name
|
|
27
|
+
# @param opts [Hash] options
|
|
28
|
+
# @option opts [Symbol] :var instance variable we should read from
|
|
29
|
+
#
|
|
30
|
+
# @!macro [attach] value_class_property
|
|
31
|
+
# @!method $1
|
|
32
|
+
# The $1 property.
|
|
33
|
+
def self.property(name, opts = {})
|
|
34
|
+
var_name = opts.fetch(:var, name)
|
|
35
|
+
|
|
36
|
+
@props ||= []
|
|
37
|
+
@props << var_name
|
|
38
|
+
|
|
39
|
+
# we could do super, but we want to allow defining :acttive? or so
|
|
40
|
+
class_eval "def #{name}; @#{var_name}; end"
|
|
41
|
+
|
|
42
|
+
var_name
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Array<Symbol>] names of all properties
|
|
46
|
+
def self.properties
|
|
47
|
+
@props ||= []
|
|
48
|
+
if self.superclass.respond_to?(:properties)
|
|
49
|
+
self.superclass.properties + @props
|
|
50
|
+
else
|
|
51
|
+
@props
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/appear/util.rb
ADDED
data/lib/appear.rb
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
|
+
# Appear your terminal programs in your gui!
|
|
2
|
+
#
|
|
3
|
+
# Appear is a tool for revealing a given process in your terminal. Given a
|
|
4
|
+
# process ID, `appear` finds the terminal emulator view (be it a window, tab,
|
|
5
|
+
# or pane) containing that process and shows it to you. Appear understands
|
|
6
|
+
# terminal multiplexers like `tmux`, so if your target process is in a
|
|
7
|
+
# multiplexer session, `appear` will reveal a client connected to that session,
|
|
8
|
+
# or start one if needed.
|
|
9
|
+
#
|
|
10
|
+
# Most users of this library will find the {Appear.appear} method sufficient,
|
|
11
|
+
# although you may construct and control library internals using the
|
|
12
|
+
# {Appear::Instance} class, which is our "main" class.
|
|
13
|
+
#
|
|
14
|
+
# Other useful ideas include the {Appear::BaseService} class, which is a
|
|
15
|
+
# super-simple dependency-injection base class.
|
|
16
|
+
#
|
|
17
|
+
# @author Jake Teton-Landis <just.1.jake@gmail.com>
|
|
1
18
|
module Appear
|
|
2
|
-
# This method is an easy public interface to Appear for ruby consumers.
|
|
3
19
|
# Appear the given PID in your user interfaces.
|
|
20
|
+
# This method is an easy public interface to Appear for ruby consumers.
|
|
4
21
|
# @param pid [Number] pid to Appear.
|
|
5
22
|
# @param config [Appear::Config, nil] a config for adjusting verbosity and logging.
|
|
6
23
|
def self.appear(pid, config = nil)
|
|
@@ -8,7 +25,44 @@ module Appear
|
|
|
8
25
|
instance = Appear::Instance.new(config)
|
|
9
26
|
instance.call(pid)
|
|
10
27
|
end
|
|
28
|
+
|
|
29
|
+
# Build a command string that will execute `appear` with the given config and
|
|
30
|
+
# arguments. If `appear` is in your PATH, we will use that binary. Otherwise,
|
|
31
|
+
# we will call the script in ./bin/ folder near this library, which has a
|
|
32
|
+
# #!/usr/bin/env ruby shbang.
|
|
33
|
+
#
|
|
34
|
+
# You may optionally need to prepend "PATH=#{ENV['PATH']} " to the command if
|
|
35
|
+
# `tmux` is not in your command execution environment's PATH.
|
|
36
|
+
#
|
|
37
|
+
# Intended for use with the terminal-notifier gem.
|
|
38
|
+
# @see https://github.com/julienXX/terminal-notifier/tree/master/Ruby
|
|
39
|
+
#
|
|
40
|
+
# @example Show a notification that will raise your program
|
|
41
|
+
# require 'appear'
|
|
42
|
+
# require 'terminal-notifier'
|
|
43
|
+
# TerminalNotifier.notify('Click to appear!', :execute => Appear.build_command(Process.pid))
|
|
44
|
+
#
|
|
45
|
+
# @param pid [Number] pid to Appear.
|
|
46
|
+
# @param config [Appear::Config, nil] a config for adjusting verbosity and logging.
|
|
47
|
+
# @return [String] a shell command that will execute `appear`
|
|
48
|
+
def self.build_command(pid, config = nil)
|
|
49
|
+
binary = `which appear`.strip
|
|
50
|
+
if binary.empty?
|
|
51
|
+
binary = Appear::MODULE_DIR.join('bin/appear').to_s
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
command = Appear::Util::CommandBuilder.new(binary).args(pid)
|
|
55
|
+
|
|
56
|
+
if config
|
|
57
|
+
command.flag('verbose', true) unless config.silent
|
|
58
|
+
command.flag('log-file', config.log_file) if config.log_file
|
|
59
|
+
command.flag('record-runs', true) if config.record_runs
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
command.to_s
|
|
63
|
+
end
|
|
11
64
|
end
|
|
12
65
|
|
|
13
66
|
require 'appear/config'
|
|
14
67
|
require 'appear/instance'
|
|
68
|
+
require 'appear/util/command_builder'
|
data/scripts/console
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
require "bundler/setup"
|
|
8
8
|
require "pry"
|
|
9
9
|
require "appear"
|
|
10
|
+
require "appear/editor"
|
|
10
11
|
|
|
11
12
|
def create_appear_instance
|
|
12
13
|
config = Appear::Config.new
|
|
@@ -16,6 +17,13 @@ def create_appear_instance
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
instance, config = create_appear_instance
|
|
19
|
-
|
|
20
|
+
services = instance.instance_variable_get('@all_services')
|
|
21
|
+
# nvim = Appear::Editor::Nvim.find_for_file(File.expand_path('.'))
|
|
22
|
+
# nvim = Appear::Editor::Nvim.new(Appear::Editor::Nvim.sockets.last, services)
|
|
23
|
+
nvim = Appear::Editor::Nvim.find_for_file(__FILE__, services)
|
|
24
|
+
ide = Appear::Editor::TmuxIde.new(services)
|
|
25
|
+
puts "generated instance = #{instance} from config = #{config}"
|
|
26
|
+
puts "connected nvim = #{nvim}"
|
|
27
|
+
puts "generated ide = #{ide}"
|
|
20
28
|
|
|
21
29
|
binding.pry
|
data/tools/macOS-helper.js
CHANGED
|
@@ -27,6 +27,15 @@ var ScriptContext = this
|
|
|
27
27
|
// -----------------------------------------------------------
|
|
28
28
|
var PROGRAM_NAME = 'appear-macOS-helper'
|
|
29
29
|
var Methods = {}
|
|
30
|
+
function delegateMethod(methodName, klass, fn) {
|
|
31
|
+
result = function() {
|
|
32
|
+
var instance = new klass();
|
|
33
|
+
return instance[fn].apply(instance, arguments)
|
|
34
|
+
}
|
|
35
|
+
result.name = methodName
|
|
36
|
+
Methods[methodName] = result
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
30
39
|
|
|
31
40
|
// entrypoint -------------------------------------------------
|
|
32
41
|
// this is the main method of this script when it is called from the command line.
|
|
@@ -121,16 +130,22 @@ Iterm2.prototype.revealTty = function revealTty(tty) {
|
|
|
121
130
|
return success;
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
}
|
|
133
|
+
Iterm2.prototype.newWindow = function newWindow(command) {
|
|
134
|
+
var window = this.app.createWindowWithDefaultProfile({command: command})
|
|
135
|
+
var session = window.currentSession();
|
|
128
136
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
return {
|
|
138
|
+
win: window,
|
|
139
|
+
tab: window.currentTab(),
|
|
140
|
+
session: session,
|
|
141
|
+
tty: session.tty(),
|
|
142
|
+
};
|
|
132
143
|
}
|
|
133
144
|
|
|
145
|
+
delegateMethod('iterm2_reveal_tty', Iterm2, 'revealTty')
|
|
146
|
+
delegateMethod('iterm2_panes', Iterm2, 'panes')
|
|
147
|
+
delegateMethod('iterm2_new_window', Iterm2, 'newWindow')
|
|
148
|
+
|
|
134
149
|
// -------------------------------------------------------------
|
|
135
150
|
// Terminal.app library
|
|
136
151
|
|
|
@@ -168,15 +183,8 @@ Terminal.prototype.revealTty = function revealTty(tty) {
|
|
|
168
183
|
return success
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return terminal.revealTty(tty)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
Methods['terminal_panes'] = function terminal_panes() {
|
|
177
|
-
var terminal = new Terminal()
|
|
178
|
-
return terminal.panes()
|
|
179
|
-
}
|
|
186
|
+
delegateMethod('terminal_reveal_tty', Terminal, 'revealTty')
|
|
187
|
+
delegateMethod('terminal_panes', Terminal, 'panes')
|
|
180
188
|
|
|
181
189
|
// for tests ----------------------------------------------
|
|
182
190
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env osascript
|
|
2
|
+
(*
|
|
3
|
+
* script: unix-dropper.applescript
|
|
4
|
+
* author: Jake Teton-Landis <just.1.jake@gmail.com>
|
|
5
|
+
* date: 2016-08-30
|
|
6
|
+
*
|
|
7
|
+
* when saved as an application (open in Script Editor, then choose
|
|
8
|
+
* File->Export...), this script can be used to process files or folders with a
|
|
9
|
+
* Unix script via drag-and-drop.
|
|
10
|
+
*
|
|
11
|
+
* You can customize the preferences of your Unix script by opening the
|
|
12
|
+
* application the regular way.
|
|
13
|
+
*
|
|
14
|
+
* Your script will be called as a sh function, so you can process $@ using
|
|
15
|
+
* `shift` or something.
|
|
16
|
+
*
|
|
17
|
+
* Heplful documentation for maintainers:
|
|
18
|
+
* Technical Note TN2065 - do shell script in AppleScript
|
|
19
|
+
* https://developer.apple.com/library/mac/technotes/tn2065/_index.html
|
|
20
|
+
*
|
|
21
|
+
* Here's the default script that this app will run, creaetd from the default
|
|
22
|
+
* property `user_unix_command`, defined below, as though the dropped files were
|
|
23
|
+
* "first", "second", "third".
|
|
24
|
+
*
|
|
25
|
+
* ```sh
|
|
26
|
+
* #!/bin/sh
|
|
27
|
+
* script-wrapper () {
|
|
28
|
+
* ARG_C='3'
|
|
29
|
+
* ARG_1='first'
|
|
30
|
+
* ARG_2='second'
|
|
31
|
+
* ARG_3='third'
|
|
32
|
+
* say "dropped $ARG_C files. first file: $ARG_0"
|
|
33
|
+
* }
|
|
34
|
+
* # we do this so you can use "$@"
|
|
35
|
+
* # and unix conventions if you're unix-y
|
|
36
|
+
* script-wrapper first second third
|
|
37
|
+
* ```
|
|
38
|
+
*)
|
|
39
|
+
|
|
40
|
+
-- property default_command : "say \"dropped $ARG_C files. first file: $ARG_0\""
|
|
41
|
+
property default_command : "PATH=\"/usr/local/bin:$PATH\" /usr/local/bin/appear --edit -- \"$@\""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
--- this is a stored user preference.
|
|
45
|
+
--- this is the default, but it can be set as a preference in a .plist if this script is saved as an applicication
|
|
46
|
+
property user_unix_command : default_command
|
|
47
|
+
|
|
48
|
+
--- this function runs when the user opens the application by double-clicking it.
|
|
49
|
+
--- uer can adjust the user_unix_command by opening the application
|
|
50
|
+
on run
|
|
51
|
+
set quit_ to "Quit"
|
|
52
|
+
set reset to "Reset"
|
|
53
|
+
set save_ to "Save"
|
|
54
|
+
repeat
|
|
55
|
+
set ds to doc_string()
|
|
56
|
+
set d_res to display dialog ds buttons {quit_, reset, save_} default button save_ default answer user_unix_command
|
|
57
|
+
if the button returned of d_res is save_ then
|
|
58
|
+
set user_unix_command to the text returned of the result
|
|
59
|
+
end if
|
|
60
|
+
if the button returned of d_res is quit_ then
|
|
61
|
+
return "user quit"
|
|
62
|
+
end if
|
|
63
|
+
end repeat
|
|
64
|
+
end run
|
|
65
|
+
|
|
66
|
+
-- This droplet processes files dropped onto the applet
|
|
67
|
+
on open these_items
|
|
68
|
+
set as_strings to {}
|
|
69
|
+
repeat with cur in these_items
|
|
70
|
+
set cur_as_string to (POSIX path of cur) as string
|
|
71
|
+
copy cur_as_string to end of as_strings
|
|
72
|
+
end repeat
|
|
73
|
+
set the_command to unix_script(user_unix_command, as_strings)
|
|
74
|
+
log the_command
|
|
75
|
+
do shell script the_command
|
|
76
|
+
end open
|
|
77
|
+
|
|
78
|
+
--- here's how we end up building the shell script to call
|
|
79
|
+
--- the user's shell script with hella sweet args
|
|
80
|
+
on build_arg_vars(args)
|
|
81
|
+
-- MOAR SPEED
|
|
82
|
+
return {}
|
|
83
|
+
set argc to the count of args
|
|
84
|
+
set res to {set_env_var("ARG_C", argc)}
|
|
85
|
+
|
|
86
|
+
repeat with i from 1 to the count of args
|
|
87
|
+
set arg to item i of args
|
|
88
|
+
copy set_env_var("ARG_" & i, arg) to end of res
|
|
89
|
+
end repeat
|
|
90
|
+
res
|
|
91
|
+
end build_arg_vars
|
|
92
|
+
|
|
93
|
+
on build_script(user_script, args)
|
|
94
|
+
set fn_name to "wrapper_fn"
|
|
95
|
+
set open_fn to fn_name & " () {"
|
|
96
|
+
set close_fn to "}"
|
|
97
|
+
set call_fn to fn_name & " " & join_list(args, " ")
|
|
98
|
+
set comment to "# we do this so you can use \"$@\"
|
|
99
|
+
# and unix conventions if you're unix-y"
|
|
100
|
+
|
|
101
|
+
set res to build_arg_vars(args)
|
|
102
|
+
|
|
103
|
+
copy open_fn to beginning of res
|
|
104
|
+
copy user_script to end of res
|
|
105
|
+
copy close_fn to end of res
|
|
106
|
+
copy comment to end of res
|
|
107
|
+
copy call_fn to end of res
|
|
108
|
+
res
|
|
109
|
+
end build_script
|
|
110
|
+
|
|
111
|
+
on set_env_var(var, value)
|
|
112
|
+
|
|
113
|
+
set res to var & "=" & (the quoted form of ("" & value))
|
|
114
|
+
# display dialog res
|
|
115
|
+
|
|
116
|
+
return res
|
|
117
|
+
|
|
118
|
+
end set_env_var
|
|
119
|
+
|
|
120
|
+
on join_list(the_list, sep)
|
|
121
|
+
if (count of the_list) is 0 then
|
|
122
|
+
return ""
|
|
123
|
+
end if
|
|
124
|
+
|
|
125
|
+
if (count of the_list) is 1 then
|
|
126
|
+
return "" & item 1 of the_list
|
|
127
|
+
end if
|
|
128
|
+
|
|
129
|
+
set res to "" & item 1 of the_list
|
|
130
|
+
|
|
131
|
+
repeat with i from 2 to the count of the_list
|
|
132
|
+
set el to item i of the_list
|
|
133
|
+
set res to res & sep & el
|
|
134
|
+
end repeat
|
|
135
|
+
res
|
|
136
|
+
end join_list
|
|
137
|
+
|
|
138
|
+
on unix_script(user_command, args)
|
|
139
|
+
join_list(build_script(user_command, args), "
|
|
140
|
+
")
|
|
141
|
+
end unix_script
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
-- returns string
|
|
145
|
+
on doc_string()
|
|
146
|
+
"Enter a unix command to run when this application should open a file. Your script has several environment variables availible, see below.
|
|
147
|
+
|
|
148
|
+
Substitutions availible:
|
|
149
|
+
\"$@\": All arguments, quoted
|
|
150
|
+
(all sh default environment variables)
|
|
151
|
+
$ARG_C: Total number of arguments
|
|
152
|
+
$ARG_1: First argument
|
|
153
|
+
$ARG_2: Second argument
|
|
154
|
+
etc...
|
|
155
|
+
|
|
156
|
+
Current command:
|
|
157
|
+
`" & user_unix_command & "`
|
|
158
|
+
|
|
159
|
+
Final script, given dropped files {first, second, third}:
|
|
160
|
+
```sh
|
|
161
|
+
#!/bin/sh
|
|
162
|
+
" & unix_script(user_unix_command, {"first", "second", "third"}) & "
|
|
163
|
+
```"
|
|
164
|
+
end doc_string
|
|
165
|
+
|
|
166
|
+
--- uncomment to test
|
|
167
|
+
-- unix_script("/Users/jake/src/foo $1 a/b $ARG_4 -- \"$@\"", {"foo", "bar"})
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: appear
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jake Teton-Landis
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-09-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -114,8 +114,9 @@ files:
|
|
|
114
114
|
- lib/appear/command.rb
|
|
115
115
|
- lib/appear/config.rb
|
|
116
116
|
- lib/appear/constants.rb
|
|
117
|
+
- lib/appear/editor.rb
|
|
118
|
+
- lib/appear/editor/nvim.rb
|
|
117
119
|
- lib/appear/instance.rb
|
|
118
|
-
- lib/appear/join.rb
|
|
119
120
|
- lib/appear/lsof.rb
|
|
120
121
|
- lib/appear/mac_os.rb
|
|
121
122
|
- lib/appear/output.rb
|
|
@@ -123,11 +124,18 @@ files:
|
|
|
123
124
|
- lib/appear/revealers.rb
|
|
124
125
|
- lib/appear/runner.rb
|
|
125
126
|
- lib/appear/service.rb
|
|
127
|
+
- lib/appear/terminal.rb
|
|
126
128
|
- lib/appear/tmux.rb
|
|
129
|
+
- lib/appear/util.rb
|
|
130
|
+
- lib/appear/util/command_builder.rb
|
|
131
|
+
- lib/appear/util/join.rb
|
|
132
|
+
- lib/appear/util/memoizer.rb
|
|
133
|
+
- lib/appear/util/value_class.rb
|
|
127
134
|
- screenshot.gif
|
|
128
135
|
- scripts/console
|
|
129
136
|
- scripts/setup
|
|
130
137
|
- tools/macOS-helper.js
|
|
138
|
+
- tools/unix-dropper.applescript
|
|
131
139
|
homepage: https://github.com/airbnb/appear
|
|
132
140
|
licenses: []
|
|
133
141
|
metadata: {}
|