IDE de ardilla es un editor de código multilingüe rápido y gratuito para Android.
Equipo de edición
- Grado de dependencia
- Los basicos
- Mas opciones
- Sugerencias de código
- Cancelar Restablecer
- Navegación
- Temas
- Complemento personalizado
Idiomas
los editorkit
el módulo proporciona un editor de código sin soporte para lenguajes de programación.
Si está actualizando desde una versión anterior, eche un vistazo a guía de migración.
Tenga en cuenta que esta biblioteca solo es compatible con Kotlin.
Grado de dependencia
Agrega esto a tus formularios build.gradle
expediente:
dependencies {
...
implementation 'com.blacksquircle.ui:editorkit:2.1.2'
}
los editorkit
módulo no proporciona soporte para el resaltado de sintaxis, debe agregar una dependencia de idioma específica. Puedes ver la lista de idiomas disponibles aquí.
Los basicos
Primero, debes agregar TextProcessor
en su diseño:
<com.blacksquircle.ui.editorkit.widget.TextProcessor
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top|start"
android:id="@+id/editor" />
Segundo, usted debe proporcionar un Language
objeto para admitir el resaltado de sintaxis usando el siguiente código:
val editor = findViewById<TextProcessor>(R.id.editor)
editor.language = JavaScriptLanguage() // or any other language you want
Terceratienes que llamar setTextContent
para configurar el texto. No utilice la configuración predeterminada setText
método.
editor.setTextContent("your code here")
También puede querer usar setTextContent(PrecomputedTextCompat)
si está trabajando con archivos de texto grandes.
Por findespués de configurar el texto, debe borrar el historial de deshacer/rehacer porque no desea conservar el historial de modificaciones del archivo anterior:
import com.blacksquircle.ui.editorkit.model.UndoStack
editor.undoStack = UndoStack()
editor.redoStack = UndoStack()
Ahora puede comenzar a usar el editor de código.
Mas opciones
Configuración
Puede cambiar el comportamiento predeterminado del editor de código usando el complemento DSL como se muestra a continuación:
val pluginSupplier = PluginSupplier.create {
pinchZoom { // whether the zoom gesture enabled
minTextSize = 10f
maxTextSize = 20f
}
lineNumbers {
lineNumbers = true // line numbers visibility
highlightCurrentLine = true // whether the current line will be highlighted
}
highlightDelimiters() // highlight open/closed brackets beside the cursor
autoIndentation {
autoIndentLines = true // whether the auto indentation enabled
autoCloseBrackets = true // automatically close open parenthesis/bracket/brace
autoCloseQuotes = true // automatically close single/double quote when typing
}
}
editor.plugins(pluginSupplier)
Para habilitar / deshabilitar complementos en tiempo de ejecución, rodee los métodos necesarios con if (enabled) { ... }
operador:
val pluginSupplier = PluginSupplier.create {
if (preferences.isLineNumbersEnabled) {
lineNumbers()
}
if (preferences.isPinchZoomEnabled) {
pinchZoom()
}
// ...
}
editor.plugins(pluginSupplier)
Recordar: cada vez que llamas editor.plugins(pluginSupplier)
compare la lista de complementos actual con la nueva, luego separe los complementos que no existen en el archivo PluginSupplier
.
Desplazamiento de texto
Para adjuntar el desplazador de texto, debe agregar TextScroller
en el arreglo:
<com.blacksquircle.ui.editorkit.widget.TextScroller
android:layout_width="30dp"
android:layout_height="match_parent"
android:id="@+id/scroller"
app:thumbNormal="@drawable/fastscroll_normal"
app:thumbDragging="@drawable/fastscroll_pressed"
app:thumbTint="@color/blue" />
Ahora necesita pasar una referencia a una vista dentro attachTo
método:
val editor = findViewById<TextProcessor>(R.id.editor)
val scroller = findViewById<TextScroller>(R.id.scroller)
scroller.attachTo(editor)
// or using Plugin DSL:
val pluginSupplier = PluginSupplier.create {
...
textScroller {
scroller = findViewById<TextScroller>(R.id.scroller)
}
}
Sugerencias de código
Cuando trabaja con un editor de código, desea ver la lista de sugerencias de código. (Tenga en cuenta que debe proporcionar una Language
objeto antes de empezar a usarlo).
Primerodebe crear un archivo de diseño que represente el elemento de sugerencia dentro del menú desplegable:
<!-- item_suggestion.xml -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:padding="6dp"
android:textSize="12sp"
android:typeface="monospace"
android:id="@+id/title" />
Segundonecesitas crear personalizado SuggestionAdapter
:
class AutoCompleteAdapter(context: Context) : SuggestionAdapter(context, R.layout.item_suggestion) {
override fun createViewHolder(parent: ViewGroup): SuggestionViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.item_suggestion, parent, false)
return AutoCompleteViewHolder(view)
}
class AutoCompleteViewHolder(itemView: View) : SuggestionViewHolder(itemView) {
private val title: TextView = itemView.findViewById(R.id.title)
override fun bind(suggestion: Suggestion?, query: String) {
title.text = suggestion?.text
}
}
}
Tercerahabilite el complemento de finalización de código y configure SuggestionAdapter
:
val pluginSupplier = PluginSupplier.create {
...
codeCompletion {
suggestionAdapter = AutoCompleteAdapter(this)
}
}
UPD: Si tiene problemas con la posición de la ventana emergente (por ejemplo, desplazamiento vertical), esto podría resolverse configurando explícitamente Android: dropDownAnchor en XML.
Cancelar Restablecer
los TextProcessor
soporta operaciones de deshacer/rehacer, pero recuerde que usted deber verifique la capacidad de deshacer/rehacer antes de llamar a los métodos reales:
// Undo
if (editor.canUndo()) {
editor.undo()
}
// Redo
if (editor.canRedo()) {
editor.redo()
}
También puede tener un caso de uso cuando desee actualizar la visibilidad de los botones Deshacer/Rehacer u otra interfaz de usuario después de que se hayan realizado los reemplazos de texto, esto se puede lograr agregando OnUndoRedoChangedListener
:
editor.onUndoRedoChangedListener = object : OnUndoRedoChangedListener {
override fun onUndoRedoChanged() {
val canUndo = editor.canUndo()
val canRedo = editor.canRedo()
// ...
}
}
Navegación
Navegación de texto
Puede utilizar estos métodos de extensión para navegar por el texto:
editor.moveCaretToStartOfLine()
editor.moveCaretToEndOfLine()
editor.moveCaretToPrevWord()
editor.moveCaretToNextWord()
… o utilice la función “Ir a la línea” para colocar el cursor en la línea específica:
import com.blacksquircle.ui.editorkit.exception.LineException
try {
editor.gotoLine(lineNumber)
} catch (e: LineException) {
Toast.makeText(this, "Line does not exists", Toast.LENGTH_SHORT).show()
}
Encontrar y reemplazar
los TextProcessor
tiene soporte incorporado para operaciones de búsqueda y reemplazo, que incluyen:
- Buscar hacia adelante o hacia atrás
- Expresiones regulares
- caja de fósforos
- Solo palabras
La clase en sí contiene métodos que se explican por sí mismos para todas sus necesidades de búsqueda:
find(params)
– Encuentra todos los resultados posibles en el texto con las opciones proporcionadas.replaceFindResult(replaceText)
– Encuentra la coincidencia actual y la reemplaza con texto nuevo.replaceAllFindResults(replaceText)
– Encuentra todas las coincidencias y reemplázalas con texto nuevo.findNext()
– Encuentra la siguiente coincidencia y desplázate hasta ella.findPrevious()
– Busque la coincidencia anterior y desplácese hasta ella.clearFindResultSpans()
– Borrar todos los rangos de búsqueda en la pantalla. Llame a este método cuando haya terminado de buscar.
import com.blacksquircle.ui.editorkit.model.FindParams
val params = FindParams(
query = "function", // text to find
regex = false, // regular expressions
matchCase = true, // case sensitive
wordsOnly = true // words only
)
editor.find(params)
// To navigate between results use findNext() and findPrevious()
Atajos
Si está usando el teclado bluetooth, probablemente querrá usar atajos de teclado para escribir su código más rápido. Para admitir atajos de teclado, debe habilitar el complemento de atajos y configurar OnShortcutListener
:
val pluginSupplier = PluginSupplier.create {
...
shortcuts {
onShortcutListener = object : OnShortcutListener {
override fun onShortcut(shortcut: Shortcut): Boolean {
val (ctrl, shift, alt, keyCode) = shortcut
return when {
ctrl && keyCode == KeyEvent.KEYCODE_DPAD_LEFT -> editor.moveCaretToStartOfLine()
ctrl && keyCode == KeyEvent.KEYCODE_DPAD_RIGHT -> editor.moveCaretToEndOfLine()
alt && keyCode == KeyEvent.KEYCODE_DPAD_LEFT -> editor.moveCaretToPrevWord()
alt && keyCode == KeyEvent.KEYCODE_DPAD_RIGHT -> editor.moveCaretToNextWord()
// ...
else -> false
}
}
}
}
}
los onShortcut
el método se invocará solo si se presiona al menos una de las siguientes teclas: control, para cambiar, alternativa.
Es posible que ya haya notado que necesita devolver un Boolean
valor como resultado de onShortcut
método. Regreso true
si el oyente ha consumido el evento de enlace, false
de lo contrario.
Temas
los editorkit
el módulo incluye algunos temas predefinidos en el archivo EditorTheme
clase:
editor.colorScheme = EditorTheme.DARCULA // default
// or you can use one of these:
EditorTheme.MONOKAI
EditorTheme.OBSIDIAN
EditorTheme.LADIES_NIGHT
EditorTheme.TOMORROW_NIGHT
EditorTheme.VISUAL_STUDIO_2013
También puede escribir su propio tema editando el archivo ColorScheme
propiedad. El siguiente ejemplo muestra cómo cargar mediante programación el esquema de color:
editor.colorScheme = ColorScheme(
textColor = Color.parseColor("#C8C8C8"),
backgroundColor = Color.parseColor("#232323"),
gutterColor = Color.parseColor("#2C2C2C"),
gutterDividerColor = Color.parseColor("#555555"),
gutterCurrentLineNumberColor = Color.parseColor("#FFFFFF"),
gutterTextColor = Color.parseColor("#C6C8C6"),
selectedLineColor = Color.parseColor("#141414"),
selectionColor = Color.parseColor("#454464"),
suggestionQueryColor = Color.parseColor("#4F98F7"),
findResultBackgroundColor = Color.parseColor("#1C3D6B"),
delimiterBackgroundColor = Color.parseColor("#616161"),
numberColor = Color.parseColor("#BACDAB"),
operatorColor = Color.parseColor("#DCDCDC"),
keywordColor = Color.parseColor("#669BD1"),
typeColor = Color.parseColor("#669BD1"),
langConstColor = Color.parseColor("#669BD1"),
preprocessorColor = Color.parseColor("#C49594"),
variableColor = Color.parseColor("#9DDDFF"),
methodColor = Color.parseColor("#71C6B1"),
stringColor = Color.parseColor("#CE9F89"),
commentColor = Color.parseColor("#6BA455"),
tagColor = Color.parseColor("#DCDCDC"),
tagNameColor = Color.parseColor("#669BD1"),
attrNameColor = Color.parseColor("#C8C8C8"),
attrValueColor = Color.parseColor("#CE9F89"),
entityRefColor = Color.parseColor("#BACDAB")
)
Complemento personalizado
Desde v2.1.0 el Equipo de edición la biblioteca admite la escritura de complementos personalizados para ampliar su funcionalidad predeterminada. Si está utilizando la última versión, es posible que esté familiarizado con PluginSupplier
y saber usarlo es DSL. Ver Mas opciones para información.
Primero, necesitas crear una clase que amplíe el archivo EditorPlugin
y proporcione su id en el constructor:
class CustomPlugin : EditorPlugin("custom-plugin-id") {
var publicProperty = true
override fun onAttached(editText: TextProcessor) {
super.onAttached(editText)
// TODO enable your feature here
}
override fun onDetached(editText: TextProcessor) {
super.onDetached(editText)
// TODO disable your feature here
}
}
Segundo, puede anular los métodos del ciclo de vida, por ejemplo afterDraw
invocado inmediatamente después onDraw(Canvas)
en el editor de código:
class CustomPlugin : EditorPlugin("custom-plugin-id") {
var publicProperty = true
private val dividerPaint = Paint().apply {
color = Color.GRAY
}
override fun afterDraw(canvas: Canvas?) {
super.afterDraw(canvas)
if (publicProperty) {
var i = editText.topVisibleLine
while (i <= editText.bottomVisibleLine) {
val startX = editText.paddingStart + editText.scrollX
val startY = editText.paddingTop + editText.layout.getLineBottom(i)
val stopX = editText.paddingLeft + editText.layout.width + editText.paddingRight
val stopY = editText.paddingTop + editText.layout.getLineBottom(i)
canvas?.drawLine( // draw divider for each visible line
startX.toFloat(), startY.toFloat(),
stopX.toFloat(), stopY.toFloat(),
dividerPaint
)
i++
}
}
}
}
Tercera, crear una función de extensión para mejorar la legibilidad del código al agregar el complemento a PluginSupplier
:
fun PluginSupplier.verticalDividers(block: CustomPlugin.() -> Unit = {}) {
plugin(CustomPlugin(), block)
}
Por fin, puede conectar su complemento a través de DSL:
val pluginSupplier = PluginSupplier.create {
verticalDividers {
publicProperty = true // whether should draw the dividers
}
...
}
editor.plugins(pluginSupplier)
Los módulos de lenguaje brindan soporte para lenguajes de programación. Esto incluye resaltado de sintaxis, sugerencias de código y analizador de código fuente. (Tenga en cuenta que el analizador de código fuente actualmente solo funciona en language-javascript
módulo, pero pronto se implementará para más idiomas)
Grado de dependencia
Seleccione su idioma y agregue su dependencia a la de su módulo build.gradle
expediente:
dependencies {
...
implementation 'com.blacksquircle.ui:language-actionscript:2.1.2'
implementation 'com.blacksquircle.ui:language-base:2.1.2' // for custom language
implementation 'com.blacksquircle.ui:language-c:2.1.2'
implementation 'com.blacksquircle.ui:language-cpp:2.1.2'
implementation 'com.blacksquircle.ui:language-csharp:2.1.2'
implementation 'com.blacksquircle.ui:language-groovy:2.1.2'
implementation 'com.blacksquircle.ui:language-html:2.1.2'
implementation 'com.blacksquircle.ui:language-java:2.1.2'
implementation 'com.blacksquircle.ui:language-javascript:2.1.2'
implementation 'com.blacksquircle.ui:language-json:2.1.2'
implementation 'com.blacksquircle.ui:language-julia:2.1.2'
implementation 'com.blacksquircle.ui:language-kotlin:2.1.2'
implementation 'com.blacksquircle.ui:language-lisp:2.1.2'
implementation 'com.blacksquircle.ui:language-lua:2.1.2'
implementation 'com.blacksquircle.ui:language-markdown:2.1.2'
implementation 'com.blacksquircle.ui:language-php:2.1.2'
implementation 'com.blacksquircle.ui:language-plaintext:2.1.2'
implementation 'com.blacksquircle.ui:language-python:2.1.2'
implementation 'com.blacksquircle.ui:language-ruby:2.1.2'
implementation 'com.blacksquircle.ui:language-shell:2.1.2'
implementation 'com.blacksquircle.ui:language-sql:2.1.2'
implementation 'com.blacksquircle.ui:language-typescript:2.1.2'
implementation 'com.blacksquircle.ui:language-visualbasic:2.1.2'
implementation 'com.blacksquircle.ui:language-xml:2.1.2'
}
Idioma personalizado
Primero, agrega esto a tu formulario build.gradle
expediente:
dependencies {
...
implementation 'com.blacksquircle.ui:language-base:2.1.2'
}
Segundo, implementar el Language
interfaz:
import com.blacksquircle.ui.language.base.Language
import com.blacksquircle.ui.language.base.parser.LanguageParser
import com.blacksquircle.ui.language.base.provider.SuggestionProvider
import com.blacksquircle.ui.language.base.styler.LanguageStyler
class CustomLanguage : Language {
override fun getName(): String {
return "custom language"
}
override fun getParser(): LanguageParser {
return CustomParser()
}
override fun getProvider(): SuggestionProvider {
return CustomProvider()
}
override fun getStyler(): LanguageStyler {
return CustomStyler()
}
}
Cada idioma se compone de 3 componentes clave:
- LanguageParser se encarga de analizar el código fuente. El editor de código no usa este componente directamente.
- Proveedor de sugerencias es responsable de recopilar los nombres de funciones, campos y palabras clave dentro del alcance del archivo. El editor de código utiliza este componente para mostrar la lista de sugerencias de código.
- Estilizador de lenguaje es responsable del resaltado de sintaxis. El editor de código usa este componente para mostrar rangos de resaltado de sintaxis en la pantalla.
LanguageParser
LanguageParser
es una interfaz que detecta errores de sintaxis para que puedas verlos en el archivo TextProcessor
después.
Para crear un analizador personalizado, debe implementar execute
método que devolverá un ParseResult
.
Uno mismo ParseResult
contiene una excepción significa que el código fuente no se puede compilar y contiene errores de sintaxis. Puede resaltar una línea de error llamando editor.setErrorLine(lineNumber)
método.
Recordar que Tú no debe use este método en el hilo principal.
class CustomParser : LanguageParser {
override fun execute(name: String, source: String): ParseResult {
// TODO Implement parser
val lineNumber = 0
val columnNumber = 0
val parseException = ParseException("describe exception here", lineNumber, columnNumber)
return ParseResult(parseException)
}
}
Proveedor de sugerencias
SuggestionProvider
es una interfaz que proporciona sugerencias de código para mostrarlas en el archivo TextProcessor
.
El texto se escanea por línea. Cuando el usuario cambia el código en una sola línea, esa línea es escaneada nuevamente por la actual SuggestionsProvider
implementación, para que pueda mantener la lista de sugerencias actualizada. Esto se hace llamando al processLine
método. Este método es responsable de analizar una línea de texto y guardar sugerencias de código para esa línea.
Después de llamar setTextContent
el editor de código llamará processLine
para cada línea para encontrar todas las sugerencias de código posibles.
class CustomProvider : SuggestionProvider {
// You can use WordsManager
// if you don't want to write the language-specific implementation
private val wordsManager = WordsManager()
override fun getAll(): Set<Suggestion> {
return wordsManager.getWords()
}
override fun processLine(lineNumber: Int, text: String) {
wordsManager.processLine(lineNumber, text)
}
override fun deleteLine(lineNumber: Int) {
wordsManager.deleteLine(lineNumber)
}
override fun clearLines() {
wordsManager.clearLines()
}
}
Estilizador de lenguaje
LanguageStyler
es una interfaz que proporciona rangos de resaltado de sintaxis para mostrarlos en el archivo TextProcessor
.
los execute
el método se ejecutará en el subproceso de fondo cada vez que cambie el texto. Puede usar expresiones regulares o lexer para encontrar palabras clave en el texto.
Recordar: cuantos más intervalos agregue, más tiempo llevará renderizar en el subproceso principal.
class CustomStyler : LanguageStyler {
override fun execute(source: String, scheme: ColorScheme): List<SyntaxHighlightSpan> {
val syntaxHighlightSpans = mutableListOf<SyntaxHighlightSpan>()
// TODO Implement syntax highlighting
return syntaxHighlightSpans
}
}