Bundle cot command again

This commit is contained in:
1024jp 2015-10-29 18:03:01 +09:00
parent 090310dee0
commit 8b0507c4cb
7 changed files with 295 additions and 10 deletions

View File

@ -14,6 +14,7 @@ Change Log
- Update “R” syntax style:
- Add “Rscript” to interpreters.
- Bundle cot command to `CotEditor.app/Contents/SharedSupport/bin/` again.
- Tweak UI text.

View File

@ -23,7 +23,11 @@
<p>CotEditor has '<strong><code>cot</code></strong>' command-line tool which allows you to control CotEditor from the command-line. To use <code>cot</code> command, install it at first.</p>
<h2>Installation</h2>
<p>You can get the <code>cot</code> command-line tool from our web site on <a href="http://coteditor.com/cot">http://coteditor.com/cot</a>.</p>
<p>You can install the <code>cot</code> command-line tool to a desired location creating a symbolic link via Terminal. The <code>cot</code> substance is bundled at '<code>Contents/SharedSupport/bin/cot</code>' in CotEditor.app.</p>
<pre><code>ln -s /Applications/CotEditor.app/Contents/SharedSupport/bin/cot ~/usr/local/bin/cot</code></pre>
<p>And uninstallation via Terminal is like the following:</p>
<pre><code>unlink /usr/local/bin/cot</code></pre>
<h2>Options</h2>
<p>The following options can be used on <code>cot</code> command.</p>

View File

@ -69,6 +69,7 @@
<li>Add “Rscript” to interpreters.</li>
</ul></li>
<li>Temporary hide the “Live Update” checkbox in the find panel since this feature by OgreKit framework has actually not worked correctly in the latest versions.</li>
<li>Bundle cot command to <code>CotEditor.app/Contents/SharedSupport/bin/</code> again.</li>
<li>Update Onigmo regular expression engine to 5.15.0.</li>
</ul>
</section>

View File

@ -23,8 +23,12 @@
<p>CotEditor にはコマンドラインからの操作を可能にする <code>cot</code> コマンドラインツールがあります。<code>cot</code> コマンドを使うためには、まずインストールをしてください。</p>
<h2>インストール</h2>
<p><code>cot</code> コマンドラインツールは <a href="http://coteditor.com/cot">http://coteditor.com/cot</a> からダウンロードできます。</p>
<p><code>cot</code> コマンドは CotEditor.app 内の '<code>Contents/SharedSupport/bin/cot</code>' にバンドルされています。</p>
<p>Terminal からシンボリックリンクを作成して、任意の場所にインストールするができます。</p>
<pre><code>ln -s /Applications/CotEditor.app/Contents/SharedSupport/bin/cot ~/usr/local/bin/cot</code></pre>
<p>そして、Terminal からアンインストールする場合は以下のようにします。</p>
<pre><code>unlink /usr/local/bin/cot</code></pre>
<h2>オプション</h2>
<p><code>cot</code> コマンドは以下のオプションが使用できます。</p>

View File

@ -72,6 +72,7 @@
<li>インタープリタに“Rscript”を追加</li>
</ul></li>
<li>検索パネルの“自動更新”チェックボックスを一旦非表示に変更OgreKitフレームワークによるこの機能が実質的に機能していなかったため</li>
<li>cot コマンドを <code>CotEditor.app/Contents/SharedSupport/bin/</code> に再同梱</li>
<li>Onigmo 正規表現エンジンを 5.15.0 に更新</li>
</ul>
</section>

View File

@ -92,8 +92,8 @@
2A63122A19C25CF80088C5C6 /* CEHexColorTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A63122919C25CF80088C5C6 /* CEHexColorTransformer.m */; };
2A63122E19C2D30C0088C5C6 /* CEThemeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A63122D19C2D30C0088C5C6 /* CEThemeViewController.m */; };
2A63123119C2D3B60088C5C6 /* ThemeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A63122F19C2D3B60088C5C6 /* ThemeView.xib */; };
2A6415941BD10EF40080358E /* NSString+Indentation.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6415931BD10EF30080358E /* NSString+Indentation.m */; settings = {ASSET_TAGS = (); }; };
2A6415951BD10EFA0080358E /* NSString+Indentation.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6415931BD10EF30080358E /* NSString+Indentation.m */; settings = {ASSET_TAGS = (); }; };
2A6415941BD10EF40080358E /* NSString+Indentation.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6415931BD10EF30080358E /* NSString+Indentation.m */; };
2A6415951BD10EFA0080358E /* NSString+Indentation.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6415931BD10EF30080358E /* NSString+Indentation.m */; };
2A68F93518F9995500673440 /* CEThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A68F93418F9995500673440 /* CEThemeManager.m */; };
2A68F93818FB04F400673440 /* CEMenuItemCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A68F93718FB04F300673440 /* CEMenuItemCell.m */; };
2A6E3F3D19B5218300A63E97 /* CotEditor.help in Resources */ = {isa = PBXBuildFile; fileRef = 2A6E3F3C19B5218300A63E97 /* CotEditor.help */; };
@ -277,6 +277,8 @@
2A88A7911B8B899F0055B9AE /* NSString+Normalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A88A78F1B8B87C90055B9AE /* NSString+Normalization.m */; };
2A94A45A18D08D52009FA90D /* NSWindow+ScriptingSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A94A45918D08D52009FA90D /* NSWindow+ScriptingSupport.m */; };
2A94A45E18D0B562009FA90D /* CEOpacityPanelController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A94A45C18D0B562009FA90D /* CEOpacityPanelController.m */; };
2A94FC791BE225A200B454A8 /* cot in Copy Command-Line Tools */ = {isa = PBXBuildFile; fileRef = 2A94FC781BE2256F00B454A8 /* cot */; };
2A94FC7B1BE225F000B454A8 /* cot in Copy Command-Line Tools */ = {isa = PBXBuildFile; fileRef = 2A94FC781BE2256F00B454A8 /* cot */; };
2A961C5218FC699E002076B6 /* AppleScript.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2A961C4718FC699E002076B6 /* AppleScript.rtf */; };
2A961C5418FC699E002076B6 /* ScriptMenu Folder.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2A961C4B18FC699E002076B6 /* ScriptMenu Folder.rtf */; };
2A961C5518FC699E002076B6 /* ShellScript.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2A961C4D18FC699E002076B6 /* ShellScript.rtf */; };
@ -298,18 +300,18 @@
2AD67C2A1AA75E7F0078BB95 /* CEFindPanelTextClipView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD67C291AA75E7F0078BB95 /* CEFindPanelTextClipView.m */; };
2AD67C2F1AA7651B0078BB95 /* CEFindPanelLayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD67C2E1AA7651B0078BB95 /* CEFindPanelLayoutManager.m */; };
2AD84CAA1966314100DE49BD /* CEODBEventSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD84CA91966314100DE49BD /* CEODBEventSender.m */; };
2AD9039B1BCE86A3004F2B8A /* SpellCheck.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039A1BCE86A3004F2B8A /* SpellCheck.tiff */; settings = {ASSET_TAGS = (); }; };
2AD9039C1BCE86A3004F2B8A /* SpellCheck.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039A1BCE86A3004F2B8A /* SpellCheck.tiff */; settings = {ASSET_TAGS = (); }; };
2AD9039F1BCEE174004F2B8A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039D1BCEE174004F2B8A /* Credits.rtf */; settings = {ASSET_TAGS = (); }; };
2AD903A01BCEE174004F2B8A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039D1BCEE174004F2B8A /* Credits.rtf */; settings = {ASSET_TAGS = (); }; };
2AD9039B1BCE86A3004F2B8A /* SpellCheck.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039A1BCE86A3004F2B8A /* SpellCheck.tiff */; };
2AD9039C1BCE86A3004F2B8A /* SpellCheck.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039A1BCE86A3004F2B8A /* SpellCheck.tiff */; };
2AD9039F1BCEE174004F2B8A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039D1BCEE174004F2B8A /* Credits.rtf */; };
2AD903A01BCEE174004F2B8A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2AD9039D1BCEE174004F2B8A /* Credits.rtf */; };
2AD9625F1B53496500D46C3A /* NSString+Sandboxing.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD9625E1B53496500D46C3A /* NSString+Sandboxing.m */; };
2AE2E9D519D2CF00000D66A9 /* CEEncodingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AE2E9D419D2CF00000D66A9 /* CEEncodingManager.m */; };
2AE356461A86D32500E29FEF /* CEClipView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AE356451A86D32500E29FEF /* CEClipView.m */; };
2AEB8B7C19AF73980098D557 /* FileDropGlossary.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2AEB8B7A19AF73980098D557 /* FileDropGlossary.txt */; };
2AF4E0D7196F7958008E9EF7 /* CEStatusBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AF4E0D6196F7958008E9EF7 /* CEStatusBarController.m */; };
2AF5C3BE1A55072E00C972B5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2AF5C3BC1A55072E00C972B5 /* InfoPlist.strings */; };
2AFF21821BA9F69A008A41CD /* CESwitcherSegmentedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFF21811BA9F69A008A41CD /* CESwitcherSegmentedCell.m */; settings = {ASSET_TAGS = (); }; };
2AFF21831BA9F69A008A41CD /* CESwitcherSegmentedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFF21811BA9F69A008A41CD /* CESwitcherSegmentedCell.m */; settings = {ASSET_TAGS = (); }; };
2AFF21821BA9F69A008A41CD /* CESwitcherSegmentedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFF21811BA9F69A008A41CD /* CESwitcherSegmentedCell.m */; };
2AFF21831BA9F69A008A41CD /* CESwitcherSegmentedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFF21811BA9F69A008A41CD /* CESwitcherSegmentedCell.m */; };
2AFFB70718D7BC9300118477 /* OpacityPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2AFFB70518D7BC9300118477 /* OpacityPanel.xib */; };
2AFFB71618D7DCC000118477 /* CEPanelController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFFB71518D7DCC000118477 /* CEPanelController.m */; };
2AFFB71B18D7F18300118477 /* CEGoToSheetController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFFB71918D7F18300118477 /* CEGoToSheetController.m */; };
@ -356,6 +358,31 @@
D3F0D0D055B0931687B33C98 /* libPods-appstore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD42F3AFDE3081DC5D669D51 /* libPods-appstore.a */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
2A94FC771BE2253500B454A8 /* Copy Command-Line Tools */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = bin;
dstSubfolderSpec = 12;
files = (
2A94FC791BE225A200B454A8 /* cot in Copy Command-Line Tools */,
);
name = "Copy Command-Line Tools";
runOnlyForDeploymentPostprocessing = 0;
};
2A94FC7A1BE225E900B454A8 /* Copy Command-Line Tools */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = bin;
dstSubfolderSpec = 12;
files = (
2A94FC7B1BE225F000B454A8 /* cot in Copy Command-Line Tools */,
);
name = "Copy Command-Line Tools";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
258225FE07794C2A00A0F8D3 /* CESyntaxParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CESyntaxParser.h; sourceTree = "<group>"; };
258225FF07794C2A00A0F8D3 /* CESyntaxParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CESyntaxParser.m; sourceTree = "<group>"; };
@ -556,6 +583,7 @@
2A94A45918D08D52009FA90D /* NSWindow+ScriptingSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSWindow+ScriptingSupport.m"; sourceTree = "<group>"; };
2A94A45B18D0B562009FA90D /* CEOpacityPanelController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CEOpacityPanelController.h; sourceTree = "<group>"; };
2A94A45C18D0B562009FA90D /* CEOpacityPanelController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CEOpacityPanelController.m; sourceTree = "<group>"; };
2A94FC781BE2256F00B454A8 /* cot */ = {isa = PBXFileReference; explicitFileType = text.script.python; path = cot; sourceTree = "<group>"; };
2A961C4618FC699E002076B6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Acknowledgements.rtf; sourceTree = "<group>"; };
2A961C4818FC699E002076B6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/AppleScript.rtf; sourceTree = "<group>"; };
2A961C4C18FC699E002076B6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = "en.lproj/ScriptMenu Folder.rtf"; sourceTree = "<group>"; };
@ -1141,6 +1169,7 @@
2A75ACCA19E86DDB00444894 /* CotEditor.sdef */,
2A6E3F3C19B5218300A63E97 /* CotEditor.help */,
2A2179F51A07093B002C4AB1 /* SyntaxMap.json */,
2A94FC781BE2256F00B454A8 /* cot */,
2A9871A819AEABDB0081BADB /* KeyBindings */,
2A3A758D19E77C84001DAB88 /* Syntaxes */,
2A7846DA18FE035E006BDF00 /* Themes */,
@ -1373,6 +1402,7 @@
2A6F0D531B5500E100C2D03C /* Check Pods Manifest.lock */,
2A5DBE5F1B5D30A500F8CB13 /* Build Syntax Map */,
2A6F0D541B5500E100C2D03C /* Resources */,
2A94FC7A1BE225E900B454A8 /* Copy Command-Line Tools */,
2A6F0DA31B5500E100C2D03C /* Sources */,
2A6F0DFD1B5500E100C2D03C /* Frameworks */,
DCACFAE3957B360DC925F673 /* Embed Pods Frameworks */,
@ -1394,6 +1424,7 @@
360866EE86A144AF8F8698E8 /* Check Pods Manifest.lock */,
2A5DBE5E1B5D2E1300F8CB13 /* Build Syntax Map */,
8D15AC2B0486D014006FF6A4 /* Resources */,
2A94FC771BE2253500B454A8 /* Copy Command-Line Tools */,
8D15AC300486D014006FF6A4 /* Sources */,
8D15AC330486D014006FF6A4 /* Frameworks */,
C40FF196FC931BDEC94F689E /* Embed Pods Frameworks */,

243
CotEditor/cot Executable file
View File

@ -0,0 +1,243 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
cot
CotEditor
http://coteditor.com
Created by 1024jp on 2015-08-12.
------------------------------------------------------------------------------
© 2015 1024jp
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import print_function
import argparse
import fcntl
import os
import sys
from subprocess import Popen, PIPE
# meta data
__version__ = '2.2.0'
__description__ = 'command-line utility for CotEditor.'
# constants
APPLICATION_NAME = 'CotEditor'
# MARK: Bundle
def bundle_path():
"""Return application path if this script is bundled in an OS X application.
Returns:
path (str): Path to .app directory or None if not found.
"""
path = os.path.realpath(__file__)
# find '.app' extension
while path is not '/':
path = os.path.dirname(path)
_, extension = os.path.splitext(path)
if extension == '.app':
return path
return None
# MARK: OSA Script
def run_osascript(script):
"""Run osascript.
Args:
script (str): Osascript.
Returns:
result (str): Return value of the script.
"""
p = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE)
stdout, _ = p.communicate(script.encode())
return stdout.decode().rstrip('\n')
class ScriptableApplication(object):
"""OSA-Scriptable OS X application object.
"""
def __init__(self, name):
self.name = name
def tell(self, script):
"""Tell OSA command to the application.
Args:
script (str): OSA command
Returns:
result (str): Return value of the script.
"""
script = 'tell app "{}" to {}'.format(self.name, script)
return run_osascript(script)
def open(self, path):
"""Open given file path in the application.
Args:
path (str): Path to file.
"""
path = path.replace('"', '\\"')
self.tell('open POSIX file "{}"'.format(path))
def tell_document(self, script, index=1):
"""Tell OSA command to a document of the application.
Args:
script (str): OSA command
number (int): Index number of the document to handle.
Returns:
result (str): Return value of the script.
"""
return self.tell('tell document {} to {}'.format(index, script))
# MARK: Args Parse
def parse_args():
"""Parse command line arguments.
Returns:
Parsed args object.
"""
# create parser instance
parser = argparse.ArgumentParser(description=__description__)
# set positional argument
parser.add_argument('file',
type=argparse.FileType('rU'),
metavar='FILE',
nargs='?',
help="Path to file to open."
)
# set optional arguments
parser.add_argument('-v', '--version',
action='version',
version=__version__
)
parser.add_argument('-g', '--background',
action='store_true',
default=False,
help="Do not bring the application to the foreground."
)
parser.add_argument('-n', '--new',
action='store_true',
default=False,
help="Create a new blank document."
)
parser.add_argument('-l', '--line',
type=int,
help="Jump to specific line in opened document."
)
parser.add_argument('-c', '--column',
type=int,
help="Jump to specific column in opened document."
)
return parser.parse_args()
# MARK: - Main
def main(args, stdin):
app_identifier = bundle_path() or APPLICATION_NAME
app = ScriptableApplication(app_identifier)
# launch
if args.background:
app.tell('run')
else:
app.tell('activate')
# create document
if args.file:
# open file
path = os.path.abspath(args.file.name)
app.open(path)
elif stdin:
# new document with piped text
sanitized_stdin = stdin.replace('"', '\\"')
app.tell('make new document')
app.tell_document('set contents to "{}"'.format(sanitized_stdin))
app.tell_document('set range of selection to {0, 0}')
elif args.new:
# new blank document
app.tell('make new document')
if app.tell('number of documents') is '0':
return
# jump to location
if args.line is not None or args.column is not None:
contents = app.tell_document('contents')
lines = contents.splitlines(keepends=True)
# sanitize line number
line = args.line or 1 # line number starts with 1
if line == 0:
line = 1
elif line < 0: # negative line number counts from the last line
line_count = len(lines)
line = max(1, line_count - abs(line) + 1)
# count location of line head
location = 0
for a_line in lines[0:line - 1]:
location += len(a_line)
# sanitize and add column position
if args.column is not None:
last_line = lines[line - 1]
column = min(args.column, len(last_line))
location += column
# set selection range
app.tell_document('set range of selection to {{{}, 0}}'.format(
location))
# jump
app.tell_document('scroll to caret')
if __name__ == "__main__":
# parse arguments
args = parse_args()
# read piped text if exists
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) # non-block
try:
stdin = sys.stdin.read()
except:
stdin = None
main(args, stdin)