lasso-require
Version:
Lasso.js plugin to support Node.js style module require in the browser
366 lines (324 loc) • 12.3 kB
JavaScript
(function() {
var Emitter, EmitterMixin, Grammar, Grim, Injections, OnigRegExp, Pattern, Rule, ScopeSelector, TokenizeLineResult, fs, path, _;
path = require('path');
_ = require('underscore-plus');
fs = require('fs-plus');
OnigRegExp = require('oniguruma').OnigRegExp;
Emitter = require('event-kit').Emitter;
Grim = require('grim');
Injections = require('./injections');
Pattern = require('./pattern');
Rule = require('./rule');
ScopeSelector = require('./scope-selector');
module.exports = Grammar = (function() {
Grammar.prototype.registration = null;
function Grammar(registry, options) {
var firstLineMatch, injectionSelector, injections, patterns, repository;
this.registry = registry;
if (options == null) {
options = {};
}
this.name = options.name, this.fileTypes = options.fileTypes, this.scopeName = options.scopeName, this.foldingStopMarker = options.foldingStopMarker, this.maxTokensPerLine = options.maxTokensPerLine;
injections = options.injections, injectionSelector = options.injectionSelector, patterns = options.patterns, repository = options.repository, firstLineMatch = options.firstLineMatch;
this.emitter = new Emitter;
this.repository = null;
this.initialRule = null;
this.rawPatterns = patterns;
this.rawRepository = repository;
if (injectionSelector != null) {
this.injectionSelector = new ScopeSelector(injectionSelector);
} else {
this.injectionSelector = null;
}
if (firstLineMatch) {
this.firstLineRegex = new OnigRegExp(firstLineMatch);
} else {
this.firstLineRegex = null;
}
if (this.fileTypes == null) {
this.fileTypes = [];
}
this.includedGrammarScopes = [];
this.injections = new Injections(this, injections);
}
/*
Section: Event Subscription
*/
Grammar.prototype.onDidUpdate = function(callback) {
return this.emitter.on('did-update', callback);
};
/*
Section: Tokenizing
*/
Grammar.prototype.tokenizeLines = function(text) {
var line, lineNumber, lines, ruleStack, scopes, tags, _i, _len, _ref, _results;
lines = text.split('\n');
ruleStack = null;
scopes = [];
_results = [];
for (lineNumber = _i = 0, _len = lines.length; _i < _len; lineNumber = ++_i) {
line = lines[lineNumber];
_ref = this.tokenizeLine(line, ruleStack, lineNumber === 0), tags = _ref.tags, ruleStack = _ref.ruleStack;
_results.push(this.registry.decodeTokens(line, tags, scopes));
}
return _results;
};
Grammar.prototype.tokenizeLine = function(line, ruleStack, firstLine, compatibilityMode) {
var contentScopeName, initialRule, initialRuleStackLength, lastRule, lastSymbol, match, nextTags, openScopeTags, penultimateRule, popStack, position, previousPosition, previousRuleStackLength, rule, scopeName, tag, tags, tagsEnd, tagsStart, tokenCount, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
if (firstLine == null) {
firstLine = false;
}
if (compatibilityMode == null) {
compatibilityMode = true;
}
tags = [];
if (ruleStack != null) {
ruleStack = ruleStack.slice();
if (compatibilityMode) {
openScopeTags = [];
for (_i = 0, _len = ruleStack.length; _i < _len; _i++) {
_ref = ruleStack[_i], scopeName = _ref.scopeName, contentScopeName = _ref.contentScopeName;
if (scopeName) {
openScopeTags.push(this.registry.startIdForScope(scopeName));
}
if (contentScopeName) {
openScopeTags.push(this.registry.startIdForScope(contentScopeName));
}
}
}
} else {
if (compatibilityMode) {
openScopeTags = [];
}
initialRule = this.getInitialRule();
scopeName = initialRule.scopeName, contentScopeName = initialRule.contentScopeName;
ruleStack = [
{
rule: initialRule,
scopeName: scopeName,
contentScopeName: contentScopeName
}
];
if (scopeName) {
tags.push(this.startIdForScope(initialRule.scopeName));
}
if (contentScopeName) {
tags.push(this.startIdForScope(initialRule.contentScopeName));
}
}
initialRuleStackLength = ruleStack.length;
position = 0;
tokenCount = 0;
while (true) {
previousRuleStackLength = ruleStack.length;
previousPosition = position;
if (position === line.length + 1) {
break;
}
if (tokenCount >= this.getMaxTokensPerLine() - 1) {
tags.push(line.length - position);
while (ruleStack.length > initialRuleStackLength) {
_ref1 = ruleStack.pop(), scopeName = _ref1.scopeName, contentScopeName = _ref1.contentScopeName;
if (contentScopeName) {
tags.push(this.endIdForScope(contentScopeName));
}
if (scopeName) {
tags.push(this.endIdForScope(scopeName));
}
}
break;
}
if (match = _.last(ruleStack).rule.getNextTags(ruleStack, line, position, firstLine)) {
nextTags = match.nextTags, tagsStart = match.tagsStart, tagsEnd = match.tagsEnd;
if (position < tagsStart) {
tags.push(tagsStart - position);
tokenCount++;
}
tags.push.apply(tags, nextTags);
for (_j = 0, _len1 = nextTags.length; _j < _len1; _j++) {
tag = nextTags[_j];
if (tag >= 0) {
tokenCount++;
}
}
position = tagsEnd;
} else {
if (position < line.length || line.length === 0) {
tags.push(line.length - position);
}
break;
}
if (position === previousPosition) {
if (ruleStack.length === previousRuleStackLength) {
console.error("Popping rule because it loops at column " + position + " of line '" + line + "'", _.clone(ruleStack));
if (ruleStack.length > 1) {
_ref2 = ruleStack.pop(), scopeName = _ref2.scopeName, contentScopeName = _ref2.contentScopeName;
if (contentScopeName) {
tags.push(this.endIdForScope(contentScopeName));
}
if (scopeName) {
tags.push(this.endIdForScope(scopeName));
}
} else {
if (position < line.length || (line.length === 0 && tags.length === 0)) {
tags.push(line.length - position);
}
break;
}
} else if (ruleStack.length > previousRuleStackLength) {
_ref3 = ruleStack.slice(-2), (_ref4 = _ref3[0], penultimateRule = _ref4.rule), (_ref5 = _ref3[1], lastRule = _ref5.rule);
if ((lastRule != null) && lastRule === penultimateRule) {
popStack = true;
}
if (((lastRule != null ? lastRule.scopeName : void 0) != null) && penultimateRule.scopeName === lastRule.scopeName) {
popStack = true;
}
if (popStack) {
ruleStack.pop();
lastSymbol = _.last(tags);
if (lastSymbol < 0 && lastSymbol === this.startIdForScope(lastRule.scopeName)) {
tags.pop();
}
tags.push(line.length - position);
break;
}
}
}
}
for (_k = 0, _len2 = ruleStack.length; _k < _len2; _k++) {
rule = ruleStack[_k].rule;
rule.clearAnchorPosition();
}
if (compatibilityMode) {
return new TokenizeLineResult(line, openScopeTags, tags, ruleStack, this.registry);
} else {
return {
line: line,
tags: tags,
ruleStack: ruleStack
};
}
};
Grammar.prototype.activate = function() {
return this.registration = this.registry.addGrammar(this);
};
Grammar.prototype.deactivate = function() {
var _ref;
this.emitter = new Emitter;
if ((_ref = this.registration) != null) {
_ref.dispose();
}
return this.registration = null;
};
Grammar.prototype.clearRules = function() {
this.initialRule = null;
return this.repository = null;
};
Grammar.prototype.getInitialRule = function() {
return this.initialRule != null ? this.initialRule : this.initialRule = this.createRule({
scopeName: this.scopeName,
patterns: this.rawPatterns
});
};
Grammar.prototype.getRepository = function() {
return this.repository != null ? this.repository : this.repository = (function(_this) {
return function() {
var data, name, repository, _ref;
repository = {};
_ref = _this.rawRepository;
for (name in _ref) {
data = _ref[name];
if ((data.begin != null) || (data.match != null)) {
data = {
patterns: [data],
tempName: name
};
}
repository[name] = _this.createRule(data);
}
return repository;
};
})(this)();
};
Grammar.prototype.addIncludedGrammarScope = function(scope) {
if (!_.include(this.includedGrammarScopes, scope)) {
return this.includedGrammarScopes.push(scope);
}
};
Grammar.prototype.grammarUpdated = function(scopeName) {
if (!_.include(this.includedGrammarScopes, scopeName)) {
return false;
}
this.clearRules();
this.registry.grammarUpdated(this.scopeName);
if (Grim.includeDeprecatedAPIs) {
this.emit('grammar-updated');
}
this.emitter.emit('did-update');
return true;
};
Grammar.prototype.startIdForScope = function(scope) {
return this.registry.startIdForScope(scope);
};
Grammar.prototype.endIdForScope = function(scope) {
return this.registry.endIdForScope(scope);
};
Grammar.prototype.scopeForId = function(id) {
return this.registry.scopeForId(id);
};
Grammar.prototype.createRule = function(options) {
return new Rule(this, this.registry, options);
};
Grammar.prototype.createPattern = function(options) {
return new Pattern(this, this.registry, options);
};
Grammar.prototype.getMaxTokensPerLine = function() {
return this.maxTokensPerLine;
};
Grammar.prototype.scopesFromStack = function(stack, rule, endPatternMatch) {
var contentScopeName, scopeName, scopes, _i, _len, _ref;
scopes = [];
for (_i = 0, _len = stack.length; _i < _len; _i++) {
_ref = stack[_i], scopeName = _ref.scopeName, contentScopeName = _ref.contentScopeName;
if (scopeName) {
scopes.push(scopeName);
}
if (contentScopeName) {
scopes.push(contentScopeName);
}
}
if (endPatternMatch && (rule != null ? rule.contentScopeName : void 0) && rule === stack[stack.length - 1]) {
scopes.pop();
}
return scopes;
};
return Grammar;
})();
if (Grim.includeDeprecatedAPIs) {
EmitterMixin = require('emissary').Emitter;
EmitterMixin.includeInto(Grammar);
Grammar.prototype.on = function(eventName) {
if (eventName === 'did-update') {
Grim.deprecate("Call Grammar::onDidUpdate instead");
} else {
Grim.deprecate("Call explicit event subscription methods instead");
}
return EmitterMixin.prototype.on.apply(this, arguments);
};
}
TokenizeLineResult = (function() {
function TokenizeLineResult(line, openScopeTags, tags, ruleStack, registry) {
this.line = line;
this.openScopeTags = openScopeTags;
this.tags = tags;
this.ruleStack = ruleStack;
this.registry = registry;
}
Object.defineProperty(TokenizeLineResult.prototype, 'tokens', {
get: function() {
return this.registry.decodeTokens(this.line, this.tags, this.openScopeTags);
}
});
return TokenizeLineResult;
})();
}).call(this);