Skip to content

Commit 17b5afc

Browse files
committed
Upgrade to Kotlin 1.1, extract xenocom
1 parent 8c2b630 commit 17b5afc

File tree

4 files changed

+21
-223
lines changed

4 files changed

+21
-223
lines changed

build.gradle

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
buildscript {
2020
repositories {
21-
mavenCentral()
2221
jcenter()
2322
}
2423

@@ -41,9 +40,6 @@ apply plugin: 'maven-publish'
4140

4241
assert name == 'kotlin-argparser'
4342
description = 'Concise, easy, powerful and robust command line argument parsing for Kotlin'
44-
project.ext {
45-
prettyName = 'Kotlin ArgParser'
46-
}
4743

4844
group 'com.xenomachina'
4945
project.ext {
@@ -54,12 +50,13 @@ project.ext {
5450
sourceCompatibility = 1.6
5551

5652
repositories {
57-
mavenCentral()
53+
jcenter()
5854
}
5955

6056
dependencies {
6157
testCompile 'junit:junit:4.12'
6258
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
59+
compile "com.xenomachina:xenocom:$xenocom_version"
6360
}
6461

6562
sourceSets {
@@ -77,7 +74,7 @@ configurations {
7774
}
7875

7976
dependencies {
80-
ktlint 'com.github.shyiko:ktlint:0.3.1'
77+
ktlint 'com.github.shyiko:ktlint:0.6.0'
8178
}
8279

8380
task ktlint(type: JavaExec) {
@@ -154,7 +151,7 @@ bintray {
154151

155152
version {
156153
name = project.version
157-
desc = "$project.ext.prettyName version $project.version"
154+
desc = "$project.name version $project.version"
158155
released = new Date()
159156
vcsTag = "$project.version"
160157
}
@@ -200,7 +197,7 @@ publishing {
200197
pom.withXml {
201198
def root = asNode()
202199
root.appendNode('description', project.description)
203-
root.appendNode('name', project.ext.prettyName)
200+
root.appendNode('name', project.name)
204201
root.appendNode('url', project.ext.vcsUrl)
205202
root.children().last() + pomConfig
206203
}

gradle.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
version=1.0.2-SNAPSHOT
22

33
# Dependency versions
4-
kotlin_version = 1.0.6
5-
dokka_version = 0.9.11
4+
kotlin_version = 1.1.0
5+
dokka_version = 0.9.13
6+
xenocom_version = 0.0.1

src/main/kotlin/ArgParser.kt

Lines changed: 10 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818

1919
package com.xenomachina.argparser
2020

21+
import com.xenomachina.common.Holder
22+
import com.xenomachina.common.orElse
23+
import com.xenomachina.text.term.codePointWidth
24+
import com.xenomachina.text.term.columnize
25+
import com.xenomachina.text.term.wrapText
2126
import java.io.Writer
2227
import kotlin.reflect.KProperty
2328

@@ -662,6 +667,9 @@ interface HelpFormatter {
662667
val help: String)
663668
}
664669

670+
// TODO make verison in com.xenomachina.text public
671+
internal const val NBSP_CODEPOINT = 160
672+
665673
/**
666674
* Default implementation of [HelpFormatter]. Output is modelled after that of common UNIX utilities and looks
667675
* something like this:
@@ -708,6 +716,7 @@ class DefaultHelpFormatter(
708716
): String {
709717
val sb = StringBuilder()
710718
appendUsage(sb, columns, progName, values)
719+
sb.append("\n")
711720

712721
if (!prologue.isNullOrEmpty()) {
713722
sb.append("\n")
@@ -754,7 +763,7 @@ class DefaultHelpFormatter(
754763
val left = value.usages.map { it.replace(' ', '\u00a0') }.joinToString(", ").wrapText(helpPos - indentWidth).prependIndent(indent)
755764
val right = value.help.wrapText(columns - helpPos - 2 * indentWidth).prependIndent(indent)
756765
sb.append(columnize(left, right, minWidths = intArrayOf(helpPos)))
757-
sb.append("\n")
766+
sb.append("\n\n")
758767
}
759768
}
760769
}
@@ -857,213 +866,3 @@ open class UnexpectedOptionArgumentException(val optName: String) :
857866
*/
858867
open class UnexpectedPositionalArgumentException(val valueName: String?) :
859868
SystemExitException("unexpected argument${if (valueName == null) "" else " after $valueName"}", 2)
860-
861-
// TODO: move this to com.xenomachina.common
862-
863-
/**
864-
* a `Holder<T>?` can be used where one needs to be able to distinguish between having a T or not having a T, even
865-
* when T is a nullable type.
866-
*
867-
* @property value the value being held
868-
*/
869-
data class Holder<T>(val value: T)
870-
871-
/**
872-
* Dereferences the [Holder] if non-null, otherwise returns the result of calling [fallback].
873-
*/
874-
fun <T> Holder<T>?.orElse(fallback: () -> T): T {
875-
if (this == null) {
876-
return fallback()
877-
} else {
878-
return value
879-
}
880-
}
881-
882-
// TODO: move all declarations below this point to com.xenomachina.text
883-
884-
/**
885-
* Performs the given [operation] on each line in this [String].
886-
*/
887-
internal inline fun String.forEachLine(operation: (String) -> Unit) {
888-
var index = 0
889-
while (true) {
890-
val nextNewline = indexOf("\n", index)
891-
if (nextNewline < 0) break
892-
operation(substring(index, nextNewline + 1))
893-
index = nextNewline + 1
894-
}
895-
operation(substring(index))
896-
}
897-
898-
/**
899-
* Performs the given [operation] on each Unicode codepoint in this [String].
900-
*/
901-
internal inline fun String.forEachCodePoint(operation: (Int) -> Unit) {
902-
val length = this.length
903-
var offset = 0
904-
while (offset < length) {
905-
val codePoint = this.codePointAt(offset)
906-
operation(codePoint)
907-
offset += Character.charCount(codePoint)
908-
}
909-
}
910-
911-
internal fun StringBuilder.clear() {
912-
this.setLength(0)
913-
}
914-
915-
internal val SPACE_WIDTH = 1
916-
917-
internal fun String.padTo(width: Int): String {
918-
val sb = StringBuilder()
919-
var lineWidth = 0
920-
forEachCodePoint {
921-
if (it == '\n'.toInt()) {
922-
while (lineWidth < width) {
923-
sb.append(" ")
924-
lineWidth += SPACE_WIDTH
925-
}
926-
sb.append("\n")
927-
lineWidth = 0
928-
} else {
929-
sb.appendCodePoint(it)
930-
lineWidth += codePointWidth(it)
931-
}
932-
}
933-
return sb.toString()
934-
}
935-
936-
internal val NBSP_CODEPOINT = 0xa0
937-
938-
internal fun String.wrapText(maxWidth: Int): String {
939-
val sb = StringBuilder()
940-
val word = StringBuilder()
941-
var lineWidth = 0
942-
var wordWidth = 0
943-
fun handleSpace() {
944-
if (wordWidth > 0) {
945-
if (lineWidth > 0) {
946-
sb.append(" ")
947-
lineWidth += SPACE_WIDTH
948-
}
949-
sb.append(word)
950-
lineWidth += wordWidth
951-
word.clear()
952-
wordWidth = 0
953-
}
954-
}
955-
forEachCodePoint {
956-
if (Character.isSpaceChar(it) && it != NBSP_CODEPOINT) {
957-
// space
958-
handleSpace()
959-
} else {
960-
// non-space
961-
val codepoint = if (it == NBSP_CODEPOINT) ' '.toInt() else it
962-
val charWidth = codePointWidth(codepoint).toInt()
963-
if (lineWidth > 0 && lineWidth + SPACE_WIDTH + wordWidth + charWidth > maxWidth) {
964-
sb.append("\n")
965-
lineWidth = 0
966-
}
967-
if (lineWidth == 0 && lineWidth + SPACE_WIDTH + wordWidth + charWidth > maxWidth) {
968-
// Eep! Word would be longer than line. Need to break it.
969-
sb.append(word)
970-
word.clear()
971-
wordWidth = 0
972-
sb.append("\n")
973-
lineWidth = 0
974-
}
975-
word.appendCodePoint(codepoint)
976-
wordWidth += charWidth
977-
}
978-
}
979-
handleSpace()
980-
981-
return sb.toString()
982-
}
983-
984-
/**
985-
* Returns an estimated cell width of a Unicode code point when displayed on a monospace terminal.
986-
* Possible return values are -1, 0, 1 or 2. Control characters (other than null) and Del return -1.
987-
*
988-
* This function is based on the public domain [wcwidth.c](https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c)
989-
* written by Markus Kuhn.
990-
*/
991-
internal fun codePointWidth(ucs: Int): Byte {
992-
// 8-bit control characters
993-
if (ucs == 0) return 0
994-
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1
995-
996-
// Non-spacing characters. This is simulating the binary search of
997-
// `uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B`.
998-
if (ucs != 0x00AD) { // soft hyphen
999-
val category = Character.getType(ucs).toByte()
1000-
if (category == Character.ENCLOSING_MARK || // "Me"
1001-
category == Character.NON_SPACING_MARK || // "Mn"
1002-
category == Character.FORMAT || // "Cf"
1003-
(0x1160 <= ucs && ucs <= 0x11FF) || // Hangul Jungseong & Jongseong
1004-
ucs == 0x200B) // zero width space
1005-
return 0
1006-
}
1007-
1008-
// If we arrive here, ucs is not a combining or C0/C1 control character.
1009-
return if (ucs >= 0x1100 && (ucs <= 0x115f || // Hangul Jamo init. consonants
1010-
ucs == 0x2329 || ucs == 0x232a ||
1011-
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || // CJK ... Yi
1012-
(ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables
1013-
(ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compatibility Ideographs
1014-
(ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms
1015-
(ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compatibility Forms
1016-
(ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
1017-
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
1018-
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
1019-
(ucs >= 0x30000 && ucs <= 0x3fffd)))
1020-
2 else 1
1021-
}
1022-
1023-
internal fun String.codePointWidth(): Int {
1024-
var result = 0
1025-
forEachCodePoint {
1026-
result += codePointWidth(it)
1027-
}
1028-
return result
1029-
}
1030-
1031-
internal fun String.trimNewline(): String {
1032-
if (endsWith('\n')) {
1033-
return substring(0, length - 1)
1034-
} else {
1035-
return this
1036-
}
1037-
}
1038-
1039-
internal fun columnize(vararg s: String, minWidths: IntArray? = null): String {
1040-
val columns = Array(s.size) { mutableListOf<String>() }
1041-
val widths = Array(s.size) { 0 }
1042-
for (i in 0..s.size - 1) {
1043-
if (minWidths != null && i < minWidths.size) {
1044-
widths[i] = minWidths[i]
1045-
}
1046-
s[i].forEachLine {
1047-
val cell = it.trimNewline()
1048-
columns[i].add(cell)
1049-
widths[i] = widths[i].coerceAtLeast(cell.codePointWidth())
1050-
}
1051-
}
1052-
val height = columns.maxBy { it.size }?.size ?: 0
1053-
val sb = StringBuilder()
1054-
for (j in 0..height - 1) {
1055-
var lineWidth = 0
1056-
var columnStart = 0
1057-
for (i in 0..columns.size - 1) {
1058-
columns[i].getOrNull(j)?.let { cell ->
1059-
for (k in 1..columnStart - lineWidth) sb.append(" ")
1060-
lineWidth = columnStart
1061-
sb.append(cell)
1062-
lineWidth += cell.codePointWidth()
1063-
}
1064-
columnStart += widths[i]
1065-
}
1066-
sb.append("\n")
1067-
}
1068-
return sb.toString()
1069-
}

src/test/kotlin/ArgParserTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package com.xenomachina.argparser
2020

21+
import com.xenomachina.common.orElse
2122
import org.junit.Assert.assertEquals
2223
import org.junit.Assert.assertTrue
2324
import org.junit.Assert.assertFalse
@@ -548,7 +549,7 @@ class ArgParserTest {
548549

549550
// A better way to accomplish validation that only depends on one Delegate is to use
550551
// Delegate.addValidator. See testAddValidator for an example of this.
551-
if (x.mod(2) != 0)
552+
if (x.rem(2) != 0)
552553
throw InvalidArgumentException("${xDelegate.errorName} must be even, $x is odd")
553554
}
554555
}
@@ -587,7 +588,7 @@ class ArgParserTest {
587588
val xDelegate = parser.storing("-x",
588589
help = TEST_HELP) { toInt() }
589590
.addValidtator {
590-
if (value.mod(2) != 0)
591+
if (value.rem(2) != 0)
591592
throw InvalidArgumentException("$errorName must be even, $value is odd")
592593
}
593594
val x by xDelegate

0 commit comments

Comments
 (0)