Skip to content

Instantly share code, notes, and snippets.

@fish2000
Last active February 26, 2020 00:19
Show Gist options
  • Save fish2000/e70c387d05fb17b00953a4dd8708fe39 to your computer and use it in GitHub Desktop.
Save fish2000/e70c387d05fb17b00953a4dd8708fe39 to your computer and use it in GitHub Desktop.
TextMate® Cap‘n’Proto™ bundle commands – not pretty but also thus far not unreliable
#!/usr/bin/env ruby
#
# The estutely aware will of course immediately recognize this hackjob as
# a copypasta from assorted TextMate Ruby bundle sources – I am in no way
# a Ruby programmer so feel free to completely rewrite all of it in a single
# terse statement, or derisively mock it or ignore it completely – don’t ever
# say I never gave you options doggie
#
# • Scope Selector = ‘source.capnp’
# • Semantic Class = ‘process.run.capnp’
# • Save Current Document →
# Input From Document, Format as Text →
# Show Output in New Window, Format as HTML →
# Place Caret After Output
#
require "#{ENV['TM_BUNDLE_SUPPORT']}/compile_capnp.rb"
filepath = ENV['TM_FILEPATH']
language = ENV['TM_CAPNP_LANGUAGE'] || 'c++'
compiler = ENV['TM_CAPNP_COMPILER'] || 'capnp'
TMI.compile_capnp(filepath, language, compiler)
#!/usr/bin/env ruby
# encoding: utf-8
require "#{ENV['TM_SUPPORT_PATH']}/lib/textmate"
require "#{ENV['TM_SUPPORT_PATH']}/lib/tm/executor"
require "#{ENV['TM_SUPPORT_PATH']}/lib/tm/save_current_document"
require "shellwords"
abort "Unsaved schema" unless ENV['TM_FILEPATH']
module TMI
def self.compile_capnp(filepath, language, compiler)
incpath = "#{ENV['TM_CAPNP_INCLUDE_PATH']}".split(":")
.map { |frag| Shellwords.escape(frag) }
.select { |frag| File.directory?(frag) }
.map { |frag| "-I" + frag }
.join(' ')
directory = File.dirname(filepath)
dirname = File.basename(directory)
outname = "#{dirname}-#{language}"
directory = File.join(directory, outname)
# dirname = Shellwords.escape(dirname)
if not File.directory?(directory)
Dir.mkdir directory
end
# The “--src-prefix” command, contrary to what you might assume, is how you
# tell the compiler to trim certain “prefix” directory patterns off of the
# final output-file destination that `capnp` decides to use (in what feels
# like a weirdly circuitous and over-complicated heuristic, but what do I
# know anyway I am just a fucking guy with ideas about what names to use for
# some things)
subcommand = "compile #{incpath} --verbose --src-prefix=#{dirname} -o#{language}:#{directory}"
identifier = "id --verbose"
TextMate::Executor.make_project_master_current_document
args = Shellwords.split(compiler) << Shellwords.split(subcommand) << filepath
TextMate::Executor.run(args, :version_args => ["--version"],
:version_regex => /Cap'n Proto version (?:\(?([^\(\)]*)\)?)$(?:\n.*)*/i,
:version_replace => 'Cap‘n’Proto version \1',
:use_hashbang => false,
:verb => "Transpiling Cap‘n’Proto schema to #{language}:",
:noun => "#{ ENV['TM_DISPLAYNAME'] }") do |str, type|
if type == :err
str = TextMate::Executor.send :linkify_file_references, str, dirname
str = %Q[
<!-- <h3>Executed “#{ args.flatten }”…</h3> -->
<span class="stderr">#{ str }</span>]
else
str = %Q[
<!-- <h3>Executed “#{ args.flatten }”…</h3> -->
<span class="stderr">
Successfully compiled Cap‘n’p schema #{ ENV['TM_DISPLAYNAME'] }<br>
Files written to #{ dirname }<br>
#{ str }</span>]
end
end
end
end
#!/usr/bin/env bash
#
# Paste this into a new TextMate bundle command
# And then (if you like) set it up such that:
#
# • Scope Selector = ‘source.capnp’
# • Key Equivalent = Whatever dogg cut loose it’s your oyster, I mean
# I use ⌥⌘I like but so why would that matter to others
# • Semantic Class = ‘process.run.capnpc’ (note the “c” there at the end)
# • Save Current Document →
# Input From Selection, Format as Text →
# Output Replaces Selection, Format as Text →
# Caret Placement Selects Output →
# …and the ‘Scroll For New Output’ is another chance for you to go
# a little crazy and show everyone your creative side with whatever
# you choose
#
function error () { printf >&2 "%s\n\nPlease see README.md for help on living your life.\n" "$1"; exit 1; }
STDIN=$(cat -)
final=${STDIN:${#STDIN}-1:1}
if which -s capnpc; then
out=$(capnpc -i 2> /dev/null) # Generate the new ID;
echo -n ${out%%*( )} # Write it out (trimming trailing whitespace);
if [[ $final == ";" ]]; then
echo -n ";" # and i…f the our selection ended in a semicolon,
fi # throw one of those out on the end there too.
fi
# else
# error "[ERROR] couldn’t find the \`capnpc\` CLT – is it on the TextMate PATH??"
# echo ""
# echo "«${STDIN:${#STDIN}-1:1}»"
# N.B. This is a *relative import* from somewhere on Cap‘n’P’s
# include path – apparently “import "/capnp/c++.capnp"” (with
# the misleadingly absolute-path-looking slash prefix) is how
# Cap‘n’P says something like e.g. “#include <capnp/c++.capnp>”
# …I just spent like 45 minutes trying to figure out why it
# didn’t work with only `using Cpp = import "c++.capnp"` and
# this comment will now stand here until the end of time to
# commemorate this completely irritating waste of my time. Yes.
using Cpp = import "/capnp/c++.capnp";
@0xf7b1168c865460f0;
$Cpp.namespace("tmi::bundleshop");
using String = Text;
using Email = Text;
using Path = Text;
using URL = Text;
struct Time {
union {
undefined @0 :Void;
timestamp @1 :UInt64;
formatted @2 :String;
}
}
struct UUID {
union {
undefined @0 :Void;
unbounded @1 :Data;
bytearray @2 :List(UInt8);
longwords :group {
hi @3 :UInt64;
lo @4 :UInt64;
}
}
}
struct MapT(K, T) {
entries @0 :List(Pair);
struct Pair {
key @0 :K;
value @1 :T;
nextHint :union {
undefined @2 :Void;
reference @3 :AnyPointer;
}
}
}
using StringMap = MapT(String, String);
using KeychainT = StringMap;
struct Source {
name @0 :String;
identifier @1 :String;
url @2 :URL;
path @3 :Path;
rank @4 :Int32;
disabled @5 :Bool;
keychain @6 :KeychainT;
pathUpdated @7 :Time;
updateAvailable @8 :Bool; # née “needs_update”
}
struct Bundle {
uuid @0 :UUID;
origin @1 :String;
name @2 :String;
category @3 :String;
htmlURL @4 :URL;
description @5 :String;
contactName @6 :String;
contactEmail @7 :Email;
url @8 :URL;
urlUpdated @9 :Time;
path @10 :Path;
pathUpdated @11 :Time;
rank @12 :Int32;
size @13 :Int32;
requisite @14 :Bool;
default @15 :Bool;
dependency @16 :Bool;
installed @17 :Bool;
updateAvailable @18 :Bool; # née “has_update”
keychain @19 :KeychainT;
grammars @20 :List(Grammar);
dependants @21 :List(Bundle);
source @22 :Source;
}
struct Grammar {
uuid @0 :UUID;
name @2 :String;
scope @1 :String;
fileTypes @3 :List(String);
modeLine @4 :String;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment