From d33fc79e9fd23f5ef0e221d0a75a2ab0c5c3f67a Mon Sep 17 00:00:00 2001 From: macuser Date: Fri, 25 Nov 2016 01:01:27 +0300 Subject: [PATCH 1/5] Updated interface --- .../io/github/kbiakov/codeview/CodeView.kt | 170 +++++++-------- .../io/github/kbiakov/codeview/Highlighter.kt | 141 ------------ .../java/io/github/kbiakov/codeview/Utils.kt | 10 +- .../codeview/adapters/AbstractCodeAdapter.kt | 201 +++++++++++------- .../codeview/adapters/CodeWithDiffsAdapter.kt | 9 +- .../codeview/adapters/CodeWithNotesAdapter.kt | 15 +- .../kbiakov/codeview/views/LineDiffView.kt | 3 +- .../kbiakov/codeview/views/LineNoteView.kt | 4 +- codeview/src/main/res/values/attrs.xml | 3 +- .../codeviewexample/CustomAdapter.java | 4 +- .../codeviewexample/ListingsActivity.java | 36 ++-- 11 files changed, 235 insertions(+), 361 deletions(-) delete mode 100644 codeview/src/main/java/io/github/kbiakov/codeview/Highlighter.kt diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt index 0e69793..aca92ae 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt @@ -9,66 +9,49 @@ import android.widget.RelativeLayout import io.github.kbiakov.codeview.Thread.delayed import io.github.kbiakov.codeview.adapters.AbstractCodeAdapter import io.github.kbiakov.codeview.adapters.CodeWithNotesAdapter +import io.github.kbiakov.codeview.adapters.Options /** * @class CodeView * - * Presents your code content. - * - * Before view built or started to, as the first step, placeholder - * measures & prepare place for code view. Amount of view params is - * not big, view has mutable state & non-standard initialization behavior. - * That is why there is no usual & well-known Builder pattern implementation. - * - * To control interaction state, being & built, was selected tasks queue. - * If user has already built view his task performs immediately, otherwise - * it puts in queue to awaiting adapter creation & processing by build flow. - * This helps to avoid errors & solve the init tasks in more elegant way. + * View for showing code content with syntax highlighting. * * @author Kirill Biakov */ -open class CodeView : RelativeLayout { +class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) { private val vShadowRight: View private val vShadowBottomLine: View private val vShadowBottomContent: View - /** - * Core view to draw code by lines. - */ private val vCodeList: RecyclerView - fun getRecyclerView(): RecyclerView { - return vCodeList - } - /** - * Default constructor. + * Primary constructor. */ - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - val defaultAlpha = 0.7531f - var animateOnStart = true - - val a = context.theme.obtainStyledAttributes(attrs, R.styleable.CodeView, 0, 0) - try { - animateOnStart = a.getBoolean(R.styleable.CodeView_animateOnStart, animateOnStart) - } finally { - a.recycle() - } - animateOnStart = animateOnStart && visibility == View.VISIBLE + init { + val isAnimateOnStart = visibility == VISIBLE && { ctx: Context, ats: AttributeSet -> + val a = ctx.theme.obtainStyledAttributes(ats, R.styleable.CodeView, 0, 0) + + try { + a.getBoolean(R.styleable.CodeView_animateOnStart, true) + } finally { + a.recycle() + } + }(context, attrs) - alpha = if (animateOnStart) 0f else defaultAlpha + alpha = if (isAnimateOnStart) 0f else Consts.ALPHA inflate(context, R.layout.layout_code_view, this) - if (animateOnStart) { - animate().setDuration(Utils.DELAY * 5) - .alpha(defaultAlpha) - } + if (isAnimateOnStart) + animate().setDuration(Consts.DELAY * 5) + .alpha(Consts.ALPHA) - vShadowRight = findViewById(R.id.v_shadow_right)//todo: shadow color customization - vShadowBottomLine = findViewById(R.id.v_shadow_bottom_line)//todo: shadow color customization - vShadowBottomContent = findViewById(R.id.v_shadow_bottom_content)//todo: shadow color customization + // TODO: add shadow color customization + vShadowRight = findViewById(R.id.v_shadow_right) + vShadowBottomLine = findViewById(R.id.v_shadow_bottom_line) + vShadowBottomContent = findViewById(R.id.v_shadow_bottom_content) vCodeList = findViewById(R.id.rv_code_content) as RecyclerView vCodeList.layoutManager = LinearLayoutManager(context) @@ -76,48 +59,18 @@ open class CodeView : RelativeLayout { } /** - * Initialize RecyclerView with adapter - * then start highlighting - */ - fun init(h: Highlighter, adapter: AbstractCodeAdapter<*>) { - if (h.code.isEmpty()) { - throw IllegalStateException("Please set code() before init/highlight") - } - - vCodeList.adapter = adapter - - setupShadows(adapter.highlighter.shadows) - - highlight() - } - - /** - * Initialize RecyclerView with adapter - * then start highlighting - */ - fun init(adapter: AbstractCodeAdapter<*>) { - init(adapter.highlighter, adapter) - } - - /** - * Initialize RecyclerView with adapter - * then start highlighting + * Code adapter accessor. */ - fun init(h: Highlighter) { - init(h, CodeWithNotesAdapter(context, h)) - } + private fun getAdapter() = vCodeList.adapter as? AbstractCodeAdapter<*> /** * Highlight code by defined programming language. - * It holds the placeholder on the view until code is highlighted. + * It holds the placeholder on the view until code is not highlighted. */ - fun highlight() { - if (vCodeList.adapter == null) { - throw IllegalStateException("Please set adapter or use init(highlighter) before highlight()") - } + private fun highlight() { + getAdapter()?.highlight { - getAdapter()?.highlight() { - animate().setDuration(Utils.DELAY * 2) + animate().setDuration(Consts.DELAY * 2) .alpha(.1f) delayed { @@ -128,45 +81,72 @@ open class CodeView : RelativeLayout { } /** - * Remove code listener. + * Border shadows will shown if presented full code listing. + * It helps to see what part of code is scrolled & hidden. + * + * @param isShadows Is shadows needed */ - fun removeLineClickListener() { - getAdapter()?.highlighter?.lineClickListener = null - } + private fun setupShadows(isShadows: Boolean) { + val visibility = if (isShadows) VISIBLE else GONE - fun getAdapter() = vCodeList.adapter as? AbstractCodeAdapter<*> + vShadowRight.visibility = visibility + vShadowBottomLine.visibility = visibility + vShadowBottomContent.visibility = visibility + } /** - * Update code. + * Prepare view with default adapter & options. */ - fun update(code: String) { - getAdapter()?.updateCode(code) + private fun prepare() { + setAdapter(CodeWithNotesAdapter(context)) } /** - * Update code. + * Initialize with options. + * + * @param opts Options */ - fun update(h: Highlighter) { - init(getAdapter()!!.highlighter.update(h)) + fun setOptions(opts: Options) { + setAdapter(CodeWithNotesAdapter(context, opts)) } - // - Setup actions + /** + * Initialize with adapter. + * + * @param adapter Adapter + */ + fun setAdapter(adapter: AbstractCodeAdapter<*>) { + vCodeList.adapter = adapter + setupShadows(adapter.opts.shadows) + highlight() + } /** - * Border shadows will shown if presented full code listing. - * It helps user to see what part of code are scrolled & hidden. + * Set code content. + * At this point view should be prepared, otherwise it + * will be configured automatically with default params. + * + * @param code Code content */ - private fun setupShadows(shadows: Boolean) { - val visibility = if (shadows) VISIBLE else GONE + fun setCode(code: String) { + getAdapter() ?: prepare() + getAdapter()?.updateCode(code) + } - vShadowRight.visibility = visibility - vShadowBottomLine.visibility = visibility - vShadowBottomContent.visibility = visibility + /** + * Set code content. + * + * @param code Code content + * @param language Programming language + */ + fun setCode(code: String, language: String) { + getAdapter() ?: setOptions(Options(context, language = language)) + getAdapter()?.updateCode(code) } } /** - * Provides listener to code line clicks. + * Provide listener to code line clicks. */ interface OnCodeLineClickListener { fun onLineClicked(n: Int, line: String) diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/Highlighter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/Highlighter.kt deleted file mode 100644 index 7cd22a6..0000000 --- a/codeview/src/main/java/io/github/kbiakov/codeview/Highlighter.kt +++ /dev/null @@ -1,141 +0,0 @@ -package io.github.kbiakov.codeview - -import android.content.Context -import io.github.kbiakov.codeview.highlight.ColorTheme -import io.github.kbiakov.codeview.highlight.ColorThemeData - -data class Highlighter(val context: Context) { - - var theme: ColorThemeData = ColorTheme.DEFAULT.theme() - var code = "" - var language: String? = null - var codeResId = 0 - var shortcut = false - var maxLines = 0 - var shortcutNote: String? = null - var shortcutNoteResId = 0 - var lineClickListener: OnCodeLineClickListener? = null - var shadows = false - - // - updated - - var themeUpdated = false - var codeUpdated = false - var languageUpdated = false - var codeResIdUpdated = false - var shortcutUpdated = false - var maxLinesUpdated = false - var shortcutNoteUpdated = false - var shortcutNoteResIdUpdated = false - var lineClickListenerUpdated = false - var shadowsUpdated = true - - fun language(language: String): Highlighter { - this.language = language - languageUpdated = true - return this - } - - fun code(code: String): Highlighter { - this.code = code - codeUpdated = true - return this - } - - fun code(codeResId: Int): Highlighter { - this.code = context.getString(codeResId) - codeResIdUpdated = true - return this - } - - fun shortcut(shortcut: Boolean): Highlighter { - this.shortcut = shortcut - shortcutUpdated = true - return this - } - - fun maxLines(maxLines: Int): Highlighter { - this.maxLines = maxLines - maxLinesUpdated = true - return this - } - - fun shortcutNote(shortcutNote: String): Highlighter { - this.shortcutNote = shortcutNote - shortcutNoteUpdated = true - return this - } - - fun shortcutNote(shortcutNoteResId: Int): Highlighter { - this.shortcutNote = context.getString(shortcutNoteResId) - shortcutNoteResIdUpdated = true - return this - } - - fun theme(theme: ColorTheme): Highlighter { - return this.theme(theme.theme()) - } - - fun theme(theme: ColorThemeData): Highlighter { - this.theme = theme - themeUpdated = true - return this - } - - fun lineClickListener(listener: OnCodeLineClickListener): Highlighter { - lineClickListener = listener - lineClickListenerUpdated = true - return this - } - - fun shadows(shadows: Boolean = true): Highlighter { - this.shadows = shadows - shadowsUpdated = true - return this - } - - /** - * Highlight code finally. - */ - fun highlight(codeView: CodeView) { - codeView.init(this) - } - - /** - * Update highlighter. - */ - fun update(newSettings: Highlighter): Highlighter { - if (newSettings.themeUpdated) { - theme = newSettings.theme - } - if (newSettings.codeUpdated) { - code = newSettings.code - } - if (newSettings.languageUpdated) { - language = newSettings.language - } - if (newSettings.codeResIdUpdated) { - codeResId = newSettings.codeResId - } - if (newSettings.shortcutUpdated) { - shortcut = newSettings.shortcut - } - if (newSettings.maxLinesUpdated) { - maxLines = newSettings.maxLines - } - if (newSettings.shortcutNoteUpdated) { - shortcutNote = newSettings.shortcutNote - } - if (newSettings.shortcutNoteResIdUpdated) { - shortcutNoteResId = newSettings.shortcutNoteResId - } - if (newSettings.lineClickListenerUpdated) { - lineClickListener = newSettings.lineClickListener - } - if (newSettings.shadowsUpdated) { - shadows = newSettings.shadows - } - - return this - } -} \ No newline at end of file diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/Utils.kt b/codeview/src/main/java/io/github/kbiakov/codeview/Utils.kt index 4f60c6b..66b3283 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/Utils.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/Utils.kt @@ -10,10 +10,9 @@ import java.io.BufferedReader import java.io.InputStreamReader import java.util.concurrent.Executors -class Utils { - companion object { - val DELAY: Long = 250 - } +object Consts { + val ALPHA = 0.7F + val DELAY = 250L } /** @@ -81,7 +80,8 @@ object Thread { * @param body Operation body * @param delayMs Delay in m */ - fun delayed(delayMs: Long = Utils.DELAY, body: () -> Unit) = Handler().postDelayed(body, delayMs) + fun delayed(delayMs: Long = Consts.DELAY, body: () -> Unit) = + Handler().postDelayed(body, delayMs) // - Extensions for block manipulations diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt index 72693e6..044e693 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt @@ -12,10 +12,7 @@ import io.github.kbiakov.codeview.Thread.async import io.github.kbiakov.codeview.Thread.ui import io.github.kbiakov.codeview.classifier.CodeClassifier import io.github.kbiakov.codeview.classifier.CodeProcessor -import io.github.kbiakov.codeview.highlight.CodeHighlighter -import io.github.kbiakov.codeview.highlight.ColorThemeData -import io.github.kbiakov.codeview.highlight.MonoFontCache -import io.github.kbiakov.codeview.highlight.color +import io.github.kbiakov.codeview.highlight.* import java.util.* /** @@ -29,79 +26,51 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter = ArrayList() //items + protected var lines: List = ArrayList() // items protected var droppedLines: List? = null private var footerEntities: HashMap> = HashMap() - constructor(context: Context, h: Highlighter) { + constructor(context: Context) { this.context = context - this.highlighter = h - + this.opts = Options(context) prepareCodeLines() } - /** - * Adapter constructor. - * - * @param content Context - * @param content Code content - * @param isShowFull Do you want to show all code content? - * @param maxLines Max lines to show (when limit is reached, rest is dropped) - * @param shortcutNote When rest lines is dropped, note is shown as last string - * @param listener Listener to code line clicks - */ - constructor(context: Context, - content: String, - theme: ColorThemeData, - isShowFull: Boolean = true, - maxLines: Int = MAX_SHORTCUT_LINES, - shortcutNote: String = context.getString(R.string.show_all), - listener: OnCodeLineClickListener? = null) { + constructor(context: Context, code: String) { this.context = context - - highlighter = Highlighter(context) - - highlighter.code = content - highlighter.maxLines = maxLines - highlighter.lineClickListener = listener - highlighter.theme = theme - highlighter.shortcut = !isShowFull - highlighter.shortcutNote = shortcutNote - + this.opts = Options(context, code) prepareCodeLines() } - fun isShorted(): Boolean = droppedLines != null - - //todo: showAllNotes() + constructor(context: Context, options: Options) { + this.context = context + this.opts = options + prepareCodeLines() + } /** - * Split code content by lines. If listing must not be shown full it shows - * only necessary lines & rest are dropped (and stores in named variable). + * Split code content by lines. If listing must not be shown full, it shows + * only necessary lines & the rest are dropped (and stores in named variable). */ internal fun prepareCodeLines() { - val allLines = extractLines(highlighter.code) - val isFullShowing = !highlighter.shortcut || allLines.size <= highlighter.maxLines // limit is not reached + val allLines = extractLines(opts.code) + val isFullShowing = !opts.shortcut || allLines.size <= opts.maxLines // limit is not reached if (isFullShowing) { lines = allLines - return - } // else - - val resultLines = ArrayList(allLines.subList(0, highlighter.maxLines)) - - if (!isFullShowing) { - droppedLines = ArrayList(allLines.subList(highlighter.maxLines, allLines.lastIndex)) - if (highlighter.shortcutNote != null) { - resultLines.add(highlighter.shortcutNote!!.toUpperCase()) + } else { + val resultLines = ArrayList(allLines.subList(0, opts.maxLines)) + opts.shortcutNote?.let { + resultLines.add(it.toUpperCase()) } - } + lines = resultLines - lines = resultLines + droppedLines = ArrayList(allLines.subList(opts.maxLines, allLines.lastIndex)) + } } // - Adapter interface @@ -110,7 +79,7 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter Unit) { async() { - val classifiedLanguage = highlighter.language ?: classifyContent() - highlighting(classifiedLanguage, onReady) + val language = opts.language ?: classifyContent() + highlighting(language, onReady) } } @@ -170,7 +138,7 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter Unit) { //todo: !!!performance (1) highlight next 10 then repeat (1) - val code = CodeHighlighter.highlight(language, highlighter.code, highlighter.theme) + val code = CodeHighlighter.highlight(language, opts.code, opts.theme) updateContent(code, onReady) } @@ -193,8 +161,9 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter Unit) { - highlighter.code = code + opts.code = code prepareCodeLines() + ui { onUpdated() } @@ -207,12 +176,12 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter val footerView = createFooter(context, entity, isFirst) - holder.llLineFooter.addView(footerView) - isFirst = false } } @@ -302,9 +269,9 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter { - constructor(context: Context, h: Highlighter) : super(context, h) - - /** - * Default constructor. - */ - constructor(context: Context, content: String, theme: ColorThemeData) : super(context, content, theme) + constructor(context: Context) : super(context) /** * Create footer view. diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt index bd36c0f..1cdee13 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt @@ -1,8 +1,6 @@ package io.github.kbiakov.codeview.adapters import android.content.Context -import io.github.kbiakov.codeview.Highlighter -import io.github.kbiakov.codeview.highlight.ColorThemeData import io.github.kbiakov.codeview.highlight.color import io.github.kbiakov.codeview.views.LineNoteView @@ -15,14 +13,9 @@ import io.github.kbiakov.codeview.views.LineNoteView */ open class CodeWithNotesAdapter : AbstractCodeAdapter { - constructor(context: Context, h: Highlighter) : super(context, h) + constructor(context: Context) : super(context) - /** - * Default constructor. - */ - constructor(context: Context, content: String, colorTheme: ColorThemeData) : super(context, content, colorTheme) - - //todo: inflateFooter(int layoutId) + constructor(context: Context, options: Options) : super(context, options) /** * Create footer view. @@ -34,6 +27,6 @@ open class CodeWithNotesAdapter : AbstractCodeAdapter { LineNoteView.create(context, text = entity, isFirst = isFirst, - bgColor = highlighter.theme.bgNum.color(), - textColor = highlighter.theme.noteColor.color()) + bgColor = opts.theme.bgNum.color(), + textColor = opts.theme.noteColor.color()) } diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/views/LineDiffView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/views/LineDiffView.kt index d496c73..d1af231 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/views/LineDiffView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/views/LineDiffView.kt @@ -48,7 +48,8 @@ class LineDiffView : RelativeLayout { diffView.setBackgroundColor(ContextCompat.getColor(context, if (model.isAddition) R.color.diff_add_background - else R.color.diff_del_background)) + else + R.color.diff_del_background)) return diffView } diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/views/LineNoteView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/views/LineNoteView.kt index 9a23369..53c6880 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/views/LineNoteView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/views/LineNoteView.kt @@ -37,7 +37,9 @@ class LineNoteView(context: Context?) : TextView(context) { val leftPadding = context.resources.getDimension( R.dimen.line_num_width).toInt() + dpToPx(context, 14) - noteView.setPadding(leftPadding, if (isFirst) dp8 else 0, dp8, dp8) + val topPadding = if (isFirst) dp8 else 0 + + noteView.setPadding(leftPadding, topPadding, dp8, dp8) return noteView } diff --git a/codeview/src/main/res/values/attrs.xml b/codeview/src/main/res/values/attrs.xml index ccf0da2..5bec0a9 100644 --- a/codeview/src/main/res/values/attrs.xml +++ b/codeview/src/main/res/values/attrs.xml @@ -3,5 +3,4 @@ - - \ No newline at end of file + diff --git a/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java b/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java index d7883a4..b9887c8 100644 --- a/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java +++ b/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java @@ -10,8 +10,9 @@ import io.github.kbiakov.codeview.adapters.AbstractCodeAdapter; import io.github.kbiakov.codeview.highlight.ColorTheme; -public class CustomAdapter extends AbstractCodeAdapter { +public class CustomAdapter { //extends AbstractCodeAdapter { + /* public CustomAdapter(@NotNull Context context, @NotNull String content) { super(context, content, ColorTheme.DEFAULT.theme(), true, 10, context.getString(R.string.show_all), null); } @@ -42,4 +43,5 @@ public String getLastName() { return lastName; } } + */ } diff --git a/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java b/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java index 0ffba70..9707980 100644 --- a/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java +++ b/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull; import io.github.kbiakov.codeview.CodeView; -import io.github.kbiakov.codeview.Highlighter; import io.github.kbiakov.codeview.OnCodeLineClickListener; import io.github.kbiakov.codeview.adapters.CodeWithDiffsAdapter; import io.github.kbiakov.codeview.highlight.ColorTheme; @@ -25,20 +24,23 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { final CodeView codeView = (CodeView) findViewById(R.id.code_view); /** - * 1: default adapter with chaining build flow + * 1: default initialization */ - // use chaining to build view with default adapter - new Highlighter(this) + codeView.init(); + + codeView.init(Highlighter.Builder.default(this). .code(R.string.listing_js) .language("js") - .theme(ColorTheme.DEFAULT.withBgContent(Color.WHITE)) - .highlight(codeView); + .theme(ColorTheme.DEFAULT.withBgContent(Color.WHITE))); + */ + + // use chaining to build view with default adapter /** * 2: updating built view - */ + * // do not use chaining for built view // (you can, but it should be performed sequentially) @@ -58,12 +60,12 @@ public void onLineClicked(int n, @NotNull String line) { /** * 3: custom adapter with footer views - */ + * final CustomAdapter adapter = new CustomAdapter(this, getString(R.string.listing_md)); codeView.init(adapter); - codeView.update(new Highlighter(this) + codeView.updateWithHighlighter(new Highlighter(this) .theme(ColorTheme.MONOKAI) .lineClickListener(new OnCodeLineClickListener() { @Override @@ -75,16 +77,11 @@ public void onLineClicked(int n, @NotNull String line) { /** * 4: diff adapter with footer views - */ - - final CodeWithDiffsAdapter diffsAdapter = new CodeWithDiffsAdapter(ListingsActivity.this, - getString(R.string.listing_py), ColorTheme.SOLARIZED_LIGHT.theme()); + * - diffsAdapter.getHighlighter().language("python"); - - codeView.removeLineClickListener(); - - codeView.init(diffsAdapter); + final CodeWithDiffsAdapter diffsAdapter = new CodeWithDiffsAdapter(this, getString(R.string.listing_py)); + diffsAdapter.getHighlighter().setLanguage("python"); + codeView.setAdapter(diffsAdapter); diffsAdapter.addFooterEntity(16, new DiffModel(getString(R.string.py_addition_16), true)); diffsAdapter.addFooterEntity(11, new DiffModel(getString(R.string.py_deletion_11), false)); @@ -92,12 +89,13 @@ public void onLineClicked(int n, @NotNull String line) { /** * 5: shortcut adapter with footer views - */ + * new Highlighter(this).code(R.string.listing_py) .shortcut(true) .language("python") .maxLines(3) .shortcutNote("Show All") .highlight(codeView); + */ } } From 1f182535a1b5cc320fdb08a11f99691fb80b66f7 Mon Sep 17 00:00:00 2001 From: macuser Date: Sun, 27 Nov 2016 01:14:22 +0300 Subject: [PATCH 2/5] Revisioned view API --- codeview/build.gradle | 10 +- .../assets/training-set/javascript/new 3.txt | 32 +++--- .../io/github/kbiakov/codeview/CodeView.kt | 79 ++++++++++---- .../codeview/adapters/AbstractCodeAdapter.kt | 70 ++++++------ .../codeview/adapters/CodeWithDiffsAdapter.kt | 4 + .../codeview/adapters/CodeWithNotesAdapter.kt | 6 +- .../codeview/highlight/CodeHighlighter.kt | 6 +- .../codeviewexample/CustomAdapter.java | 11 +- .../codeviewexample/ListingsActivity.java | 101 +++++++++--------- 9 files changed, 182 insertions(+), 137 deletions(-) diff --git a/codeview/build.gradle b/codeview/build.gradle index 4813467..79feaca 100644 --- a/codeview/build.gradle +++ b/codeview/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 24 - buildToolsVersion "24.0.2" + compileSdkVersion 25 + buildToolsVersion "25.0.1" defaultConfig { minSdkVersion 15 - targetSdkVersion 24 + targetSdkVersion 25 versionCode 1 versionName "1.0" } @@ -25,8 +25,8 @@ android { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - compile 'com.android.support:appcompat-v7:24.2.0' - compile 'com.android.support:recyclerview-v7:24.2.0' + compile 'com.android.support:appcompat-v7:25.0.1' + compile 'com.android.support:recyclerview-v7:25.0.1' } repositories { mavenCentral() diff --git a/codeview/src/main/assets/training-set/javascript/new 3.txt b/codeview/src/main/assets/training-set/javascript/new 3.txt index ae5ac2b..37bc1f0 100755 --- a/codeview/src/main/assets/training-set/javascript/new 3.txt +++ b/codeview/src/main/assets/training-set/javascript/new 3.txt @@ -67,32 +67,32 @@ var minExpectedLifetime = '20s'; * @api public */ -function UpServer (server, file, opts) { - if (this == global) return new UpServer(server, file, opts); +function UpServer (server, file, options) { + if (this == global) return new UpServer(server, file, options); Distributor.call(this, server); var self = this; - opts = opts || {}; + options = options || {}; this.file = file; - this.numWorkers = eq(opts.numWorkers || numWorkers, { cpus: cpus }); - this.workerTimeout = ms(null != opts.workerTimeout - ? opts.workerTimeout : workerTimeout); - this.requires = opts.requires || []; - this.assumeReady = opts.assumeReady === undefined ? true : !!opts.assumeReady; - this.keepAlive = opts.keepAlive || false; - this.minExpectedLifetime = ms(opts.minExpectedLifetime != null ? opts.minExpectedLifetime : minExpectedLifetime); - if (false !== opts.workerPingInterval) { - this.workerPingInterval = ms(opts.workerPingInterval || '1m'); + this.numWorkers = eq(options.numWorkers || numWorkers, { cpus: cpus }); + this.workerTimeout = ms(null != options.workerTimeout + ? options.workerTimeout : workerTimeout); + this.requires = options.requires || []; + this.assumeReady = options.assumeReady === undefined ? true : !!options.assumeReady; + this.keepAlive = options.keepAlive || false; + this.minExpectedLifetime = ms(options.minExpectedLifetime != null ? options.minExpectedLifetime : minExpectedLifetime); + if (false !== options.workerPingInterval) { + this.workerPingInterval = ms(options.workerPingInterval || '1m'); } this.workers = []; this.spawning = []; this.lastIndex = -1; - if (opts.title) { - this.title = opts.title; + if (options.title) { + this.title = options.title; process.title = this.title + ' master'; } @@ -290,7 +290,7 @@ function Worker (server) { this.server = server; this.readyState = 'spawning'; - var opts = JSON.stringify({ + var options = JSON.stringify({ file: server.file , requires: server.requires , assumeReady: server.assumeReady @@ -298,7 +298,7 @@ function Worker (server) { , title: server.title }); - this.proc = fork(__dirname + '/worker.js', [opts], { env: process.env }); + this.proc = fork(__dirname + '/worker.js', [options], { env: process.env }); this.proc.on('message', this.onMessage.bind(this)); this.proc.on('exit', this.onExit.bind(this)); this.pid = this.proc.pid; diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt index aca92ae..1726bbd 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt @@ -59,13 +59,8 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, } /** - * Code adapter accessor. - */ - private fun getAdapter() = vCodeList.adapter as? AbstractCodeAdapter<*> - - /** - * Highlight code by defined programming language. - * It holds the placeholder on the view until code is not highlighted. + * Highlight code with defined programming language. + * It holds the placeholder on view until code is not highlighted. */ private fun highlight() { getAdapter()?.highlight { @@ -97,51 +92,95 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, /** * Prepare view with default adapter & options. */ - private fun prepare() { - setAdapter(CodeWithNotesAdapter(context)) - } + private fun prepare() = setAdapter(CodeWithNotesAdapter(context)) + + /** + * View options accessor. + */ + fun getOptions(): Options? = getAdapter()?.options /** * Initialize with options. * - * @param opts Options + * @param options Options + */ + fun setOptions(options: Options) = setOptions(options, false) + + /** + * Set options & initialize if needed. + * + * @param options Options + * @param isSaveAdapter Save adapter? */ - fun setOptions(opts: Options) { - setAdapter(CodeWithNotesAdapter(context, opts)) + fun setOptions(options: Options, isSaveAdapter: Boolean = true) { + setAdapter(if (isSaveAdapter) + getAdapter() ?: CodeWithNotesAdapter(context, options) + else + CodeWithNotesAdapter(context, options)) } + /** + * Code adapter accessor. + */ + fun getAdapter() = vCodeList.adapter as? AbstractCodeAdapter<*> + /** * Initialize with adapter. * * @param adapter Adapter */ - fun setAdapter(adapter: AbstractCodeAdapter<*>) { + fun setAdapter(adapter: AbstractCodeAdapter<*>) = setAdapter(adapter, false) + + /** + * Set adapter & initialize if needed. + * + * @param adapter Adapter + * @param isSaveOptions Save options? + */ + fun setAdapter(adapter: AbstractCodeAdapter<*>, isSaveOptions: Boolean = true) { + if (isSaveOptions) + adapter.options = getOptions() ?: Options(context) + vCodeList.adapter = adapter - setupShadows(adapter.opts.shadows) + setupShadows(adapter.options.shadows) highlight() } /** * Set code content. - * At this point view should be prepared, otherwise it - * will be configured automatically with default params. + * + * There are two ways before code will be highlighted: + * 1) view is not initialized (adapter or options are not set), + * prepare with default params & try to classify language + * 2) view initialized with some params, language: + * a) is set: used defined programming language + * b) not set: try to classify * * @param code Code content */ fun setCode(code: String) { getAdapter() ?: prepare() - getAdapter()?.updateCode(code) + getAdapter()!!.updateCode(code) } /** * Set code content. * + * There are two ways before code will be highlighted: + * 1) view is not initialized, prepare with default params + * 2) view initialized with some params, set new language + * * @param code Code content * @param language Programming language */ fun setCode(code: String, language: String) { - getAdapter() ?: setOptions(Options(context, language = language)) - getAdapter()?.updateCode(code) + val options = if (getAdapter() == null) + Options(context) + else + getAdapter()!!.options + + setOptions(options.withLanguage(language)) + getAdapter()!!.updateCode(code) } } diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt index 044e693..fa91d44 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt @@ -25,30 +25,28 @@ import java.util.* abstract class AbstractCodeAdapter : RecyclerView.Adapter { protected val context: Context - - var opts: Options - internal set - protected var lines: List = ArrayList() // items protected var droppedLines: List? = null + internal var options: Options + private var footerEntities: HashMap> = HashMap() constructor(context: Context) { this.context = context - this.opts = Options(context) + this.options = Options(context) prepareCodeLines() } constructor(context: Context, code: String) { this.context = context - this.opts = Options(context, code) + this.options = Options(context, code) prepareCodeLines() } constructor(context: Context, options: Options) { this.context = context - this.opts = options + this.options = options prepareCodeLines() } @@ -57,19 +55,17 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter Unit) { + internal fun highlight(onReady: () -> Unit) { async() { - val language = opts.language ?: classifyContent() + val language = options.language ?: classifyContent() highlighting(language, onReady) } } @@ -138,7 +134,7 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter Unit) { - //todo: !!!performance (1) highlight next 10 then repeat (1) - val code = CodeHighlighter.highlight(language, opts.code, opts.theme) + // TODO: highlight by 10 lines + val code = CodeHighlighter.highlight(language, options.code, options.theme) updateContent(code, onReady) } @@ -161,7 +157,7 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter Unit) { - opts.code = code + options.code = code prepareCodeLines() ui { @@ -176,12 +172,12 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter : RecyclerView.Adapter { constructor(context: Context) : super(context) + constructor(context: Context, code: String) : super(context, code) + + constructor(context: Context, options: Options) : super(context, options) + /** * Create footer view. * diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt index 1cdee13..dff3663 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/CodeWithNotesAdapter.kt @@ -15,6 +15,8 @@ open class CodeWithNotesAdapter : AbstractCodeAdapter { constructor(context: Context) : super(context) + constructor(context: Context, code: String) : super(context, code) + constructor(context: Context, options: Options) : super(context, options) /** @@ -27,6 +29,6 @@ open class CodeWithNotesAdapter : AbstractCodeAdapter { LineNoteView.create(context, text = entity, isFirst = isFirst, - bgColor = opts.theme.bgNum.color(), - textColor = opts.theme.noteColor.color()) + bgColor = options.theme.bgNum.color(), + textColor = options.theme.noteColor.color()) } diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/highlight/CodeHighlighter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/highlight/CodeHighlighter.kt index 829842f..60ac6d3 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/highlight/CodeHighlighter.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/highlight/CodeHighlighter.kt @@ -33,9 +33,9 @@ object CodeHighlighter { val colorsMap = buildColorsMap(colorTheme) val highlighted = StringBuilder() - results.forEach { result -> - val color = colorsMap.getColor(result) - val content = parseContent(source, result) + results.forEach { + val color = colorsMap.getColor(it) + val content = parseContent(source, it) highlighted.append(content.withFontParams(color)) } diff --git a/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java b/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java index b9887c8..59d79a4 100644 --- a/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java +++ b/example/src/main/java/io/github/kbiakov/codeviewexample/CustomAdapter.java @@ -1,20 +1,24 @@ package io.github.kbiakov.codeviewexample; import android.content.Context; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import org.jetbrains.annotations.NotNull; +import io.github.kbiakov.codeview.OnCodeLineClickListener; import io.github.kbiakov.codeview.adapters.AbstractCodeAdapter; +import io.github.kbiakov.codeview.adapters.Options; import io.github.kbiakov.codeview.highlight.ColorTheme; -public class CustomAdapter { //extends AbstractCodeAdapter { +public class CustomAdapter extends AbstractCodeAdapter { - /* public CustomAdapter(@NotNull Context context, @NotNull String content) { - super(context, content, ColorTheme.DEFAULT.theme(), true, 10, context.getString(R.string.show_all), null); + super(context, Options.Default.get(context) + .withCode(content) + .withTheme(ColorTheme.SOLARIZED_LIGHT.theme())); } @NotNull @@ -43,5 +47,4 @@ public String getLastName() { return lastName; } } - */ } diff --git a/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java b/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java index 9707980..5d3fd88 100644 --- a/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java +++ b/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java @@ -1,6 +1,5 @@ package io.github.kbiakov.codeviewexample; -import android.graphics.Color; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; @@ -11,6 +10,7 @@ import io.github.kbiakov.codeview.CodeView; import io.github.kbiakov.codeview.OnCodeLineClickListener; import io.github.kbiakov.codeview.adapters.CodeWithDiffsAdapter; +import io.github.kbiakov.codeview.adapters.Options; import io.github.kbiakov.codeview.highlight.ColorTheme; import io.github.kbiakov.codeview.views.DiffModel; @@ -24,64 +24,69 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { final CodeView codeView = (CodeView) findViewById(R.id.code_view); /** - * 1: default initialization + * 1: set code content */ - codeView.init(); - - codeView.init(Highlighter.Builder.default(this). - .code(R.string.listing_js) - .language("js") - .theme(ColorTheme.DEFAULT.withBgContent(Color.WHITE))); - */ - - // use chaining to build view with default adapter + // auto language recognition + codeView.setCode(getString(R.string.listing_js)); + // specify language for code listing + codeView.setCode(getString(R.string.listing_py), "py"); /** - * 2: updating built view - * - - // do not use chaining for built view - // (you can, but it should be performed sequentially) - final Highlighter h = new Highlighter(this) - .code(R.string.listing_java) - .language("java") - .theme(ColorTheme.SOLARIZED_LIGHT); - - h.setLineClickListener(new OnCodeLineClickListener() { - @Override - public void onLineClicked(int n, @NotNull String line) { - Log.i("ListingsActivity", "On " + (n + 1) + " line clicked"); - } - }); + * 2: working with options + */ - codeView.init(h); + // you can change params as follows (for initialized view) + codeView.getOptions().setTheme(ColorTheme.MONOKAI.theme()); + + // short initialization with default params (can be expanded using with() methods) + codeView.setOptions(Options.Default.get(this) + .withLanguage("python") + .withCode(getString(R.string.listing_py)) + .withTheme(ColorTheme.MONOKAI)); + + // expanded form of initialization + codeView.setOptions(new Options( + this, // context + getString(R.string.listing_js), // code + "js", // language + ColorTheme.MONOKAI.theme(), // theme (data) + true, // shadows + true, // shortcut + getString(R.string.show_all), // shortcut note + 10, // max lines + new OnCodeLineClickListener() { // line click listener + @Override + public void onLineClicked(int n, @NotNull String line) { + Log.i("ListingsActivity", "On " + (n + 1) + " line clicked"); + } + })); /** * 3: custom adapter with footer views - * - - final CustomAdapter adapter = new CustomAdapter(this, getString(R.string.listing_md)); - codeView.init(adapter); + */ - codeView.updateWithHighlighter(new Highlighter(this) - .theme(ColorTheme.MONOKAI) - .lineClickListener(new OnCodeLineClickListener() { + final CustomAdapter myAdapter = new CustomAdapter(this, getString(R.string.listing_md)); + codeView.setAdapter(myAdapter); + codeView.getOptions() + .withLanguage("md") + .addLineClickListener(new OnCodeLineClickListener() { @Override public void onLineClicked(int n, @NotNull String line) { - adapter.addFooterEntity(n, new CustomAdapter.CustomModel("Line " + (n + 1), line)); + myAdapter.addFooterEntity(n, new CustomAdapter.CustomModel("Line " + (n + 1), line)); } - }) - .language("md")); + }); /** * 4: diff adapter with footer views - * + */ - final CodeWithDiffsAdapter diffsAdapter = new CodeWithDiffsAdapter(this, getString(R.string.listing_py)); - diffsAdapter.getHighlighter().setLanguage("python"); - codeView.setAdapter(diffsAdapter); + final CodeWithDiffsAdapter diffsAdapter = new CodeWithDiffsAdapter(this); + codeView.getOptions() + .withLanguage("python") + .setCode(getString(R.string.listing_py)); + codeView.setAdapter(diffsAdapter, true); diffsAdapter.addFooterEntity(16, new DiffModel(getString(R.string.py_addition_16), true)); diffsAdapter.addFooterEntity(11, new DiffModel(getString(R.string.py_deletion_11), false)); @@ -89,13 +94,9 @@ public void onLineClicked(int n, @NotNull String line) { /** * 5: shortcut adapter with footer views - * - new Highlighter(this).code(R.string.listing_py) - .shortcut(true) - .language("python") - .maxLines(3) - .shortcutNote("Show All") - .highlight(codeView); - */ + */ + + codeView.getOptions() + .shortcut(10, "Show all"); } } From 07b4aac0d32d0e553cca75513bdc8e841989aed1 Mon Sep 17 00:00:00 2001 From: Kirill Biakov Date: Mon, 28 Nov 2016 00:01:42 +0300 Subject: [PATCH 3/5] Refactored & updated README --- README.md | 104 ++++++++++-------- .../io/github/kbiakov/codeview/CodeView.kt | 81 +++++++------- .../codeview/adapters/AbstractCodeAdapter.kt | 25 ++++- .../codeview/highlight/CodeHighlighter.kt | 42 ++++--- .../codeviewexample/CustomAdapter.java | 8 +- .../codeviewexample/ListingsActivity.java | 36 ++++-- 6 files changed, 166 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index b44d250..034fb95 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,16 @@ [![Release](https://jitpack.io/v/softwee/codeview-android.svg)](https://jitpack.io/#softwee/codeview-android) [![Build Status](https://travis-ci.org/Softwee/codeview-android.svg?branch=master)](https://travis-ci.org/Softwee/codeview-android) -CodeView helps to show code content with syntax highlighting in native way. +CodeView helps to show code content with syntax highlighting in native way. ## Description -CodeView contains 3 core parts to implement necessary logic:
+CodeView contains 3 core parts to implement necessary logic:
-1. CodeClassifier is trying to define what language presented in code snippet. It built upon [Naive Bayes classifier](https://github.com/ptnplanet/Java-Naive-Bayes-Classifier). There is no need to work with this class directly & you must just follow instructions below. (Experimental module, may not work properly!)
+1. CodeView & related abstract adapter to provide options & customization (see below).
2. For highlighting it uses CodeHighlighter, just highlights your code & returns formatted content. It based on [Google Prettify](https://github.com/google/code-prettify) and their Java implementation & [fork](https://github.com/google/code-prettify).
-3. CodeView & related abstract adapter to provide customization (see below).
+3. CodeClassifier is trying to define what language presented in code snippet. It built using [Naive Bayes classifier](https://en.wikipedia.org/wiki/Naive_Bayes_classifier) upon found open-source [implementation](https://github.com/ptnplanet/Java-Naive-Bayes-Classifier), which I rewrote in Kotlin. There is no need to work with this class directly & you must just follow instructions below. (Experimental module, may not work properly!)
## Download Add it in your root ```build.gradle``` at the end of repositories: @@ -28,7 +28,7 @@ allprojects { Add the dependency: ```groovy -compile 'com.github.softwee:codeview-android:1.1.2' +compile 'com.github.softwee:codeview-android:1.2.0' ``` ## Usage @@ -38,7 +38,9 @@ If you want to use code classifier to auto language recognizing just add to your CodeProcessor.init(this); ``` -Add view for your layout: +Having done ones on app start you can classify language for different snippets more faster, because algorithm needs time for training on sets for presented listings of languages library have. + +Add view to your layout: ```xml ``` -Use chaining syntax when build view: +So now you can set code: ```java +// bind view CodeView codeView = (CodeView) findViewById(R.id.code_view); - -codeView.highlightCode("js") - .setColorTheme(ColorTheme.SOLARIZED_LIGHT.withBgContent(myColor)) - .setCodeContent(getString(R.string.listing_js)); +// auto language recognition +codeView.setCode(getString(R.string.listing_js)); +// explicit form (will work faster!), see available extensions below +codeView.setCode(getString(R.string.listing_py), "py"); ``` -And perform actions sequentially when view built: -```java -codeView.setCodeContent(getString(R.string.listing_java)); -codeView.highlightCode("java"); -``` +## Customization +When you call ```setCode(...)``` view will prepared with default params if view was not initialized before. So if you want some customization, it can be done using options and/or adapter. -You can use both forms for build & built view, but note: ```setCodeContent(String)``` is final step when you build your view, otherwise not. If you firstly highlight and then set code content, code will not be highlighted if view was not built yet. Instructions above helps you to avoid errors. View has state to handle this behavior. - -## Customizing -Use implicit form to code highlighting: -```java -codeView.highlightCode(); -``` -or eplixit (see available extensions below): +### Initialization +You can initialize view with options: ```java -codeView.highlightCode("js"); // it will work fast! +codeView.setOptions(Options.Default.get(this) + .withLanguage("python") + .withCode(R.string.listing_py) + .withTheme(ColorTheme.MONOKAI)); ``` -Use default color theme: +Or using adapter (see Adapter customization or example for more details): ```java -codeView.setColorTheme(ColorTheme.SOLARIZED_LIGHT); +final CustomAdapter myAdapter = new CustomAdapter(this, getString(R.string.listing_md)); +codeView.setAdapter(myAdapter); ``` -or extend default: + +### Options +Options helps to easily set necessary params, such as code & language, color theme, shortcut params & line click listener. Some params are unnecessary. + +When view initialized (options or adapter set) you can manipulate options in various ways: ```java -int myColor = ContextCompat.getColor(this, R.color.my_color); -codeView.setColorTheme(ColorTheme.MONOKAI.withBgContent(myColor)); +codeView.getOptions() + .withCode(R.string.listing_java) + .withLanguage("java") + .withTheme(ColorTheme.MONOKAI); ``` -or provide your own (don't forget to open PR with this stuff!) + +### Color themes +There are some default themes (see full list below): ```java -codeView.setColorTheme(new ColorThemeData(new SyntaxColors(...), ...)); +codeView.getOptions().setTheme(ColorTheme.SOLARIZED_LIGHT); ``` -Handle user clicks on code lines: +But you can build your own from existing: ```java -codeView.setCodeListener(new OnCodeLineClickListener() { - @Override - public void onCodeLineClicked(int n, @NotNull String line) { - Log.i("ListingsActivity", "On " + (n + 1) + " line clicked"); - } -}); +ColorThemeData myTheme = ColorTheme.SOLARIZED_LIGHT.theme() + .withBgContent(android.R.color.black) + .withNoteColor(android.R.color.white); + +codeView.getOptions().setTheme(myTheme); ``` -Enable shadows to hide scrolled content: +Or create your own from scratch (don't forget to open PR with this stuff!): ```java -codeView.setShadowsEnabled(true); +ColorThemeData customTheme = new ColorThemeData(new SyntaxColors(...), ...); +codeView.getOptions().setTheme(customTheme); ``` -## Adapter customization -Sometimes you may want to add some content under line. You can create your own implementation as follows: +### Adapter +Sometimes you may want to take code lines under your control, and that's why you need Adapter. + +You can create your own implementation as follows: 1. Create your model to store data, for example some ```MyModel``` class.
2. Extend ```AbstractCodeAdapter``` typed by your model class.
@@ -169,14 +177,14 @@ C/C++/Objective-C (```"c"```, ```"cc"```, ```"cpp"```, ```"cxx"```, ```"cyc"```, Didn't found yours? Please, open issue to show your interest & I'll try to add this language in next releases. ## List of available themes -1. Default (simple light theme) -2. Solarized Light -3. Monokai +1. Default (simple light theme). +2. Solarized Light. +3. Monokai. ## Contribute 1. You can add your theme (see [ColorTheme](https://github.com/Softwee/codeview-android/blob/master/codeview/src/main/java/io/github/kbiakov/codeview/highlight/CodeHighlighter.kt) class). Try to add some classic color themes or create your own if it looks cool. You can find many of them in different open-source text editors.
-2. If you are strong in a regex add missed language as shown [here](https://github.com/Softwee/codeview-android/blob/master/codeview/src/main/java/io/github/kbiakov/codeview/highlight/prettify/lang/LangScala.java). You can find existing regex for some language in different sources of js-libraries, etc, which plays the same role.
-3. Various adapters also welcome, customization is unlimited. +2. If you are strong in regex, add missed language as shown [here](https://github.com/Softwee/codeview-android/blob/master/codeview/src/main/java/io/github/kbiakov/codeview/highlight/prettify/lang/LangScala.java). You can find existing regex for some language in different sources of libraries, which plays the same role.
+3. Various adapters also welcome. ## Author ### [Kirill Biakov](https://github.com/kbiakov) diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt index 1726bbd..830b266 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt @@ -45,7 +45,8 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, inflate(context, R.layout.layout_code_view, this) if (isAnimateOnStart) - animate().setDuration(Consts.DELAY * 5) + animate() + .setDuration(Consts.DELAY * 5) .alpha(Consts.ALPHA) // TODO: add shadow color customization @@ -65,18 +66,19 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, private fun highlight() { getAdapter()?.highlight { - animate().setDuration(Consts.DELAY * 2) + animate() + .setDuration(Consts.DELAY * 2) .alpha(.1f) delayed { animate().alpha(1f) - vCodeList.adapter?.notifyDataSetChanged() + getAdapter()?.notifyDataSetChanged() } } } /** - * Border shadows will shown if presented full code listing. + * Border shadows will shown if full listing presented. * It helps to see what part of code is scrolled & hidden. * * @param isShadows Is shadows needed @@ -89,63 +91,70 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, vShadowBottomContent.visibility = visibility } + // - Initialization + /** * Prepare view with default adapter & options. */ private fun prepare() = setAdapter(CodeWithNotesAdapter(context)) /** - * View options accessor. + * Initialize with options. + * + * @param options Options */ - fun getOptions(): Options? = getAdapter()?.options + fun setOptions(options: Options) = setAdapter(CodeWithNotesAdapter(context, options)) /** - * Initialize with options. + * Initialize with adapter. * - * @param options Options + * @param adapter Adapter + */ + fun setAdapter(adapter: AbstractCodeAdapter<*>) { + vCodeList.adapter = adapter + setupShadows(adapter.options.shadows) + highlight() + } + + // - Options + + /** + * View options accessor. */ - fun setOptions(options: Options) = setOptions(options, false) + fun getOptions(): Options? = getAdapter()?.options + fun getOptionsOrDefault() = getOptions() ?: Options(context) /** - * Set options & initialize if needed. + * Update options or initialize if needed. * * @param options Options - * @param isSaveAdapter Save adapter? */ - fun setOptions(options: Options, isSaveAdapter: Boolean = true) { - setAdapter(if (isSaveAdapter) - getAdapter() ?: CodeWithNotesAdapter(context, options) + fun updateOptions(options: Options) { + if (getAdapter() == null) + setOptions(options) else - CodeWithNotesAdapter(context, options)) + getAdapter()!!.options = options } + // - Adapter + /** * Code adapter accessor. */ fun getAdapter() = vCodeList.adapter as? AbstractCodeAdapter<*> /** - * Initialize with adapter. + * Update adapter or initialize if needed. * * @param adapter Adapter */ - fun setAdapter(adapter: AbstractCodeAdapter<*>) = setAdapter(adapter, false) - - /** - * Set adapter & initialize if needed. - * - * @param adapter Adapter - * @param isSaveOptions Save options? - */ - fun setAdapter(adapter: AbstractCodeAdapter<*>, isSaveOptions: Boolean = true) { - if (isSaveOptions) - adapter.options = getOptions() ?: Options(context) - - vCodeList.adapter = adapter - setupShadows(adapter.options.shadows) - highlight() + fun updateAdapter(adapter: AbstractCodeAdapter<*>) { + adapter.options = getOptionsOrDefault() + setAdapter(adapter) } + // - Set code + /** * Set code content. * @@ -174,12 +183,8 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, * @param language Programming language */ fun setCode(code: String, language: String) { - val options = if (getAdapter() == null) - Options(context) - else - getAdapter()!!.options - - setOptions(options.withLanguage(language)) + val options = getOptionsOrDefault() + updateOptions(options.withLanguage(language)) getAdapter()!!.updateCode(code) } } @@ -188,5 +193,5 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, * Provide listener to code line clicks. */ interface OnCodeLineClickListener { - fun onLineClicked(n: Int, line: String) + fun onCodeLineClicked(n: Int, line: String) } diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt index fa91d44..e6decf3 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt @@ -117,7 +117,7 @@ abstract class AbstractCodeAdapter : RecyclerView.Adapter : RecyclerView.Adapter { - public CustomAdapter(@NotNull Context context, @NotNull String content) { + public CustomAdapter(@NotNull Context context, @NotNull String code) { super(context, Options.Default.get(context) - .withCode(content) - .withTheme(ColorTheme.SOLARIZED_LIGHT.theme())); + .withCode(code) + .withTheme(ColorTheme.SOLARIZED_LIGHT)); } @NotNull diff --git a/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java b/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java index 5d3fd88..1771daf 100644 --- a/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java +++ b/example/src/main/java/io/github/kbiakov/codeviewexample/ListingsActivity.java @@ -12,6 +12,7 @@ import io.github.kbiakov.codeview.adapters.CodeWithDiffsAdapter; import io.github.kbiakov.codeview.adapters.Options; import io.github.kbiakov.codeview.highlight.ColorTheme; +import io.github.kbiakov.codeview.highlight.ColorThemeData; import io.github.kbiakov.codeview.views.DiffModel; public class ListingsActivity extends AppCompatActivity { @@ -37,13 +38,15 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { * 2: working with options */ - // you can change params as follows (for initialized view) - codeView.getOptions().setTheme(ColorTheme.MONOKAI.theme()); + // you can change params as follows (unsafe, initialized view only) + codeView.getOptions() + .withCode(R.string.listing_java) + .withTheme(ColorTheme.MONOKAI); // short initialization with default params (can be expanded using with() methods) codeView.setOptions(Options.Default.get(this) .withLanguage("python") - .withCode(getString(R.string.listing_py)) + .withCode(R.string.listing_py) .withTheme(ColorTheme.MONOKAI)); // expanded form of initialization @@ -58,42 +61,55 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { 10, // max lines new OnCodeLineClickListener() { // line click listener @Override - public void onLineClicked(int n, @NotNull String line) { + public void onCodeLineClicked(int n, @NotNull String line) { Log.i("ListingsActivity", "On " + (n + 1) + " line clicked"); } })); /** - * 3: custom adapter with footer views + * 3: color themes + */ + + codeView.getOptions().setTheme(ColorTheme.SOLARIZED_LIGHT); + + // custom theme + ColorThemeData myTheme = ColorTheme.SOLARIZED_LIGHT.theme() + .withBgContent(android.R.color.black) + .withNoteColor(android.R.color.white); + + codeView.getOptions().setTheme(myTheme); + + /** + * 4: custom adapter with footer views */ final CustomAdapter myAdapter = new CustomAdapter(this, getString(R.string.listing_md)); codeView.setAdapter(myAdapter); codeView.getOptions() .withLanguage("md") - .addLineClickListener(new OnCodeLineClickListener() { + .addCodeLineClickListener(new OnCodeLineClickListener() { @Override - public void onLineClicked(int n, @NotNull String line) { + public void onCodeLineClicked(int n, @NotNull String line) { myAdapter.addFooterEntity(n, new CustomAdapter.CustomModel("Line " + (n + 1), line)); } }); /** - * 4: diff adapter with footer views + * 5: diff adapter with footer views */ final CodeWithDiffsAdapter diffsAdapter = new CodeWithDiffsAdapter(this); codeView.getOptions() .withLanguage("python") .setCode(getString(R.string.listing_py)); - codeView.setAdapter(diffsAdapter, true); + codeView.updateAdapter(diffsAdapter); diffsAdapter.addFooterEntity(16, new DiffModel(getString(R.string.py_addition_16), true)); diffsAdapter.addFooterEntity(11, new DiffModel(getString(R.string.py_deletion_11), false)); /** - * 5: shortcut adapter with footer views + * 6: shortcut adapter with footer views */ codeView.getOptions() From 411c9338fbe4a3efcf550e70c3b28ab0c9bb3a2a Mon Sep 17 00:00:00 2001 From: Kirill Biakov Date: Mon, 28 Nov 2016 00:13:12 +0300 Subject: [PATCH 4/5] Update README.md --- README.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 034fb95..93e1d94 100644 --- a/README.md +++ b/README.md @@ -40,21 +40,26 @@ CodeProcessor.init(this); Having done ones on app start you can classify language for different snippets more faster, because algorithm needs time for training on sets for presented listings of languages library have. -Add view to your layout: +Add view to your layout & bind as usual: ```xml ``` - -So now you can set code: ```java -// bind view CodeView codeView = (CodeView) findViewById(R.id.code_view); +``` + +So now you can set code using implicit form: +```java // auto language recognition codeView.setCode(getString(R.string.listing_js)); -// explicit form (will work faster!), see available extensions below +``` + +Or explicit (see available extensions below): +```java +// will work faster! codeView.setCode(getString(R.string.listing_py), "py"); ``` @@ -70,16 +75,16 @@ codeView.setOptions(Options.Default.get(this) .withTheme(ColorTheme.MONOKAI)); ``` -Or using adapter (see Adapter customization or example for more details): +Or using adapter (see Adapter or example for more details): ```java final CustomAdapter myAdapter = new CustomAdapter(this, getString(R.string.listing_md)); codeView.setAdapter(myAdapter); ``` ### Options -Options helps to easily set necessary params, such as code & language, color theme, shortcut params & line click listener. Some params are unnecessary. +Options helps to easily set necessary params, such as code & language, color theme, shortcut params & code line click listener. Some params are unnecessary. -When view initialized (options or adapter set) you can manipulate options in various ways: +When view initialized (options or adapter are set) you can manipulate options in various ways: ```java codeView.getOptions() .withCode(R.string.listing_java) @@ -93,7 +98,7 @@ There are some default themes (see full list below): codeView.getOptions().setTheme(ColorTheme.SOLARIZED_LIGHT); ``` -But you can build your own from existing: +But you can build your own from existing one: ```java ColorThemeData myTheme = ColorTheme.SOLARIZED_LIGHT.theme() .withBgContent(android.R.color.black) From 30138e9263e2a55eff977bda0899bf9eea93f4f6 Mon Sep 17 00:00:00 2001 From: Kirill Biakov Date: Mon, 28 Nov 2016 00:30:39 +0300 Subject: [PATCH 5/5] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 93e1d94..f614e2b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ If you want to use code classifier to auto language recognizing just add to your CodeProcessor.init(this); ``` -Having done ones on app start you can classify language for different snippets more faster, because algorithm needs time for training on sets for presented listings of languages library have. +Having done ones on app start you can classify language for different snippets more faster, because algorithm needs time for training on sets for presented listings of languages which library has. Add view to your layout & bind as usual: ```xml @@ -81,8 +81,10 @@ final CustomAdapter myAdapter = new CustomAdapter(this, getString(R.string.listi codeView.setAdapter(myAdapter); ``` +Note: Each CodeView has adapter and each adapter has options. When calling ```setOptions(...)``` or ```setAdapter(...)``` current adapter "flushed" with current options. If you want to save the state and just update options saving adapter or set adapter saving options you must call ```updateOptions(...)``` or ```updateAdapter(...)``` accordingly. + ### Options -Options helps to easily set necessary params, such as code & language, color theme, shortcut params & code line click listener. Some params are unnecessary. +Options helps to easily set necessary params, such as code & language, color theme, shortcut params (max lines, note), code line click listener. Some params are unnecessary. When view initialized (options or adapter are set) you can manipulate options in various ways: ```java @@ -92,7 +94,7 @@ codeView.getOptions() .withTheme(ColorTheme.MONOKAI); ``` -### Color themes +### Color theme There are some default themes (see full list below): ```java codeView.getOptions().setTheme(ColorTheme.SOLARIZED_LIGHT);