Skip to content

Commit 0c46245

Browse files
committed
Fixed tabs/spaces trimming
1 parent a5201e5 commit 0c46245

2 files changed

Lines changed: 54 additions & 53 deletions

File tree

codeview/src/main/java/io/github/kbiakov/codeview/Utils.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import android.content.Context
44
import android.os.Handler
55
import android.os.Looper
66
import android.text.Html
7+
import android.text.Spanned
78
import android.util.TypedValue
89
import java.io.BufferedReader
910
import java.io.InputStreamReader
11+
import java.util.*
1012
import java.util.concurrent.Executors
1113

1214
object Const {
@@ -46,6 +48,14 @@ fun spaceSplit(source: String) = source.split("\\s".toRegex())
4648
*/
4749
fun extractLines(source: String) = listOf(*source.split("\n").toTypedArray())
4850

51+
/**
52+
* Slice list by index.
53+
*
54+
* @param idx Index to slice
55+
* @return Pair of lists with head and tail
56+
*/
57+
fun <T> List<T>.slice(idx: Int) = Pair(this.subList(0, idx), this.subList(idx, this.lastIndex))
58+
4959
/**
5060
* Get HTML from string.
5161
*
@@ -55,9 +65,11 @@ fun extractLines(source: String) = listOf(*source.split("\n").toTypedArray())
5565
@Suppress("deprecation")
5666
fun html(content: String) =
5767
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
58-
Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY)
68+
Html.fromHtml(content.htmlSafe(), Html.FROM_HTML_MODE_LEGACY)
5969
else
60-
Html.fromHtml(content)
70+
Html.fromHtml(content.htmlSafe())
71+
72+
fun String.htmlSafe() = replace(" ", "&nbsp;")
6173

6274
object Thread {
6375
/**

codeview/src/main/java/io/github/kbiakov/codeview/adapters/AbstractCodeAdapter.kt

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.kbiakov.codeview.adapters
22

3+
import android.annotation.SuppressLint
34
import android.content.Context
45
import android.support.v7.widget.RecyclerView
56
import android.view.LayoutInflater
@@ -55,17 +56,13 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
5556
* only necessary lines & the rest are dropped (and stores in named variable).
5657
*/
5758
internal fun prepareCodeLines() {
58-
val allLines = extractLines(options.code)
59-
val isFullShowing = !options.shortcut || allLines.size <= options.maxLines // limit is not reached
60-
61-
if (isFullShowing)
62-
lines = allLines
63-
else {
64-
val resultLines = ArrayList(allLines.subList(0, options.maxLines))
65-
resultLines.add(options.shortcutNote.toUpperCase())
66-
lines = resultLines
67-
68-
droppedLines = ArrayList(allLines.subList(options.maxLines, allLines.lastIndex))
59+
extractLines(options.code).apply {
60+
if (!options.shortcut || size <= options.maxLines) // limit is not reached, show full
61+
lines = this
62+
else slice(options.maxLines).apply {
63+
lines = linesToShow() + options.shortcutNote.toUpperCase()
64+
droppedLines = droppedLines()
65+
}
6966
}
7067
}
7168

@@ -74,17 +71,17 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
7471
/**
7572
* Update code.
7673
*/
77-
internal fun updateCode(newContent: String) {
78-
options.code = newContent
74+
internal fun updateCode(newCode: String) {
75+
options.code = newCode
7976
prepareCodeLines()
8077
notifyDataSetChanged()
8178
}
8279

8380
/**
8481
* Update code with new Highlighter.
8582
*/
86-
internal fun updateCode(opts: Options) {
87-
options = opts
83+
internal fun updateCode(newOptions: Options) {
84+
options = newOptions
8885
prepareCodeLines()
8986
notifyDataSetChanged()
9087
}
@@ -107,7 +104,7 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
107104
* @param onReady Callback when content is highlighted
108105
*/
109106
internal fun highlight(onReady: () -> Unit) {
110-
async() {
107+
async {
111108
val language = options.language ?: classifyContent()
112109
highlighting(language, onReady)
113110
}
@@ -159,10 +156,7 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
159156
private fun updateContent(code: String, onUpdated: () -> Unit) {
160157
options.code = code
161158
prepareCodeLines()
162-
163-
ui {
164-
onUpdated()
165-
}
159+
ui(onUpdated)
166160
}
167161

168162
private fun monoTypeface() = MonoFontCache.getInstance(context).typeface
@@ -187,19 +181,19 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
187181
return holder
188182
}
189183

190-
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
191-
val codeLine = lines[position]
184+
override fun onBindViewHolder(holder: ViewHolder, pos: Int) {
185+
val codeLine = lines[pos]
192186
holder.mItem = codeLine
193187

194188
options.lineClickListener?.let {
195189
holder.itemView.setOnClickListener {
196-
options.lineClickListener?.onCodeLineClicked(position, codeLine)
190+
options.lineClickListener?.onCodeLineClicked(pos, codeLine)
197191
}
198192
}
199193

200-
setupLine(position, codeLine, holder)
201-
displayLineFooter(position, holder)
202-
addExtraPadding(position, holder)
194+
setupLine(pos, codeLine, holder)
195+
displayLineFooter(pos, holder)
196+
addExtraPadding(pos, holder)
203197
}
204198

205199
override fun getItemCount() = lines.size
@@ -212,42 +206,40 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
212206

213207
// - Helpers (for view holder)
214208

215-
private fun setupLine(position: Int, line: String, holder: ViewHolder) {
209+
@SuppressLint("SetTextI18n")
210+
private fun setupLine(pos: Int, line: String, holder: ViewHolder) {
216211
holder.tvLineContent.text = html(line)
217212
holder.tvLineContent.setTextColor(options.theme.noteColor.color())
218213

219-
if (options.shortcut && position == MAX_SHORTCUT_LINES) {
214+
if (options.shortcut && pos == MAX_SHORTCUT_LINES) {
220215
holder.tvLineNum.textSize = 10f
221216
holder.tvLineNum.text = context.getString(R.string.dots)
222217
} else {
223218
holder.tvLineNum.textSize = 12f
224-
holder.tvLineNum.text = "${position + 1}"
219+
holder.tvLineNum.text = "${pos + 1}"
225220
}
226221
}
227222

228-
private fun displayLineFooter(position: Int, holder: ViewHolder) {
229-
val entityList = footerEntities[position]
223+
private fun displayLineFooter(pos: Int, holder: ViewHolder) {
224+
val entityList = footerEntities[pos]
230225

231226
holder.llLineFooter.removeAllViews()
232227

233228
entityList?.let {
234229
holder.llLineFooter.visibility = if (it.isNotEmpty()) View.VISIBLE else View.GONE
235230

236-
var isFirst = true
237-
238-
it.forEach { entity ->
239-
val footerView = createFooter(context, entity, isFirst)
231+
it.forEachIndexed { idx, entity ->
232+
val footerView = createFooter(context, entity, idx == 0)
240233
holder.llLineFooter.addView(footerView)
241-
isFirst = false
242234
}
243235
}
244236
}
245237

246-
private fun addExtraPadding(position: Int, holder: ViewHolder) {
247-
if (position.isBorder()) {
238+
private fun addExtraPadding(pos: Int, holder: ViewHolder) {
239+
if (pos.isBorder()) {
248240
val dp8 = dpToPx(context, 8)
249-
val topPadding = if (position.isJustFirst()) dp8 else 0
250-
val bottomPadding = if (position.isJustLast()) dp8 else 0
241+
val topPadding = if (pos.isJustFirst()) dp8 else 0
242+
val bottomPadding = if (pos.isJustLast()) dp8 else 0
251243
holder.tvLineNum.setPadding(0, topPadding, 0, bottomPadding)
252244
holder.tvLineContent.setPadding(0, topPadding, 0, bottomPadding)
253245
} else {
@@ -257,26 +249,23 @@ abstract class AbstractCodeAdapter<T> : RecyclerView.Adapter<AbstractCodeAdapter
257249
}
258250

259251
companion object {
260-
internal const val MAX_SHORTCUT_LINES = 6
252+
private const val MAX_SHORTCUT_LINES = 6
253+
254+
private fun Pair<List<String>, List<String>>.linesToShow() = first
255+
private fun Pair<List<String>, List<String>>.droppedLines() = second
261256
}
262257

263258
/**
264259
* View holder for code adapter.
265260
* Stores all views related to code line layout.
266261
*/
267262
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
268-
val tvLineNum: TextView
269-
val tvLineContent: TextView
270-
val llLineFooter: LinearLayout
263+
val tvLineNum = itemView.findViewById(R.id.tv_line_num) as TextView
264+
val tvLineContent = itemView.findViewById(R.id.tv_line_content) as TextView
265+
val llLineFooter = itemView.findViewById(R.id.ll_line_footer) as LinearLayout
271266

272267
var mItem: String? = null
273268

274-
init {
275-
tvLineNum = itemView.findViewById(R.id.tv_line_num) as TextView
276-
tvLineContent = itemView.findViewById(R.id.tv_line_content) as TextView
277-
llLineFooter = itemView.findViewById(R.id.ll_line_footer) as LinearLayout
278-
}
279-
280269
override fun toString() = "${super.toString()} '$mItem'"
281270
}
282271
}
@@ -370,6 +359,6 @@ data class Options(
370359
}
371360

372361
companion object Default {
373-
fun get(context: Context): Options = Options(context)
362+
fun get(context: Context) = Options(context)
374363
}
375364
}

0 commit comments

Comments
 (0)