Feature: ConfigureLoader

This commit is contained in:
zt515
2017-08-06 18:43:41 +08:00
parent bd4ea35ed9
commit 0c3eaae764
55 changed files with 793 additions and 296 deletions

View File

@ -8,8 +8,9 @@ import io.neolang.runtime.type.NeoLangValue
class NeoLangContext(val contextName: String) {
private val attributes = mutableMapOf<String, NeoLangValue>()
fun defineAttribute(attributeName: String, attributeValue: NeoLangValue) {
fun defineAttribute(attributeName: String, attributeValue: NeoLangValue): NeoLangContext {
attributes[attributeName] = attributeValue
return this
}
fun getAttribute(attributeName: String): NeoLangValue {

View File

@ -0,0 +1,10 @@
color-scheme: {
name: "Default"
version: 1.1
colors: {
foreground: #ffffff
cursor: #a9aaa9
background: #14181c
}
}

View File

@ -0,0 +1,34 @@
color-scheme: {
name: "Dracula"
version: 1.1
colors: {
foreground: #f8f8f2
cursor: #f8f8f2
background: #282a36
color0: #000000
color8: #4d4d4d
color1: #ff5555
color9: #ff6e67
color2: #50fa7b
color10: #5af78e
color3: #f1fa8c
color11: #f4f99d
color4: #caa9fa
color12: #caa9fa
color5: #ff79c6
color13: #ff92d0
color6: #8be9fd
color14: #9aedfe
color7: #bfbfbf
color15: #e6e6e6
}
}

View File

@ -0,0 +1,33 @@
color-scheme: {
name: "Material"
version: 1.1
colors: {
background: #263238
foreground: #eceff1
color0: #263238
color8: #37474f
color1: #ff9800
color9: #ffa74d
color2: #8bc34a
color10: #9ccc65
color3: #ffc107
color11: #ffa000
color4: #03a9f4
color12: #81d4fa
color5: #e91e63
color13: #ad1457
color6: #009688
color14: #26a69a
color7: #cfd8dc
color15: #eceff1
}
}

View File

@ -0,0 +1,28 @@
color-scheme: {
name: "Solarized Dark"
version: 1.1
colors: {
background: #002b36
foreground: #839496
cursor: #93a1a1
color0: #073642
color1: #dc322f
color2: #859900
color3: #b58900
color4: #268bd2
color5: #d33682
color6: #2aa198
color7: #eee8d5
color9: #cb4b16
color8: #002b36
color10: #586e75
color11: #657b83
color12: #839496
color13: #6c71c4
color14: #93a1a1
color15: #fdf6e3
}
}

View File

@ -0,0 +1,27 @@
color-scheme: {
name: "Solarized Light"
version: 1.1
colors: {
background: #fdf6e3
foreground: #657b83
cursor: #586e75
color0: #073642
color1: #dc322f
color2: #859900
color3: #b58900
color4: #268bd2
color5: #d33682
color6: #2aa198
color7: #eee8d5
color8: #002b36
color9: #cb4b16
color10: #586e75
color11: #657b83
color12: #839496
color13: #6c71c4
color14: #93a1a1
color15: #fdf6e3
}
}

View File

@ -1,28 +0,0 @@
foreground=#f8f8f2
cursor=#f8f8f2
background=#282a36
color0=#000000
color8=#4d4d4d
color1=#ff5555
color9=#ff6e67
color2=#50fa7b
color10=#5af78e
color3=#f1fa8c
color11=#f4f99d
color4=#caa9fa
color12=#caa9fa
color5=#ff79c6
color13=#ff92d0
color6=#8be9fd
color14=#9aedfe
color7=#bfbfbf
color15=#e6e6e6

View File

@ -1,26 +0,0 @@
background=#263238
foreground=#eceff1
color0=#263238
color8=#37474f
color1=#ff9800
color9=#ffa74d
color2=#8bc34a
color10=#9ccc65
color3=#ffc107
color11=#ffa000
color4=#03a9f4
color12=#81d4fa
color5=#e91e63
color13=#ad1457
color6=#009688
color14=#26a69a
color7=#cfd8dc
color15=#eceff1

View File

@ -1,21 +0,0 @@
background=#002b36
foreground=#839496
cursor=#93a1a1
color0=#073642
color1=#dc322f
color2=#859900
color3=#b58900
color4=#268bd2
color5=#d33682
color6=#2aa198
color7=#eee8d5
color9=#cb4b16
color8=#002b36
color10=#586e75
color11=#657b83
color12=#839496
color13=#6c71c4
color14=#93a1a1
color15=#fdf6e3

View File

@ -1,20 +0,0 @@
background=#fdf6e3
foreground=#657b83
cursor=#586e75
color0=#073642
color1=#dc322f
color2=#859900
color3=#b58900
color4=#268bd2
color5=#d33682
color6=#2aa198
color7=#eee8d5
color8=#002b36
color9=#cb4b16
color10=#586e75
color11=#657b83
color12=#839496
color13=#6c71c4
color14=#93a1a1
color15=#fdf6e3

View File

@ -1,8 +1,8 @@
package io.neoterm
import android.app.Application
import io.neoterm.frontend.NeoFrontend
import io.neoterm.preference.NeoPreference
import io.neoterm.customize.NeoInitializer
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.utils.CrashHandler
/**
@ -14,7 +14,7 @@ class App : Application() {
app = this
NeoPreference.init(this)
CrashHandler.init()
NeoFrontend.initialize()
NeoInitializer.initialize(this)
}
companion object {

View File

@ -0,0 +1,28 @@
package io.neoterm.customize
import android.content.Context
import io.neoterm.customize.color.ColorSchemeService
import io.neoterm.customize.completion.CompletionService
import io.neoterm.customize.config.ConfigureService
import io.neoterm.customize.eks.ExtraKeysService
import io.neoterm.customize.font.FontService
import io.neoterm.customize.pm.NeoPackageService
import io.neoterm.customize.script.UserScriptService
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.service.ServiceManager
/**
* @author kiva
*/
object NeoInitializer {
fun initialize(context: Context) {
NLog.init(context)
ServiceManager.registerService(ConfigureService::class.java)
ServiceManager.registerService(ColorSchemeService::class.java)
ServiceManager.registerService(FontService::class.java)
ServiceManager.registerService(UserScriptService::class.java)
ServiceManager.registerService(ExtraKeysService::class.java)
ServiceManager.registerService(CompletionService::class.java)
ServiceManager.registerService(NeoPackageService::class.java)
}
}

View File

@ -1,21 +1,21 @@
package io.neoterm.customize.color
import android.content.Context
import android.util.Log
import io.neoterm.App
import io.neoterm.R
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.frontend.service.NeoService
import io.neoterm.preference.NeoPreference
import io.neoterm.preference.NeoTermPath
import io.neoterm.utils.FileUtils
import io.neoterm.view.eks.ExtraKeysView
import io.neoterm.view.TerminalView
import io.neoterm.view.eks.ExtraKeysView
import java.io.File
import java.io.FileOutputStream
/**
* @author kiva
*/
class ColorSchemeManager : NeoService {
class ColorSchemeService : NeoService {
override fun onServiceObtained() {
checkForFiles()
}
@ -27,18 +27,11 @@ class ColorSchemeManager : NeoService {
override fun onServiceDestroy() {
}
private val DEFAULT_COLOR_NAME = "Default"
private lateinit var DEFAULT_COLOR: NeoColorScheme
private lateinit var colors: MutableMap<String, NeoColorScheme>
private fun extractDefaultColor(context: Context, defaultColorFile: File): Boolean {
private fun extractDefaultColor(context: Context): Boolean {
try {
val prop = DefaultColorScheme.createConfig()
FileOutputStream(defaultColorFile).use {
prop.store(it, "Created by NeoColorSchemeManager")
}
val asset = context.assets
for (i in asset.list("colors")) {
val targetFile = File(NeoTermPath.COLORS_PATH, i)
@ -57,15 +50,17 @@ class ColorSchemeManager : NeoService {
fun refreshColorList(): Boolean {
colors.clear()
val colorDir = File(NeoTermPath.COLORS_PATH)
for (file in colorDir.listFiles({ pathname -> pathname.name.endsWith(".color") })) {
val colorName = colorName(file)
val color = NeoColorScheme(colorName)
for (file in colorDir.listFiles({ pathname ->
pathname.name.endsWith(".color") || pathname.name.endsWith(".nl")
})) {
val color = NeoColorScheme()
color.parseConfig(file)
colors.put(colorName, color)
if (color.loadConfigure(file)) {
colors.put(color.colorName, color)
}
if (colors.containsKey(DEFAULT_COLOR_NAME)) {
DEFAULT_COLOR = colors[DEFAULT_COLOR_NAME]!!
}
if (colors.containsKey(DefaultColorScheme.colorName)) {
DEFAULT_COLOR = colors[DefaultColorScheme.colorName]!!
return true
}
return false
@ -77,21 +72,13 @@ class ColorSchemeManager : NeoService {
}
}
private fun colorFile(colorName: String): File {
return File("${NeoTermPath.COLORS_PATH}/$colorName.color")
}
private fun colorName(colorFile: File): String {
return colorFile.nameWithoutExtension
}
private fun checkForFiles() {
File(NeoTermPath.COLORS_PATH).mkdirs()
colors = mutableMapOf()
val defaultColorFile = colorFile(DEFAULT_COLOR_NAME)
val defaultColorFile = colorFile(DefaultColorScheme.colorName)
if (!defaultColorFile.exists()) {
if (extractDefaultColor(App.get(), defaultColorFile)) {
if (!extractDefaultColor(App.get())) {
DEFAULT_COLOR = DefaultColorScheme
colors[DEFAULT_COLOR.colorName] = DEFAULT_COLOR
return
@ -109,10 +96,10 @@ class ColorSchemeManager : NeoService {
}
fun getCurrentColorName(): String {
var currentColorName = NeoPreference.loadString(R.string.key_customization_color_scheme, DEFAULT_COLOR_NAME)
var currentColorName = NeoPreference.loadString(R.string.key_customization_color_scheme, DefaultColorScheme.colorName)
if (!colors.containsKey(currentColorName)) {
currentColorName = DEFAULT_COLOR_NAME
NeoPreference.store(R.string.key_customization_color_scheme, DEFAULT_COLOR_NAME)
currentColorName = DefaultColorScheme.colorName
NeoPreference.store(R.string.key_customization_color_scheme, DefaultColorScheme.colorName)
}
return currentColorName
}
@ -130,4 +117,10 @@ class ColorSchemeManager : NeoService {
fun setCurrentColor(colorName: String) {
NeoPreference.store(R.string.key_customization_color_scheme, colorName)
}
companion object {
fun colorFile(colorName: String): File {
return File("${NeoTermPath.COLORS_PATH}/$colorName.nl")
}
}
}

View File

@ -3,8 +3,11 @@ package io.neoterm.customize.color
/**
* @author kiva
*/
object DefaultColorScheme : NeoColorScheme("Default") {
object DefaultColorScheme : NeoColorScheme() {
init {
/* NOTE: Keep in sync with assets/colors/Default.nl */
colorName = "Default"
foregroundColor = "#ffffff"
backgroundColor = "#14181c"
cursorColor = "#a9aaa9"

View File

@ -2,37 +2,47 @@ package io.neoterm.customize.color
import io.neoterm.backend.TerminalColorScheme
import io.neoterm.backend.TerminalColors
import io.neoterm.view.eks.ExtraKeysView
import io.neoterm.customize.config.ConfigureService
import io.neoterm.frontend.config.ConfigVisitor
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.service.ServiceManager
import io.neoterm.view.TerminalView
import io.neoterm.view.eks.ExtraKeysView
import java.io.File
import java.io.FileInputStream
import java.util.*
/**
* @author kiva
*/
open class NeoColorScheme(val colorName: String) {
open class NeoColorScheme {
companion object {
private const val COLOR_PREFIX = "color"
const val COLOR_DIM_BLACK = 0
const val COLOR_DIM_RED = 1
const val COLOR_DIM_GREEN = 2
const val COLOR_DIM_YELLOW = 3
const val COLOR_DIM_BLUE = 4
const val COLOR_DIM_MAGENTA = 5
const val COLOR_DIM_CYAN = 6
const val COLOR_DIM_WHITE = 7
const val COLOR_CONTEXT_NAME = "colors"
const val COLOR_META_CONTEXT_NAME = "color-scheme"
const val COLOR_BRIGHT_BLACK = 8
const val COLOR_BRIGHT_RED = 9
const val COLOR_BRIGHT_GREEN = 10
const val COLOR_BRIGHT_YELLOW = 11
const val COLOR_BRIGHT_BLUE = 12
const val COLOR_BRIGHT_MAGENTA = 13
const val COLOR_BRIGHT_CYAN = 14
const val COLOR_BRIGHT_WHITE = 15
const val COLOR_META_NAME = "name"
const val COLOR_META_VERSION = "version"
// const val COLOR_DIM_BLACK = 0
// const val COLOR_DIM_RED = 1
// const val COLOR_DIM_GREEN = 2
// const val COLOR_DIM_YELLOW = 3
// const val COLOR_DIM_BLUE = 4
// const val COLOR_DIM_MAGENTA = 5
// const val COLOR_DIM_CYAN = 6
// const val COLOR_DIM_WHITE = 7
//
// const val COLOR_BRIGHT_BLACK = 8
// const val COLOR_BRIGHT_RED = 9
// const val COLOR_BRIGHT_GREEN = 10
// const val COLOR_BRIGHT_YELLOW = 11
// const val COLOR_BRIGHT_BLUE = 12
// const val COLOR_BRIGHT_MAGENTA = 13
// const val COLOR_BRIGHT_CYAN = 14
// const val COLOR_BRIGHT_WHITE = 15
}
lateinit var colorName: String
var colorVersion: String? = null
var foregroundColor: String? = null
var backgroundColor: String? = null
var cursorColor: String? = null
@ -62,41 +72,54 @@ open class NeoColorScheme(val colorName: String) {
cursorColor = cursorColor ?: DefaultColorScheme.cursorColor
}
fun createConfig(): Properties {
// TODO: 兼容旧版本的配置并且解析新版本的配置文件
validateColors()
val prop = Properties()
prop.put("foreground", foregroundColor)
prop.put("background", backgroundColor)
prop.put("cursor", cursorColor)
for (i in color.keys) {
prop.put(COLOR_PREFIX + i, color[i])
}
return prop
}
fun loadConfigure(file: File): Boolean {
val loaderService = ServiceManager.getService<ConfigureService>()
fun parseConfig(file: File): Boolean {
// TODO: 兼容旧版本的配置并且解析新版本的配置文件
try {
return FileInputStream(file).use {
val prop = Properties()
prop.load(it)
prop.all {
when (it.key) {
"foreground" -> foregroundColor = it.value as String
"background" -> backgroundColor = it.value as String
"cursor" -> cursorColor = it.value as String
(it.key as String).startsWith(COLOR_PREFIX) -> {
val colorType = (it.key as String).substringAfter(COLOR_PREFIX)
setColor(colorType.toInt(), it.value as String)
}
}
true
}
true
}
} catch (e: Exception) {
val configure = loaderService.newLoader(file).loadConfigure()
if (configure == null) {
NLog.e("ColorScheme", "Failed to load color config: ${file.absolutePath}")
return false
}
val visitor = configure.getVisitor()
val colorName = getMetaByVisitor(visitor, COLOR_META_NAME)
if (colorName == null) {
NLog.e("ColorScheme", "Failed to load color config: ${file.absolutePath}: ColorScheme must have a name")
return false
}
this.colorName = colorName
this.colorVersion = getMetaByVisitor(visitor, COLOR_META_VERSION)
backgroundColor = getColorByVisitor(visitor, "background")
foregroundColor = getColorByVisitor(visitor, "foreground")
cursorColor = getColorByVisitor(visitor, "cursor")
visitor.getContext(COLOR_CONTEXT_NAME).getAttributes().forEach {
val colorIndex = try {
it.key.substringAfter(COLOR_PREFIX).toInt()
} catch (e: Exception) {
-1
}
if (colorIndex == -1) {
NLog.w("ColorScheme", "Invalid color type: ${it.key}")
} else {
setColor(colorIndex, it.value.asString())
}
}
validateColors()
return true
}
private fun getMetaByVisitor(visitor: ConfigVisitor, metaName: String): String? {
val value = visitor.getAttribute(COLOR_META_CONTEXT_NAME, metaName)
return if (value.isValid()) value.asString() else null
}
private fun getColorByVisitor(visitor: ConfigVisitor, colorName: String): String? {
val value = visitor.getAttribute(COLOR_CONTEXT_NAME, colorName)
return if (value.isValid()) value.asString() else null
}
}

View File

@ -8,7 +8,7 @@ import io.neoterm.frontend.service.NeoService
/**
* @author kiva
*/
class CompletionProviderManager : NeoService {
class CompletionService : NeoService {
override fun onServiceInit() {
CompletionManager.registerProvider(FileCompletionProvider())
CompletionManager.registerProvider(ProgramCompletionProvider())

View File

@ -0,0 +1,27 @@
package io.neoterm.customize.config
import io.neoterm.customize.config.loader.NeoLangConfigureLoader
import io.neoterm.customize.config.loader.OldConfigureLoader
import io.neoterm.frontend.service.NeoService
import java.io.File
/**
* @author kiva
*/
class ConfigureService : NeoService {
override fun onServiceInit() {
}
override fun onServiceDestroy() {
}
override fun onServiceObtained() {
}
fun newLoader(configFile: File): IConfigureFileLoader {
return when (configFile.extension) {
"nl" -> NeoLangConfigureLoader(configFile)
else -> OldConfigureLoader(configFile)
}
}
}

View File

@ -0,0 +1,11 @@
package io.neoterm.customize.config
import io.neoterm.frontend.config.NeoConfigureFile
import java.io.File
/**
* @author kiva
*/
interface IConfigureFileLoader {
fun loadConfigure() : NeoConfigureFile?
}

View File

@ -0,0 +1,15 @@
package io.neoterm.customize.config.loader
import io.neoterm.customize.config.IConfigureFileLoader
import io.neoterm.frontend.config.NeoConfigureFile
import java.io.File
/**
* @author kiva
*/
class NeoLangConfigureLoader(val configFile: File) : IConfigureFileLoader {
override fun loadConfigure(): NeoConfigureFile? {
val configureFile = NeoConfigureFile(configFile)
return if (configureFile.parseConfigure()) configureFile else null
}
}

View File

@ -0,0 +1,47 @@
package io.neoterm.customize.config.loader
import io.neolang.runtime.type.NeoLangValue
import io.neoterm.customize.color.NeoColorScheme
import io.neoterm.frontend.config.ConfigVisitor
import io.neoterm.frontend.config.NeoConfigureFile
import io.neoterm.frontend.logging.NLog
import java.io.File
import java.io.FileInputStream
import java.util.*
/**
* @author kiva
*/
class OldColorSchemeConfigureFile(configureFile: File) : NeoConfigureFile(configureFile) {
override var configVisitor: ConfigVisitor? = null
override fun parseConfigure(): Boolean {
try {
val visitor = ConfigVisitor()
visitor.onStart()
visitor.onEnterContext(NeoColorScheme.COLOR_META_CONTEXT_NAME)
visitor.getCurrentContext()
.defineAttribute(NeoColorScheme.COLOR_META_NAME, NeoLangValue(configureFile.nameWithoutExtension))
.defineAttribute(NeoColorScheme.COLOR_META_VERSION, NeoLangValue("1.0"))
visitor.onEnterContext(NeoColorScheme.COLOR_CONTEXT_NAME)
return FileInputStream(configureFile).use {
val prop = Properties()
prop.load(it)
prop.forEach {
visitor.getCurrentContext().defineAttribute(it.key as String, NeoLangValue(it.value as String))
}
visitor.onFinish()
this.configVisitor = visitor
true
}
} catch (e: Exception) {
this.configVisitor = null
NLog.e("ConfigureLoader", "Error while loading old config", e)
return false
}
}
}

View File

@ -0,0 +1,22 @@
package io.neoterm.customize.config.loader
import io.neoterm.customize.config.IConfigureFileLoader
import io.neoterm.frontend.config.NeoConfigureFile
import java.io.File
/**
* @author kiva
*/
class OldConfigureLoader(val configFile: File) : IConfigureFileLoader {
override fun loadConfigure(): NeoConfigureFile? {
return when (configFile.extension) {
"eks" -> returnConfigure(OldExtraKeysConfigureFile(configFile))
"color" -> returnConfigure(OldColorSchemeConfigureFile(configFile))
else -> null
}
}
private fun returnConfigure(configureFile: NeoConfigureFile): NeoConfigureFile? {
return if (configureFile.parseConfigure()) configureFile else null
}
}

View File

@ -0,0 +1,9 @@
package io.neoterm.customize.config.loader
import io.neoterm.frontend.config.NeoConfigureFile
import java.io.File
/**
* @author kiva
*/
class OldExtraKeysConfigureFile(configureFile: File) : NeoConfigureFile(configureFile)

View File

@ -1,8 +1,7 @@
package io.neoterm.customize.eks
import android.util.Log
import io.neoterm.frontend.service.ServiceManager
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.view.eks.ExtraKeysView
import java.io.File
@ -21,7 +20,7 @@ object ExtraKeyConfigLoader {
}
}
fun loadDefinedConfigs(extraKeysManager: ExtraKeysManager) {
fun loadDefinedConfigs(extraKeysManager: ExtraKeysService) {
val configDir = File(NeoTermPath.EKS_PATH)
configDir.mkdirs()
@ -38,12 +37,12 @@ object ExtraKeyConfigLoader {
}
registerConfig(extraKeysManager, config)
} catch (e: Exception) {
Log.e("NeoTerm-EKS", "Load $file failed: " + e.toString())
NLog.e("NeoTerm-EKS", "Load $file failed: " + e.toString())
}
}
}
private fun registerConfig(extraKeysManager: ExtraKeysManager, config: ExtraKeyConfig) {
private fun registerConfig(extraKeysManager: ExtraKeysService, config: ExtraKeyConfig) {
val shortcutKey = ConfiguredExtraKey(config)
for (programName in config.programNames) {
extraKeysManager.registerShortcutKeys(programName, shortcutKey)

View File

@ -7,7 +7,7 @@ import io.neoterm.view.eks.ExtraKeysView
/**
* @author kiva
*/
class ExtraKeysManager : NeoService {
class ExtraKeysService : NeoService {
override fun onServiceInit() {
checkForFiles()
}

View File

@ -1,6 +1,6 @@
package io.neoterm.customize.eks.builtin
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.customize.eks.ExtraKeyConfigParser
import io.neoterm.utils.FileUtils
import java.io.File

View File

@ -5,15 +5,15 @@ import android.graphics.Typeface
import io.neoterm.App
import io.neoterm.R
import io.neoterm.frontend.service.NeoService
import io.neoterm.preference.NeoTermPath
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.utils.FileUtils
import java.io.File
/**
* @author kiva
*/
class FontManager : NeoService {
class FontService : NeoService {
override fun onServiceInit() {
checkForFiles()
}

View File

@ -1,8 +1,8 @@
package io.neoterm.customize.pm
import io.neoterm.R
import io.neoterm.preference.NeoPreference
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import java.io.File
import java.net.URL

View File

@ -11,7 +11,7 @@ import io.neoterm.frontend.service.NeoService;
* @author kiva
*/
public class NeoPackageManager implements NeoService {
public class NeoPackageService implements NeoService {
private final Object lock = new Object();
private boolean isRefreshing = false;
private boolean queryEnabled = true;

View File

@ -1,13 +1,13 @@
package io.neoterm.customize.script
import io.neoterm.frontend.service.NeoService
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoTermPath
import java.io.File
/**
* @author kiva
*/
class UserScriptManager : NeoService {
class UserScriptService : NeoService {
override fun onServiceInit() {
userScripts = mutableListOf()
reloadScripts()

View File

@ -5,7 +5,6 @@ import android.app.ProgressDialog;
import android.content.Context;
import android.os.Build;
import android.system.Os;
import android.util.Log;
import android.util.Pair;
import java.io.BufferedReader;
@ -23,8 +22,9 @@ import java.util.zip.ZipInputStream;
import io.neoterm.R;
import io.neoterm.backend.EmulatorDebug;
import io.neoterm.preference.NeoPreference;
import io.neoterm.preference.NeoTermPath;
import io.neoterm.frontend.logging.NLog;
import io.neoterm.frontend.preference.NeoPreference;
import io.neoterm.frontend.preference.NeoTermPath;
public final class BaseFileInstaller {
public interface ResultListener {
@ -144,7 +144,7 @@ public final class BaseFileInstaller {
}
});
} catch (final Exception e) {
Log.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e);
NLog.INSTANCE.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {

View File

@ -1,27 +0,0 @@
package io.neoterm.frontend
import io.neoterm.customize.color.ColorSchemeManager
import io.neoterm.customize.completion.CompletionProviderManager
import io.neoterm.customize.eks.ExtraKeysManager
import io.neoterm.customize.font.FontManager
import io.neoterm.customize.pm.NeoPackageManager
import io.neoterm.customize.script.UserScriptManager
import io.neoterm.frontend.logger.NLog
import io.neoterm.frontend.service.ServiceManager
/**
* @author kiva
*/
object NeoFrontend {
fun initialize() {
// ensure that we can access these any time
ServiceManager.registerService(ColorSchemeManager::class.java)
ServiceManager.registerService(FontManager::class.java)
ServiceManager.registerService(UserScriptManager::class.java)
ServiceManager.registerService(ExtraKeysManager::class.java)
ServiceManager.registerService(CompletionProviderManager::class.java)
ServiceManager.registerService(NeoPackageManager::class.java)
NLog.initialize()
}
}

View File

@ -7,7 +7,7 @@ import android.media.SoundPool
import android.os.Vibrator
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoPreference
/**
* @author kiva

View File

@ -9,9 +9,9 @@ import android.view.inputmethod.InputMethodManager
import io.neoterm.R
import io.neoterm.backend.KeyHandler
import io.neoterm.backend.TerminalSession
import io.neoterm.customize.eks.ExtraKeysManager
import io.neoterm.customize.eks.ExtraKeysService
import io.neoterm.frontend.service.ServiceManager
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.view.TerminalViewClient
@ -197,7 +197,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
if (lastTitle != title || force) {
removeSuggestions()
ServiceManager.getService<ExtraKeysManager>().showShortcutKeys(title, extraKeysView)
ServiceManager.getService<ExtraKeysService>().showShortcutKeys(title, extraKeysView)
extraKeysView?.updateButtons()
lastTitle = title
}

View File

@ -11,7 +11,7 @@ import android.widget.PopupWindow
import android.widget.TextView
import io.neoterm.R
import io.neoterm.backend.TerminalColors
import io.neoterm.customize.color.ColorSchemeManager
import io.neoterm.customize.color.ColorSchemeService
import io.neoterm.frontend.completion.listener.OnCandidateSelectedListener
import io.neoterm.frontend.completion.model.CompletionCandidate
import io.neoterm.frontend.service.ServiceManager
@ -136,7 +136,7 @@ class CandidatePopupWindow(val context: Context) {
val splitView: View = rootView.findViewById(R.id.complete_split)
init {
val colorScheme = ServiceManager.getService<ColorSchemeManager>().getCurrentColorScheme()
val colorScheme = ServiceManager.getService<ColorSchemeService>().getCurrentColorScheme()
val textColor = TerminalColors.parse(colorScheme.foregroundColor)
display.setTextColor(textColor)
description.setTextColor(textColor)

View File

@ -1,23 +1,28 @@
package io.neoterm.frontend.config
import io.neolang.parser.NeoLangParser
import io.neoterm.frontend.logging.NLog
import io.neoterm.utils.FileUtils
import java.io.File
/**
* @author kiva
*/
class NeoConfigureFile(val configureFile: String) {
open class NeoConfigureFile(val configureFile: File) {
private val configParser = NeoLangParser()
private var configVisitor : ConfigVisitor? = null
open protected var configVisitor : ConfigVisitor? = null
fun getVisitor(): ConfigVisitor {
checkParsed()
return configVisitor!!
}
fun parseConfigure(): Boolean {
val configContent = FileUtils.readFile(File(configureFile)) ?: return false
open fun parseConfigure(): Boolean {
val configContent = FileUtils.readFile(configureFile)
if (configContent == null) {
NLog.e("ConfigureFile", "Cannot read file $configureFile")
return false
}
val programCode = String(configContent)
configParser.setInputSource(programCode)
@ -30,7 +35,7 @@ class NeoConfigureFile(val configureFile: String) {
private fun checkParsed() {
if (configVisitor == null) {
throw IllegalStateException("Configure file not loaded.")
throw IllegalStateException("Configure file not loaded or parse failed.")
}
}
}

View File

@ -1,9 +0,0 @@
package io.neoterm.frontend.logger
/**
* @author kiva
*/
object NLog {
fun initialize() {
}
}

View File

@ -0,0 +1,315 @@
package io.neoterm.frontend.logging
import android.content.Context
import android.support.annotation.IntDef
import android.util.Log
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.zip.DataFormatException
import java.util.zip.Deflater
import java.util.zip.Inflater
object NLog {
const val V = Log.VERBOSE
const val D = Log.DEBUG
const val I = Log.INFO
const val W = Log.WARN
const val E = Log.ERROR
const val A = Log.ASSERT
@IntDef(V.toLong(), D.toLong(), I.toLong(), W.toLong(), E.toLong(), A.toLong())
@Retention(AnnotationRetention.SOURCE)
private annotation class TYPE
private val T = charArrayOf('V', 'D', 'I', 'W', 'E', 'A')
private val FILE = 0x10
private var executor: ExecutorService? = null
private var logDir: String? = null // log存储目录
private var sLogSwitch = true // log总开关默认开
private var sLog2ConsoleSwitch = true // logcat是否打印默认打印
private var sGlobalTag: String = "" // log标签
private var sTagIsSpace = true // log标签是否为空白
private var sLogHeadSwitch = true // log头部开关默认开
private var sLog2FileSwitch = false// log写入文件开关默认关
private var sConsoleFilter = V // log控制台过滤器
private var sFileFilter = V // log文件过滤器
private val LINE_SEP = System.getProperty("line.separator")
private val MAX_LEN = 4000
private val FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS ", Locale.getDefault())
private val NULL_TIPS = "Log with null object."
private val ARGS = "args"
fun init(context: Context) {
logDir = context.getDir("logs", Context.MODE_PRIVATE).absolutePath
sGlobalTag = "NeoTerm"
}
fun v(contents: Any) {
log(V, sGlobalTag, contents)
}
fun v(tag: String, vararg contents: Any) {
log(V, tag, *contents)
}
fun d(contents: Any) {
log(D, sGlobalTag, contents)
}
fun d(tag: String, vararg contents: Any) {
log(D, tag, *contents)
}
fun i(contents: Any) {
log(I, sGlobalTag, contents)
}
fun i(tag: String, vararg contents: Any) {
log(I, tag, *contents)
}
fun w(contents: Any) {
log(W, sGlobalTag, contents)
}
fun w(tag: String, vararg contents: Any) {
log(W, tag, *contents)
}
fun e(contents: Any) {
log(E, sGlobalTag, contents)
}
fun e(tag: String, vararg contents: Any) {
log(E, tag, *contents)
}
fun a(contents: Any) {
log(A, sGlobalTag, contents)
}
fun a(tag: String, vararg contents: Any) {
log(A, tag, *contents)
}
fun file(contents: Any) {
log(FILE or D, sGlobalTag, contents)
}
fun file(@TYPE type: Int, contents: Any) {
log(FILE or type, sGlobalTag, contents)
}
fun file(tag: String, contents: Any) {
log(FILE or D, tag, contents)
}
fun file(@TYPE type: Int, tag: String, contents: Any) {
log(FILE or type, tag, contents)
}
private fun log(type: Int, tag: String, vararg contents: Any) {
if (!sLogSwitch || !sLog2ConsoleSwitch && !sLog2FileSwitch) return
val type_low = type and 0x0f
val type_high = type and 0xf0
if (type_low < sConsoleFilter && type_low < sFileFilter) return
val tagAndHead = processTagAndHead(tag)
val body = processBody(*contents)
if (sLog2ConsoleSwitch && type_low >= sConsoleFilter) {
printToConsole(type_low, tagAndHead[0], tagAndHead[1] + body)
}
if (sLog2FileSwitch || type_high == FILE) {
if (type_low >= sFileFilter) printToFile(type_low, tagAndHead[0], tagAndHead[2] + body)
}
}
private fun processTagAndHead(tag: String): Array<String> {
var returnTag = tag
if (!sTagIsSpace && !sLogHeadSwitch) {
returnTag = sGlobalTag
} else {
returnTag = "$sGlobalTag-$returnTag"
val targetElement = Throwable().stackTrace[3]
var className = targetElement.className
val classNameInfo = className.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (classNameInfo.isNotEmpty()) {
className = classNameInfo[classNameInfo.size - 1]
}
if (className.contains("$")) {
className = className.split("\\$".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
}
if (sTagIsSpace) {
returnTag = if (isSpace(returnTag)) className else returnTag
}
if (sLogHeadSwitch) {
val head = Formatter()
.format("%s, %s(%s:%d)",
Thread.currentThread().name,
targetElement.methodName,
targetElement.fileName,
targetElement.lineNumber)
.toString()
return arrayOf(returnTag, head + LINE_SEP, " [$head]: ")
}
}
return arrayOf(returnTag, "", ": ")
}
private fun processBody(vararg contents: Any): String {
var body = NULL_TIPS
if (contents.isNotEmpty()) {
if (contents.size == 1) {
body = contents[0].toString()
} else {
body = buildString {
var index = 0
contents.forEach {
append(ARGS)
.append("[")
.append(index++)
.append("]")
.append(" = ")
.append(it.toString())
.append(LINE_SEP)
}
}
}
}
return body
}
private fun printToConsole(type: Int, tag: String, msg: String) {
val len = msg.length
val countOfSub = len / MAX_LEN
if (countOfSub > 0) {
print(type, tag, msg.substring(0, MAX_LEN))
var sub: String
var index = MAX_LEN
for (i in 1..countOfSub - 1) {
sub = msg.substring(index, index + MAX_LEN)
print(type, tag, sub)
index += MAX_LEN
}
sub = msg.substring(index, len)
print(type, tag, sub)
} else {
print(type, tag, msg)
}
}
private fun print(type: Int, tag: String, msg: String) {
Log.println(type, tag, msg)
}
private fun printToFile(type: Int, tag: String, msg: String) {
val now = Date(System.currentTimeMillis())
val format = FORMAT.format(now)
val date = format.substring(0, 5)
val time = format.substring(6)
val fullPath = logDir + date + ".txt"
if (!createOrExistsFile(fullPath)) {
Log.e(tag, "log to $fullPath failed!")
return
}
val sb = StringBuilder()
sb.append(time)
.append(T[type - V])
.append("/")
.append(tag)
.append(msg)
.append(LINE_SEP)
val content = sb.toString()
if (executor == null) {
executor = Executors.newSingleThreadExecutor()
}
executor!!.execute {
var bw: BufferedWriter? = null
try {
bw = BufferedWriter(FileWriter(fullPath, true))
bw.write(content)
Log.d(tag, "log to $fullPath success!")
} catch (e: IOException) {
e.printStackTrace()
Log.e(tag, "log to $fullPath failed!")
} finally {
try {
if (bw != null) {
bw.close()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
private fun createOrExistsFile(filePath: String): Boolean {
val file = File(filePath)
if (file.exists()) return file.isFile
if (!createOrExistsDir(file.parentFile)) return false
try {
return file.createNewFile()
} catch (e: IOException) {
e.printStackTrace()
return false
}
}
private fun createOrExistsDir(file: File?): Boolean {
return file != null && if (file.exists()) file.isDirectory else file.mkdirs()
}
private fun isSpace(s: String?): Boolean {
return s?.isEmpty() ?: true
}
fun compress(input: ByteArray): ByteArray {
val bos = ByteArrayOutputStream()
val compressor = Deflater(1)
try {
compressor.setInput(input)
compressor.finish()
val buf = ByteArray(2048)
while (!compressor.finished()) {
val count = compressor.deflate(buf)
bos.write(buf, 0, count)
}
} finally {
compressor.end()
}
return bos.toByteArray()
}
fun uncompress(input: ByteArray): ByteArray {
val bos = ByteArrayOutputStream()
val inflater = Inflater()
try {
inflater.setInput(input)
val buf = ByteArray(2048)
while (!inflater.finished()) {
var count = 0
try {
count = inflater.inflate(buf)
} catch (e: DataFormatException) {
e.printStackTrace()
}
bos.write(buf, 0, count)
}
} finally {
inflater.end()
}
return bos.toByteArray()
}
}

View File

@ -1,4 +1,4 @@
package io.neoterm.preference
package io.neoterm.frontend.preference
import android.Manifest
import android.app.Activity

View File

@ -1,4 +1,4 @@
package io.neoterm.preference
package io.neoterm.frontend.preference
import android.content.Context
import android.content.SharedPreferences

View File

@ -1,4 +1,4 @@
package io.neoterm.preference
package io.neoterm.frontend.preference
import android.annotation.SuppressLint
import io.neoterm.BuildConfig

View File

@ -28,7 +28,6 @@ object ServiceManager {
}
inline fun <reified T : NeoService> getService(): T {
Log.e("NeoTerm", SERVICE_CACHE.keys.toString())
val serviceInterface = T::class.java
val service: NeoService? = SERVICE_CACHE[serviceInterface]

View File

@ -4,8 +4,8 @@ import android.content.Context
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.client.TermSessionCallback
import io.neoterm.preference.NeoPreference
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import java.io.File
/**

View File

@ -10,9 +10,9 @@ import android.view.View
import android.widget.*
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.customize.color.ColorSchemeManager
import io.neoterm.preference.NeoTermPath
import io.neoterm.customize.font.FontManager
import io.neoterm.customize.color.ColorSchemeService
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.customize.font.FontService
import io.neoterm.frontend.shell.ShellParameter
import io.neoterm.frontend.service.ServiceManager
import io.neoterm.utils.FileUtils
@ -77,8 +77,8 @@ class CustomizeActivity : AppCompatActivity() {
}
private fun setupSpinners() {
val fontManager = ServiceManager.getService<FontManager>()
val colorSchemeManager = ServiceManager.getService<ColorSchemeManager>()
val fontManager = ServiceManager.getService<FontService>()
val colorSchemeManager = ServiceManager.getService<ColorSchemeService>()
setupSpinner(R.id.custom_font_spinner, fontManager.getFontNames(),
fontManager.getCurrentFontName(), object : AdapterView.OnItemSelectedListener {

View File

@ -23,10 +23,10 @@ import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.customize.pm.NeoPackageManager
import io.neoterm.customize.pm.NeoPackageService
import io.neoterm.customize.pm.NeoPackageManagerUtils
import io.neoterm.preference.NeoPreference
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.ui.pm.adapter.PackageAdapter
import io.neoterm.ui.pm.model.PackageModel
import io.neoterm.utils.PackageUtils
@ -193,7 +193,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
progressBar.visibility = View.VISIBLE
progressBar.alpha = 0.0f
Thread {
val pm = ServiceManager.getService<NeoPackageManager>()
val pm = ServiceManager.getService<NeoPackageService>()
val sourceFiles = NeoPackageManagerUtils.detectSourceFiles()
pm.clearPackages()

View File

@ -6,8 +6,8 @@ import android.support.v7.app.AppCompatPreferenceActivity
import android.view.MenuItem
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.preference.NeoPreference
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.frontend.floating.TerminalDialog
/**

View File

@ -26,8 +26,8 @@ import io.neoterm.customize.setup.BaseFileInstaller
import io.neoterm.frontend.shell.ShellParameter
import io.neoterm.frontend.client.TermSessionCallback
import io.neoterm.frontend.client.TermViewClient
import io.neoterm.preference.NeoPermission
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoPermission
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.services.NeoTermService
import io.neoterm.ui.bonus.BonusActivity
import io.neoterm.ui.pm.PackageManagerActivity

View File

@ -15,10 +15,10 @@ import android.widget.ListView
import android.widget.Toast
import io.neoterm.R
import io.neoterm.customize.script.UserScript
import io.neoterm.customize.script.UserScriptManager
import io.neoterm.customize.script.UserScriptService
import io.neoterm.frontend.shell.ShellParameter
import io.neoterm.frontend.client.TermSessionCallback
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.services.NeoTermService
import io.neoterm.utils.TerminalUtils
import java.io.File
@ -149,7 +149,7 @@ class NeoTermRemoteInterface : AppCompatActivity(), ServiceConnection {
}
}
val userScriptManager = ServiceManager.getService<UserScriptManager>()
val userScriptManager = ServiceManager.getService<UserScriptService>()
val userScripts = userScriptManager.userScripts
if (userScripts.isNotEmpty() && filesToHandle.isNotEmpty()) {
setupUserScriptView(filesToHandle, userScripts)

View File

@ -5,8 +5,8 @@ import android.support.v7.widget.Toolbar
import android.view.inputmethod.InputMethodManager
import de.mrapp.android.tabswitcher.Tab
import io.neoterm.R
import io.neoterm.customize.color.ColorSchemeManager
import io.neoterm.preference.NeoPreference
import io.neoterm.customize.color.ColorSchemeService
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.client.TermDataHolder
import io.neoterm.frontend.client.TermUiPresenter
import io.neoterm.frontend.service.ServiceManager
@ -24,7 +24,7 @@ class TermTab(title: CharSequence) : Tab(title), TermUiPresenter {
var toolbar: Toolbar? = null
fun updateColorScheme() {
val colorSchemeManager = ServiceManager.getService<ColorSchemeManager>()
val colorSchemeManager = ServiceManager.getService<ColorSchemeService>()
colorSchemeManager.applyColorScheme(termData.termView, termData.extraKeysView,
colorSchemeManager.getCurrentColorScheme())
}

View File

@ -8,10 +8,9 @@ import android.view.ViewGroup
import de.mrapp.android.tabswitcher.Tab
import de.mrapp.android.tabswitcher.TabSwitcher
import de.mrapp.android.tabswitcher.TabSwitcherDecorator
import io.neoterm.BuildConfig
import io.neoterm.R
import io.neoterm.customize.color.ColorSchemeManager
import io.neoterm.preference.NeoPreference
import io.neoterm.customize.color.ColorSchemeService
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.client.TermCompleteListener
import io.neoterm.ui.term.NeoTermActivity
import io.neoterm.utils.TerminalUtils
@ -52,7 +51,7 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
TerminalUtils.setupTerminalView(view)
TerminalUtils.setupExtraKeysView(extraKeysView)
val colorSchemeManager = ServiceManager.getService<ColorSchemeManager>()
val colorSchemeManager = ServiceManager.getService<ColorSchemeService>()
colorSchemeManager.applyColorScheme(view, extraKeysView, colorSchemeManager.getCurrentColorScheme())
if (tab is TermTab) {

View File

@ -3,8 +3,8 @@ package io.neoterm.utils
import android.content.Context
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.preference.NeoPreference
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.frontend.floating.TerminalDialog
import java.io.File

View File

@ -3,11 +3,11 @@ package io.neoterm.utils
import android.content.Context
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.customize.font.FontManager
import io.neoterm.customize.font.FontService
import io.neoterm.frontend.shell.ShellParameter
import io.neoterm.frontend.shell.ShellTermSession
import io.neoterm.frontend.service.ServiceManager
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.view.eks.ExtraKeysView
import io.neoterm.view.TerminalView
import io.neoterm.view.TerminalViewClient
@ -18,14 +18,14 @@ import io.neoterm.view.TerminalViewClient
object TerminalUtils {
fun setupTerminalView(terminalView: TerminalView?, terminalViewClient: TerminalViewClient? = null) {
terminalView?.textSize = NeoPreference.loadInt(NeoPreference.KEY_FONT_SIZE, 30)
terminalView?.setTypeface(ServiceManager.getService<FontManager>().getCurrentFont().getTypeFace())
terminalView?.setTypeface(ServiceManager.getService<FontService>().getCurrentFont().getTypeFace())
if (terminalViewClient != null) {
terminalView?.setTerminalViewClient(terminalViewClient)
}
}
fun setupExtraKeysView(extraKeysView: ExtraKeysView?) {
extraKeysView?.setTypeface(ServiceManager.getService<FontManager>().getCurrentFont().getTypeFace())
extraKeysView?.setTypeface(ServiceManager.getService<FontService>().getCurrentFont().getTypeFace())
}
fun setupTerminalSession(session: TerminalSession?) {

View File

@ -1,7 +1,7 @@
package io.neoterm.view.eks
import io.neoterm.customize.eks.ExtraKeyConfigParser
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.utils.FileUtils
import java.io.File

View File

@ -8,7 +8,7 @@ import android.widget.GridLayout
import android.widget.LinearLayout
import io.neoterm.R
import io.neoterm.customize.eks.ExtraKeyConfigParser
import io.neoterm.preference.NeoPreference
import io.neoterm.frontend.preference.NeoPreference
import io.neoterm.ui.term.event.ToggleImeEvent
import io.neoterm.view.eks.button.ControlButton
import io.neoterm.view.eks.button.IExtraButton

View File

@ -16,7 +16,7 @@ import java.io.File
import java.util.ArrayList
import io.neoterm.customize.eks.ExtraKeyConfigParser
import io.neoterm.preference.NeoTermPath
import io.neoterm.frontend.preference.NeoTermPath
import io.neoterm.utils.FileUtils
import io.neoterm.view.eks.button.ControlButton
import io.neoterm.view.eks.button.IExtraButton