Skip to content

Instantly share code, notes, and snippets.

@keplersj
Last active October 18, 2024 13:12
Show Gist options
  • Save keplersj/8464003 to your computer and use it in GitHub Desktop.
Save keplersj/8464003 to your computer and use it in GitHub Desktop.
Groovy syntax handling for Xcode
// groovy lang specs
(
/****************************************************************************/
// MARK: Groovydoc
/****************************************************************************/
{
Identifier = "xcode.lang.groovy.comment.groovydoc.keyword";
Syntax = {
StartChars = "@";
Chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Words = (
"@author",
"@beaninfo",
"@deprecated",
"@docRoot",
"@exception",
"@inheritDoc",
"@link",
"@linkplain",
"@param",
"@return",
"@see",
"@serial",
"@serialData",
"@serialField",
"@since",
"@throws",
"@value",
"@version"
);
Type = "xcode.syntax.comment.doc.keyword";
};
},
{
Identifier = "xcode.lang.groovy.comment.groovydoc";
Syntax = {
Start = "/**";
End = "*/";
Foldable = YES;
IncludeRules = ( "xcode.lang.groovy.comment.groovydoc.keyword", "xcode.lang.url", "xcode.lang.url.mail", "xcode.lang.comment.mark" );
Type = "xcode.syntax.comment.doc";
};
},
{
// Handle the degenerate case of '/**/' comments. They look like the start of a groovyDoc comment, but they aren't.
Identifier = "xcode.lang.groovy.comment.degenerate";
Syntax = {
StartChars = "/";
Chars = "/*";
Words = ( "/**/" );
Type = "xcode.syntax.comment";
};
},
/****************************************************************************/
// MARK: Groovy Keywords
/****************************************************************************/
{
Identifier = "xcode.lang.groovy.identifier";
Syntax = {
StartChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
Chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
Words = (
"abstract",
"as",
"assert",
"break",
"case",
"catch",
"class",
"const",
"continue",
"def",
"default",
"do",
"else",
"extends",
"false",
"final",
"finally",
"for",
"goto",
"if",
"implements",
"import",
"instanceof",
"interface",
"native",
"new",
"null",
"package",
"println",
"private",
"property",
"protected",
"public",
"return",
"static",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
"true",
"try",
"while"
);
AltKeywords = (
"any",
"boolean",
"byte",
"char",
"double",
"float",
"int",
"long",
"short",
"void"
);
Type = "xcode.syntax.keyword";
AltType = "xcode.syntax.identifier"; // non-keywords are identifiers
};
},
/****************************************************************************/
// MARK: Groovy Top Level
/****************************************************************************/
{
Identifier = "xcode.lang.groovy";
Description = "groovy Coloring";
BasedOn = "xcode.lang.simpleColoring";
IncludeInMenu = YES;
UsesCLikeIndentation = YES;
Name = "groovy";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
IncludeRules = ( // processed in order
"xcode.lang.groovy.package",
"xcode.lang.groovy.import",
"xcode.lang.groovy.class",
"xcode.lang.groovy.interface",
);
Type = "xcode.syntax.plain";
};
},
/****************************************************************************/
// MARK: Lexers
/****************************************************************************/
// The following rule returns tokens to the other rules
{
Identifier = "xcode.lang.groovy.lexer";
Syntax = {
IncludeRules = (
"xcode.lang.groovy.comment.degenerate",
"xcode.lang.groovy.comment.groovydoc",
"xcode.lang.comment",
"xcode.lang.comment.singleline",
"xcode.lang.string",
"xcode.lang.character",
"xcode.lang.completionplaceholder",
"xcode.lang.groovy.identifier",
"xcode.lang.number",
);
};
},
/****************************************************************************/
// MARK: Classes/Interfaces
/****************************************************************************/
{
Identifier = "xcode.lang.groovy.class";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.class.declarator",
"xcode.lang.groovy.class.block",
);
Type = "xcode.syntax.definition.groovy.class";
};
},
{
Identifier = "xcode.lang.groovy.innerclass";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.method.declarator",
"xcode.lang.groovy.class.block",
);
Type = "xcode.syntax.definition.groovy.class";
};
},
{
Identifier = "xcode.lang.groovy.class.declarator";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"class",
"xcode.lang.groovy.class.name",
"xcode.lang.groovy.class.declarator.extends?",
"xcode.lang.groovy.class.declarator.implements?",
);
};
},
{
Identifier = "xcode.lang.groovy.class.name";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.identifier",
"xcode.lang.groovy.templateclause?",
);
Type = "xcode.syntax.name.partial";
};
},
{
Identifier = "xcode.lang.groovy.class.declarator.extends";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"extends",
"xcode.lang.groovy.qualifiedname",
);
};
},
{
Identifier = "xcode.lang.groovy.class.declarator.implements";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"implements",
"xcode.lang.groovy.qualifiedname",
"xcode.lang.groovy.classnames.list*",
);
};
},
{
Identifier = "xcode.lang.groovy.interface";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Foldable = YES;
Recursive = YES;
Rules = (
"xcode.lang.groovy.interface.declarator",
"xcode.lang.groovy.interface.block",
);
Type = "xcode.syntax.declaration.groovy.interface";
};
},
{
Identifier = "xcode.lang.groovy.interface.declarator";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"interface",
"xcode.lang.groovy.class.name",
"xcode.lang.groovy.interface.declarator.extends?",
);
};
},
{
Identifier = "xcode.lang.groovy.interface.declarator.extends";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"extends",
"xcode.lang.groovy.qualifiedname",
"xcode.lang.groovy.classnames.list*",
);
};
},
{
Identifier = "xcode.lang.groovy.classnames.list";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
",",
"xcode.lang.groovy.qualifiedname",
);
};
},
{
Identifier = "xcode.lang.groovy.qualifiedname";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.identifier",
"xcode.lang.groovy.qualifiedname.more*",
"xcode.lang.groovy.templateclause?",
);
};
},
{
Identifier = "xcode.lang.groovy.templateclause";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "<";
End = ">";
Recursive = YES;
};
},
{
Identifier = "xcode.lang.groovy.package.qualifiedname";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.identifier",
"xcode.lang.groovy.import.qualifiedname.more*",
);
Type = "xcode.syntax.groovy.package";
};
},
{
Identifier = "xcode.lang.groovy.import.qualifiedname";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.identifier",
"xcode.lang.groovy.import.qualifiedname.more*",
"*?",
);
Type = "xcode.syntax.groovy.import";
};
},
{
Identifier = "xcode.lang.groovy.import.qualifiedname.more";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
".",
"xcode.lang.groovy.identifier?",
);
};
},
{
Identifier = "xcode.lang.groovy.qualifiedname.more";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
".",
"xcode.lang.groovy.identifier",
);
};
},
{
Identifier = "xcode.lang.groovy.package";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"package",
"xcode.lang.groovy.package.qualifiedname",
";",
);
};
},
{
Identifier = "xcode.lang.groovy.import";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"import",
"xcode.lang.groovy.import.qualifiedname",
";",
);
};
},
/****************************************************************************/
// MARK: Methods
/****************************************************************************/
{
Identifier = "xcode.lang.groovy.method.declaration";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.method.declarator",
"xcode.lang.groovy.method.declarator.throws?",
";",
);
Type = "xcode.syntax.declaration.method";
};
},
{
Identifier = "xcode.lang.groovy.method.definition";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.method.declarator",
"xcode.lang.groovy.method.declarator.throws?",
"xcode.lang.groovy.block",
);
Type = "xcode.syntax.definition.method";
};
},
{
Identifier = "xcode.lang.groovy.method.declarator";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"xcode.lang.groovy.identifier",
"xcode.lang.groovy.parenexpr",
);
Type = "xcode.syntax.name.partial";
};
},
{
Identifier = "xcode.lang.groovy.method.declarator.throws";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Rules = (
"throws",
"xcode.lang.groovy.qualifiedname",
"xcode.lang.groovy.classnames.list*",
);
};
},
/****************************************************************************/
// MARK: Blocks
/****************************************************************************/
{
Identifier = "xcode.lang.groovy.class.block";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "{";
End = "}";
Foldable = YES;
Recursive = YES;
IncludeRules = (
"xcode.lang.groovy.class",
"xcode.lang.groovy.interface",
"xcode.lang.groovy.method.definition",
"xcode.lang.groovy.method.declaration",
"xcode.lang.groovy.bracketexpr",
"xcode.lang.groovy.parenexpr",
"xcode.lang.groovy.initializer",
);
};
},
{
Identifier = "xcode.lang.groovy.interface.block";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "{";
End = "}";
Foldable = YES;
Recursive = YES;
IncludeRules = (
"xcode.lang.groovy.interface",
"xcode.lang.groovy.method.definition",
"xcode.lang.groovy.method.declaration",
"xcode.lang.groovy.bracketexpr",
"xcode.lang.groovy.parenexpr",
"xcode.lang.groovy.initializer",
);
};
},
{
Identifier = "xcode.lang.groovy.block";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "{";
End = "}";
Foldable = YES;
Recursive = YES;
IncludeRules = (
"xcode.lang.groovy.class",
"xcode.lang.groovy.interface",
"xcode.lang.groovy.bracketexpr",
"xcode.lang.groovy.parenexpr",
);
};
},
{
Identifier = "xcode.lang.groovy.parenexpr";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "(";
End = ")";
Recursive = YES;
IncludeRules = (
"xcode.lang.groovy.bracketexpr",
"xcode.lang.groovy.block",
"xcode.lang.groovy.innerclass",
);
};
},
{
Identifier = "xcode.lang.groovy.bracketexpr";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "[";
End = "]";
Recursive = YES;
IncludeRules = (
"xcode.lang.groovy.parenexpr",
);
};
},
{
Identifier = "xcode.lang.groovy.initializer";
Syntax = {
Tokenizer = "xcode.lang.groovy.lexer";
Start = "=";
End = ";";
Recursive = NO;
IncludeRules = (
"xcode.lang.groovy.parenexpr",
"xcode.lang.groovy.bracketexpr",
);
};
},
)
#!/usr/bin/env bash
set -e
# Assumes Xcode 4+.
XCODE_MAJOR_VERSION=`xcodebuild -version | awk 'NR == 1 {print substr($2,1,1)}'`
if [ "$XCODE_MAJOR_VERSION" -lt "4" ]; then
echo "Xcode 4+ not found."
exit 1
fi
# DVTFOUNDATION_DIR may vary depending on Xcode setup. If Xcode has installed
# the `xcode-select` command, it will be determined automatically. Otherwise,
# change it to reflect your current Xcode setup. Find suitable path with e.g.:
#
# find / -type f -name 'DVTFoundation.xcplugindata' 2> /dev/null
#
# Example of DVTFOUNDATION_DIR's from "default" Xcode 4+ setups;
#
# Xcode 4.1: /Developer/Library/PrivateFrameworks/DVTFoundation.framework/Versions/A/Resources/
# Xcode 4.3: /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/
# Defaults to Xcode 4.3's DVTFOUNDATION_DIR. Path is modified automatically if
# `xcode-select` command is available, as mentioned above.
DVTFOUNDATION_DIR="/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/"
if type "xcode-select" > /dev/null; then
DVTFOUNDATION_DIR=`xcode-select --print-path`
DVTFOUNDATION_DIR+="/.."
FRAMEWORK_NAME="DVTFoundation.framework"
DVTFOUNDATION_DIR=`find $DVTFOUNDATION_DIR -name $FRAMEWORK_NAME -print`
DVTFOUNDATION_DIR+="/Versions/A/Resources"
fi
PLUGINDATA_FILE="DVTFoundation.xcplugindata"
PLISTBUDDY=/usr/libexec/PlistBuddy
PLIST_FILE=tmp.plist
# Provide means of deleting the Go entry from the plugindata file.
if [ "$1" = "--delete-entry" ]; then
echo "Removing Go language specification entry."
$PLISTBUDDY -c "Delete :plug-in:extensions:Xcode.SourceCodeLanguage.Groovy" $DVTFOUNDATION_DIR/$PLUGINDATA_FILE
echo "Run 'sudo rm -rf /var/folders/*' and restart Xcode to update change immediately."
exit 0
fi
GROOVY_LANG_ENTRY="
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
<key>Xcode.SourceCodeLanguage.Groovy</key>
<dict>
<key>conformsTo</key>
<array>
<dict>
<key>identifier</key>
<string>Xcode.SourceCodeLanguage.Generic</string>
</dict>
</array>
<key>documentationAbbreviation</key>
<string>groovy</string>
<key>fileDataType</key>
<array>
<dict>
<key>identifier</key>
<string>com.apple.xcode.groovy-source</string>
</dict>
</array>
<key>id</key>
<string>Xcode.SourceCodeLanguage.Groovy</string>
<key>languageName</key>
<string>Groovy</string>
<key>languageSpecification</key>
<string>xcode.lang.groovy</string>
<key>name</key>
<string>Groovy</string>
<key>point</key>
<string>Xcode.SourceCodeLanguage</string>
<key>version</key>
<string>0</string>
</dict>
</dict>
</plist>
"
echo "Backing up plugindata file (copied to $PLUGINDATA_FILE.bak)."
cp $DVTFOUNDATION_DIR/$PLUGINDATA_FILE $DVTFOUNDATION_DIR/$PLUGINDATA_FILE.bak
echo "Adding Go language specification entry."
echo $GROOVY_LANG_ENTRY > $PLIST_FILE
$PLISTBUDDY -c "Merge $PLIST_FILE plug-in:extensions" $DVTFOUNDATION_DIR/$PLUGINDATA_FILE
rm -f $PLIST_FILE
echo "Installing Groovy language specification file for Xcode."
cp Groovy.xclangspec $DVTFOUNDATION_DIR
echo "Run 'sudo rm -rf /var/folders/*' and restart Xcode to update change immediately."
echo "Syntax coloring must be manually selected from the Editor - Syntax Coloring menu in Xcode."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment