function activeView() { return sublime.active_window().active_view(); } var editorProxy = emmet.exec(function(require, _) { return { getSelectionRange: function() { var view = activeView(); var sel = view.sel()[0]; return { start: sel.begin(), end: sel.end() }; }, createSelection: function(start, end) { var view = activeView(); view.sel().clear(); view.sel().add(new sublime.Region(start, end || start)); view.show(view.sel()); }, getCurrentLineRange: function() { var view = activeView(); var selection = view.sel()[0]; var line = view.line(selection); return { start: line.begin(), end: line.end() }; }, getCaretPos: function() { var view = activeView(); var sel = view.sel(); return sel && sel[0] ? sel[0].begin() : 0; }, setCaretPos: function(pos){ this.createSelection(pos, pos); }, getCurrentLine: function() { var view = activeView(); return view.substr(view.line(view.sel()[0])); }, replaceContent: function(value, start, end, noIndent) { if (_.isUndefined(end)) end = _.isUndefined(start) ? this.getContent().length : start; if (_.isUndefined(start)) start = 0; // update tabstops: make sure all caret placeholder are unique // by default, abbreviation parser generates all unlinked (un-mirrored) // tabstops as ${0}, so we have upgrade all caret tabstops with unique // positions but make sure that all other tabstops are not linked accidentally value = pyPreprocessText(value); sublimeReplaceSubstring(start, end, value, !!noIndent); }, getContent: function() { var view = activeView(); return view.substr(new sublime.Region(0, view.size())); }, getSyntax: function() { return pyGetSyntax(); }, getProfileName: function() { var view = activeView(); var pos = this.getCaretPos(); if (view.match_selector(pos, 'text.html') && sublimeGetOption('autodetect_xhtml', false) && require('actionUtils').isXHTML(this)) { return 'xhtml'; } if (view.match_selector(pos, 'string.quoted.double.block.python') || view.match_selector(pos, 'source.coffee string') || view.match_selector(pos, 'string.unquoted.heredoc')) { // use html's default profile for: // * Python's multiline block // * CoffeeScript string // * PHP heredoc return pyDetectProfile(); } if (view.score_selector(pos, 'source string')) { return 'line'; } return pyDetectProfile(); }, prompt: function(title) { return pyEditor.prompt(); }, getSelection: function() { var view = activeView(); return view.sel() ? view.substr(view.sel()[0]) : ''; }, getFilePath: function() { return activeView().file_name(); } }; }); var _completions = {}; function require(name) { return emmet.require(name); } function pyPreprocessText(value) { var base = 1000; var zeroBase = 0; var lastZero = null; var range = require('range'); var ts = require('tabStops'); var tabstopOptions = { tabstop: function(data) { var group = parseInt(data.group, 10); var isZero = group === 0; if (isZero) group = ++zeroBase; else group += base; var placeholder = data.placeholder; if (placeholder) { // recursively update nested tabstops placeholder = ts.processText(placeholder, tabstopOptions); } var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}'; if (isZero) { lastZero = range.create(data.start, result); } return result }, escape: function(ch) { if (ch == '$') { return '\\$'; } if (ch == '\\') { return '\\\\'; } return ch; } }; value = ts.processText(value, tabstopOptions); if (sublimeGetOption('insert_final_tabstop', false) && !/\$\{0\}$/.test(value)) { value += '${0}'; } else if (lastZero) { value = require('utils').replaceSubstring(value, '${0}', lastZero); } return value; } function pyExpandAbbreviationAsYouType(abbr) { var info = require('editorUtils').outputInfo(editorProxy); try { var result = emmet.expandAbbreviation(abbr, info.syntax, info.profile, require('actionUtils').captureContext(editorProxy)); return pyPreprocessText(result); } catch (e) { return ''; } } function pyWrapAsYouType(abbr, content) { var info = require('editorUtils').outputInfo(editorProxy); content = require('utils').escapeText(content); var ctx = require('actionUtils').captureContext(editorProxy); try { var result = require('wrapWithAbbreviation').wrap(abbr, content, info.syntax, info.profile, ctx); return pyPreprocessText(result); } catch(e) { return ''; } } function pyCaptureWrappingRange() { var info = require('editorUtils').outputInfo(editorProxy); var range = editorProxy.getSelectionRange(); var startOffset = range.start; var endOffset = range.end; if (startOffset == endOffset) { // no selection, find tag pair var match = require('htmlMatcher').find(info.content, startOffset); if (!match) { // nothing to wrap return null; } /** @type Range */ var utils = require('utils'); var narrowedSel = utils.narrowToNonSpace(info.content, match.range); startOffset = narrowedSel.start; endOffset = narrowedSel.end; } return [startOffset, endOffset]; } function pyGetTagNameRanges(pos) { var ranges = []; var info = require('editorUtils').outputInfo(editorProxy); // search for tag try { var tag = require('htmlMatcher').tag(info.content, pos); if (tag) { var open = tag.open.range; var tagName = /^<([\w\-\:]+)/i.exec(open.substring(info.content))[1]; ranges.push([open.start + 1, open.start + 1 + tagName.length]); if (tag.close) { ranges.push([tag.close.range.start + 2, tag.close.range.start + 2 + tagName.length]); } } } catch (e) {} return ranges; } function pyGetTagRanges() { var ranges = []; var info = require('editorUtils').outputInfo(editorProxy); // search for tag try { var tag = require('htmlMatcher').tag(info.content, editorProxy.getCaretPos()); if (tag) { ranges.push(tag.open.range.toArray()); if (tag.close) { ranges.push(tag.close.range.toArray()); } } } catch (e) {} return ranges; } function pyExtractAbbreviation() { return require('expandAbbreviation').findAbbreviation(editorProxy); } function pyHasSnippet(name) { return !!emmet.require('resources').findSnippet(editorProxy.getSyntax(), name); } /** * Get all available CSS completions. This method is optimized for CSS * only since it should contain snippets only so it's not required * to do extra parsing */ function pyGetCSSCompletions(dialect) { dialect = dialect || pyGetSyntax(); if (!_completions[dialect]) { var all = require('resources').getAllSnippets(dialect); var css = require('cssResolver'); _completions[dialect] = _.map(all, function(v, k) { var snippetValue = typeof v.parsedValue == 'object' ? v.parsedValue.data : v.value; var snippet = css.transformSnippet(snippetValue, false, dialect); return { k: v.nk, label: snippet.replace(/\:\s*\$\{0\}\s*;?$/, ''), v: css.expandToSnippet(v.nk, dialect) }; }); } return _completions[dialect]; } /** * Returns current syntax name * @return {String} */ function pyGetSyntax() { var view = activeView(); var pt = view.sel()[0].begin(); var scope = 'scope_name' in view ? view.scope_name(pt) : view.syntax_name(pt); if (~scope.indexOf('xsl')) { return 'xsl'; } var syntax = 'html'; // detect CSS-like syntaxes independently, // since it may cause collisions with some highlighters if (/\b(less|scss|sass|css|stylus)\b/.test(scope)) { syntax = RegExp.$1; } else if (/\b(html|xml|haml)\b/.test(scope)) { syntax = RegExp.$1; } return require('actionUtils').detectSyntax(editorProxy, syntax); } function pyDetectProfile(argument) { return require('actionUtils').detectProfile(editorProxy); }