7e858d2812c1c91e399264aa3ded0f00.js 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256
  1. ace.define("ace/snippets",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/range_list","ace/keyboard/hash_handler","ace/tokenizer","ace/clipboard","ace/editor"], function(require, exports, module){"use strict";
  2. var dom = require("./lib/dom");
  3. var oop = require("./lib/oop");
  4. var EventEmitter = require("./lib/event_emitter").EventEmitter;
  5. var lang = require("./lib/lang");
  6. var Range = require("./range").Range;
  7. var RangeList = require("./range_list").RangeList;
  8. var HashHandler = require("./keyboard/hash_handler").HashHandler;
  9. var Tokenizer = require("./tokenizer").Tokenizer;
  10. var clipboard = require("./clipboard");
  11. var VARIABLES = {
  12. CURRENT_WORD: function (editor) {
  13. return editor.session.getTextRange(editor.session.getWordRange());
  14. },
  15. SELECTION: function (editor, name, indentation) {
  16. var text = editor.session.getTextRange();
  17. if (indentation)
  18. return text.replace(/\n\r?([ \t]*\S)/g, "\n" + indentation + "$1");
  19. return text;
  20. },
  21. CURRENT_LINE: function (editor) {
  22. return editor.session.getLine(editor.getCursorPosition().row);
  23. },
  24. PREV_LINE: function (editor) {
  25. return editor.session.getLine(editor.getCursorPosition().row - 1);
  26. },
  27. LINE_INDEX: function (editor) {
  28. return editor.getCursorPosition().row;
  29. },
  30. LINE_NUMBER: function (editor) {
  31. return editor.getCursorPosition().row + 1;
  32. },
  33. SOFT_TABS: function (editor) {
  34. return editor.session.getUseSoftTabs() ? "YES" : "NO";
  35. },
  36. TAB_SIZE: function (editor) {
  37. return editor.session.getTabSize();
  38. },
  39. CLIPBOARD: function (editor) {
  40. return clipboard.getText && clipboard.getText();
  41. },
  42. FILENAME: function (editor) {
  43. return /[^/\\]*$/.exec(this.FILEPATH(editor))[0];
  44. },
  45. FILENAME_BASE: function (editor) {
  46. return /[^/\\]*$/.exec(this.FILEPATH(editor))[0].replace(/\.[^.]*$/, "");
  47. },
  48. DIRECTORY: function (editor) {
  49. return this.FILEPATH(editor).replace(/[^/\\]*$/, "");
  50. },
  51. FILEPATH: function (editor) { return "/not implemented.txt"; },
  52. WORKSPACE_NAME: function () { return "Unknown"; },
  53. FULLNAME: function () { return "Unknown"; },
  54. BLOCK_COMMENT_START: function (editor) {
  55. var mode = editor.session.$mode || {};
  56. return mode.blockComment && mode.blockComment.start || "";
  57. },
  58. BLOCK_COMMENT_END: function (editor) {
  59. var mode = editor.session.$mode || {};
  60. return mode.blockComment && mode.blockComment.end || "";
  61. },
  62. LINE_COMMENT: function (editor) {
  63. var mode = editor.session.$mode || {};
  64. return mode.lineCommentStart || "";
  65. },
  66. CURRENT_YEAR: date.bind(null, { year: "numeric" }),
  67. CURRENT_YEAR_SHORT: date.bind(null, { year: "2-digit" }),
  68. CURRENT_MONTH: date.bind(null, { month: "numeric" }),
  69. CURRENT_MONTH_NAME: date.bind(null, { month: "long" }),
  70. CURRENT_MONTH_NAME_SHORT: date.bind(null, { month: "short" }),
  71. CURRENT_DATE: date.bind(null, { day: "2-digit" }),
  72. CURRENT_DAY_NAME: date.bind(null, { weekday: "long" }),
  73. CURRENT_DAY_NAME_SHORT: date.bind(null, { weekday: "short" }),
  74. CURRENT_HOUR: date.bind(null, { hour: "2-digit", hour12: false }),
  75. CURRENT_MINUTE: date.bind(null, { minute: "2-digit" }),
  76. CURRENT_SECOND: date.bind(null, { second: "2-digit" })
  77. };
  78. VARIABLES.SELECTED_TEXT = VARIABLES.SELECTION;
  79. function date(dateFormat) {
  80. var str = new Date().toLocaleString("en-us", dateFormat);
  81. return str.length == 1 ? "0" + str : str;
  82. }
  83. var SnippetManager = function () {
  84. this.snippetMap = {};
  85. this.snippetNameMap = {};
  86. };
  87. (function () {
  88. oop.implement(this, EventEmitter);
  89. this.getTokenizer = function () {
  90. return SnippetManager.$tokenizer || this.createTokenizer();
  91. };
  92. this.createTokenizer = function () {
  93. function TabstopToken(str) {
  94. str = str.substr(1);
  95. if (/^\d+$/.test(str))
  96. return [{ tabstopId: parseInt(str, 10) }];
  97. return [{ text: str }];
  98. }
  99. function escape(ch) {
  100. return "(?:[^\\\\" + ch + "]|\\\\.)";
  101. }
  102. var formatMatcher = {
  103. regex: "/(" + escape("/") + "+)/",
  104. onMatch: function (val, state, stack) {
  105. var ts = stack[0];
  106. ts.fmtString = true;
  107. ts.guard = val.slice(1, -1);
  108. ts.flag = "";
  109. return "";
  110. },
  111. next: "formatString"
  112. };
  113. SnippetManager.$tokenizer = new Tokenizer({
  114. start: [
  115. { regex: /\\./, onMatch: function (val, state, stack) {
  116. var ch = val[1];
  117. if (ch == "}" && stack.length) {
  118. val = ch;
  119. }
  120. else if ("`$\\".indexOf(ch) != -1) {
  121. val = ch;
  122. }
  123. return [val];
  124. } },
  125. { regex: /}/, onMatch: function (val, state, stack) {
  126. return [stack.length ? stack.shift() : val];
  127. } },
  128. { regex: /\$(?:\d+|\w+)/, onMatch: TabstopToken },
  129. { regex: /\$\{[\dA-Z_a-z]+/, onMatch: function (str, state, stack) {
  130. var t = TabstopToken(str.substr(1));
  131. stack.unshift(t[0]);
  132. return t;
  133. }, next: "snippetVar" },
  134. { regex: /\n/, token: "newline", merge: false }
  135. ],
  136. snippetVar: [
  137. { regex: "\\|" + escape("\\|") + "*\\|", onMatch: function (val, state, stack) {
  138. var choices = val.slice(1, -1).replace(/\\[,|\\]|,/g, function (operator) {
  139. return operator.length == 2 ? operator[1] : "\x00";
  140. }).split("\x00").map(function (value) {
  141. return { value: value };
  142. });
  143. stack[0].choices = choices;
  144. return [choices[0]];
  145. }, next: "start" },
  146. formatMatcher,
  147. { regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "start" }
  148. ],
  149. formatString: [
  150. { regex: /:/, onMatch: function (val, state, stack) {
  151. if (stack.length && stack[0].expectElse) {
  152. stack[0].expectElse = false;
  153. stack[0].ifEnd = { elseEnd: stack[0] };
  154. return [stack[0].ifEnd];
  155. }
  156. return ":";
  157. } },
  158. { regex: /\\./, onMatch: function (val, state, stack) {
  159. var ch = val[1];
  160. if (ch == "}" && stack.length)
  161. val = ch;
  162. else if ("`$\\".indexOf(ch) != -1)
  163. val = ch;
  164. else if (ch == "n")
  165. val = "\n";
  166. else if (ch == "t")
  167. val = "\t";
  168. else if ("ulULE".indexOf(ch) != -1)
  169. val = { changeCase: ch, local: ch > "a" };
  170. return [val];
  171. } },
  172. { regex: "/\\w*}", onMatch: function (val, state, stack) {
  173. var next = stack.shift();
  174. if (next)
  175. next.flag = val.slice(1, -1);
  176. this.next = next && next.tabstopId ? "start" : "";
  177. return [next || val];
  178. }, next: "start" },
  179. { regex: /\$(?:\d+|\w+)/, onMatch: function (val, state, stack) {
  180. return [{ text: val.slice(1) }];
  181. } },
  182. { regex: /\${\w+/, onMatch: function (val, state, stack) {
  183. var token = { text: val.slice(2) };
  184. stack.unshift(token);
  185. return [token];
  186. }, next: "formatStringVar" },
  187. { regex: /\n/, token: "newline", merge: false },
  188. { regex: /}/, onMatch: function (val, state, stack) {
  189. var next = stack.shift();
  190. this.next = next && next.tabstopId ? "start" : "";
  191. return [next || val];
  192. }, next: "start" }
  193. ],
  194. formatStringVar: [
  195. { regex: /:\/\w+}/, onMatch: function (val, state, stack) {
  196. var ts = stack[0];
  197. ts.formatFunction = val.slice(2, -1);
  198. return [stack.shift()];
  199. }, next: "formatString" },
  200. formatMatcher,
  201. { regex: /:[\?\-+]?/, onMatch: function (val, state, stack) {
  202. if (val[1] == "+")
  203. stack[0].ifEnd = stack[0];
  204. if (val[1] == "?")
  205. stack[0].expectElse = true;
  206. }, next: "formatString" },
  207. { regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "formatString" }
  208. ]
  209. });
  210. return SnippetManager.$tokenizer;
  211. };
  212. this.tokenizeTmSnippet = function (str, startState) {
  213. return this.getTokenizer().getLineTokens(str, startState).tokens.map(function (x) {
  214. return x.value || x;
  215. });
  216. };
  217. this.getVariableValue = function (editor, name, indentation) {
  218. if (/^\d+$/.test(name))
  219. return (this.variables.__ || {})[name] || "";
  220. if (/^[A-Z]\d+$/.test(name))
  221. return (this.variables[name[0] + "__"] || {})[name.substr(1)] || "";
  222. name = name.replace(/^TM_/, "");
  223. if (!this.variables.hasOwnProperty(name))
  224. return "";
  225. var value = this.variables[name];
  226. if (typeof value == "function")
  227. value = this.variables[name](editor, name, indentation);
  228. return value == null ? "" : value;
  229. };
  230. this.variables = VARIABLES;
  231. this.tmStrFormat = function (str, ch, editor) {
  232. if (!ch.fmt)
  233. return str;
  234. var flag = ch.flag || "";
  235. var re = ch.guard;
  236. re = new RegExp(re, flag.replace(/[^gim]/g, ""));
  237. var fmtTokens = typeof ch.fmt == "string" ? this.tokenizeTmSnippet(ch.fmt, "formatString") : ch.fmt;
  238. var _self = this;
  239. var formatted = str.replace(re, function () {
  240. var oldArgs = _self.variables.__;
  241. _self.variables.__ = [].slice.call(arguments);
  242. var fmtParts = _self.resolveVariables(fmtTokens, editor);
  243. var gChangeCase = "E";
  244. for (var i = 0; i < fmtParts.length; i++) {
  245. var ch = fmtParts[i];
  246. if (typeof ch == "object") {
  247. fmtParts[i] = "";
  248. if (ch.changeCase && ch.local) {
  249. var next = fmtParts[i + 1];
  250. if (next && typeof next == "string") {
  251. if (ch.changeCase == "u")
  252. fmtParts[i] = next[0].toUpperCase();
  253. else
  254. fmtParts[i] = next[0].toLowerCase();
  255. fmtParts[i + 1] = next.substr(1);
  256. }
  257. }
  258. else if (ch.changeCase) {
  259. gChangeCase = ch.changeCase;
  260. }
  261. }
  262. else if (gChangeCase == "U") {
  263. fmtParts[i] = ch.toUpperCase();
  264. }
  265. else if (gChangeCase == "L") {
  266. fmtParts[i] = ch.toLowerCase();
  267. }
  268. }
  269. _self.variables.__ = oldArgs;
  270. return fmtParts.join("");
  271. });
  272. return formatted;
  273. };
  274. this.tmFormatFunction = function (str, ch, editor) {
  275. if (ch.formatFunction == "upcase")
  276. return str.toUpperCase();
  277. if (ch.formatFunction == "downcase")
  278. return str.toLowerCase();
  279. return str;
  280. };
  281. this.resolveVariables = function (snippet, editor) {
  282. var result = [];
  283. var indentation = "";
  284. var afterNewLine = true;
  285. for (var i = 0; i < snippet.length; i++) {
  286. var ch = snippet[i];
  287. if (typeof ch == "string") {
  288. result.push(ch);
  289. if (ch == "\n") {
  290. afterNewLine = true;
  291. indentation = "";
  292. }
  293. else if (afterNewLine) {
  294. indentation = /^\t*/.exec(ch)[0];
  295. afterNewLine = /\S/.test(ch);
  296. }
  297. continue;
  298. }
  299. if (!ch)
  300. continue;
  301. afterNewLine = false;
  302. if (ch.fmtString) {
  303. var j = snippet.indexOf(ch, i + 1);
  304. if (j == -1)
  305. j = snippet.length;
  306. ch.fmt = snippet.slice(i + 1, j);
  307. i = j;
  308. }
  309. if (ch.text) {
  310. var value = this.getVariableValue(editor, ch.text, indentation) + "";
  311. if (ch.fmtString)
  312. value = this.tmStrFormat(value, ch, editor);
  313. if (ch.formatFunction)
  314. value = this.tmFormatFunction(value, ch, editor);
  315. if (value && !ch.ifEnd) {
  316. result.push(value);
  317. gotoNext(ch);
  318. }
  319. else if (!value && ch.ifEnd) {
  320. gotoNext(ch.ifEnd);
  321. }
  322. }
  323. else if (ch.elseEnd) {
  324. gotoNext(ch.elseEnd);
  325. }
  326. else if (ch.tabstopId != null) {
  327. result.push(ch);
  328. }
  329. else if (ch.changeCase != null) {
  330. result.push(ch);
  331. }
  332. }
  333. function gotoNext(ch) {
  334. var i1 = snippet.indexOf(ch, i + 1);
  335. if (i1 != -1)
  336. i = i1;
  337. }
  338. return result;
  339. };
  340. this.insertSnippetForSelection = function (editor, snippetText) {
  341. var cursor = editor.getCursorPosition();
  342. var line = editor.session.getLine(cursor.row);
  343. var tabString = editor.session.getTabString();
  344. var indentString = line.match(/^\s*/)[0];
  345. if (cursor.column < indentString.length)
  346. indentString = indentString.slice(0, cursor.column);
  347. snippetText = snippetText.replace(/\r/g, "");
  348. var tokens = this.tokenizeTmSnippet(snippetText);
  349. tokens = this.resolveVariables(tokens, editor);
  350. tokens = tokens.map(function (x) {
  351. if (x == "\n")
  352. return x + indentString;
  353. if (typeof x == "string")
  354. return x.replace(/\t/g, tabString);
  355. return x;
  356. });
  357. var tabstops = [];
  358. tokens.forEach(function (p, i) {
  359. if (typeof p != "object")
  360. return;
  361. var id = p.tabstopId;
  362. var ts = tabstops[id];
  363. if (!ts) {
  364. ts = tabstops[id] = [];
  365. ts.index = id;
  366. ts.value = "";
  367. ts.parents = {};
  368. }
  369. if (ts.indexOf(p) !== -1)
  370. return;
  371. if (p.choices && !ts.choices)
  372. ts.choices = p.choices;
  373. ts.push(p);
  374. var i1 = tokens.indexOf(p, i + 1);
  375. if (i1 === -1)
  376. return;
  377. var value = tokens.slice(i + 1, i1);
  378. var isNested = value.some(function (t) { return typeof t === "object"; });
  379. if (isNested && !ts.value) {
  380. ts.value = value;
  381. }
  382. else if (value.length && (!ts.value || typeof ts.value !== "string")) {
  383. ts.value = value.join("");
  384. }
  385. });
  386. tabstops.forEach(function (ts) { ts.length = 0; });
  387. var expanding = {};
  388. function copyValue(val) {
  389. var copy = [];
  390. for (var i = 0; i < val.length; i++) {
  391. var p = val[i];
  392. if (typeof p == "object") {
  393. if (expanding[p.tabstopId])
  394. continue;
  395. var j = val.lastIndexOf(p, i - 1);
  396. p = copy[j] || { tabstopId: p.tabstopId };
  397. }
  398. copy[i] = p;
  399. }
  400. return copy;
  401. }
  402. for (var i = 0; i < tokens.length; i++) {
  403. var p = tokens[i];
  404. if (typeof p != "object")
  405. continue;
  406. var id = p.tabstopId;
  407. var ts = tabstops[id];
  408. var i1 = tokens.indexOf(p, i + 1);
  409. if (expanding[id]) {
  410. if (expanding[id] === p) {
  411. delete expanding[id];
  412. Object.keys(expanding).forEach(function (parentId) {
  413. ts.parents[parentId] = true;
  414. });
  415. }
  416. continue;
  417. }
  418. expanding[id] = p;
  419. var value = ts.value;
  420. if (typeof value !== "string")
  421. value = copyValue(value);
  422. else if (p.fmt)
  423. value = this.tmStrFormat(value, p, editor);
  424. tokens.splice.apply(tokens, [i + 1, Math.max(0, i1 - i)].concat(value, p));
  425. if (ts.indexOf(p) === -1)
  426. ts.push(p);
  427. }
  428. var row = 0, column = 0;
  429. var text = "";
  430. tokens.forEach(function (t) {
  431. if (typeof t === "string") {
  432. var lines = t.split("\n");
  433. if (lines.length > 1) {
  434. column = lines[lines.length - 1].length;
  435. row += lines.length - 1;
  436. }
  437. else
  438. column += t.length;
  439. text += t;
  440. }
  441. else if (t) {
  442. if (!t.start)
  443. t.start = { row: row, column: column };
  444. else
  445. t.end = { row: row, column: column };
  446. }
  447. });
  448. var range = editor.getSelectionRange();
  449. var end = editor.session.replace(range, text);
  450. var tabstopManager = new TabstopManager(editor);
  451. var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
  452. tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
  453. };
  454. this.insertSnippet = function (editor, snippetText) {
  455. var self = this;
  456. if (editor.inVirtualSelectionMode)
  457. return self.insertSnippetForSelection(editor, snippetText);
  458. editor.forEachSelection(function () {
  459. self.insertSnippetForSelection(editor, snippetText);
  460. }, null, { keepOrder: true });
  461. if (editor.tabstopManager)
  462. editor.tabstopManager.tabNext();
  463. };
  464. this.$getScope = function (editor) {
  465. var scope = editor.session.$mode.$id || "";
  466. scope = scope.split("/").pop();
  467. if (scope === "html" || scope === "php") {
  468. if (scope === "php" && !editor.session.$mode.inlinePhp)
  469. scope = "html";
  470. var c = editor.getCursorPosition();
  471. var state = editor.session.getState(c.row);
  472. if (typeof state === "object") {
  473. state = state[0];
  474. }
  475. if (state.substring) {
  476. if (state.substring(0, 3) == "js-")
  477. scope = "javascript";
  478. else if (state.substring(0, 4) == "css-")
  479. scope = "css";
  480. else if (state.substring(0, 4) == "php-")
  481. scope = "php";
  482. }
  483. }
  484. return scope;
  485. };
  486. this.getActiveScopes = function (editor) {
  487. var scope = this.$getScope(editor);
  488. var scopes = [scope];
  489. var snippetMap = this.snippetMap;
  490. if (snippetMap[scope] && snippetMap[scope].includeScopes) {
  491. scopes.push.apply(scopes, snippetMap[scope].includeScopes);
  492. }
  493. scopes.push("_");
  494. return scopes;
  495. };
  496. this.expandWithTab = function (editor, options) {
  497. var self = this;
  498. var result = editor.forEachSelection(function () {
  499. return self.expandSnippetForSelection(editor, options);
  500. }, null, { keepOrder: true });
  501. if (result && editor.tabstopManager)
  502. editor.tabstopManager.tabNext();
  503. return result;
  504. };
  505. this.expandSnippetForSelection = function (editor, options) {
  506. var cursor = editor.getCursorPosition();
  507. var line = editor.session.getLine(cursor.row);
  508. var before = line.substring(0, cursor.column);
  509. var after = line.substr(cursor.column);
  510. var snippetMap = this.snippetMap;
  511. var snippet;
  512. this.getActiveScopes(editor).some(function (scope) {
  513. var snippets = snippetMap[scope];
  514. if (snippets)
  515. snippet = this.findMatchingSnippet(snippets, before, after);
  516. return !!snippet;
  517. }, this);
  518. if (!snippet)
  519. return false;
  520. if (options && options.dryRun)
  521. return true;
  522. editor.session.doc.removeInLine(cursor.row, cursor.column - snippet.replaceBefore.length, cursor.column + snippet.replaceAfter.length);
  523. this.variables.M__ = snippet.matchBefore;
  524. this.variables.T__ = snippet.matchAfter;
  525. this.insertSnippetForSelection(editor, snippet.content);
  526. this.variables.M__ = this.variables.T__ = null;
  527. return true;
  528. };
  529. this.findMatchingSnippet = function (snippetList, before, after) {
  530. for (var i = snippetList.length; i--;) {
  531. var s = snippetList[i];
  532. if (s.startRe && !s.startRe.test(before))
  533. continue;
  534. if (s.endRe && !s.endRe.test(after))
  535. continue;
  536. if (!s.startRe && !s.endRe)
  537. continue;
  538. s.matchBefore = s.startRe ? s.startRe.exec(before) : [""];
  539. s.matchAfter = s.endRe ? s.endRe.exec(after) : [""];
  540. s.replaceBefore = s.triggerRe ? s.triggerRe.exec(before)[0] : "";
  541. s.replaceAfter = s.endTriggerRe ? s.endTriggerRe.exec(after)[0] : "";
  542. return s;
  543. }
  544. };
  545. this.snippetMap = {};
  546. this.snippetNameMap = {};
  547. this.register = function (snippets, scope) {
  548. var snippetMap = this.snippetMap;
  549. var snippetNameMap = this.snippetNameMap;
  550. var self = this;
  551. if (!snippets)
  552. snippets = [];
  553. function wrapRegexp(src) {
  554. if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src))
  555. src = "(?:" + src + ")";
  556. return src || "";
  557. }
  558. function guardedRegexp(re, guard, opening) {
  559. re = wrapRegexp(re);
  560. guard = wrapRegexp(guard);
  561. if (opening) {
  562. re = guard + re;
  563. if (re && re[re.length - 1] != "$")
  564. re = re + "$";
  565. }
  566. else {
  567. re = re + guard;
  568. if (re && re[0] != "^")
  569. re = "^" + re;
  570. }
  571. return new RegExp(re);
  572. }
  573. function addSnippet(s) {
  574. if (!s.scope)
  575. s.scope = scope || "_";
  576. scope = s.scope;
  577. if (!snippetMap[scope]) {
  578. snippetMap[scope] = [];
  579. snippetNameMap[scope] = {};
  580. }
  581. var map = snippetNameMap[scope];
  582. if (s.name) {
  583. var old = map[s.name];
  584. if (old)
  585. self.unregister(old);
  586. map[s.name] = s;
  587. }
  588. snippetMap[scope].push(s);
  589. if (s.prefix)
  590. s.tabTrigger = s.prefix;
  591. if (!s.content && s.body)
  592. s.content = Array.isArray(s.body) ? s.body.join("\n") : s.body;
  593. if (s.tabTrigger && !s.trigger) {
  594. if (!s.guard && /^\w/.test(s.tabTrigger))
  595. s.guard = "\\b";
  596. s.trigger = lang.escapeRegExp(s.tabTrigger);
  597. }
  598. if (!s.trigger && !s.guard && !s.endTrigger && !s.endGuard)
  599. return;
  600. s.startRe = guardedRegexp(s.trigger, s.guard, true);
  601. s.triggerRe = new RegExp(s.trigger);
  602. s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true);
  603. s.endTriggerRe = new RegExp(s.endTrigger);
  604. }
  605. if (Array.isArray(snippets)) {
  606. snippets.forEach(addSnippet);
  607. }
  608. else {
  609. Object.keys(snippets).forEach(function (key) {
  610. addSnippet(snippets[key]);
  611. });
  612. }
  613. this._signal("registerSnippets", { scope: scope });
  614. };
  615. this.unregister = function (snippets, scope) {
  616. var snippetMap = this.snippetMap;
  617. var snippetNameMap = this.snippetNameMap;
  618. function removeSnippet(s) {
  619. var nameMap = snippetNameMap[s.scope || scope];
  620. if (nameMap && nameMap[s.name]) {
  621. delete nameMap[s.name];
  622. var map = snippetMap[s.scope || scope];
  623. var i = map && map.indexOf(s);
  624. if (i >= 0)
  625. map.splice(i, 1);
  626. }
  627. }
  628. if (snippets.content)
  629. removeSnippet(snippets);
  630. else if (Array.isArray(snippets))
  631. snippets.forEach(removeSnippet);
  632. };
  633. this.parseSnippetFile = function (str) {
  634. str = str.replace(/\r/g, "");
  635. var list = [], snippet = {};
  636. var re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm;
  637. var m;
  638. while (m = re.exec(str)) {
  639. if (m[1]) {
  640. try {
  641. snippet = JSON.parse(m[1]);
  642. list.push(snippet);
  643. }
  644. catch (e) { }
  645. }
  646. if (m[4]) {
  647. snippet.content = m[4].replace(/^\t/gm, "");
  648. list.push(snippet);
  649. snippet = {};
  650. }
  651. else {
  652. var key = m[2], val = m[3];
  653. if (key == "regex") {
  654. var guardRe = /\/((?:[^\/\\]|\\.)*)|$/g;
  655. snippet.guard = guardRe.exec(val)[1];
  656. snippet.trigger = guardRe.exec(val)[1];
  657. snippet.endTrigger = guardRe.exec(val)[1];
  658. snippet.endGuard = guardRe.exec(val)[1];
  659. }
  660. else if (key == "snippet") {
  661. snippet.tabTrigger = val.match(/^\S*/)[0];
  662. if (!snippet.name)
  663. snippet.name = val;
  664. }
  665. else if (key) {
  666. snippet[key] = val;
  667. }
  668. }
  669. }
  670. return list;
  671. };
  672. this.getSnippetByName = function (name, editor) {
  673. var snippetMap = this.snippetNameMap;
  674. var snippet;
  675. this.getActiveScopes(editor).some(function (scope) {
  676. var snippets = snippetMap[scope];
  677. if (snippets)
  678. snippet = snippets[name];
  679. return !!snippet;
  680. }, this);
  681. return snippet;
  682. };
  683. }).call(SnippetManager.prototype);
  684. var TabstopManager = function (editor) {
  685. if (editor.tabstopManager)
  686. return editor.tabstopManager;
  687. editor.tabstopManager = this;
  688. this.$onChange = this.onChange.bind(this);
  689. this.$onChangeSelection = lang.delayedCall(this.onChangeSelection.bind(this)).schedule;
  690. this.$onChangeSession = this.onChangeSession.bind(this);
  691. this.$onAfterExec = this.onAfterExec.bind(this);
  692. this.attach(editor);
  693. };
  694. (function () {
  695. this.attach = function (editor) {
  696. this.index = 0;
  697. this.ranges = [];
  698. this.tabstops = [];
  699. this.$openTabstops = null;
  700. this.selectedTabstop = null;
  701. this.editor = editor;
  702. this.editor.on("change", this.$onChange);
  703. this.editor.on("changeSelection", this.$onChangeSelection);
  704. this.editor.on("changeSession", this.$onChangeSession);
  705. this.editor.commands.on("afterExec", this.$onAfterExec);
  706. this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
  707. };
  708. this.detach = function () {
  709. this.tabstops.forEach(this.removeTabstopMarkers, this);
  710. this.ranges = null;
  711. this.tabstops = null;
  712. this.selectedTabstop = null;
  713. this.editor.removeListener("change", this.$onChange);
  714. this.editor.removeListener("changeSelection", this.$onChangeSelection);
  715. this.editor.removeListener("changeSession", this.$onChangeSession);
  716. this.editor.commands.removeListener("afterExec", this.$onAfterExec);
  717. this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
  718. this.editor.tabstopManager = null;
  719. this.editor = null;
  720. };
  721. this.onChange = function (delta) {
  722. var isRemove = delta.action[0] == "r";
  723. var selectedTabstop = this.selectedTabstop || {};
  724. var parents = selectedTabstop.parents || {};
  725. var tabstops = (this.tabstops || []).slice();
  726. for (var i = 0; i < tabstops.length; i++) {
  727. var ts = tabstops[i];
  728. var active = ts == selectedTabstop || parents[ts.index];
  729. ts.rangeList.$bias = active ? 0 : 1;
  730. if (delta.action == "remove" && ts !== selectedTabstop) {
  731. var parentActive = ts.parents && ts.parents[selectedTabstop.index];
  732. var startIndex = ts.rangeList.pointIndex(delta.start, parentActive);
  733. startIndex = startIndex < 0 ? -startIndex - 1 : startIndex + 1;
  734. var endIndex = ts.rangeList.pointIndex(delta.end, parentActive);
  735. endIndex = endIndex < 0 ? -endIndex - 1 : endIndex - 1;
  736. var toRemove = ts.rangeList.ranges.slice(startIndex, endIndex);
  737. for (var j = 0; j < toRemove.length; j++)
  738. this.removeRange(toRemove[j]);
  739. }
  740. ts.rangeList.$onChange(delta);
  741. }
  742. var session = this.editor.session;
  743. if (!this.$inChange && isRemove && session.getLength() == 1 && !session.getValue())
  744. this.detach();
  745. };
  746. this.updateLinkedFields = function () {
  747. var ts = this.selectedTabstop;
  748. if (!ts || !ts.hasLinkedRanges || !ts.firstNonLinked)
  749. return;
  750. this.$inChange = true;
  751. var session = this.editor.session;
  752. var text = session.getTextRange(ts.firstNonLinked);
  753. for (var i = 0; i < ts.length; i++) {
  754. var range = ts[i];
  755. if (!range.linked)
  756. continue;
  757. var original = range.original;
  758. var fmt = exports.snippetManager.tmStrFormat(text, original, this.editor);
  759. session.replace(range, fmt);
  760. }
  761. this.$inChange = false;
  762. };
  763. this.onAfterExec = function (e) {
  764. if (e.command && !e.command.readOnly)
  765. this.updateLinkedFields();
  766. };
  767. this.onChangeSelection = function () {
  768. if (!this.editor)
  769. return;
  770. var lead = this.editor.selection.lead;
  771. var anchor = this.editor.selection.anchor;
  772. var isEmpty = this.editor.selection.isEmpty();
  773. for (var i = 0; i < this.ranges.length; i++) {
  774. if (this.ranges[i].linked)
  775. continue;
  776. var containsLead = this.ranges[i].contains(lead.row, lead.column);
  777. var containsAnchor = isEmpty || this.ranges[i].contains(anchor.row, anchor.column);
  778. if (containsLead && containsAnchor)
  779. return;
  780. }
  781. this.detach();
  782. };
  783. this.onChangeSession = function () {
  784. this.detach();
  785. };
  786. this.tabNext = function (dir) {
  787. var max = this.tabstops.length;
  788. var index = this.index + (dir || 1);
  789. index = Math.min(Math.max(index, 1), max);
  790. if (index == max)
  791. index = 0;
  792. this.selectTabstop(index);
  793. if (index === 0)
  794. this.detach();
  795. };
  796. this.selectTabstop = function (index) {
  797. this.$openTabstops = null;
  798. var ts = this.tabstops[this.index];
  799. if (ts)
  800. this.addTabstopMarkers(ts);
  801. this.index = index;
  802. ts = this.tabstops[this.index];
  803. if (!ts || !ts.length)
  804. return;
  805. this.selectedTabstop = ts;
  806. var range = ts.firstNonLinked || ts;
  807. if (ts.choices)
  808. range.cursor = range.start;
  809. if (!this.editor.inVirtualSelectionMode) {
  810. var sel = this.editor.multiSelect;
  811. sel.toSingleRange(range);
  812. for (var i = 0; i < ts.length; i++) {
  813. if (ts.hasLinkedRanges && ts[i].linked)
  814. continue;
  815. sel.addRange(ts[i].clone(), true);
  816. }
  817. }
  818. else {
  819. this.editor.selection.fromOrientedRange(range);
  820. }
  821. this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
  822. if (this.selectedTabstop && this.selectedTabstop.choices)
  823. this.editor.execCommand("startAutocomplete", { matches: this.selectedTabstop.choices });
  824. };
  825. this.addTabstops = function (tabstops, start, end) {
  826. var useLink = this.useLink || !this.editor.getOption("enableMultiselect");
  827. if (!this.$openTabstops)
  828. this.$openTabstops = [];
  829. if (!tabstops[0]) {
  830. var p = Range.fromPoints(end, end);
  831. moveRelative(p.start, start);
  832. moveRelative(p.end, start);
  833. tabstops[0] = [p];
  834. tabstops[0].index = 0;
  835. }
  836. var i = this.index;
  837. var arg = [i + 1, 0];
  838. var ranges = this.ranges;
  839. tabstops.forEach(function (ts, index) {
  840. var dest = this.$openTabstops[index] || ts;
  841. for (var i = 0; i < ts.length; i++) {
  842. var p = ts[i];
  843. var range = Range.fromPoints(p.start, p.end || p.start);
  844. movePoint(range.start, start);
  845. movePoint(range.end, start);
  846. range.original = p;
  847. range.tabstop = dest;
  848. ranges.push(range);
  849. if (dest != ts)
  850. dest.unshift(range);
  851. else
  852. dest[i] = range;
  853. if (p.fmtString || (dest.firstNonLinked && useLink)) {
  854. range.linked = true;
  855. dest.hasLinkedRanges = true;
  856. }
  857. else if (!dest.firstNonLinked)
  858. dest.firstNonLinked = range;
  859. }
  860. if (!dest.firstNonLinked)
  861. dest.hasLinkedRanges = false;
  862. if (dest === ts) {
  863. arg.push(dest);
  864. this.$openTabstops[index] = dest;
  865. }
  866. this.addTabstopMarkers(dest);
  867. dest.rangeList = dest.rangeList || new RangeList();
  868. dest.rangeList.$bias = 0;
  869. dest.rangeList.addList(dest);
  870. }, this);
  871. if (arg.length > 2) {
  872. if (this.tabstops.length)
  873. arg.push(arg.splice(2, 1)[0]);
  874. this.tabstops.splice.apply(this.tabstops, arg);
  875. }
  876. };
  877. this.addTabstopMarkers = function (ts) {
  878. var session = this.editor.session;
  879. ts.forEach(function (range) {
  880. if (!range.markerId)
  881. range.markerId = session.addMarker(range, "ace_snippet-marker", "text");
  882. });
  883. };
  884. this.removeTabstopMarkers = function (ts) {
  885. var session = this.editor.session;
  886. ts.forEach(function (range) {
  887. session.removeMarker(range.markerId);
  888. range.markerId = null;
  889. });
  890. };
  891. this.removeRange = function (range) {
  892. var i = range.tabstop.indexOf(range);
  893. if (i != -1)
  894. range.tabstop.splice(i, 1);
  895. i = this.ranges.indexOf(range);
  896. if (i != -1)
  897. this.ranges.splice(i, 1);
  898. i = range.tabstop.rangeList.ranges.indexOf(range);
  899. if (i != -1)
  900. range.tabstop.splice(i, 1);
  901. this.editor.session.removeMarker(range.markerId);
  902. if (!range.tabstop.length) {
  903. i = this.tabstops.indexOf(range.tabstop);
  904. if (i != -1)
  905. this.tabstops.splice(i, 1);
  906. if (!this.tabstops.length)
  907. this.detach();
  908. }
  909. };
  910. this.keyboardHandler = new HashHandler();
  911. this.keyboardHandler.bindKeys({
  912. "Tab": function (editor) {
  913. if (exports.snippetManager && exports.snippetManager.expandWithTab(editor))
  914. return;
  915. editor.tabstopManager.tabNext(1);
  916. editor.renderer.scrollCursorIntoView();
  917. },
  918. "Shift-Tab": function (editor) {
  919. editor.tabstopManager.tabNext(-1);
  920. editor.renderer.scrollCursorIntoView();
  921. },
  922. "Esc": function (editor) {
  923. editor.tabstopManager.detach();
  924. }
  925. });
  926. }).call(TabstopManager.prototype);
  927. var movePoint = function (point, diff) {
  928. if (point.row == 0)
  929. point.column += diff.column;
  930. point.row += diff.row;
  931. };
  932. var moveRelative = function (point, start) {
  933. if (point.row == start.row)
  934. point.column -= start.column;
  935. point.row -= start.row;
  936. };
  937. dom.importCssString("\n.ace_snippet-marker {\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n background: rgba(194, 193, 208, 0.09);\n border: 1px dotted rgba(211, 208, 235, 0.62);\n position: absolute;\n}", "snippets.css", false);
  938. exports.snippetManager = new SnippetManager();
  939. var Editor = require("./editor").Editor;
  940. (function () {
  941. this.insertSnippet = function (content, options) {
  942. return exports.snippetManager.insertSnippet(this, content, options);
  943. };
  944. this.expandSnippet = function (options) {
  945. return exports.snippetManager.expandWithTab(this, options);
  946. };
  947. }).call(Editor.prototype);
  948. });
  949. ace.define("ace/ext/emmet",["require","exports","module","ace/keyboard/hash_handler","ace/editor","ace/snippets","ace/range","ace/config","resources","resources","tabStops","resources","utils","actions"], function(require, exports, module){"use strict";
  950. var HashHandler = require("../keyboard/hash_handler").HashHandler;
  951. var Editor = require("../editor").Editor;
  952. var snippetManager = require("../snippets").snippetManager;
  953. var Range = require("../range").Range;
  954. var config = require("../config");
  955. var emmet, emmetPath;
  956. function AceEmmetEditor() { }
  957. AceEmmetEditor.prototype = {
  958. setupContext: function (editor) {
  959. this.ace = editor;
  960. this.indentation = editor.session.getTabString();
  961. if (!emmet)
  962. emmet = window.emmet;
  963. var resources = emmet.resources || emmet.require("resources");
  964. resources.setVariable("indentation", this.indentation);
  965. this.$syntax = null;
  966. this.$syntax = this.getSyntax();
  967. },
  968. getSelectionRange: function () {
  969. var range = this.ace.getSelectionRange();
  970. var doc = this.ace.session.doc;
  971. return {
  972. start: doc.positionToIndex(range.start),
  973. end: doc.positionToIndex(range.end)
  974. };
  975. },
  976. createSelection: function (start, end) {
  977. var doc = this.ace.session.doc;
  978. this.ace.selection.setRange({
  979. start: doc.indexToPosition(start),
  980. end: doc.indexToPosition(end)
  981. });
  982. },
  983. getCurrentLineRange: function () {
  984. var ace = this.ace;
  985. var row = ace.getCursorPosition().row;
  986. var lineLength = ace.session.getLine(row).length;
  987. var index = ace.session.doc.positionToIndex({ row: row, column: 0 });
  988. return {
  989. start: index,
  990. end: index + lineLength
  991. };
  992. },
  993. getCaretPos: function () {
  994. var pos = this.ace.getCursorPosition();
  995. return this.ace.session.doc.positionToIndex(pos);
  996. },
  997. setCaretPos: function (index) {
  998. var pos = this.ace.session.doc.indexToPosition(index);
  999. this.ace.selection.moveToPosition(pos);
  1000. },
  1001. getCurrentLine: function () {
  1002. var row = this.ace.getCursorPosition().row;
  1003. return this.ace.session.getLine(row);
  1004. },
  1005. replaceContent: function (value, start, end, noIndent) {
  1006. if (end == null)
  1007. end = start == null ? this.getContent().length : start;
  1008. if (start == null)
  1009. start = 0;
  1010. var editor = this.ace;
  1011. var doc = editor.session.doc;
  1012. var range = Range.fromPoints(doc.indexToPosition(start), doc.indexToPosition(end));
  1013. editor.session.remove(range);
  1014. range.end = range.start;
  1015. value = this.$updateTabstops(value);
  1016. snippetManager.insertSnippet(editor, value);
  1017. },
  1018. getContent: function () {
  1019. return this.ace.getValue();
  1020. },
  1021. getSyntax: function () {
  1022. if (this.$syntax)
  1023. return this.$syntax;
  1024. var syntax = this.ace.session.$modeId.split("/").pop();
  1025. if (syntax == "html" || syntax == "php") {
  1026. var cursor = this.ace.getCursorPosition();
  1027. var state = this.ace.session.getState(cursor.row);
  1028. if (typeof state != "string")
  1029. state = state[0];
  1030. if (state) {
  1031. state = state.split("-");
  1032. if (state.length > 1)
  1033. syntax = state[0];
  1034. else if (syntax == "php")
  1035. syntax = "html";
  1036. }
  1037. }
  1038. return syntax;
  1039. },
  1040. getProfileName: function () {
  1041. var resources = emmet.resources || emmet.require("resources");
  1042. switch (this.getSyntax()) {
  1043. case "css": return "css";
  1044. case "xml":
  1045. case "xsl":
  1046. return "xml";
  1047. case "html":
  1048. var profile = resources.getVariable("profile");
  1049. if (!profile)
  1050. profile = this.ace.session.getLines(0, 2).join("").search(/<!DOCTYPE[^>]+XHTML/i) != -1 ? "xhtml" : "html";
  1051. return profile;
  1052. default:
  1053. var mode = this.ace.session.$mode;
  1054. return mode.emmetConfig && mode.emmetConfig.profile || "xhtml";
  1055. }
  1056. },
  1057. prompt: function (title) {
  1058. return prompt(title); // eslint-disable-line no-alert
  1059. },
  1060. getSelection: function () {
  1061. return this.ace.session.getTextRange();
  1062. },
  1063. getFilePath: function () {
  1064. return "";
  1065. },
  1066. $updateTabstops: function (value) {
  1067. var base = 1000;
  1068. var zeroBase = 0;
  1069. var lastZero = null;
  1070. var ts = emmet.tabStops || emmet.require('tabStops');
  1071. var resources = emmet.resources || emmet.require("resources");
  1072. var settings = resources.getVocabulary("user");
  1073. var tabstopOptions = {
  1074. tabstop: function (data) {
  1075. var group = parseInt(data.group, 10);
  1076. var isZero = group === 0;
  1077. if (isZero)
  1078. group = ++zeroBase;
  1079. else
  1080. group += base;
  1081. var placeholder = data.placeholder;
  1082. if (placeholder) {
  1083. placeholder = ts.processText(placeholder, tabstopOptions);
  1084. }
  1085. var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
  1086. if (isZero) {
  1087. lastZero = [data.start, result];
  1088. }
  1089. return result;
  1090. },
  1091. escape: function (ch) {
  1092. if (ch == '$')
  1093. return '\\$';
  1094. if (ch == '\\')
  1095. return '\\\\';
  1096. return ch;
  1097. }
  1098. };
  1099. value = ts.processText(value, tabstopOptions);
  1100. if (settings.variables['insert_final_tabstop'] && !/\$\{0\}$/.test(value)) {
  1101. value += '${0}';
  1102. }
  1103. else if (lastZero) {
  1104. var common = emmet.utils ? emmet.utils.common : emmet.require('utils');
  1105. value = common.replaceSubstring(value, '${0}', lastZero[0], lastZero[1]);
  1106. }
  1107. return value;
  1108. }
  1109. };
  1110. var keymap = {
  1111. expand_abbreviation: { "mac": "ctrl+alt+e", "win": "alt+e" },
  1112. match_pair_outward: { "mac": "ctrl+d", "win": "ctrl+," },
  1113. match_pair_inward: { "mac": "ctrl+j", "win": "ctrl+shift+0" },
  1114. matching_pair: { "mac": "ctrl+alt+j", "win": "alt+j" },
  1115. next_edit_point: "alt+right",
  1116. prev_edit_point: "alt+left",
  1117. toggle_comment: { "mac": "command+/", "win": "ctrl+/" },
  1118. split_join_tag: { "mac": "shift+command+'", "win": "shift+ctrl+`" },
  1119. remove_tag: { "mac": "command+'", "win": "shift+ctrl+;" },
  1120. evaluate_math_expression: { "mac": "shift+command+y", "win": "shift+ctrl+y" },
  1121. increment_number_by_1: "ctrl+up",
  1122. decrement_number_by_1: "ctrl+down",
  1123. increment_number_by_01: "alt+up",
  1124. decrement_number_by_01: "alt+down",
  1125. increment_number_by_10: { "mac": "alt+command+up", "win": "shift+alt+up" },
  1126. decrement_number_by_10: { "mac": "alt+command+down", "win": "shift+alt+down" },
  1127. select_next_item: { "mac": "shift+command+.", "win": "shift+ctrl+." },
  1128. select_previous_item: { "mac": "shift+command+,", "win": "shift+ctrl+," },
  1129. reflect_css_value: { "mac": "shift+command+r", "win": "shift+ctrl+r" },
  1130. encode_decode_data_url: { "mac": "shift+ctrl+d", "win": "ctrl+'" },
  1131. expand_abbreviation_with_tab: "Tab",
  1132. wrap_with_abbreviation: { "mac": "shift+ctrl+a", "win": "shift+ctrl+a" }
  1133. };
  1134. var editorProxy = new AceEmmetEditor();
  1135. exports.commands = new HashHandler();
  1136. exports.runEmmetCommand = function runEmmetCommand(editor) {
  1137. if (this.action == "expand_abbreviation_with_tab") {
  1138. if (!editor.selection.isEmpty())
  1139. return false;
  1140. var pos = editor.selection.lead;
  1141. var token = editor.session.getTokenAt(pos.row, pos.column);
  1142. if (token && /\btag\b/.test(token.type))
  1143. return false;
  1144. }
  1145. try {
  1146. editorProxy.setupContext(editor);
  1147. var actions = emmet.actions || emmet.require("actions");
  1148. if (this.action == "wrap_with_abbreviation") {
  1149. return setTimeout(function () {
  1150. actions.run("wrap_with_abbreviation", editorProxy);
  1151. }, 0);
  1152. }
  1153. var result = actions.run(this.action, editorProxy);
  1154. }
  1155. catch (e) {
  1156. if (!emmet) {
  1157. var loading = exports.load(runEmmetCommand.bind(this, editor));
  1158. if (this.action == "expand_abbreviation_with_tab")
  1159. return false;
  1160. return loading;
  1161. }
  1162. editor._signal("changeStatus", typeof e == "string" ? e : e.message);
  1163. config.warn(e);
  1164. result = false;
  1165. }
  1166. return result;
  1167. };
  1168. for (var command in keymap) {
  1169. exports.commands.addCommand({
  1170. name: "emmet:" + command,
  1171. action: command,
  1172. bindKey: keymap[command],
  1173. exec: exports.runEmmetCommand,
  1174. multiSelectAction: "forEach"
  1175. });
  1176. }
  1177. exports.updateCommands = function (editor, enabled) {
  1178. if (enabled) {
  1179. editor.keyBinding.addKeyboardHandler(exports.commands);
  1180. }
  1181. else {
  1182. editor.keyBinding.removeKeyboardHandler(exports.commands);
  1183. }
  1184. };
  1185. exports.isSupportedMode = function (mode) {
  1186. if (!mode)
  1187. return false;
  1188. if (mode.emmetConfig)
  1189. return true;
  1190. var id = mode.$id || mode;
  1191. return /css|less|scss|sass|stylus|html|php|twig|ejs|handlebars/.test(id);
  1192. };
  1193. exports.isAvailable = function (editor, command) {
  1194. if (/(evaluate_math_expression|expand_abbreviation)$/.test(command))
  1195. return true;
  1196. var mode = editor.session.$mode;
  1197. var isSupported = exports.isSupportedMode(mode);
  1198. if (isSupported && mode.$modes) {
  1199. try {
  1200. editorProxy.setupContext(editor);
  1201. if (/js|php/.test(editorProxy.getSyntax()))
  1202. isSupported = false;
  1203. }
  1204. catch (e) { }
  1205. }
  1206. return isSupported;
  1207. };
  1208. var onChangeMode = function (e, target) {
  1209. var editor = target;
  1210. if (!editor)
  1211. return;
  1212. var enabled = exports.isSupportedMode(editor.session.$mode);
  1213. if (e.enableEmmet === false)
  1214. enabled = false;
  1215. if (enabled)
  1216. exports.load();
  1217. exports.updateCommands(editor, enabled);
  1218. };
  1219. exports.load = function (cb) {
  1220. if (typeof emmetPath !== "string") {
  1221. config.warn("script for emmet-core is not loaded");
  1222. return false;
  1223. }
  1224. config.loadModule(emmetPath, function () {
  1225. emmetPath = null;
  1226. cb && cb();
  1227. });
  1228. return true;
  1229. };
  1230. exports.AceEmmetEditor = AceEmmetEditor;
  1231. config.defineOptions(Editor.prototype, "editor", {
  1232. enableEmmet: {
  1233. set: function (val) {
  1234. this[val ? "on" : "removeListener"]("changeMode", onChangeMode);
  1235. onChangeMode({ enableEmmet: !!val }, this);
  1236. },
  1237. value: true
  1238. }
  1239. });
  1240. exports.setCore = function (e) {
  1241. if (typeof e == "string")
  1242. emmetPath = e;
  1243. else
  1244. emmet = e;
  1245. };
  1246. }); (function() {
  1247. ace.require(["ace/ext/emmet"], function(m) {
  1248. if (typeof module == "object" && typeof exports == "object" && module) {
  1249. module.exports = m;
  1250. }
  1251. });
  1252. })();