<commit_before>/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.psi.impl.source.codeStyle;

import com.intellij.lang.Language;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.util.HighlighterIteratorWrapper;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.lang.Integer.min;

/**
 * @author <NAME>
 */
public class SemanticEditorPosition {
  public interface SyntaxElement {}
  
  private final EditorEx myEditor;
  private final HighlighterIterator myIterator;
  private final CharSequence myChars;
  private final Function<IElementType, SyntaxElement> myTypeMapper;
  private final BiFunction<? super EditorEx, ? super Integer, ? extends HighlighterIterator> myCreateHighlighterIteratorAtOffset;

  private SemanticEditorPosition(@NotNull EditorEx editor, int offset,
                                 @NotNull BiFunction<? super EditorEx, ? super Integer, ? extends HighlighterIterator> createHighlighterIteratorAtOffset,
                                 @NotNull Function<IElementType, SyntaxElement> typeMapper) {
    myCreateHighlighterIteratorAtOffset = createHighlighterIteratorAtOffset;
    myEditor = editor;
    myChars = myEditor.getDocument().getCharsSequence();
    myIterator = createHighlighterIteratorAtOffset.apply(editor, offset);
    myTypeMapper = typeMapper;
  }

  public void moveBeforeOptional(@NotNull SyntaxElement syntaxElement) {
    if (!myIterator.atEnd()) {
      if (syntaxElement.equals(map(myIterator.getTokenType()))) myIterator.retreat();
    }
  }

  public SemanticEditorPosition beforeOptional(@NotNull SyntaxElement syntaxElement) {
    return copyAnd(position -> position.moveBeforeOptional(syntaxElement));
  }
  
  public void moveBeforeOptionalMix(SyntaxElement @NotNull ... elements) {
    while (isAtAnyOf(elements)) {
      myIterator.retreat();
    }
  }

  public SemanticEditorPosition beforeOptionalMix(SyntaxElement @NotNull ... elements) {
    return copyAnd(position -> position.moveBeforeOptionalMix(elements));
  }
  
  public void moveAfterOptionalMix(SyntaxElement @NotNull ... elements)  {
    while (isAtAnyOf(elements)) {
      myIterator.advance();
    }
  }


  public SemanticEditorPosition afterOptionalMix(SyntaxElement @NotNull ... elements) {
    return copyAnd(position -> position.moveAfterOptionalMix(elements));
  }

  public boolean isAtMultiline() {
    if (!myIterator.atEnd()) {
      return CharArrayUtil.containLineBreaks(myChars, myIterator.getStart(), myIterator.getEnd());
    }
    return false;
  }

  /**
   * Checks if there are line breaks strictly after the given offset till the end of the current element.
   *
   * @param offset The offset to search line breaks after.
   * @return True if there are line breaks after the given offset.
   */
  public boolean hasLineBreaksAfter(int offset) {
    if (!myIterator.atEnd() && offset >= 0) {
      int offsetAfter = offset + 1;
      if (offsetAfter < myIterator.getEnd()) {
        return CharArrayUtil.containLineBreaks(myChars, offsetAfter, myIterator.getEnd());
      }
    }
    return false;
  }

  public boolean isAtMultiline(SyntaxElement... elements) {
    return isAtAnyOf(elements) && CharArrayUtil.containLineBreaks(myChars, myIterator.getStart(), myIterator.getEnd());
  }
  
  public void moveBefore() {
    if (!myIterator.atEnd()) {
      myIterator.retreat();
    }
  }

  public SemanticEditorPosition before() {
    return copyAnd(position -> position.moveBefore());
  }
  
  public void moveAfterOptional(@NotNull SyntaxElement syntaxElement) {
    if (!myIterator.atEnd()) {
      if (syntaxElement.equals(map(myIterator.getTokenType()))) myIterator.advance();
    }
  }

  public SemanticEditorPosition afterOptional(@NotNull SyntaxElement syntaxElement) {
    return copyAnd(position -> position.moveAfterOptional(syntaxElement));
  }

  public void moveAfter() {
    if (!myIterator.atEnd()) {
      myIterator.advance();
    }
  }

  public SemanticEditorPosition after() {
    return copyAnd(position -> position.moveAfter());
  }
  
  public void moveBeforeParentheses(@NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    moveParentheses(true,leftParenthesis, rightParenthesis);
  }

  public void moveAfterParentheses(@NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    moveParentheses(false,leftParenthesis, rightParenthesis);
  }

  protected void moveParentheses(boolean before, @NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    int parenLevel = 0;
    while (!myIterator.atEnd()) {
      SyntaxElement currElement = map(myIterator.getTokenType());
      if (before) {
        myIterator.retreat();
      }
      else {
        myIterator.advance();
      }
      if (rightParenthesis.equals(currElement)) {
        parenLevel++;
      }
      else if (leftParenthesis.equals(currElement)) {
        parenLevel--;
        if (parenLevel < 1) {
          break;
        }
      }
    }
  }

  public SemanticEditorPosition beforeParentheses(@NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    return copyAnd(position -> position.moveBeforeParentheses(leftParenthesis, rightParenthesis));
  }

  public void moveToLeftParenthesisBackwardsSkippingNested(@NotNull SyntaxElement leftParenthesis,
                                                           @NotNull SyntaxElement rightParenthesis) {
    moveToLeftParenthesisBackwardsSkippingNestedWithPredicate(leftParenthesis, rightParenthesis, any -> false);
  }<commit_msg>CPP-13764 "Align when multiline" ignored when inserting newline<commit_after>/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.psi.impl.source.codeStyle;

import com.intellij.lang.Language;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.util.HighlighterIteratorWrapper;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.lang.Integer.min;

/**
 * @author <NAME>
 */
public class SemanticEditorPosition {
  public interface SyntaxElement {}
  
  private final EditorEx myEditor;
  private final HighlighterIterator myIterator;
  private final CharSequence myChars;
  private final Function<IElementType, SyntaxElement> myTypeMapper;
  private final BiFunction<? super EditorEx, ? super Integer, ? extends HighlighterIterator> myCreateHighlighterIteratorAtOffset;

  private SemanticEditorPosition(@NotNull EditorEx editor, int offset,
                                 @NotNull BiFunction<? super EditorEx, ? super Integer, ? extends HighlighterIterator> createHighlighterIteratorAtOffset,
                                 @NotNull Function<IElementType, SyntaxElement> typeMapper) {
    myCreateHighlighterIteratorAtOffset = createHighlighterIteratorAtOffset;
    myEditor = editor;
    myChars = myEditor.getDocument().getCharsSequence();
    myIterator = createHighlighterIteratorAtOffset.apply(editor, offset);
    myTypeMapper = typeMapper;
  }

  public void moveBeforeOptional(@NotNull SyntaxElement syntaxElement) {
    if (!myIterator.atEnd()) {
      if (syntaxElement.equals(map(myIterator.getTokenType()))) myIterator.retreat();
    }
  }

  public SemanticEditorPosition beforeOptional(@NotNull SyntaxElement syntaxElement) {
    return copyAnd(position -> position.moveBeforeOptional(syntaxElement));
  }
  
  public void moveBeforeOptionalMix(SyntaxElement @NotNull ... elements) {
    while (isAtAnyOf(elements)) {
      myIterator.retreat();
    }
  }

  public SemanticEditorPosition beforeOptionalMix(SyntaxElement @NotNull ... elements) {
    return copyAnd(position -> position.moveBeforeOptionalMix(elements));
  }
  
  public void moveAfterOptionalMix(SyntaxElement @NotNull ... elements)  {
    while (isAtAnyOf(elements)) {
      myIterator.advance();
    }
  }


  public SemanticEditorPosition afterOptionalMix(SyntaxElement @NotNull ... elements) {
    return copyAnd(position -> position.moveAfterOptionalMix(elements));
  }

  public boolean isAtMultiline() {
    if (!myIterator.atEnd()) {
      return CharArrayUtil.containLineBreaks(myChars, myIterator.getStart(), myIterator.getEnd());
    }
    return false;
  }

  /**
   * Checks if there are line breaks strictly after the given offset till the end of the current element.
   *
   * @param offset The offset to search line breaks after.
   * @return True if there are line breaks after the given offset.
   */
  public boolean hasLineBreaksAfter(int offset) {
    if (!myIterator.atEnd() && offset >= 0) {
      int offsetAfter = offset + 1;
      if (offsetAfter < myIterator.getEnd()) {
        return CharArrayUtil.containLineBreaks(myChars, offsetAfter, myIterator.getEnd());
      }
    }
    return false;
  }

  public boolean isAtMultiline(SyntaxElement... elements) {
    return isAtAnyOf(elements) && CharArrayUtil.containLineBreaks(myChars, myIterator.getStart(), myIterator.getEnd());
  }
  
  public void moveBefore() {
    if (!myIterator.atEnd()) {
      myIterator.retreat();
    }
  }

  public SemanticEditorPosition before() {
    return copyAnd(position -> position.moveBefore());
  }
  
  public void moveAfterOptional(@NotNull SyntaxElement syntaxElement) {
    if (!myIterator.atEnd()) {
      if (syntaxElement.equals(map(myIterator.getTokenType()))) myIterator.advance();
    }
  }

  public SemanticEditorPosition afterOptional(@NotNull SyntaxElement syntaxElement) {
    return copyAnd(position -> position.moveAfterOptional(syntaxElement));
  }

  public void moveAfter() {
    if (!myIterator.atEnd()) {
      myIterator.advance();
    }
  }

  public SemanticEditorPosition after() {
    return copyAnd(position -> position.moveAfter());
  }
  
  public void moveBeforeParentheses(@NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    skipParentheses(false, leftParenthesis, rightParenthesis);
  }

  public void moveAfterParentheses(@NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    skipParentheses(true, leftParenthesis, rightParenthesis);
  }

  protected void skipParentheses(boolean forward, @NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    int parenLevel = 0;
    while (!myIterator.atEnd()) {
      SyntaxElement currElement = map(myIterator.getTokenType());
      if (forward) {
        myIterator.advance();
      }
      else {
        myIterator.retreat();
      }
      if (rightParenthesis.equals(currElement)) {
        parenLevel++;
      }
      else if (leftParenthesis.equals(currElement)) {
        parenLevel--;
        if (parenLevel < 1) {
          break;
        }
      }
    }
  }

  public SemanticEditorPosition beforeParentheses(@NotNull SyntaxElement leftParenthesis, @NotNull SyntaxElement rightParenthesis) {
    return copyAnd(position -> position.moveBeforeParentheses(leftParenthesis, rightParenthesis));
  }

  public void moveToLeftParenthesisBackwardsSkippingNested(@NotNull SyntaxElement leftParenthesis,
                                                           @NotNull SyntaxElement rightParenthesis) {
    moveToLeftParenthesisBackwardsSkippingNestedWithPredicate(leftParenthesis, rightParenthesis, any -> false);
  }
{'fraction_non_alphanumeric': 0.09276850007660488, 'fraction_numerical': 0.0028343802665849547, 'mean_word_length': 3.931998488855308, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3877420', 'n_tokens_mistral': 3604, 'n_tokens_neox': 3503, 'n_words': 861}