// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { var Pos = CodeMirror.Pos; // Helper functions borrowed from the javascript-hint addon. function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; while (i--) { if (arr[i] === item) { return true; } } return false; } return arr.indexOf(item) != -1; } function scriptHint(editor, keywords, getToken, options) { // Find the token at the cursor var cur = editor.getCursor(), token = getToken(editor, cur); var found = [], start = token.string; // some helpers that need access to the same editor/cursor instance to function function maybeAdd(str) { if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function prevToken(start) { return editor.getTokenAt({line:cur.line, ch: start-1});} function classOperator(token) { // for operators that act on classes, we want to know the class of the // object it's acting on, so we can fetch the right autocomplete list var prev_token = prevToken(token.start); // autocomplete list returned depends on the operator type if (token.string == "->") { // Class function operator return methodsByClass[s2vars[prev_token.string]]; } else if(token.string == ".") { // Class property operator return propsByClass[s2vars[prev_token.string]]; } else if(token.string == "::") { // Class method operator return methodsByClass[prev_token.string]; } } // The brains: show different autocomplete lists depending on the current token. if (token.type == "def") { // Find out if this is a class-specific def var prev_token = prevToken(); if (prev_token.type == "operator" && (token.string == "->" || token.string == "." || token.string == "::")){ // if so, return the autocomplete based on class var list = classOperator(prev_token); forEach(list, maybeAdd); } else { // otherwise it may be a class or standard method? var classlist = Object.keys(methodsByClass); forEach(s2Methods.concat(classlist), maybeAdd); } } else if(/variable/.test(token.type)) { // If we're adding a variable, use the variable autocomplete list. var s2varlist = Object.keys(s2vars); forEach(s2varlist, maybeAdd); } else if (token.type == "operator"){ if (token.string == "->" || token.string == "." || token.string == "::") { var list = classOperator(token); // for all of these, we need to empty to token, so we don't try to match // the operator, and move it's start, so when the new text is inserted // it doesn't overwrite the operator. token.start = token.end; token.string = ""; found = list || []; } } else if (token.type == null) { // Slightly messy, because null tokens aren't grouped, // they're just the character before the cursor. So we // need to examine the whole line up to that token for context. var linetext = editor.getLine(cur.line).slice(0, token.end + 1); //split on whitespace, and then the last block is the one closest to the cursor var blocks = linetext.split(" ") || [linetext]; token.string = blocks[blocks.length - 1]; token.start = token.end - blocks[blocks.length - 1].length; start = token.string; forEach(keywords.concat(s2Methods), maybeAdd); } else { // we got nothin' found = []; } return {list: found, from: Pos(cur.line, token.start), to: Pos(cur.line, token.end)}; } function s2Hint(editor, options) { return scriptHint(editor, s2Keywords, function (e, cur) {return e.getTokenAt(cur);}, options); }; CodeMirror.registerHelper("hint", "s2", s2Hint); // Helper methods for computing lists from s2library.js and assigning them to variables. function getMethodsByClass() { var hints = {}; for (i=0; i