@Override
 public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
   try {
     if ("-".equals(c.text) && c.offset >= 3 && d.get(c.offset - 3, 3).equals("<!-")) {
       c.text = "-  -->";
       c.shiftsCaret = false;
       c.caretOffset = c.offset + 2;
       c.doit = false;
       return;
     }
     if ("[".equals(c.text) && c.offset >= 2 && d.get(c.offset - 2, 2).equals("<!")) {
       c.text = "[CDATA[]]>";
       c.shiftsCaret = false;
       c.caretOffset = c.offset + 7;
       c.doit = false;
       return;
     }
     if ("l".equals(c.text) && c.offset >= 4 && d.get(c.offset - 4, 4).equals("<?xm")) {
       c.text = "l version = \"1.0\" encoding = \"" + charset + "\"?>";
       return;
     }
   } catch (BadLocationException e) {
     HTMLPlugin.logException(e);
   }
   super.customizeDocumentCommand(d, c);
 }
 @Override
 public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
   try {
     //			if("%".equals(c.text) && c.offset > 0){
     //				if(d.getChar(c.offset - 1) == '<'){
     //					c.text = "% %>";
     //					c.shiftsCaret = false;
     //					c.caretOffset = c.offset + 1;
     //					c.doit = false;
     //					return;
     //				}
     //			}
     if ("{".equals(c.text) && c.offset > 0) {
       if (d.getChar(c.offset - 1) == '$') {
         c.text = "{}";
         c.shiftsCaret = false;
         c.caretOffset = c.offset + 1;
         c.doit = false;
         return;
       }
     }
     if ("-".equals(c.text) && c.offset >= 3 && d.get(c.offset - 3, 3).equals("<%-")) {
       c.text = "-  --%>";
       c.shiftsCaret = false;
       c.caretOffset = c.offset + 2;
       c.doit = false;
       return;
     }
   } catch (BadLocationException e) {
     HTMLPlugin.logException(e);
   }
   super.customizeDocumentCommand(d, c);
 }
  @Test
  public void testBacketHandling() {

    IDocument document = new Document();
    document.set(input.replace(CURSOR, ""));
    DocumentCommand command = new DocumentCommandStub();
    command.text = textToInsert;
    // insert text at cursor position
    command.offset = input.indexOf(CURSOR);
    // position cursor at correct position in input text
    command.caretOffset = input.indexOf(CURSOR);
    command.shiftsCaret = true;
    stategy.customizeDocumentCommand(document, command);
    System.out.println("text = '" + command.text + "'");
    String expectedDocumentAfter = expected.replace(CURSOR, "");

    System.out.println("Cursor before " + command.caretOffset);
    executeCommand(document, command);
    System.out.println("Cursor after " + command.caretOffset);
    String documentAfter = document.get();
    System.out.println("Document after: '" + documentAfter + "'");
    assertEquals(expectedDocumentAfter, documentAfter);
    int expectedCursorOffset = expected.indexOf(CURSOR);
    assertEquals("Cursor at unexpected position", expectedCursorOffset, command.caretOffset);
  }
 private void appendIndent(
     boolean isContinuation,
     boolean isOpening,
     boolean isClosing,
     boolean isListContinuation,
     int start,
     int end,
     boolean closeBraces,
     StringBuilder buf)
     throws BadLocationException {
   int line = document.getLineOfOffset(start);
   String indent = getIndent(start, end);
   String delim = getLineDelimiter(document, line);
   buf.append(indent);
   if (isOpening || isListContinuation) {
     if (isClosing) {
       if (closeBraces && isOpening) {
         // increment the indent level
         incrementIndent(buf, indent);
         // move the closing brace to next line
         command.shiftsCaret = false;
         command.caretOffset = command.offset + buf.length();
         buf.append(delim).append(indent);
       }
       //                else if (closeBraces && //hack just to distinguish a newline from a
       // correct indentation!
       //                        isListContinuation) {
       //                    //just increment the indent level
       //                    incrementIndent(buf, indent);
       //                }
     } else {
       // increment the indent level
       incrementIndent(buf, indent);
       if (closeBraces && count("{") > count("}")) {
         // close the opening brace
         command.shiftsCaret = false;
         command.caretOffset = command.offset + buf.length();
         buf.append(delim).append(indent).append('}');
       }
     }
   } else if (isContinuation) {
     incrementIndent(buf, indent);
     incrementIndent(buf, indent);
   } else if (isClosing) {
     decrementIndent(buf, indent);
   }
 }
  private void fixIndentOfCurrentLine() throws BadLocationException {

    int start = getStartOfCurrentLine();
    int end = getEndOfCurrentLine();
    int endOfWs = firstEndOfWhitespace(start, end);

    // we want this to happen in three cases:
    // 1. the user types a tab in the whitespace
    //    at the start of the line
    // 2. the user types { or ( at the start of
    //    the line
    // 3. Correct Indentation is calling us
    // test for Correct Indentation action

    boolean correctingIndentation = command.offset == start && !command.shiftsCaret;
    boolean opening = command.text.equals("{") || command.text.equals("(");
    if (command.offset < endOfWs
        || // we want strictly < since we don't want to prevent the caret advancing when a space is
           // typed
        command.offset == endOfWs && endOfWs == end && opening
        || // this can cause the caret to jump *backwards* when a { or ( is typed!
        correctingIndentation) {

      int endOfPrev = getEndOfPreviousLine();
      int startOfPrev = getStartOfPreviousLine();
      char startOfCurrentLineChar =
          opening
              ? command.text.charAt(0)
              : // the typed character is now the first character in the line
              getNextNonHiddenCharacterInLine(start);
      char endOfLastLineChar = getPreviousNonHiddenCharacterInLine(endOfPrev);

      StringBuilder buf = new StringBuilder();
      appendIndent(
          start,
          end,
          startOfPrev,
          endOfPrev,
          startOfCurrentLineChar,
          endOfLastLineChar,
          false,
          buf);

      int len = endOfWs - start;
      String text = buf.toString();
      if (text.length() != len || !document.get(start, len).equals(text)) {
        if (opening) {
          text += command.text;
        }
        command.text = text;
        command.offset = start;
        command.length = len;
      } else if (!opening) {
        command.caretOffset = start + len;
        command.shiftsCaret = false;
        command.text = null;
      }
    }
  }
 private void closeUnterminatedMultlineComment() {
   if (isInUnterminatedMultilineComment(command.offset, document)) {
     command.shiftsCaret = false;
     String text = command.text;
     command.caretOffset = command.offset + text.length();
     command.text =
         text
             + text
             +
             // uncomment to get a bigger indent
             //                    (text.indexOf(' ')>=0 ?
             //                     text.replaceFirst(" ", "") : text) +
             "*/";
   }
 }
  private void handleSmartTrigger(IDocument document, char trigger, int referenceOffset)
      throws BadLocationException {
    DocumentCommand cmd = new DocumentCommand() {};

    cmd.offset = referenceOffset;
    cmd.length = 0;
    cmd.text = Character.toString(trigger);
    cmd.doit = true;
    cmd.shiftsCaret = true;
    cmd.caretOffset = getReplacementOffset() + getCursorPosition();

    SmartSemicolonAutoEditStrategy strategy =
        new SmartSemicolonAutoEditStrategy(DartPartitions.DART_PARTITIONING);
    strategy.customizeDocumentCommand(document, cmd);

    replace(document, cmd.offset, cmd.length, cmd.text);
    setCursorPosition(cmd.caretOffset - getReplacementOffset() + cmd.text.length());
  }
  private void autoEditAfterNewLine(IDocument document, DocumentCommand command) {
    if (command.offset == -1 || document.getLength() == 0) {
      return;
    }

    try {
      int p = command.offset == document.getLength() ? command.offset - 1 : command.offset;
      IRegion info = document.getLineInformationOfOffset(p);
      int start = info.getOffset();

      StringBuffer buf = new StringBuffer(command.text);

      int end = findEndOfWhiteSpaceAfter(document, start, command.offset);

      String lineSpaces = (end > start) ? document.get(start, end - start) : "";
      buf.append(lineSpaces);

      if (isAfterOpenBrace(document, command.offset - 1, start)) {
        buf.append(
            IndenterUtil.createWhiteSpace(1, 0, TextUtilities.getDefaultLineDelimiter(document)));

        if (isBeforeCloseBrace(document, command.offset, info.getOffset() + info.getLength())) {
          command.shiftsCaret = false;
          command.caretOffset = command.offset + buf.length();

          buf.append(command.text);
          buf.append(lineSpaces);
        }
        command.text = buf.toString();
      } else {
        int indent = computeIndentCount(document, command.offset);
        if (isBeforeCloseBrace(document, command.offset, info.getOffset() + info.getLength())) {
          indent--;
        }
        command.text +=
            IndenterUtil.createWhiteSpace(
                indent, 0, TextUtilities.getDefaultLineDelimiter(document));
      }
    } catch (BadLocationException e) {
      KotlinLogger.logAndThrow(e);
    }
  }
  private void closeOpening() {

    if (isCommented(command.offset)) {
      return;
    }

    boolean quoted = isQuoted(command.offset);
    String current = command.text;

    try {
      // don't close fences after a backslash escape
      // TODO: improve this, check the surrounding
      //       token type!
      if (command.offset > 0 && document.getChar(command.offset - 1) == '\\') {
        return;
      }
      // don't close fences before an identifier,
      // literal or opening paren
      if (!quoted
          &&
          // there are special rules for < below
          !current.equals("<")) {
        int curr = command.offset;
        while (curr < document.getLength()) {
          char ch = document.getChar(curr++);
          if (isLetter(ch) || isDigit(ch) || ch == '(') {
            return;
          }
          if (ch == '"' && !"\"".equals(current) && !"`".equals(current)) {
            return;
          }
          if (ch == '\'' && !"\'".equals(current)) {
            return;
          }
          if (ch != ' ') {
            break;
          }
        }
      }
    } catch (BadLocationException e) {
    }

    String opening = null;
    String closing = null;

    boolean found = false;
    IPreferenceStore store = getPreferences();
    for (String[] type : FENCES) {
      if (type[0].equals(current) || type[1].equals(current)) {
        if (store == null || store.getBoolean(type[2])) {
          opening = type[0];
          closing = type[1];
          found = true;
          break;
        }
      }
    }

    if (found) {

      if (current.equals(closing)) {
        // typed character is a closing fence
        try {
          // skip one ahead if next char is already a closing fence
          if (skipClosingFence(closing) && closeOpeningFence(opening, closing)) {
            command.text = null;
            command.shiftsCaret = false;
            command.caretOffset = command.offset + 1;
            return;
          }
        } catch (BadLocationException e) {
        }
      }

      boolean isInterpolation = isGraveAccentCharacterInStringLiteral(command.offset, opening);
      boolean isDocLink = isOpeningBracketInAnnotationStringLiteral(command.offset, opening);
      if (current.equals(opening) && (isInterpolation || isDocLink || !quoted)) {
        // typed character is an opening fence
        if (isInterpolation || isDocLink || closeOpeningFence(opening, closing)) {
          // add a closing fence
          command.shiftsCaret = false;
          command.caretOffset = command.offset + 1;
          if (isInterpolation) {
            try {
              if (command.offset > 1
                  && document.get(command.offset - 1, 1).equals("`")
                  && !document.get(command.offset - 2, 1).equals("`")) {
                command.text += "``";
              }
            } catch (BadLocationException e) {
            }
          } else if (isDocLink) {
            try {
              if (command.offset > 1
                  && document.get(command.offset - 1, 1).equals("[")
                  && !document.get(command.offset - 2, 1).equals("]")) {
                command.text += "]]";
              }
            } catch (BadLocationException e) {
            }
          } else if (opening.equals("\"")) {
            try {
              if (command.offset <= 1 || !document.get(command.offset - 1, 1).equals("\"")) {
                command.text += closing;
              } else if (command.offset > 1
                  && document.get(command.offset - 2, 2).equals("\"\"")
                  && !(command.offset > 2 && document.get(command.offset - 3, 1).equals("\""))) {
                command.text += "\"\"\"";
              }
            } catch (BadLocationException e) {
            }
          } else {
            command.text += closing;
          }
        }
      }
    }
  }
  private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) {
    JavaHeuristicScanner scanner = new JavaHeuristicScanner(d);
    JavaIndenter indenter = new JavaIndenter(d, scanner, fProject);
    StringBuffer indent = indenter.computeIndentation(c.offset);
    if (indent == null) indent = new StringBuffer();

    int docLength = d.getLength();
    if (c.offset == -1 || docLength == 0) return;

    try {
      int p = (c.offset == docLength ? c.offset - 1 : c.offset);
      int line = d.getLineOfOffset(p);

      StringBuffer buf = new StringBuffer(c.text + indent);

      IRegion reg = d.getLineInformation(line);
      int lineEnd = reg.getOffset() + reg.getLength();

      int contentStart = findEndOfWhiteSpace(d, c.offset, lineEnd);
      c.length = Math.max(contentStart - c.offset, 0);

      int start = reg.getOffset();
      ITypedRegion region = TextUtilities.getPartition(d, fPartitioning, start, true);
      if (IJavaPartitions.JAVA_DOC.equals(region.getType()))
        start = d.getLineInformationOfOffset(region.getOffset()).getOffset();

      // insert closing brace on new line after an unclosed opening brace
      if (getBracketCount(d, start, c.offset, true) > 0
          && closeBrace()
          && !isClosed(d, c.offset, c.length)) {
        c.caretOffset = c.offset + buf.length();
        c.shiftsCaret = false;

        // copy old content of line behind insertion point to new line
        // unless we think we are inserting an anonymous type definition

        if (c.offset == 0
            || computeAnonymousPosition(d, c.offset - 1, fPartitioning, lineEnd) == -1) {
          if (lineEnd - contentStart > 0) {
            c.length = lineEnd - c.offset;
            buf.append(d.get(contentStart, lineEnd - contentStart).toCharArray());
          }
        }

        buf.append(TextUtilities.getDefaultLineDelimiter(d));
        StringBuffer reference = null;
        int nonWS = findEndOfWhiteSpace(d, start, lineEnd);
        if (nonWS < c.offset && d.getChar(nonWS) == '{')
          reference = new StringBuffer(d.get(start, nonWS - start));
        else reference = indenter.getReferenceIndentation(c.offset);
        if (reference != null) buf.append(reference);
        buf.append('}');
      }
      // insert extra line upon new line between two braces
      else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') {
        int firstCharPos = scanner.findNonWhitespaceBackward(c.offset - 1, start);
        if (firstCharPos != JavaHeuristicScanner.NOT_FOUND && d.getChar(firstCharPos) == '{') {
          c.caretOffset = c.offset + buf.length();
          c.shiftsCaret = false;

          StringBuffer reference = null;
          int nonWS = findEndOfWhiteSpace(d, start, lineEnd);
          if (nonWS < c.offset && d.getChar(nonWS) == '{')
            reference = new StringBuffer(d.get(start, nonWS - start));
          else reference = indenter.getReferenceIndentation(c.offset);

          buf.append(TextUtilities.getDefaultLineDelimiter(d));

          if (reference != null) buf.append(reference);
        }
      }
      c.text = buf.toString();

    } catch (BadLocationException e) {
      JavaPlugin.log(e);
    }
  }
  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();
        }
      }
    }
  }