|
|
@ -20,7 +20,9 @@ import java.util.regex.Pattern |
|
|
|
import kotlin.math.roundToInt |
|
|
|
import kotlin.math.roundToInt |
|
|
|
|
|
|
|
|
|
|
|
@Suppress("unused") |
|
|
|
@Suppress("unused") |
|
|
|
class CodeView : AppCompatMultiAutoCompleteTextView { |
|
|
|
class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : |
|
|
|
|
|
|
|
AppCompatMultiAutoCompleteTextView(context, attrs) { |
|
|
|
|
|
|
|
|
|
|
|
private var tabWidth = 0 |
|
|
|
private var tabWidth = 0 |
|
|
|
private var tabWidthInCharacters = 0 |
|
|
|
private var tabWidthInCharacters = 0 |
|
|
|
private var mUpdateDelayTime = 500 |
|
|
|
private var mUpdateDelayTime = 500 |
|
|
@ -35,23 +37,53 @@ class CodeView : AppCompatMultiAutoCompleteTextView { |
|
|
|
private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap() |
|
|
|
private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap() |
|
|
|
private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=') |
|
|
|
private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=') |
|
|
|
|
|
|
|
|
|
|
|
constructor(context: Context?) : super(context!!) { |
|
|
|
private val mUpdateRunnable = Runnable { |
|
|
|
initEditorView() |
|
|
|
val source = text |
|
|
|
|
|
|
|
highlightWithoutChange(source) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
constructor(context: Context?, attrs: AttributeSet?) : super( |
|
|
|
private val mEditorTextWatcher: TextWatcher = object : TextWatcher { |
|
|
|
context!!, attrs |
|
|
|
private var start = 0 |
|
|
|
) { |
|
|
|
private var count = 0 |
|
|
|
initEditorView() |
|
|
|
override fun beforeTextChanged( |
|
|
|
} |
|
|
|
charSequence: CharSequence, |
|
|
|
|
|
|
|
start: Int, |
|
|
|
|
|
|
|
before: Int, |
|
|
|
|
|
|
|
count: Int |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
this.start = start |
|
|
|
|
|
|
|
this.count = count |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( |
|
|
|
override fun onTextChanged( |
|
|
|
context!!, attrs, defStyleAttr |
|
|
|
charSequence: CharSequence, |
|
|
|
) { |
|
|
|
start: Int, |
|
|
|
initEditorView() |
|
|
|
before: Int, |
|
|
|
|
|
|
|
count: Int |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
if (!modified) return |
|
|
|
|
|
|
|
if (highlightWhileTextChanging) { |
|
|
|
|
|
|
|
if (mSyntaxPatternMap.isNotEmpty()) { |
|
|
|
|
|
|
|
convertTabs(editableText, start, count) |
|
|
|
|
|
|
|
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (mRemoveErrorsWhenTextChanged) removeAllErrorLines() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun afterTextChanged(editable: Editable) { |
|
|
|
|
|
|
|
if (!highlightWhileTextChanging) { |
|
|
|
|
|
|
|
if (!modified) return |
|
|
|
|
|
|
|
cancelHighlighterRender() |
|
|
|
|
|
|
|
if (mSyntaxPatternMap.isNotEmpty()) { |
|
|
|
|
|
|
|
convertTabs(editableText, start, count) |
|
|
|
|
|
|
|
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun initEditorView() { |
|
|
|
init { |
|
|
|
if (mAutoCompleteTokenizer == null) { |
|
|
|
if (mAutoCompleteTokenizer == null) { |
|
|
|
mAutoCompleteTokenizer = KeywordTokenizer() |
|
|
|
mAutoCompleteTokenizer = KeywordTokenizer() |
|
|
|
} |
|
|
|
} |
|
|
@ -170,7 +202,7 @@ class CodeView : AppCompatMultiAutoCompleteTextView { |
|
|
|
|
|
|
|
|
|
|
|
private fun highlight(editable: Editable): Editable { |
|
|
|
private fun highlight(editable: Editable): Editable { |
|
|
|
// if (editable.isEmpty() || editable.length > 1024) return editable |
|
|
|
// if (editable.isEmpty() || editable.length > 1024) return editable |
|
|
|
if (editable.length !in 1 .. 1024) { |
|
|
|
if (editable.length !in 1..1024) { |
|
|
|
return editable |
|
|
|
return editable |
|
|
|
} |
|
|
|
} |
|
|
|
try { |
|
|
|
try { |
|
|
@ -348,51 +380,6 @@ class CodeView : AppCompatMultiAutoCompleteTextView { |
|
|
|
super.showDropDown() |
|
|
|
super.showDropDown() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private val mUpdateRunnable = Runnable { |
|
|
|
|
|
|
|
val source = text |
|
|
|
|
|
|
|
highlightWithoutChange(source) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private val mEditorTextWatcher: TextWatcher = object : TextWatcher { |
|
|
|
|
|
|
|
private var start = 0 |
|
|
|
|
|
|
|
private var count = 0 |
|
|
|
|
|
|
|
override fun beforeTextChanged( |
|
|
|
|
|
|
|
charSequence: CharSequence, |
|
|
|
|
|
|
|
start: Int, |
|
|
|
|
|
|
|
before: Int, |
|
|
|
|
|
|
|
count: Int |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
this.start = start |
|
|
|
|
|
|
|
this.count = count |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onTextChanged( |
|
|
|
|
|
|
|
charSequence: CharSequence, |
|
|
|
|
|
|
|
start: Int, |
|
|
|
|
|
|
|
before: Int, |
|
|
|
|
|
|
|
count: Int |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
if (!modified) return |
|
|
|
|
|
|
|
if (highlightWhileTextChanging) { |
|
|
|
|
|
|
|
if (mSyntaxPatternMap.isNotEmpty()) { |
|
|
|
|
|
|
|
convertTabs(editableText, start, count) |
|
|
|
|
|
|
|
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (mRemoveErrorsWhenTextChanged) removeAllErrorLines() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun afterTextChanged(editable: Editable) { |
|
|
|
|
|
|
|
if (!highlightWhileTextChanging) { |
|
|
|
|
|
|
|
if (!modified) return |
|
|
|
|
|
|
|
cancelHighlighterRender() |
|
|
|
|
|
|
|
if (mSyntaxPatternMap.isNotEmpty()) { |
|
|
|
|
|
|
|
convertTabs(editableText, start, count) |
|
|
|
|
|
|
|
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private inner class TabWidthSpan : ReplacementSpan() { |
|
|
|
private inner class TabWidthSpan : ReplacementSpan() { |
|
|
|
override fun getSize( |
|
|
|
override fun getSize( |
|
|
|
paint: Paint, |
|
|
|
paint: Paint, |
|
|
|