Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

executable file 225 lines (182 sloc) 7.215 kB
#!/usr/bin/env python
"""
coda-cli -- Shell integration for Coda
http://justinhileman.info/coda-cli
`coda` makes Panic's Coda a first-class *nix citizen.
This tool provides shell integration for Coda. You can now open files and
directories from a terminal:
coda foo.html
coda foo.html bar.css
coda ./
If `foo.html` or `bar.css` doesn't exist, `coda` will create it automatically
for you.
You can also edit a file and wait for it to close (`-w` or `--wait`). This is
really important if you plan on making `coda` your default `$EDITOR`. Now you
can use Coda for writing Git or SVN commit messages. Simply add this line to
your `.bash_profile` or `.bashrc` or `.zshrc`:
export EDITOR='coda -w'
In addition to knowing how to wait, `coda` plays nice with pipes. You can use
it just like any other POSIX utility... Invoke it from the command line and pass
the output from any command to Coda, or from Coda to another command. Try a
couple of these:
echo 'foo' | coda
echo 'bar' | coda >> out.txt
coda >> somefile.html
find . -name "*.css" | xargs coda
coda < config.yaml > config_new.yaml
Note that piping output from coda implies `--wait`.
Usage:
coda --help
License:
Copyright 2011 Justin Hileman - http://justinhileman.com
Distributed under the MIT License - http://creativecommons.org/licenses/MIT/
"""
import sys, os, time, commands, optparse, signal
from tempfile import mkstemp
from pipes import quote
version = '1.0.5'
if commands.getoutput("mdfind \"kMDItemCFBundleIdentifier == 'com.panic.Coda2'\"") != "":
bundle_id = 'com.panic.Coda2'
else:
bundle_id = 'com.panic.Coda'
parser = optparse.OptionParser(
usage='Usage: %prog [options] [file ...]',
epilog="To read input from stdin, use a pipe or pass `-` for [file] Piping output from coda implies `-w`."
)
opts = (
('-w', '--wait', {'action': 'store_true', 'default': False, 'help': 'wait for file to be closed'}),
('-d', '--change-dir', {'action': 'store_true', 'dest': 'chdir', 'default': False, 'help': "change Coda's working directory"}),
('-n', '--new-window', {'action': 'store_true', 'dest': 'new_window', 'default': False, 'help': "open in a new window"}),
('--ls-tabs', {'action': 'store_true', 'dest': 'lstabs', 'default': False, 'help': 'show all open tabs'}),
('--version', {'action': 'store_true', 'default': False, 'help': 'print version information and quit'}),
)
for opt in opts:
parser.add_option(*opt[:-1], **opt[-1])
parser.version = "%%prog: %s" % version
(options, files) = parser.parse_args()
# handle Ctrl+C gracefully
signal.signal(signal.SIGINT, lambda *x: sys.exit(1))
def osascript(scpt):
return commands.getoutput("osascript 2>/dev/null <<ENDSCRIPT\n%s\nENDSCRIPT\n:" % scpt)
def coda_is_running():
return osascript("tell application \"System Events\" to (count (every process whose creator type is \"TStu\")) as boolean") == "true"
def open_tabs():
if bundle_id == 'com.panic.Coda2':
guts = """
set openTabs to {}
set myFiles to file of every document
repeat with i from 1 to number of items in myFiles
set end of openTabs to POSIX path of (item i of myFiles as alias)
end repeat
return openTabs as text
"""
else:
guts = "return file path of every editor of every split of every tab of every document as text"
scpt = """
set AppleScript's text item delimiters to "\n"
tell application id "%s"
%s
end tell
""" % (bundle_id, guts)
if coda_is_running():
return [os.path.realpath(tab) for tab in filter(lambda a: a != "missing value" and a != "", osascript(scpt).rstrip("\n").split("\n"))]
else:
return []
def new_tab_with_contents(text):
scpt = """
tell application id "%s"
activate
tell first document
make new tab
tell current editor to set contents to "%s"
end tell
end tell
"""
osascript(scpt % (bundle_id, (text or "").replace('\\', '\\\\').replace('"', '\\"')))
def read_stdin():
if sys.stdin.isatty(): # not a pipe, wait for more stdin
sys.stderr.write("coda: Reading from stdin... (press CTRL-D to proceed)\n")
return sys.stdin.read() or None
if options.version:
parser.print_version()
exit()
if options.lstabs:
tabs = open_tabs()
if tabs:
print "\n".join(tabs)
exit()
if options.new_window and coda_is_running():
osascript("tell application id \"%s\" to make new document" % bundle_id)
# solve the `ls | xargs coda` fake stdin issue...
stdin_text = None
if not sys.stdin.isatty():
stdin_text = read_stdin()
needs_stdout = (not sys.stdout.isatty())
if stdin_text and not '-' in files:
files.append('-')
tempfile = None
if needs_stdout:
options.wait = True
if stdin_text or not files or '-' in files:
(fd, tempfile) = mkstemp(prefix='coda-')
if '-' in files:
if not stdin_text:
stdin_text = read_stdin()
files[files.index('-')] = tempfile
else:
files.append(tempfile)
if stdin_text:
open(tempfile, 'w').write(stdin_text)
if not files and not stdin_text:
os.system('open -b %s' % bundle_id)
exit()
return_to = None
if options.wait:
scpt = """
tell application "System Events"
return name of first application process whose frontmost is true
end tell
"""
return_to = osascript(scpt)
if '-' in files:
(fd, tempfile) = mkstemp(prefix='coda-')
files[files.index('-')] = tempfile
if not stdin_text:
stdin_text = read_stdin()
if stdin_text:
open(tempfile, 'w').write(stdin_text)
# handle any lingering '-' files
if not stdin_text and '-' in files:
stdin_text = read_stdin()
if not stdin_text:
sys.stderr.write('Ambiguous `-` ignored')
files.remove('-')
# open all files
for i, name in enumerate(files):
if name == '-':
new_tab_with_contents(stdin_text)
files.remove('-')
else:
files[i] = file = os.path.realpath(name)
if not os.path.exists(file):
os.system('mkdir -p %s' % quote(os.path.dirname(file)))
os.system('touch %s' % quote(file))
os.system('open -b %s %s' % (bundle_id, quote(file)))
# handle --change-dir
if options.chdir:
plen = len(os.path.commonprefix(files))
path = os.path.commonprefix([f[:plen].rsplit("/", 1)[0] for f in files])
if not os.path.isdir(path):
path = os.path.dirname(path)
os.system('open -b %s %s' % (bundle_id, quote(path)))
# handle --wait
if options.wait:
time.sleep(5)
while list(set(files) & set(open_tabs())):
time.sleep(.5)
if needs_stdout:
sys.stdout.write("\n".join([open(f).read() for f in files]))
elif tempfile:
sys.stderr.write("File written to %s" % quote(tempfile))
if return_to:
osascript("tell application \"%s\" to activate" % return_to)
Jump to Line
Something went wrong with that request. Please try again.