<commit_before>package com.digdug.wesjohnston.richedittext;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.ImageSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Menu;
import android.widget.EditText;

import com.digdug.wesjohnston.richedittext.generators.AlignmentGenerator;
import com.digdug.wesjohnston.richedittext.generators.BackgroundColorGenerator;
import com.digdug.wesjohnston.richedittext.generators.BulletGenerator;
import com.digdug.wesjohnston.richedittext.generators.FontGenerator;
import com.digdug.wesjohnston.richedittext.generators.ForegroundColorGenerator;
import com.digdug.wesjohnston.richedittext.generators.ImageGenerator;
import com.digdug.wesjohnston.richedittext.generators.QuoteGenerator;
import com.digdug.wesjohnston.richedittext.generators.SizeGenerator;
import com.digdug.wesjohnston.richedittext.generators.SpanGenerator;
import com.digdug.wesjohnston.richedittext.generators.StrikethroughGenerator;
import com.digdug.wesjohnston.richedittext.generators.StyleGenerator;
import com.digdug.wesjohnston.richedittext.generators.SubscriptSpanGenerator;
import com.digdug.wesjohnston.richedittext.generators.SuperscriptSpanGenerator;
import com.digdug.wesjohnston.richedittext.generators.TextAppearanceGenerator;
import com.digdug.wesjohnston.richedittext.generators.URLGenerator;
import com.digdug.wesjohnston.richedittext.generators.UnderlineGenerator;
import com.digdug.wesjohnston.richedittext.range.Range;
import com.digdug.wesjohnston.richedittext.range.RangeList;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class RichEditText extends EditText {
    private static final String LOGTAG = RichEditText.class.getSimpleName();
    private SpanGenerator strikethroughGenerator = new StrikethroughGenerator();
    private SpanGenerator underlineGenerator = new UnderlineGenerator();
    private SpanGenerator boldGenerator = new StyleGenerator(Typeface.BOLD);
    private SpanGenerator italicGenerator = new StyleGenerator(Typeface.ITALIC);
    private SpanGenerator subscriptGenerator = new SubscriptSpanGenerator();
    private SpanGenerator superscriptGenerator = new SuperscriptSpanGenerator();

    public RichEditText(Context context) {
        super(context);
    }

    public RichEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RichEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RichEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public void setAlignment(Layout.Alignment alignment) {
        AlignmentGenerator generator = new AlignmentGenerator(alignment);
        generator.apply(this);
        notifyStyleChange();
    }

    public Layout.Alignment getAlignment() {
        AlignmentGenerator generator = new AlignmentGenerator(Layout.Alignment.ALIGN_NORMAL);
        return generator.getCurrentValue(this);
    }

    public void setTextSize(int size) {
        SizeGenerator generator = new SizeGenerator(size);
        toggleStyle(generator, StyleMode.FORCE_SET);
    }

    @Override
    public float getTextSize() {
        SizeGenerator generator = new SizeGenerator(0);
        return generator.getCurrentValue(this);
    }

    public void addImage(File path) {
        ImageGenerator generator = new ImageGenerator(path);
        addImage(generator);
    }

    public Object getImage() {
        ImageGenerator generator = new ImageGenerator();
        return generator.getCurrentValue(this);
    }

    private enum StyleMode {
        FORCE_SET,
        FORCE_CLEAR,
        TOGGLE
    }

    private <T,V> void toggleStyle(SpanGenerator<T, V> generator, StyleMode mode) {
        Range selection = new Range(getSelectionStart(), getSelectionEnd());

        RangeList ranges = new RangeList();
        Editable text = getText();
        T startSpan = null;
        T[] styles = text.getSpans(selection.start, selection.end, generator.getClassType());

        for (T style : styles) {
            if (generator.isSpan(style)) {
                Range spanRange = new Range(text.getSpanStart(style), text.getSpanEnd(style));
                ranges.addRange(spanRange);

                if (startSpan == null || spanRange.start < ranges.getStart()) {
                    startSpan = style;
                }

                text.removeSpan(style);
            }
        }

        // If this is a zero length selection, and we have a range inside it, we need to cut off the tail of the style.
        //if (selection.start == selection.end && ranges.size() > 0) {
        //    text.setSpan(startSpan, ranges.getStart(), ranges.getEnd(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        //} else
        if (ranges.contains(selection)) {
            ranges.remove(selection);
            int i = 0;
            for (Range r : ranges) {
                if (ranges.size() == 1) {
                    text.setSpan(startSpan, r.start, r.end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                } else {
                    if (i == 0) {
                        text.setSpan(startSpan, r.start, r.end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);<commit_msg>Fix FORCE_SET mode for styles that can't/shouldn't be toggled<commit_after>package com.digdug.wesjohnston.richedittext;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.ImageSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Menu;
import android.widget.EditText;

import com.digdug.wesjohnston.richedittext.generators.AlignmentGenerator;
import com.digdug.wesjohnston.richedittext.generators.BackgroundColorGenerator;
import com.digdug.wesjohnston.richedittext.generators.BulletGenerator;
import com.digdug.wesjohnston.richedittext.generators.FontGenerator;
import com.digdug.wesjohnston.richedittext.generators.ForegroundColorGenerator;
import com.digdug.wesjohnston.richedittext.generators.ImageGenerator;
import com.digdug.wesjohnston.richedittext.generators.QuoteGenerator;
import com.digdug.wesjohnston.richedittext.generators.SizeGenerator;
import com.digdug.wesjohnston.richedittext.generators.SpanGenerator;
import com.digdug.wesjohnston.richedittext.generators.StrikethroughGenerator;
import com.digdug.wesjohnston.richedittext.generators.StyleGenerator;
import com.digdug.wesjohnston.richedittext.generators.SubscriptSpanGenerator;
import com.digdug.wesjohnston.richedittext.generators.SuperscriptSpanGenerator;
import com.digdug.wesjohnston.richedittext.generators.TextAppearanceGenerator;
import com.digdug.wesjohnston.richedittext.generators.URLGenerator;
import com.digdug.wesjohnston.richedittext.generators.UnderlineGenerator;
import com.digdug.wesjohnston.richedittext.range.Range;
import com.digdug.wesjohnston.richedittext.range.RangeList;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class RichEditText extends EditText {
    private static final String LOGTAG = RichEditText.class.getSimpleName();
    private SpanGenerator strikethroughGenerator = new StrikethroughGenerator();
    private SpanGenerator underlineGenerator = new UnderlineGenerator();
    private SpanGenerator boldGenerator = new StyleGenerator(Typeface.BOLD);
    private SpanGenerator italicGenerator = new StyleGenerator(Typeface.ITALIC);
    private SpanGenerator subscriptGenerator = new SubscriptSpanGenerator();
    private SpanGenerator superscriptGenerator = new SuperscriptSpanGenerator();

    public RichEditText(Context context) {
        super(context);
    }

    public RichEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RichEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RichEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public void setAlignment(Layout.Alignment alignment) {
        AlignmentGenerator generator = new AlignmentGenerator(alignment);
        generator.apply(this);
        notifyStyleChange();
    }

    public Layout.Alignment getAlignment() {
        AlignmentGenerator generator = new AlignmentGenerator(Layout.Alignment.ALIGN_NORMAL);
        return generator.getCurrentValue(this);
    }

    public void setTextSize(int size) {
        SizeGenerator generator = new SizeGenerator(size);
        toggleStyle(generator, StyleMode.FORCE_SET);
    }

    @Override
    public float getTextSize() {
        SizeGenerator generator = new SizeGenerator(0);
        return generator.getCurrentValue(this);
    }

    public void addImage(File path) {
        ImageGenerator generator = new ImageGenerator(path);
        addImage(generator);
    }

    public Object getImage() {
        ImageGenerator generator = new ImageGenerator();
        return generator.getCurrentValue(this);
    }

    private enum StyleMode {
        FORCE_SET,
        FORCE_CLEAR,
        TOGGLE
    }

    private <T,V> void toggleStyle(SpanGenerator<T, V> generator, StyleMode mode) {
        Range selection = new Range(getSelectionStart(), getSelectionEnd());

        RangeList ranges = new RangeList();
        Editable text = getText();
        T startSpan = null;
        T[] styles = text.getSpans(selection.start, selection.end, generator.getClassType());

        for (T style : styles) {
            if (generator.isSpan(style)) {
                Range spanRange = new Range(text.getSpanStart(style), text.getSpanEnd(style));
                ranges.addRange(spanRange);

                if (startSpan == null || spanRange.start < ranges.getStart()) {
                    startSpan = style;
                }

                text.removeSpan(style);
            }
        }

        // If this is a zero length selection, and we have a range inside it, we need to cut off the tail of the style.
        //if (selection.start == selection.end && ranges.size() > 0) {
        //    text.setSpan(startSpan, ranges.getStart(), ranges.getEnd(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        //} else
        if (ranges.contains(selection) && mode != StyleMode.FORCE_SET) {
            ranges.remove(selection);
            int i = 0;
            for (Range r : ranges) {
                if (ranges.size() == 1) {
                    text.setSpan(startSpan, r.start, r.end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                } else {
                    if (i == 0) {
                        text.setSpan(startSpan, r.start, r.end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
{'fraction_non_alphanumeric': 0.09122927583540932, 'fraction_numerical': 0.0010308392749763765, 'mean_word_length': 3.6034005535784894, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4267387', 'n_tokens_mistral': 3395, 'n_tokens_neox': 3277, 'n_words': 663}