diff --git a/doc/api/editorInfo.md b/doc/api/editorInfo.md index 31361a750..afb790b99 100644 --- a/doc/api/editorInfo.md +++ b/doc/api/editorInfo.md @@ -37,13 +37,19 @@ Returns the `rep` object. ## editorInfo.ace_isCaret(?) ## editorInfo.ace_getLineAndCharForPoint(?) ## editorInfo.ace_performDocumentApplyAttributesToCharRange(?) -## editorInfo.ace_setAttributeOnSelection(?) +## editorInfo.ace_setAttributeOnSelection(attribute, enabled) +Sets an attribute on current range. +Example: `call.editorInfo.ace_setAttributeOnSelection("turkey::balls", true); // turkey is the attribute here, balls is the value +Notes: to remove the attribute pass enabled as false ## editorInfo.ace_toggleAttributeOnSelection(?) -## editorInfo.ace_getAttributeOnSelection(attribute) +## editorInfo.ace_getAttributeOnSelection(attribute, prevChar) Returns a boolean if an attribute exists on a selected range. +prevChar value should be true if you want to get the previous Character attribute instead of the current selection for example +if the caret is at position 0,1 (after first character) it's probable you want the attributes on the character at 0,0 The attribute should be the string name of the attribute applied to the selection IE subscript Example usage: Apply the activeButton Class to a button if an attribute is on a highlighted/selected caret position or range. -Example: `call.editorInfo.ace_getAttributeOnSelection("subscript");` // call here is the callstack from aceEditEvent. +Example `var isItThere = documentAttributeManager.getAttributeOnSelection("turkey::balls", true);` + See the ep_subscript plugin for an example of this function in action. Notes: Does not work on first or last character of a line. Suffers from a race condition if called with aceEditEvent. ## editorInfo.ace_performSelectionChange(?) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index d4d20a1f7..2bd3b6a27 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -179,6 +179,89 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return []; }, + /* + Gets a given attribute on a selection + @param attributeName + @param prevChar + returns true or false if an attribute is visible in range + */ + getAttributeOnSelection: function(attributeName, prevChar){ + var rep = this.rep; + if (!(rep.selStart && rep.selEnd)) return + // If we're looking for the caret attribute not the selection + // has the user already got a selection or is this purely a caret location? + var isNotSelection = (rep.selStart[0] == rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]); + if(isNotSelection){ + if(prevChar){ + // If it's not the start of the line + if(rep.selStart[1] !== 0){ + rep.selStart[1]--; + } + } + } + + var withIt = Changeset.makeAttribsString('+', [ + [attributeName, 'true'] + ], rep.apool); + var withItRegex = new RegExp(withIt.replace(/\*/g, '\\*') + "(\\*|$)"); + function hasIt(attribs) + { + return withItRegex.test(attribs); + } + + return rangeHasAttrib(rep.selStart, rep.selEnd) + + function rangeHasAttrib(selStart, selEnd) { + // if range is collapsed -> no attribs in range + if(selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false + + if(selStart[0] != selEnd[0]) { // -> More than one line selected + var hasAttrib = true + + // from selStart to the end of the first line + hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]) + + // for all lines in between + for(var n=selStart[0]+1; n < selEnd[0]; n++) { + hasAttrib = hasAttrib && rangeHasAttrib([n, 0], [n, rep.lines.atIndex(n).text.length]) + } + + // for the last, potentially partial, line + hasAttrib = hasAttrib && rangeHasAttrib([selEnd[0], 0], [selEnd[0], selEnd[1]]) + + return hasAttrib + } + + // Logic tells us we now have a range on a single line + + var lineNum = selStart[0] + , start = selStart[1] + , end = selEnd[1] + , hasAttrib = true + + // Iterate over attribs on this line + + var opIter = Changeset.opIterator(rep.alines[lineNum]) + , indexIntoLine = 0 + + while (opIter.hasNext()) { + var op = opIter.next(); + var opStartInLine = indexIntoLine; + var opEndInLine = opStartInLine + op.chars; + if (!hasIt(op.attribs)) { + // does op overlap selection? + if (!(opEndInLine <= start || opStartInLine >= end)) { + hasAttrib = false; // since it's overlapping but hasn't got the attrib -> range hasn't got it + break; + } + } + indexIntoLine = opEndInLine; + } + + return hasAttrib + } + }, + /* Gets all attributes at a position containing line number and column @param lineNumber starting with zero