Moved Indentation functions to new Class for better modularization

This commit is contained in:
TrianguloY 2019-04-03 17:47:34 +02:00
parent d11f5aff26
commit bc1176eeeb
2 changed files with 236 additions and 216 deletions

View file

@ -23,7 +23,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.Pair;
import android.util.SparseArray;
@ -75,6 +74,7 @@ import net.pierrox.lightning_launcher.script.api.RectL;
import net.pierrox.lightning_launcher.script.api.StopPoint;
import net.pierrox.lightning_launcher.util.FileAndDirectoryPickerDialog;
import net.pierrox.lightning_launcher.util.FileProvider;
import net.pierrox.lightning_launcher.util.Indentation;
import net.pierrox.lightning_launcher_extreme.BuildConfig;
import net.pierrox.lightning_launcher_extreme.R;
@ -82,9 +82,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import fr.xgouchet.texteditor.ui.AdvancedEditText;
@ -125,6 +123,7 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
private CheckBox mMenuCustom;
private ArrayAdapter<Script> mScriptAdapter;
private List<Script> mAllScripts = new ArrayList<>();
private Indentation mIndentation;
private boolean mShowSubDirs;
@ -348,7 +347,9 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
mScriptText = (AdvancedEditText) findViewById(R.id.sc_text);
mScriptText.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL));
mScriptText.addTextChangedListener(mScriptTextWatcher);
mScriptText.addTextChangedListener(mScriptTextWatcherIndent);
mIndentation = new Indentation(mScriptText);
mScriptText.addTextChangedListener(mIndentation);
mScriptText.setListener(new AdvancedEditText.OnAdvancedEditTextEvent() {
private float mInitialTextSize;
@Override
@ -1246,216 +1247,6 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
mCompletionsViewGroup.addView(b);
}
// ------------- autoindentation ------------
String mSpanNewline = "mSpanNewline";
String mSpanEndBracket = "mSpanEndBracket";
boolean mEditing = false;
public static final int INDENT_SIZE = 2;
TextWatcher mScriptTextWatcherIndent = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(mEditing) return;
if(count == 1 && s.charAt(start) == '\n'){
// newline inserted
mScriptText.getText().setSpan(mSpanNewline,start,start,0);
}
if(count == 1 && s.charAt(start) == '}'){
// end bracket inserted
mScriptText.getText().setSpan(mSpanEndBracket, start, start, 0);
}
}
@Override
public void afterTextChanged(Editable editable) {
mEditing = true;
int spanPos;
// check newline
spanPos = editable.getSpanStart(mSpanNewline);
editable.removeSpan(mSpanNewline);
if (spanPos != -1 && editable.charAt(spanPos) == '\n')
onNewLine(spanPos, editable);
// check endbracket
spanPos = editable.getSpanStart(mSpanEndBracket);
editable.removeSpan(mSpanEndBracket);
if (spanPos != -1 && editable.charAt(spanPos) == '}')
onEndBracket(spanPos, editable);
mEditing = false;
}
};
/**
* Returns the size of the indent in the current line (spaces at the left) and the position of the first non-space char
* @param currentpos pos of current line (any char)
* @param editable where to search
* @return length of indent (number of spaces) and position of first non-space char (can be end of file)
*/
private Pair<Integer, Integer> getLineIndent(int currentpos, Editable editable){
// goto beginning of line
if(currentpos != 0) {
do{
currentpos--;
}while (currentpos >= 0 && editable.charAt(currentpos) != '\n');
currentpos++;
}
// find indent size
int n = 0;
boolean cont = true;
while(cont && currentpos < editable.length()){
switch (editable.charAt(currentpos)){
case ' ':
n++;
currentpos++;
break;
case '\t':
n+=INDENT_SIZE;
currentpos++;
break;
//case '\n':
default:
cont = false;
}
}
return new Pair<>(n, currentpos);
}
/**
* Called when a newline is inserted. Indents it with the same indentation as the previous line (if any)
* @param posEnter position of the newline char
* @param editable where to indent
*/
private void onNewLine(int posEnter, Editable editable){
int n = getLineIndent(posEnter, editable).first;
StringBuilder indent = new StringBuilder();
for(int i=0;i<n;++i){
indent.append(" ");
}
// do if previous line ends in open bracket
if(posEnter > 0 && editable.charAt(posEnter - 1) == '{'){
// add newline if also following close bracket
if(posEnter < editable.length() - 1 && editable.charAt(posEnter + 1) == '}'){
editable.insert(posEnter + 2, "\n" + indent.toString() + "}");
// this avoids moving the cursor
editable.replace(posEnter + 1, posEnter + 2, "");
}
// add indent size
for(int i=0;i<INDENT_SIZE;++i){
indent.append(" ");
}
}
// write indent
editable.insert(posEnter + 1, indent.toString());
}
/**
* Called when an ending bracket is inserted. Decreases the indentation.
* @param posBracket where the endbracket was
* @param editable where to unindent
*/
private void onEndBracket(int posBracket, Editable editable){
// check if beginning of line
if( posBracket == getLineIndent(posBracket, editable).second ){
// decrease indent
decreaseIndent( posBracket, editable );
}
}
/**
* Changes the indentation of all the lines selected
* @param posLeft start of selection
* @param posRight end of selection
* @param increase if trur increase indent, descrease otherwise
* @param editable where to apply the indentation
* @return the new selection (may have changed due to the indentation changes)
*/
private Pair<Integer, Integer> modifyIndent(int posLeft, int posRight, boolean increase, Editable editable){
String span = "modifyIntent";
editable.setSpan(span, posLeft, posRight, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
Deque<Integer> positions = new ArrayDeque<>();
while(posLeft <= posRight && posLeft < editable.length()){
// mark position to indent
positions.push(posLeft);
posLeft++;
// find next line
while(posLeft <= posRight && posLeft < editable.length() && editable.charAt(posLeft) != '\n')
posLeft++;
posLeft++;
}
for (Integer position : positions) {
// indent the lines in reverse order
if(increase)
increaseIndent(position, editable);
else
decreaseIndent(position, editable);
}
//restore span
return new Pair<>(editable.getSpanStart(span), editable.getSpanEnd(span));
}
/**
* Increases the indentation of a single line
* @param posCursor position of a character in the line that will be indented
* @param editable where to apply the indentation
*/
private void increaseIndent(int posCursor, Editable editable){
Pair<Integer, Integer> n_beg = getLineIndent(posCursor, editable);
int beg = n_beg.second;
// increase indent adding spaces
for(int i=0; i< INDENT_SIZE; i++) editable.insert(beg, " ");
}
/**
* Decreases the indentation of a single line
* @param posCursor position of a character in the line that will be indented
* @param editable where to apply the indentation
*/
private void decreaseIndent(int posCursor, Editable editable){
Pair<Integer, Integer> n_beg = getLineIndent(posCursor, editable);
int n = n_beg.first;
int beg = n_beg.second;
if ( n >= INDENT_SIZE ){
// enough intent to remove, remove the first tab, or all the spaces if no tabs found
int p = 1;
while (p <= INDENT_SIZE) {
if (editable.charAt(beg - p) == '\t') {
//tab found, remove
editable.delete(beg - p, beg - p + 1);
return;
}
p++;
}
// no tabs found, only spaces, remove them
editable.delete(beg - INDENT_SIZE, beg);
}
}
// -------------- shortcuts --------------------
/**
@ -1555,12 +1346,12 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
switch (action) {
case DEC_TAB:
Pair<Integer, Integer> selectionI =
modifyIndent(editText.getSelectionStart(), editText.getSelectionEnd(), false, editText.getEditableText());
mIndentation.modifyIndent(editText.getSelectionStart(), editText.getSelectionEnd(), false, editText.getEditableText());
editText.setSelection(selectionI.first, selectionI.second);
break;
case INC_TAB:
Pair<Integer, Integer> selectionD =
modifyIndent(editText.getSelectionStart(), editText.getSelectionEnd(), true, editText.getEditableText());
mIndentation.modifyIndent(editText.getSelectionStart(), editText.getSelectionEnd(), true, editText.getEditableText());
editText.setSelection(selectionD.first, selectionD.second);
break;
}

View file

@ -0,0 +1,229 @@
package net.pierrox.lightning_launcher.util;
import android.text.Editable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.Pair;
import android.widget.EditText;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* Class that [INSERT DESCRIPTION HERE]
*/
public class Indentation implements TextWatcher {
private static final int INDENT_SIZE = 2;
private EditText edTxt;
public Indentation(EditText edTxt) {
this.edTxt = edTxt;
}
// ------------ textwatcher ----------------
private String mSpanNewline = "mSpanNewline";
private String mSpanEndBracket = "mSpanEndBracket";
private boolean mEditing = false;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(mEditing) return;
if(count == 1 && s.charAt(start) == '\n'){
// newline inserted
edTxt.getEditableText().setSpan(mSpanNewline,start,start,0);
}
if(count == 1 && s.charAt(start) == '}'){
// end bracket inserted
edTxt.getEditableText().setSpan(mSpanEndBracket, start, start, 0);
}
}
@Override
public void afterTextChanged(Editable editable) {
mEditing = true;
int spanPos;
// check newline
spanPos = editable.getSpanStart(mSpanNewline);
editable.removeSpan(mSpanNewline);
if (spanPos != -1 && editable.charAt(spanPos) == '\n')
onNewLine(spanPos, editable);
// check endbracket
spanPos = editable.getSpanStart(mSpanEndBracket);
editable.removeSpan(mSpanEndBracket);
if (spanPos != -1 && editable.charAt(spanPos) == '}')
onEndBracket(spanPos, editable);
mEditing = false;
}
// ------------ functions -----------------
/**
* Returns the size of the indent in the current line (spaces at the left) and the position of the first non-space char
* @param currentpos pos of current line (any char)
* @param editable where to search
* @return length of indent (number of spaces) and position of first non-space char (can be end of file)
*/
private Pair<Integer, Integer> getLineIndent(int currentpos, Editable editable){
// goto beginning of line
if(currentpos != 0) {
do{
currentpos--;
}while (currentpos >= 0 && editable.charAt(currentpos) != '\n');
currentpos++;
}
// find indent size
int n = 0;
boolean cont = true;
while(cont && currentpos < editable.length()){
switch (editable.charAt(currentpos)){
case ' ':
n++;
currentpos++;
break;
case '\t':
n+=INDENT_SIZE;
currentpos++;
break;
//case '\n':
default:
cont = false;
}
}
return new Pair<>(n, currentpos);
}
/**
* Called when a newline is inserted. Indents it with the same indentation as the previous line (if any)
* @param posEnter position of the newline char
* @param editable where to indent
*/
private void onNewLine(int posEnter, Editable editable){
int n = getLineIndent(posEnter, editable).first;
StringBuilder indent = new StringBuilder();
for(int i=0;i<n;++i){
indent.append(" ");
}
// do if previous line ends in open bracket
if(posEnter > 0 && editable.charAt(posEnter - 1) == '{'){
// add newline if also following close bracket
if(posEnter < editable.length() - 1 && editable.charAt(posEnter + 1) == '}'){
editable.insert(posEnter + 2, "\n" + indent.toString() + "}");
// this avoids moving the cursor
editable.replace(posEnter + 1, posEnter + 2, "");
}
// add indent size
for(int i=0;i<INDENT_SIZE;++i){
indent.append(" ");
}
}
// write indent
editable.insert(posEnter + 1, indent.toString());
}
/**
* Called when an ending bracket is inserted. Decreases the indentation.
* @param posBracket where the endbracket was
* @param editable where to unindent
*/
private void onEndBracket(int posBracket, Editable editable){
// check if beginning of line
if( posBracket == getLineIndent(posBracket, editable).second ){
// decrease indent
decreaseIndent( posBracket, editable );
}
}
/**
* Changes the indentation of all the lines selected
* @param posLeft start of selection
* @param posRight end of selection
* @param increase if trur increase indent, descrease otherwise
* @param editable where to apply the indentation
* @return the new selection (may have changed due to the indentation changes)
*/
public Pair<Integer, Integer> modifyIndent(int posLeft, int posRight, boolean increase, Editable editable){
String span = "modifyIntent";
editable.setSpan(span, posLeft, posRight, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
Deque<Integer> positions = new ArrayDeque<>();
while(posLeft <= posRight && posLeft < editable.length()){
// mark position to indent
positions.push(posLeft);
posLeft++;
// find next line
while(posLeft <= posRight && posLeft < editable.length() && editable.charAt(posLeft) != '\n')
posLeft++;
posLeft++;
}
for (Integer position : positions) {
// indent the lines in reverse order
if(increase)
increaseIndent(position, editable);
else
decreaseIndent(position, editable);
}
//restore span
return new Pair<>(editable.getSpanStart(span), editable.getSpanEnd(span));
}
/**
* Increases the indentation of a single line
* @param posCursor position of a character in the line that will be indented
* @param editable where to apply the indentation
*/
private void increaseIndent(int posCursor, Editable editable){
Pair<Integer, Integer> n_beg = getLineIndent(posCursor, editable);
int beg = n_beg.second;
// increase indent adding spaces
for(int i=0; i< INDENT_SIZE; i++) editable.insert(beg, " ");
}
/**
* Decreases the indentation of a single line
* @param posCursor position of a character in the line that will be indented
* @param editable where to apply the indentation
*/
private void decreaseIndent(int posCursor, Editable editable){
Pair<Integer, Integer> n_beg = getLineIndent(posCursor, editable);
int n = n_beg.first;
int beg = n_beg.second;
if ( n >= INDENT_SIZE ){
// enough intent to remove, remove the first tab, or all the spaces if no tabs found
int p = 1;
while (p <= INDENT_SIZE) {
if (editable.charAt(beg - p) == '\t') {
//tab found, remove
editable.delete(beg - p, beg - p + 1);
return;
}
p++;
}
// no tabs found, only spaces, remove them
editable.delete(beg - INDENT_SIZE, beg);
}
}
}