ink-docstrap
Version:
[](https://nodei.co/npm/ink-docstrap/)
414 lines (354 loc) • 13.4 kB
JavaScript
(function(sunlight, undefined){
if (sunlight === undefined || sunlight["registerLanguage"] === undefined) {
throw "Include sunlight.js before including language files";
}
sunlight.registerLanguage("vb", {
//http://msdn.microsoft.com/en-us/library/dd409611.aspx
keywords: [
"AddHandler","AddressOf","Alias","AndAlso","And","As","Boolean","ByRef","Byte","ByVal","Call","Case","Catch",
"CBool","CByte","CChar","CDate","CDbl","CDec","Char","CInt","Class","CLng","CObj","Const","Continue","CSByte",
"CShort","CSng","CStr","CType","CUInt","CULng","CUShort","Date","Decimal","Declare","Default","Delegate","Dim",
"DirectCast","Double","Do","Each","ElseIf","Else","EndStatement","EndIf","End","Enum","Erase","Error","Event",
"Exit","False","Finally","ForEach","For","Friend","Function","GetType","GetXMLNamespace","Get","Global","GoSub",
"GoTo","Handles","If","Implements","Imports","Inherits","Integer","Interface","In","IsNot","Is","Let","Lib",
"Like","Long","Loop","Me","Module","Mod","MustInherit","MustOverride","MyBase","MyClass","Namespace","Narrowing",
"New","Next","Nothing","NotInheritable","NotOverridable","Not","Object","Of","On","Operator","Option","Optional",
"OrElse","Or","Out","Overloads","Overridable","Overrides","ParamArray","Partial","Private","Property","Protected",
"Public","RaiseEvent","ReadOnly","ReDim","RemoveHandler","Resume","Return","SByte","Select","Set","Shadows",
"Shared","Short","Single","Static","Step","Stop","String","Structure","Sub","SyncLock","Then","Throw","To","True",
"TryCast","Try","TypeOf","UInteger","ULong","UShort","Using","Variant","Wend","When","While","Widening",
"WithEvents","With","WriteOnly","Xor"
],
customTokens: {
reservedWord: {
values: [
"Aggregate","Ansi","Assembly","Auto","Binary","Compare","Custom","Distinct","Equals","Explicit","From","Group By","Group Join",
"Into","IsFalse","IsTrue","Join","Key","Mid","Off","Order By","Preserve","Skip","Skip While","Strict","Take While","Take","Text",
"Unicode","Until","Where"
],
boundary: "\\b"
}
},
scopes: {
string: [ ["\"", "\"", sunlight.util.escapeSequences.concat(["\\\""])] ],
comment: [ ["'", "\n", null, true], ["REM", "\n", null, true] ],
compilerDirective: [ ["#", "\n", null, true] ]
},
customParseRules: [
//xml doc comments (copypasted from c# file)
function(context) {
var metaName = "xmlDocCommentMeta",
contentName = "xmlDocCommentContent",
tokens,
peek,
current;
if (context.reader.current() !== "'" || context.reader.peek(2) !== "''") {
return null;
}
tokens = [context.createToken(metaName, "'''", context.reader.getLine(), context.reader.getColumn())];
current = { line: 0, column: 0, value: "", name: null };
context.reader.read(2);
while ((peek = context.reader.peek()) !== context.reader.EOF) {
if (peek === "<" && current.name !== metaName) {
//push the current token
if (current.value !== "") {
tokens.push(context.createToken(current.name, current.value, current.line, current.column));
}
//amd create a token for the tag
current.line = context.reader.getLine();
current.column = context.reader.getColumn();
current.name = metaName;
current.value = context.reader.read();
continue;
}
if (peek === ">" && current.name === metaName) {
//close the tag
current.value += context.reader.read();
tokens.push(context.createToken(current.name, current.value, current.line, current.column));
current.name = null;
current.value = "";
continue;
}
if (peek === "\n") {
break;
}
if (current.name === null) {
current.name = contentName;
current.line = context.reader.getLine();
current.column = context.reader.getColumn();
}
current.value += context.reader.read();
}
if (current.name === contentName) {
tokens.push(context.createToken(current.name, current.value, current.line, current.column));
}
return tokens.length > 0 ? tokens : null;
},
//keyword escaping: e.g. "[In]"
function(context) {
var line = context.reader.getLine(),
column = context.reader.getColumn(),
next,
value = "[";
if (context.reader.current() !== "[") {
return null;
}
//read until "]"
next = context.reader.read();
while (next !== context.reader.EOF) {
value += next;
if (next === "]") {
break;
}
next = context.reader.read();
}
return context.createToken("escapedKeyword", value, line, column);
},
//handles New/GetType contextual keywords
//e.g. prevents "New" in "SomeClass.New()" from being a keyword
function() {
var hashmap = sunlight.util.createHashMap(["New", "GetType"], "\\b");
return function(context) {
var token = sunlight.util.matchWord(context, hashmap, "keyword"),
prevToken;
if (!token) {
return null;
}
//if the previous non-ws token is the "." operator then it's an ident, not a keyword
//or if it's a subprocedure name
prevToken = sunlight.util.getPreviousNonWsToken(context.getAllTokens(), context.count());
if (prevToken && ((prevToken.name === "operator" && prevToken.value === ".") || (prevToken.name === "keyword" && prevToken.value === "Sub"))) {
token.name = "ident";
}
return token;
};
}()
],
identFirstLetter: /[A-Za-z_]/,
identAfterFirstLetter: /\w/,
namedIdentRules: {
custom: [
//attributes (copypasted (mostly) from c# file)
function(context) {
//if the next token is an equals sign, this is a named parameter (or something else not inside of an attribute)
var token,
nextToken = sunlight.util.getNextNonWsToken(context.tokens, context.index),
index = context.index,
bracketCount = [0, 0],
indexOfLastBracket = -1;
if (nextToken && nextToken.name === "operator" && (nextToken.value === "=" || nextToken.value === ".")) {
return false;
}
//we need to verify that we're between <>
//first, verify that we're inside an opening bracket
while ((token = context.tokens[--index]) !== undefined) {
if (token.name === "operator") {
if (token.value === "<") {
bracketCount[0]++;
} else if (token.value === ">") {
bracketCount[1]++;
}
} else if (token.name === "keyword" && sunlight.util.contains(["Public", "Class", "Protected", "Private", "Friend"], token.value)) {
//early exits
break;
}
}
if (bracketCount[0] === 0 || bracketCount[0] === bracketCount[1]) {
//if no brackets were found OR...
//all the found brackets are closed, so this ident is actually outside of the brackets
//duh.
return false;
}
//next, verify we're inside a closing bracket
index = context.index;
while ((token = context.tokens[++index]) !== undefined) {
if (token.name === "operator") {
if (token.value === "<") {
bracketCount[0]++;
} else if (token.value === ">") {
indexOfLastBracket = index;
bracketCount[1]++;
}
} else if (token.name === "keyword" && sunlight.util.contains(["Public", "Class", "Protected", "Private", "Friend", "ByVal"], token.value)) {
//early exits
break;
}
}
if (indexOfLastBracket < 0 || bracketCount[0] !== bracketCount[1]) {
return false;
}
//next token after the last closing bracket should be either a keyword or an ident
token = sunlight.util.getNextNonWsToken(context.tokens, indexOfLastBracket);
if (token && (token.name === "keyword" || token.name === "ident")) {
return true;
}
return false;
},
//casts
function(context) {
var token,
index = context.index,
parenCount = 1;
//look backward for CType, DirectCast or TryCast
//could be goofy because of nesting, so we need to count parens
//verify that this ident is the second argument to the function call
if (!sunlight.util.createProceduralRule(context.index - 1, -1, [{ token: "punctuation", values: [","] }, sunlight.util.whitespace])(context.tokens)) {
return false;
}
while (token = context.tokens[--index]) {
if (token.name === "punctuation" && token.value === "(") {
parenCount--;
if (parenCount === 0) {
//we found the opening paren, so if the previous token is one of the cast functions, this is a cast
token = context.tokens[--index];
if (token && token.name === "keyword" && sunlight.util.contains(["CType", "DirectCast", "TryCast"], token.value)) {
return true;
}
return false;
}
} else if (token.name === "punctuation" && token.value === ")") {
parenCount++;
}
}
return false;
},
//implemented interfaces
function(context) {
var prevToken = sunlight.util.getPreviousNonWsToken(context.tokens, context.index),
token,
index = context.index;
//if previous non-ws token was a "." then it's an implemented method
if (prevToken && prevToken.name === "operator" && prevToken.value === ".") {
return false;
}
//look backward for "Implements"
while (token = context.tokens[--index]) {
if (token.name === "keyword") {
switch (token.value) {
case "Class":
case "New":
break;
case "Implements":
return true;
default:
return false;
}
} else if (token.name === "default" && token.value.indexOf(sunlight.util.eol) >= 0) {
//apparently they must be on the same line...?
return false;
}
}
return false;
},
//type constraints: "As {ident, ident, ...}"
function(context) {
//look backward for "As {"
var token,
index = context.index,
isValid = function() {
while (token = context.tokens[--index]) {
if (token.name === "punctuation") {
switch (token.value) {
case "(":
case ")":
return false;
case "{":
//previous non-ws token should be keyword "As"
token = sunlight.util.getPreviousNonWsToken(context.tokens, index);
if (!token || token.name !== "keyword" || token.value !== "As") {
return false;
}
return true;
}
} else if (token.name === "keyword" && sunlight.util.contains(["Public", "Protected", "Friend", "Private", "End"], token.value)) {
return false;
}
}
return false;
}();
if (!isValid) {
return false;
}
//"}" before )
index = context.index;
while (token = context.tokens[++index]) {
if (token.name === "punctuation") {
switch (token.value) {
case "}":
return true;
case "(":
case ")":
case ";":
return false;
}
}
}
return false;
}
],
follows: [
[{ token: "keyword", values: ["Of", "As", "Class", "Implements", "Inherits", "New", "AddressOf", "Interface", "Structure", "Event", "Module", "Enum"] }, { token: "default" }],
[{ token: "keyword", values: ["GetType"] }, sunlight.util.whitespace, { token: "punctuation", values: ["("] }, sunlight.util.whitespace]
]
},
numberParser: function(context) {
var current = context.reader.current(),
number,
line = context.reader.getLine(),
column = context.reader.getColumn(),
allowDecimal = true,
peek;
if (current === "&" && /[Hh][A-Fa-f0-9]/.test(context.reader.peek(2))) {
number = current + context.reader.read(2);
allowDecimal = false;
} else if (/\d/.test(current)) {
number = current;
} else {
if (current !== "." || !/\d/.test(context.reader.peek())) {
return null;
}
number = current + context.reader.read();
allowDecimal = false;
}
while ((peek = context.reader.peek()) !== context.reader.EOF) {
if (!/[A-Za-z0-9]/.test(peek)) {
if (peek === "." && allowDecimal && /\d$/.test(context.reader.peek(2))) {
number += context.reader.read();
allowDecimal = false;
continue;
}
break;
}
number += context.reader.read();
}
return context.createToken("number", number, line, column);
},
operators: [
".",
"<>",
"=",
"&=",
"&",
"*=",
"*",
"/=",
"/",
"\\=",
"\\",
"^=",
"^",
"+=",
"+",
"-=",
"-",
">>=",
">>",
"<<=",
"<<",
"<=",
">=",
"<",
">"
]
});
}(this["Sunlight"]));