protected List<IRegion> getAttributeValueRegions(ITextViewer viewer) { List<IRegion> regions = new ArrayList<IRegion>(); IDocument document = viewer.getDocument(); int startOffset = 0; int endOffset = document.getLength(); IStructuredDocumentRegion sdRegion = null; while (startOffset < endOffset && (sdRegion = ContentAssistUtils.getStructuredDocumentRegion(viewer, startOffset)) != null) { ITextRegionList list = sdRegion.getRegions(); for (int i = 0; list != null && i < list.size(); i++) { ITextRegion region = list.get(i); if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { final int regionOffset = sdRegion.getStartOffset() + region.getStart(); final int regionLength = region.getTextLength(); regions.add(new Region(regionOffset, regionLength)); } } startOffset += sdRegion.getLength(); } return regions; }
/** * Returns a pair of (tag-balance,bracket-balance) for the range textStart to offset. * * @param doc the document * @param start the offset of the starting character (inclusive) * @param end the offset of the ending character (exclusive) * @return the balance of tags and brackets */ private static Pair<Integer, Integer> getBalance(IStructuredDocument doc, int start, int end) { // Balance of open and closing tags // <foo></foo> has tagBalance = 0, <foo> has tagBalance = 1 int tagBalance = 0; // Balance of open and closing brackets // <foo attr1="value1"> has bracketBalance = 1, <foo has bracketBalance = 1 int bracketBalance = 0; IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(start); if (region != null) { boolean inOpenTag = true; while (region != null && region.getStartOffset() < end) { int regionStart = region.getStartOffset(); ITextRegionList subRegions = region.getRegions(); for (int i = 0, n = subRegions.size(); i < n; i++) { ITextRegion subRegion = subRegions.get(i); int subRegionStart = regionStart + subRegion.getStart(); int subRegionEnd = regionStart + subRegion.getEnd(); if (subRegionEnd < start || subRegionStart >= end) { continue; } String type = subRegion.getType(); if (XML_TAG_OPEN.equals(type)) { bracketBalance++; inOpenTag = true; } else if (XML_TAG_CLOSE.equals(type)) { bracketBalance--; if (inOpenTag) { tagBalance++; } else { tagBalance--; } } else if (XML_END_TAG_OPEN.equals(type)) { bracketBalance++; inOpenTag = false; } else if (XML_EMPTY_TAG_CLOSE.equals(type)) { bracketBalance--; } } region = region.getNext(); } } return Pair.of(tagBalance, bracketBalance); }
private boolean prepareTwigRegions( Collection<StyleRange> holdResults, ITwigScriptRegion region, int regionStart, int partitionStartOffset, int partitionLength) { assert (region.getType() == TwigRegionContext.TWIG_CONTENT || region.getType() == TwigRegionContext.TWIG_COMMENT); StyleRange styleRange = null; TextAttribute attr; TextAttribute previousAttr = null; ITextRegion[] twigTokens = null; try { int from; int length; if (partitionStartOffset < regionStart) { from = 0; length = partitionLength - (regionStart - partitionStartOffset); } else { from = partitionStartOffset - regionStart; length = partitionLength; } twigTokens = region.getTwigTokens(from, Math.min(length, region.getLength())); ITextRegion prevElement = null; for (int i = 0; i < twigTokens.length; i++) { ITextRegion element = twigTokens[i]; attr = getAttributeFor(element); // Check that the elements are different - otherwise the // coloring is not valid if (prevElement == element || attr == null) { continue; } if ((styleRange != null) && (previousAttr != null) && (previousAttr.equals(attr)) && prevElement != null && prevElement.getLength() == prevElement.getLength()) { // extends the prev styleRange with the current element // length styleRange.length += element.getLength(); if (styleRange.start + styleRange.length > partitionStartOffset + partitionLength) { styleRange.length -= (styleRange.start + styleRange.length) - (partitionStartOffset + partitionLength); } } else { // create new styleRange int styleStart = regionStart + element.getStart(); int styleLength = element.getLength(); if (styleStart + styleLength < partitionStartOffset) { // if // the // range // ends // before // the // requested // starting // position // - // ignoring // it continue; } if (styleStart < partitionStartOffset) { // if the region // starts before // the requested // starting // position - // adjusting the // style start // position styleLength -= (partitionStartOffset - styleStart); styleStart = partitionStartOffset; } if (styleStart > partitionStartOffset + partitionLength) { // if the region ends after the requested end position - // making it shorter styleLength -= styleStart - (partitionStartOffset + partitionLength); } if (attr.getBackground() != null && element.getTextEnd() != element.getEnd()) { // in // case // of // background // color // make // sure // the // highlighting // will // not // paint // the // whitespaces // applying style to the region w/o the whitespace styleRange = new StyleRange( styleStart, styleLength - (element.getEnd() - element.getTextEnd()), attr.getForeground(), attr.getBackground(), attr.getStyle()); if ((attr.getStyle() & TextAttribute.UNDERLINE) != 0) { styleRange.underline = true; styleRange.fontStyle &= ~TextAttribute.UNDERLINE; } if ((attr.getStyle() & TextAttribute.STRIKETHROUGH) != 0) { styleRange.strikeout = true; styleRange.fontStyle &= ~TextAttribute.STRIKETHROUGH; } holdResults.add(styleRange); // applying style to the whitespace (important for the // refresh of the specific range styleRange = new StyleRange( regionStart + element.getTextEnd(), element.getEnd() - element.getTextEnd(), attr.getForeground(), null, attr.getStyle()); holdResults.add(styleRange); previousAttr = null; } else { styleRange = new StyleRange( styleStart, styleLength, attr.getForeground(), attr.getBackground(), attr.getStyle()); if ((attr.getStyle() & TextAttribute.UNDERLINE) != 0) { styleRange.underline = true; styleRange.fontStyle &= ~TextAttribute.UNDERLINE; } if ((attr.getStyle() & TextAttribute.STRIKETHROUGH) != 0) { styleRange.strikeout = true; styleRange.fontStyle &= ~TextAttribute.STRIKETHROUGH; } holdResults.add(styleRange); // technically speaking, we don't need to update // previousAttr // in the other case, because the other case is when // it hasn't changed previousAttr = attr; } } prevElement = element; } return true; } catch (BadLocationException e) { Logger.logException(e); return false; } }
public void customizeDocumentCommand(IDocument document, DocumentCommand c) { if (!isSmartInsertMode()) { return; } if (!(document instanceof IStructuredDocument)) { // This shouldn't happen unless this strategy is used on an invalid document return; } IStructuredDocument doc = (IStructuredDocument) document; // Handle newlines/indentation if (c.length == 0 && c.text != null && TextUtilities.endsWith(doc.getLegalLineDelimiters(), c.text) != -1) { IModelManager modelManager = StructuredModelManager.getModelManager(); IStructuredModel model = modelManager.getModelForRead(doc); if (model != null) { try { final int offset = c.offset; int lineStart = findLineStart(doc, offset); int textStart = findTextStart(doc, lineStart, offset); IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(textStart); if (region != null && region.getType().equals(XML_TAG_NAME)) { Pair<Integer, Integer> balance = getBalance(doc, textStart, offset); int tagBalance = balance.getFirst(); int bracketBalance = balance.getSecond(); String lineIndent = ""; // $NON-NLS-1$ if (textStart > lineStart) { lineIndent = doc.get(lineStart, textStart - lineStart); } // We only care if tag or bracket balance is greater than 0; // we never *dedent* on negative balances boolean addIndent = false; if (bracketBalance < 0) { // Handle // <foo // ></foo>^ // and // <foo // />^ ITextRegion left = getRegionAt(doc, offset, true /*biasLeft*/); if (left != null && (left.getType().equals(XML_TAG_CLOSE) || left.getType().equals(XML_EMPTY_TAG_CLOSE))) { // Find the corresponding open tag... // The org.eclipse.wst.xml.ui.gotoMatchingTag frequently // doesn't work, it just says "No matching brace found" // (or I would use that here). int targetBalance = 0; ITextRegion right = getRegionAt(doc, offset, false /*biasLeft*/); if (right != null && right.getType().equals(XML_END_TAG_OPEN)) { targetBalance = -1; } int openTag = AndroidXmlCharacterMatcher.findTagBackwards(doc, offset, targetBalance); if (openTag != -1) { // Look up the indentation of the given line lineIndent = AndroidXmlEditor.getIndentAtOffset(doc, openTag); } } } else if (tagBalance > 0 || bracketBalance > 0) { // Add indentation addIndent = true; } StringBuilder sb = new StringBuilder(c.text); sb.append(lineIndent); String oneIndentUnit = XmlFormatPreferences.create().getOneIndentUnit(); if (addIndent) { sb.append(oneIndentUnit); } // Handle // <foo>^</foo> // turning into // <foo> // ^ // </foo> ITextRegion left = getRegionAt(doc, offset, true /*biasLeft*/); ITextRegion right = getRegionAt(doc, offset, false /*biasLeft*/); if (left != null && right != null && left.getType().equals(XML_TAG_CLOSE) && right.getType().equals(XML_END_TAG_OPEN)) { // Move end tag if (tagBalance > 0 && bracketBalance < 0) { sb.append(oneIndentUnit); } c.caretOffset = offset + sb.length(); c.shiftsCaret = false; sb.append(TextUtilities.getDefaultLineDelimiter(doc)); sb.append(lineIndent); } c.text = sb.toString(); } else if (region != null && region.getType().equals(XML_CONTENT)) { // Indenting in text content. If you're in the middle of editing // text, just copy the current line indentation. // However, if you're editing in leading whitespace (e.g. you press // newline on a blank line following say an element) then figure // out the indentation as if the newline had been pressed at the // end of the element, and insert that amount of indentation. // In this case we need to also make sure to subtract any existing // whitespace on the current line such that if we have // // <foo> // ^ <bar/> // </foo> // // you end up with // // <foo> // // ^<bar/> // </foo> // String text = region.getText(); int regionStart = region.getStartOffset(); int delta = offset - regionStart; boolean inWhitespacePrefix = true; for (int i = 0, n = Math.min(delta, text.length()); i < n; i++) { char ch = text.charAt(i); if (!Character.isWhitespace(ch)) { inWhitespacePrefix = false; break; } } if (inWhitespacePrefix) { IStructuredDocumentRegion previous = region.getPrevious(); if (previous != null && previous.getType() == XML_TAG_NAME) { ITextRegionList subRegions = previous.getRegions(); ITextRegion last = subRegions.get(subRegions.size() - 1); if (last.getType() == XML_TAG_CLOSE || last.getType() == XML_EMPTY_TAG_CLOSE) { int begin = AndroidXmlCharacterMatcher.findTagBackwards( doc, previous.getStartOffset() + last.getStart(), 0); int prevLineStart = findLineStart(doc, begin); int prevTextStart = findTextStart(doc, prevLineStart, begin); String lineIndent = ""; // $NON-NLS-1$ if (prevTextStart > prevLineStart) { lineIndent = doc.get(prevLineStart, prevTextStart - prevLineStart); } StringBuilder sb = new StringBuilder(c.text); sb.append(lineIndent); String oneIndentUnit = XmlFormatPreferences.create().getOneIndentUnit(); // See if there is whitespace on the insert line that // we should also remove for (int i = delta, n = text.length(); i < n; i++) { char ch = text.charAt(i); if (ch == ' ') { c.length++; } else { break; } } boolean onClosingTagLine = false; if (text.indexOf('\n', delta) == -1) { IStructuredDocumentRegion next = region.getNext(); if (next != null && next.getType() == XML_TAG_NAME) { String nextType = next.getRegions().get(0).getType(); if (nextType == XML_END_TAG_OPEN) { onClosingTagLine = true; } } } boolean addIndent = (last.getType() == XML_TAG_CLOSE) && !onClosingTagLine; if (addIndent) { sb.append(oneIndentUnit); } c.text = sb.toString(); return; } } } copyPreviousLineIndentation(doc, c); } else { copyPreviousLineIndentation(doc, c); } } catch (BadLocationException e) { AdtPlugin.log(e, null); } finally { model.releaseFromRead(); } } } }
public void equatePositions(ITextRegion region) { fStart = region.getStart(); fLength = region.getLength(); fTextLength = region.getTextLength(); }
protected int getOffset(ITextRegion valueRegion, IDOMNode beanNode) { return valueRegion.getStart() + beanNode.getStartOffset() + 1; }