refactor: extract IEditor impl to delegate class

This commit is contained in:
Akash Yadav
2023-10-24 18:07:11 +05:30
parent 5804f4e001
commit 540e33734b
2 changed files with 168 additions and 142 deletions
@@ -0,0 +1,149 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.editor.ui
import com.itsaky.androidide.editor.api.IEditor
import com.itsaky.androidide.editor.ui.IDEEditor.Companion.log
import com.itsaky.androidide.models.Position
import com.itsaky.androidide.models.Range
import java.io.File
/**
* Handler which implements various features in [IEditor].
*
* @author Akash Yadav
*/
class EditorFeatures(
var editor: IDEEditor? = null
) : IEditor {
override fun getFile(): File? = withEditor { _file }
override fun isModified(): Boolean = withEditor { this.isModified } ?: false
override fun setSelection(position: Position) {
withEditor {
setSelection(position.line, position.column)
}
}
override fun setSelection(start: Position, end: Position) {
withEditor {
if (!isValidPosition(start, true) || !isValidPosition(end, true)) {
log.warn("Invalid selection range: start=$start end=$end")
return@withEditor
}
setSelectionRegion(start.line, start.column, end.line, end.column)
}
}
override fun getCursorLSPRange(): Range = withEditor {
val end = cursor.right().let {
Position(line = it.line, column = it.column, index = it.index)
}
return@withEditor Range(cursorLSPPosition, end)
} ?: Range.NONE
override fun getCursorLSPPosition(): Position = withEditor {
return@withEditor cursor.left().let {
Position(line = it.line, column = it.column, index = it.index)
}
} ?: Position.NONE
override fun validateRange(range: Range) {
withEditor {
val start = range.start
val end = range.end
val text = text
val lineCount = text.lineCount
start.line = 0.coerceAtLeast(start.line).coerceAtMost(lineCount - 1)
start.column = 0.coerceAtLeast(start.column).coerceAtMost(text.getColumnCount(start.line))
end.line = 0.coerceAtLeast(end.line).coerceAtMost(lineCount - 1)
end.column = 0.coerceAtLeast(end.column).coerceAtMost(text.getColumnCount(end.line))
}
}
override fun isValidRange(range: Range?, allowColumnEqual: Boolean): Boolean = withEditor {
if (range == null) {
return@withEditor false
}
val start = range.start
val end = range.end
return@withEditor isValidPosition(start, allowColumnEqual)
// make sure start position is before end position
&& isValidPosition(end, allowColumnEqual) && start < end
} ?: false
override fun isValidPosition(position: Position?, allowColumnEqual: Boolean): Boolean =
withEditor {
return@withEditor if (position == null) {
false
} else isValidLine(position.line) &&
isValidColumn(position.line, position.column, allowColumnEqual)
} ?: false
override fun isValidLine(line: Int): Boolean =
withEditor { line >= 0 && line < text.lineCount } ?: false
override fun isValidColumn(line: Int, column: Int, allowColumnEqual: Boolean): Boolean =
withEditor {
val columnCount = text.getColumnCount(line)
return@withEditor column >= 0 && (column < columnCount || allowColumnEqual && column == columnCount)
} ?: false
override fun append(text: CharSequence?): Int = withEditor {
val content = getText()
if (lineCount <= 0) {
return@withEditor 0
}
val line = lineCount - 1
var col = content.getColumnCount(line)
if (col < 0) {
col = 0
}
content.insert(line, col, text)
return@withEditor line
} ?: -1
override fun replaceContent(newContent: CharSequence?) {
withEditor {
val lastLine = text.lineCount - 1
val lastColumn = text.getColumnCount(lastLine)
text.replace(0, 0, lastLine, lastColumn, newContent ?: "")
}
}
override fun goToEnd() {
withEditor {
val line = text.lineCount - 1
setSelection(line, 0)
}
}
private inline fun <T> withEditor(crossinline action: IDEEditor.() -> T): T? {
return this.editor?.run {
if (isReleased) {
null
} else action()
}
}
}
@@ -20,13 +20,11 @@ package com.itsaky.androidide.editor.ui
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.os.Debug
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.inputmethod.EditorInfo
import androidx.annotation.StringRes
import androidx.annotation.UiThread
import com.blankj.utilcode.util.FileUtils
import com.itsaky.androidide.editor.R.string
import com.itsaky.androidide.editor.adapters.CompletionListAdapter
@@ -80,7 +78,6 @@ import io.github.rosemoe.sora.event.ContentChangeEvent
import io.github.rosemoe.sora.event.SelectionChangeEvent
import io.github.rosemoe.sora.lang.EmptyLanguage
import io.github.rosemoe.sora.lang.Language
import io.github.rosemoe.sora.text.Content
import io.github.rosemoe.sora.widget.CodeEditor
import io.github.rosemoe.sora.widget.EditorSearcher
import io.github.rosemoe.sora.widget.IDEEditorSearcher
@@ -108,15 +105,17 @@ open class IDEEditor @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : CodeEditor(context, attrs, defStyleAttr, defStyleRes), IEditor, ILspEditor {
defStyleRes: Int = 0,
private val editorFeatures: EditorFeatures = EditorFeatures()
) : CodeEditor(context, attrs, defStyleAttr, defStyleRes), IEditor by editorFeatures, ILspEditor {
internal var _file: File? = null
private var _actionsMenu: EditorActionsMenu? = null
private var _signatureHelpWindow: SignatureHelpWindow? = null
private var _diagnosticWindow: DiagnosticWindow? = null
private var file: File? = null
private var fileVersion = 0
private var isModified = false
internal var isModified = false
private val selectionChangeHandler = Handler(Looper.getMainLooper())
private var selectionChangeRunner: Runnable? = Runnable {
@@ -172,7 +171,7 @@ open class IDEEditor @JvmOverloads constructor(
private const val SELECTION_CHANGE_DELAY = 500L
private val log = ILogger.newInstance("IDEEditor")
internal val log = ILogger.newInstance("IDEEditor")
/**
* Create input type flags for the editor.
@@ -188,11 +187,10 @@ open class IDEEditor @JvmOverloads constructor(
}
init {
initEditor()
}
override fun getFile(): File? {
return this.file
run {
editorFeatures.editor = this
initEditor()
}
}
/**
@@ -203,135 +201,12 @@ open class IDEEditor @JvmOverloads constructor(
return
}
this.file = file
this._file = file
file?.also {
dispatchDocumentOpenEvent()
}
}
override fun isModified(): Boolean {
return this.isModified
}
override fun setSelection(position: Position) {
if (isReleased) {
return
}
setSelection(position.line, position.column)
}
override fun setSelection(start: Position, end: Position) {
if (isReleased) {
return
}
if (!isValidPosition(start, true) || !isValidPosition(end, true)) {
log.warn("Invalid selection range: start=$start end=$end")
return
}
setSelectionRegion(start.line, start.column, end.line, end.column)
}
override fun getCursorLSPRange(): Range {
val end = cursor.right().let {
Position(line = it.line, column = it.column, index = it.index)
}
return Range(cursorLSPPosition, end)
}
override fun getCursorLSPPosition(): Position {
return cursor.left().let {
Position(line = it.line, column = it.column, index = it.index)
}
}
override fun validateRange(range: Range) {
if (isReleased) {
return
}
val start = range.start
val end = range.end
val text = text
val lineCount = text.lineCount
start.line = 0.coerceAtLeast(start.line).coerceAtMost(lineCount - 1)
start.column = 0.coerceAtLeast(start.column).coerceAtMost(text.getColumnCount(start.line))
end.line = 0.coerceAtLeast(end.line).coerceAtMost(lineCount - 1)
end.column = 0.coerceAtLeast(end.column).coerceAtMost(text.getColumnCount(end.line))
}
override fun isValidRange(range: Range?, allowColumnEqual: Boolean): Boolean {
if (isReleased) {
return false
}
if (range == null) {
return false
}
val start = range.start
val end = range.end
return isValidPosition(start, allowColumnEqual)
// make sure start position is before end position
&& isValidPosition(end, allowColumnEqual) && start < end
}
override fun isValidPosition(position: Position?, allowColumnEqual: Boolean): Boolean {
if (isReleased) {
return false
}
return if (position == null) {
false
} else isValidLine(position.line) &&
isValidColumn(position.line, position.column, allowColumnEqual)
}
override fun isValidLine(line: Int): Boolean {
if (isReleased) {
return false
}
return line >= 0 && line < text.lineCount
}
override fun isValidColumn(line: Int, column: Int, allowEqual: Boolean): Boolean {
val columnCount = text.getColumnCount(line)
return column >= 0 && (column < columnCount || allowEqual && column == columnCount)
}
override fun append(text: CharSequence?): Int {
if (isReleased) {
return 0
}
val content = getText()
if (lineCount <= 0) {
return 0
}
val line = lineCount - 1
var col = content.getColumnCount(line)
if (col < 0) {
col = 0
}
content.insert(line, col, text)
return line
}
@UiThread
override fun replaceContent(newContent: CharSequence?) {
if (isReleased) {
return
}
val lastLine = text.lineCount - 1
val lastColumn = text.getColumnCount(lastLine)
text.replace(0, 0, lastLine, lastColumn, newContent ?: "")
}
override fun goToEnd() {
if (isReleased) {
return
}
val line = text.lineCount - 1
setSelection(line, 0)
}
override fun setLanguageServer(server: ILanguageServer?) {
if (isReleased) {
return
@@ -498,10 +373,12 @@ open class IDEEditor @JvmOverloads constructor(
languageServer = null
languageClient = null
file = null
_file = null
fileVersion = 0
markUnmodified()
editorFeatures.editor = null
selectionChangeRunner?.also { selectionChangeHandler.removeCallbacks(it) }
selectionChangeRunner = null
@@ -622,10 +499,10 @@ open class IDEEditor @JvmOverloads constructor(
if (isReleased) {
return
}
if (getFile() == null) {
if (file == null) {
return
}
val saveEvent = DocumentSaveEvent(getFile()!!.toPath())
val saveEvent = DocumentSaveEvent(file!!.toPath())
EventBus.getDefault().post(saveEvent)
}
@@ -636,7 +513,7 @@ open class IDEEditor @JvmOverloads constructor(
@Suppress("unused")
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
open fun onColorSchemeInvalidated(event: ColorSchemeInvalidatedEvent?) {
val file = getFile() ?: return
val file = file ?: return
setupLanguage(file)
}
@@ -817,7 +694,7 @@ open class IDEEditor @JvmOverloads constructor(
}
val (file1, range) = locations[0]
if (DocumentUtils.isSameFile(file1, getFile()!!.toPath())) {
if (DocumentUtils.isSameFile(file1, file!!.toPath())) {
setSelection(range)
return@withContext
}