diff --git a/CHANGELOG.md b/CHANGELOG.md index ce4fd62..5dea0ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [0.9.0] + +### Added +- Atom One theme. Thanks Nek-12! +- languages snippet tests + +### Fixed +- keyword highlighted in oneline comment +- keyword highlighted inside other word + +### Changed +- Kotlin version to 1.9.23 + ## [0.8.1] ### Added diff --git a/README.md b/README.md index 4fd42d5..c6e8505 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![highlights_banner_opaque](https://github.com/SnipMeDev/Highlights/assets/8405055/e123ce0f-6f58-451a-9e0a-893c0809b909) [![Maven Central](https://img.shields.io/maven-central/v/dev.snipme/highlights)](https://mvnrepository.com/artifact/dev.snipme) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.22-blue.svg?logo=kotlin)](http://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.23-blue.svg?logo=kotlin)](http://kotlinlang.org) [![GitHub License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) # Highlights @@ -15,7 +15,7 @@ repositories { ``` ```shell -implementation("dev.snipme:highlights:0.8.1") +implementation("dev.snipme:highlights:0.9.0") ``` ## Features ✨ @@ -97,6 +97,7 @@ The library comes with predefined syntax coloring themes available in `SyntaxThe Notepad Matrix Pastel + Atom One @@ -157,6 +158,19 @@ The library comes with predefined syntax coloring themes available in `SyntaxThe - ![#888A85](https://placehold.co/15x15/888A85/888A85.png) Multiline Comment - ![#CB956D](https://placehold.co/15x15/CB956D/CB956D.png) Punctuation - ![#CB956D](https://placehold.co/15x15/CB956D/CB956D.png) Mark + + + +- ![#DFDEE0](https://placehold.co/15x15/BBBBBB/BBBBBB.png) Code +- ![#729FCF](https://placehold.co/15x15/D55FDE/D55FDE.png) Keyword +- ![#93CF55](https://placehold.co/15x15/89CA78/89CA78.png) String +- ![#8AE234](https://placehold.co/15x15/D19A66/D19A66.png) Literal +- ![#888A85](https://placehold.co/15x15/5C6370/5C6370.png) Comment +- ![#5DB895](https://placehold.co/15x15/E5C07B/E5C07B.png) Metadata +- ![#888A85](https://placehold.co/15x15/5C6370/5C6370.png) Multiline Comment +- ![#CB956D](https://placehold.co/15x15/EF596F/EF596F.png) Punctuation +- ![#CB956D](https://placehold.co/15x15/2BBAC5/2BBAC5.png) Mark + @@ -169,6 +183,7 @@ The library comes with predefined syntax coloring themes available in `SyntaxThe Notepad Matrix Pastel + Atom One @@ -229,6 +244,19 @@ The library comes with predefined syntax coloring themes available in `SyntaxThe - ![#888A85](https://placehold.co/15x15/888A85/888A85.png) Multiline Comment - ![#CB956D](https://placehold.co/15x15/CB956D/CB956D.png) Punctuation - ![#CB956D](https://placehold.co/15x15/CB956D/CB956D.png) Mark + + + +- ![#DFDEE0](https://placehold.co/15x15/383A42/383A42.png) Code +- ![#729FCF](https://placehold.co/15x15/A626A4/A626A4.png) Keyword +- ![#93CF55](https://placehold.co/15x15/50A14F/50A14F.png) String +- ![#8AE234](https://placehold.co/15x15/986801/986801.png) Literal +- ![#888A85](https://placehold.co/15x15/A1A1A1/A1A1A1.png) Comment +- ![#5DB895](https://placehold.co/15x15/C18401/C18401.png) Metadata +- ![#888A85](https://placehold.co/15x15/A1A1A1/A1A1A1.png) Multiline Comment +- ![#CB956D](https://placehold.co/15x15/E45649/E45649.png) Punctuation +- ![#CB956D](https://placehold.co/15x15/526FFF/526FFF.png) Mark + @@ -265,14 +293,16 @@ If your project uses this code, please write me or add your info Application - - SnippLog - + SnippLog + + + Application + FlowMVI Sample ## TODO 🚧 -- [ ] Migrate some lists to sets +- [X] Migrate some lists to sets - [ ] Optimize code analysis - [ ] Add more themes and languages - [ ] Support italic and underline text style @@ -289,7 +319,7 @@ Then make sure: License 🖋️ ======= - Copyright 2023 Tomasz Kądziołka. + Copyright 2023-2024 Tomasz Kądziołka. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/build.gradle.kts b/build.gradle.kts index 5b5ac38..2a521a7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,14 @@ apply(from = "publish-root.gradle") plugins { - kotlin("multiplatform") version "1.9.22" + kotlin("multiplatform") version "1.9.23" id("maven-publish") id("io.github.gradle-nexus.publish-plugin") version "1.3.0" id("signing") } group = "dev.snipme" -version = "0.8.1" +version = "0.9.0" kotlin { // Android @@ -36,6 +36,7 @@ kotlin { browser() nodejs() } + wasmJs() // Dependencies sourceSets { val commonTest by getting { diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index a616c35..8ca610e 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.9.22" + kotlin("jvm") version "1.9.23" application } @@ -31,5 +31,5 @@ application { } dependencies { - implementation("dev.snipme:highlights:0.7.0") + implementation(":highlights") } \ No newline at end of file diff --git a/sample/src/main/kotlin/Main.kt b/sample/src/main/kotlin/Main.kt index 94d89e9..dde0f36 100644 --- a/sample/src/main/kotlin/Main.kt +++ b/sample/src/main/kotlin/Main.kt @@ -30,7 +30,6 @@ val sampleClass = """ } } """.trimIndent() - fun main() { println("### HIGHLIGHTS ###") println() diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/CodeAnalyzer.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/CodeAnalyzer.kt index fd7c538..42e4ef3 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/CodeAnalyzer.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/CodeAnalyzer.kt @@ -112,13 +112,15 @@ internal object CodeAnalyzer { PHP -> analyzeCodeWithKeywords(code, PHP_KEYWORDS) } - private fun analyzeCodeWithKeywords(code: String, keywords: List): CodeStructure { + private fun analyzeCodeWithKeywords(code: String, keywords: Set): CodeStructure { val comments = CommentLocator.locate(code) val multiLineComments = MultilineCommentLocator.locate(code) - val strings = StringLocator.locate(code) + val commentRanges = (comments + multiLineComments).toRangeSet() - val plainTextRanges = comments + multiLineComments + strings + val strings = StringLocator.locate(code, commentRanges) + val plainTextRanges = (comments + multiLineComments + strings).toRangeSet() + // TODO Apply ignored ranges to other locators return CodeStructure( marks = MarkLocator.locate(code), punctuations = PunctuationLocator.locate(code), diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/Extensions.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/Extensions.kt index 7529e57..b374a00 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/Extensions.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/Extensions.kt @@ -1,5 +1,15 @@ package dev.snipme.highlights.internal +import dev.snipme.highlights.model.PhraseLocation + +inline operator fun Set.get(i: Int): E? { + this.forEachIndexed { index, t -> + if (i == index) return t + } + + return null +} + fun String.indicesOf( phrase: String, ): Set { @@ -42,18 +52,29 @@ fun String.lengthToEOF(start: Int = 0): Int { return endIndex - start } -// TODO Create unit tests for this // Sometimes keyword can be found in the middle of word. // This returns information if index points only to the keyword fun String.isIndependentPhrase( code: String, index: Int, ): Boolean { - if (index == 0) return true if (index == code.lastIndex) return true + if (code.length == this.length) return true val charBefore = code[maxOf(index - 1, 0)] val charAfter = code[minOf(index + this.length, code.lastIndex)] - return charBefore.isLetter().not() && charAfter.isDigit().not() + if (index == 0) { + return charAfter.isDigit().not() && charAfter.isLetter().not() + } + + return charBefore.isLetter().not() && + charAfter.isDigit().not() && (charAfter == code.last() || charAfter.isLetter().not()) +} + +fun Set.toRangeSet(): Set = + this.map { IntRange(it.start, it.end) }.toSet() + +operator fun IntRange.contains(range: IntRange): Boolean { + return range.first >= this.first && range.last <= this.last } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/SyntaxTokens.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/SyntaxTokens.kt index ebea5b2..7a1a351 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/SyntaxTokens.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/SyntaxTokens.kt @@ -145,6 +145,6 @@ internal object SyntaxTokens { val PUNCTUATION_CHARACTERS = listOf(",", ".", ":", ";") val MARK_CHARACTERS = listOf("(", ")", "=", "{", "}", "<", ">", "-", "+", "[", "]", "|", "&") - private fun String.toTokenList() = trimIndent().split(",") + private fun String.toTokenList() = trimIndent().split(",").toSet() } diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocator.kt index 2b90047..f36a8f1 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocator.kt @@ -6,7 +6,7 @@ import dev.snipme.highlights.internal.indicesOf internal object AnnotationLocator { - fun locate(code: String): List { + fun locate(code: String): Set { val foundAnnotations = emptyList() val locations = mutableSetOf() code.split(*TOKEN_DELIMITERS.toTypedArray()) @@ -28,6 +28,6 @@ internal object AnnotationLocator { } } - return locations.toList() + return locations.toSet() } } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/CommentLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/CommentLocator.kt index 47fdb5c..97bf233 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/CommentLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/CommentLocator.kt @@ -1,13 +1,13 @@ package dev.snipme.highlights.internal.locator -import dev.snipme.highlights.model.PhraseLocation import dev.snipme.highlights.internal.SyntaxTokens.COMMENT_DELIMITERS import dev.snipme.highlights.internal.indicesOf import dev.snipme.highlights.internal.lengthToEOF +import dev.snipme.highlights.model.PhraseLocation internal object CommentLocator { - fun locate(code: String): List { + fun locate(code: String): Set { val locations = mutableListOf() val indices = mutableListOf() COMMENT_DELIMITERS.forEach { delimiter -> @@ -18,6 +18,7 @@ internal object CommentLocator { val end = start + code.lengthToEOF(start) locations.add(PhraseLocation(start, end)) } - return locations + + return locations.toSet() } } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/KeywordLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/KeywordLocator.kt index bd65bc8..4a5b3a1 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/KeywordLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/KeywordLocator.kt @@ -9,21 +9,16 @@ internal object KeywordLocator { fun locate( code: String, - keywords: List, - ignoreRanges: List = emptyList(), - ): List { - val locations = mutableListOf() + keywords: Set, + ignoreRanges: Set = emptySet(), + ): Set { + val locations = mutableSetOf() val foundKeywords = findKeywords(code, keywords) - val interpretedKeywords = foundKeywords.filterNot { keyword -> - val index = code.indexOf(keyword) - val length = keyword.length - ignoreRanges.any { it.start <= index && it.end >= index + length } - } - - interpretedKeywords.forEach { keyword -> + foundKeywords.forEach { keyword -> val indices = code .indicesOf(keyword) + .filterNot { index -> ignoreRanges.any { index in it } } .filter { keyword.isIndependentPhrase(code, it) } indices.forEach { index -> @@ -31,10 +26,10 @@ internal object KeywordLocator { } } - return locations.toList() + return locations } - private fun findKeywords(code: String, keywords: List): Set = + private fun findKeywords(code: String, keywords: Set): Set = TOKEN_DELIMITERS.toTypedArray().let { delimiters -> code.split(*delimiters, ignoreCase = true) // Split into words .asSequence() // Reduce amount of operations diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MarkLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MarkLocator.kt index 0dbebc3..4cb4a68 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MarkLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MarkLocator.kt @@ -5,7 +5,7 @@ import dev.snipme.highlights.internal.indicesOf import dev.snipme.highlights.model.PhraseLocation internal object MarkLocator { - fun locate(code: String): List { + fun locate(code: String): Set { val locations = mutableListOf() code.asSequence() .toSet() @@ -16,6 +16,6 @@ internal object MarkLocator { } } - return locations + return locations.toSet() } } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocator.kt index 2806128..100001c 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocator.kt @@ -1,14 +1,14 @@ package dev.snipme.highlights.internal.locator -import dev.snipme.highlights.model.PhraseLocation import dev.snipme.highlights.internal.SyntaxTokens.MULTILINE_COMMENT_DELIMITERS import dev.snipme.highlights.internal.indicesOf +import dev.snipme.highlights.model.PhraseLocation private const val START_INDEX = 0 internal object MultilineCommentLocator { - fun locate(code: String): List { + fun locate(code: String): Set { val locations = mutableListOf() val comments = mutableListOf>() val startIndices = mutableListOf() @@ -30,6 +30,6 @@ internal object MultilineCommentLocator { locations.add(PhraseLocation(start, end)) } - return locations + return locations.toSet() } } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocator.kt index 340a111..572bc56 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocator.kt @@ -11,11 +11,11 @@ private val NUMBER_SPECIAL_CHARACTERS = listOf('_') internal object NumericLiteralLocator { - fun locate(code: String): List { + fun locate(code: String): Set { return findDigitIndices(code) } - private fun findDigitIndices(code: String): List { + private fun findDigitIndices(code: String): Set { val foundPhrases = mutableSetOf() val locations = mutableSetOf() @@ -45,7 +45,7 @@ internal object NumericLiteralLocator { foundPhrases.add(number) } - return locations.toList() + return locations.toSet() } // Returns if given index is the beginning of word (there is no letter before) diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocator.kt index 9d96368..f61c65f 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocator.kt @@ -6,7 +6,7 @@ import dev.snipme.highlights.internal.indicesOf import dev.snipme.highlights.model.PhraseLocation internal object PunctuationLocator { - fun locate(code: String): List { + fun locate(code: String): Set { val locations = mutableSetOf() code.asSequence() .map { it.toString().trim() } @@ -21,6 +21,6 @@ internal object PunctuationLocator { } } - return locations.toList() + return locations.toSet() } } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/StringLocator.kt b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/StringLocator.kt index 03a251f..1956521 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/StringLocator.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/internal/locator/StringLocator.kt @@ -1,8 +1,9 @@ package dev.snipme.highlights.internal.locator -import dev.snipme.highlights.model.PhraseLocation import dev.snipme.highlights.internal.SyntaxTokens.STRING_DELIMITERS +import dev.snipme.highlights.internal.contains import dev.snipme.highlights.internal.indicesOf +import dev.snipme.highlights.model.PhraseLocation private const val START_INDEX = 0 private const val TWO_ELEMENTS = 2 @@ -10,10 +11,16 @@ private const val QUOTE_ENDING_POSITION = 1 internal object StringLocator { - fun locate(code: String): List = findStrings(code) + fun locate( + code: String, + ignoreRanges: Set = emptySet(), + ): Set = findStrings(code, ignoreRanges) - private fun findStrings(code: String): List { - val locations = mutableListOf() + private fun findStrings( + code: String, + ignoreRanges: Set, + ): Set { + val locations = mutableSetOf() // Find index of each string delimiter like " or ' or """ STRING_DELIMITERS.forEach { @@ -22,14 +29,19 @@ internal object StringLocator { // For given indices find words between for (i in START_INDEX..textIndices.lastIndex step TWO_ELEMENTS) { - if (textIndices.getOrNull(i + 1) != null) { - locations.add( - PhraseLocation( - textIndices[i], - textIndices[i + 1] + QUOTE_ENDING_POSITION - ) + if (textIndices.getOrNull(i + 1) == null) continue + + // Skip unwanted phrases + val textRange = IntRange(textIndices[i], textIndices[i + 1]) + if (ignoreRanges.any { ignored -> textRange in ignored }) + continue + + locations.add( + PhraseLocation( + textIndices[i], + textIndices[i + 1] + QUOTE_ENDING_POSITION ) - } + ) } } diff --git a/src/commonMain/kotlin/dev/snipme/highlights/model/CodeStructure.kt b/src/commonMain/kotlin/dev/snipme/highlights/model/CodeStructure.kt index 05ce814..604dc88 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/model/CodeStructure.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/model/CodeStructure.kt @@ -2,58 +2,61 @@ package dev.snipme.highlights.model data class PhraseLocation(val start: Int, val end: Int) -// TODO Migrate to set data class CodeStructure( - val marks: List, - val punctuations: List, - val keywords: List, - val strings: List, - val literals: List, - val comments: List, - val multilineComments: List, - val annotations: List, + val marks: Set, + val punctuations: Set, + val keywords: Set, + val strings: Set, + val literals: Set, + val comments: Set, + val multilineComments: Set, + val annotations: Set, val incremental: Boolean, ) { fun move(position: Int) = CodeStructure( - marks = marks.map { it.copy(start = it.start + position, end = it.end + position) }, + marks = marks.map { + it.copy(start = it.start + position, end = it.end + position) + }.toSet(), punctuations = punctuations.map { it.copy( start = it.start + position, end = it.end + position ) - }, + }.toSet(), keywords = keywords.map { it.copy( start = it.start + position, end = it.end + position ) - }, - strings = strings.map { it.copy(start = it.start + position, end = it.end + position) }, + }.toSet(), + strings = strings.map { + it.copy(start = it.start + position, end = it.end + position) + }.toSet(), literals = literals.map { it.copy( start = it.start + position, end = it.end + position ) - }, + }.toSet(), comments = comments.map { it.copy( start = it.start + position, end = it.end + position ) - }, + }.toSet(), multilineComments = multilineComments.map { it.copy( start = it.start + position, end = it.end + position ) - }, + }.toSet(), annotations = annotations.map { it.copy( start = it.start + position, end = it.end + position ) - }, + }.toSet(), incremental = true, ) @@ -83,7 +86,7 @@ data class CodeStructure( incremental = true, ) - fun printPhrases(code: String) { + fun printStructure(code: String) { print("marks = ${marks.join(code)}") print("punctuations = ${punctuations.join(code)}") print("keywords = ${keywords.join(code)}") @@ -94,6 +97,6 @@ data class CodeStructure( print("annotations = ${annotations.join(code)}") } - private fun List.join(code: String) = - this.map { code.substring(it.start, it.end) }.joinToString(separator = " ") + "\n" + private fun Set.join(code: String) = + this.joinToString(separator = " ") { code.substring(it.start, it.end) } + "\n" } diff --git a/src/commonMain/kotlin/dev/snipme/highlights/model/SyntaxThemes.kt b/src/commonMain/kotlin/dev/snipme/highlights/model/SyntaxThemes.kt index 453dd89..733e75b 100644 --- a/src/commonMain/kotlin/dev/snipme/highlights/model/SyntaxThemes.kt +++ b/src/commonMain/kotlin/dev/snipme/highlights/model/SyntaxThemes.kt @@ -5,6 +5,7 @@ private const val MONOKAI_KEY = "monokai" private const val NOTEPAD_KEY = "notepad" private const val MATRIX_KEY = "matrix" private const val PASTEL_KEY = "pastel" +private const val ATOM_ONE_KEY = "atomone" object SyntaxThemes { @@ -68,6 +69,18 @@ object SyntaxThemes { multilineComment = 0x888A85, punctuation = 0xCB956D, mark = 0xCB956D + ), + ATOM_ONE_KEY to SyntaxTheme( + key = ATOM_ONE_KEY, + code = 0xBBBBBB, + keyword = 0xD55FDE, + string = 0x89CA78, + literal = 0xD19A66, + comment = 0x5C6370, + metadata = 0xE5C07B, + multilineComment = 0x5C6370, + punctuation = 0xEF596F, + mark = 0x2BBAC5 ) ) @@ -131,7 +144,20 @@ object SyntaxThemes { multilineComment = 0x888A85, punctuation = 0xCB956D, mark = 0xCB956D + ), + ATOM_ONE_KEY to SyntaxTheme( + key = ATOM_ONE_KEY, + code = 0x383A42, + keyword = 0xA626A4, + string = 0x50A14F, + literal = 0x986801, + comment = 0xA1A1A1, + metadata = 0xC18401, + multilineComment = 0xA1A1A1, + punctuation = 0xE45649, + mark = 0x526FFF, ) + ) fun themes(darkMode: Boolean = false) = if (darkMode) dark else light @@ -143,6 +169,7 @@ object SyntaxThemes { fun notepad(darkMode: Boolean = false) = themes(darkMode)[NOTEPAD_KEY]!! fun matrix(darkMode: Boolean = false) = themes(darkMode)[MATRIX_KEY]!! fun pastel(darkMode: Boolean = false) = themes(darkMode)[PASTEL_KEY]!! + fun atom(darkMode: Boolean = false) = themes(darkMode)[ATOM_ONE_KEY]!! fun getNames(): List = SyntaxThemes.light.map { it.key @@ -151,4 +178,4 @@ object SyntaxThemes { } fun SyntaxTheme.useDark(darkMode: Boolean) = if (darkMode) dark[key] else light[key] -} \ No newline at end of file +} diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/CodeAnalyzerTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/CodeAnalyzerTest.kt index df4c45f..742a030 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/CodeAnalyzerTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/CodeAnalyzerTest.kt @@ -22,7 +22,7 @@ internal class CodeAnalyzerTest { val result = CodeAnalyzer.analyze(testCode) assertEquals( - listOf( + setOf( PhraseLocation(30, 31), PhraseLocation(31, 32) ), @@ -30,7 +30,7 @@ internal class CodeAnalyzerTest { ) assertEquals( - listOf( + setOf( PhraseLocation(36, 37), PhraseLocation(41, 42), PhraseLocation(42, 43), @@ -41,7 +41,7 @@ internal class CodeAnalyzerTest { ) assertEquals( - listOf( + setOf( PhraseLocation(14, 19), PhraseLocation(22, 29) ), @@ -49,35 +49,35 @@ internal class CodeAnalyzerTest { ) assertEquals( - listOf( + setOf( PhraseLocation(33, 36), ), result.strings ) assertEquals( - listOf( + setOf( PhraseLocation(45, 52), ), result.literals ) assertEquals( - listOf( + setOf( PhraseLocation(9, 13), ), result.comments ) assertEquals( - listOf( + setOf( PhraseLocation(0, 8), ), result.multilineComments ) assertEquals( - listOf( + setOf( PhraseLocation(38, 40), ), result.annotations @@ -119,7 +119,7 @@ internal class CodeAnalyzerTest { assertEquals(true, result.incremental) assertEquals( - listOf( + setOf( PhraseLocation(30, 31), PhraseLocation(31, 32), ), @@ -127,7 +127,7 @@ internal class CodeAnalyzerTest { ) assertEquals( - listOf( + setOf( PhraseLocation(36, 37), PhraseLocation(41, 42), PhraseLocation(42, 43), @@ -138,7 +138,7 @@ internal class CodeAnalyzerTest { ) assertEquals( - listOf( + setOf( PhraseLocation(14, 19), PhraseLocation(22, 29), ), @@ -146,35 +146,35 @@ internal class CodeAnalyzerTest { ) assertEquals( - listOf( + setOf( PhraseLocation(33, 36), ), result.strings ) assertEquals( - listOf( + setOf( PhraseLocation(45, 52), ), result.literals ) assertEquals( - listOf( + setOf( PhraseLocation(9, 13), ), result.comments ) assertEquals( - listOf( + setOf( PhraseLocation(0, 8), ), result.multilineComments ) assertEquals( - listOf( + setOf( PhraseLocation(38, 40), ), result.annotations @@ -208,48 +208,48 @@ internal class CodeAnalyzerTest { assertEquals(true, result.incremental) assertEquals( - emptyList(), + emptySet(), result.marks ) assertEquals( - emptyList(), + emptySet(), result.punctuations ) assertEquals( - listOf( + setOf( PhraseLocation(14, 19), ), result.keywords ) assertEquals( - emptyList(), + emptySet(), result.strings ) assertEquals( - emptyList(), + emptySet(), result.literals ) assertEquals( - listOf( + setOf( PhraseLocation(9, 13), ), result.comments ) assertEquals( - listOf( + setOf( PhraseLocation(0, 8), ), result.multilineComments ) assertEquals( - emptyList(), + emptySet(), result.annotations ) } diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/ExtensionsKtTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/ExtensionsKtTest.kt index 757be60..49f054e 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/ExtensionsKtTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/ExtensionsKtTest.kt @@ -5,63 +5,241 @@ import kotlin.test.assertEquals internal class ExtensionsKtTest { - @Test - fun `Returns empty result of phrase that is not present`() { - val text = "b c d" - val phrase = "//a" + class IndicesOfTest { - val result = text.indicesOf(phrase) + @Test + fun `Returns empty result of phrase that is not present`() { + val text = "b c d" + val phrase = "//a" - assertEquals(setOf(), result) - } + val result = text.indicesOf(phrase) - @Test - fun `Returns index of phrase at start`() { - val text = "//a b c d" - val phrase = "//a" + assertEquals(setOf(), result) + } - val result = text.indicesOf(phrase) + @Test + fun `Returns index of phrase at start`() { + val text = "//a b c d" + val phrase = "//a" - assertEquals(setOf(0), result) - } + val result = text.indicesOf(phrase) - @Test - fun `Returns index of phrase at end`() { - val text = "/a b c d //a" - val phrase = "//a" + assertEquals(setOf(0), result) + } - val result = text.indicesOf(phrase) + @Test + fun `Returns index of phrase at end`() { + val text = "/a b c d //a" + val phrase = "//a" - assertEquals(setOf(9), result) - } + val result = text.indicesOf(phrase) - @Test - fun `Returns index of phrase at start and end`() { - val text = "//a b c d //a" - val phrase = "//a" + assertEquals(setOf(9), result) + } - val result = text.indicesOf(phrase) + @Test + fun `Returns index of phrase at start and end`() { + val text = "//a b c d //a" + val phrase = "//a" - assertEquals(setOf(0, 10), result) - } + val result = text.indicesOf(phrase) + + assertEquals(setOf(0, 10), result) + } + + @Test + fun `Returns all indices of phrase at end`() { + val text = "//a /a b //ac d //a" + val phrase = "//a" + + val result = text.indicesOf(phrase) + + assertEquals(setOf(0, 9, 16), result) + } - @Test - fun `Returns all indices of phrase at end`() { - val text = "//a /a b //ac d //a" - val phrase = "//a" + @Test + fun `Returns all indices of special phrase`() { + val text = "//a /a b /** d //a" + val phrase = "/**" - val result = text.indicesOf(phrase) + val result = text.indicesOf(phrase) - assertEquals(setOf(0, 9, 16), result) + assertEquals(setOf(9), result) + } } - @Test - fun `Returns all indices of special phrase`() { - val text = "//a /a b /** d //a" - val phrase = "/**" + class IsIndependentPhraseTest { + + @Test + fun `Returns true for phrase at start`() { + val code = "class " + val index = 0 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns false for wrong phrase at start`() { + val code = "classa " + val index = 1 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns true for phrase at end`() { + val code = "asas class" + val index = 5 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns false for wrong phrase at end`() { + val code = "asas classa" + val index = 6 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns false for phrase with wrong prefix at end`() { + val code = "asas aclass" + val index = 6 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns false for phrase at start other phrase`() { + val code = "valuable" + val index = 0 + + val result = "val".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns false for phrase at end other phrase`() { + val code = "inval" + val index = 2 + + val result = "val".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns false for phrase inside other phrase`() { + val code = "invaluable" + val index = 2 + + val result = "val".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns true for phrase is whole code`() { + val code = "class" + val index = 0 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns true for phrase is with new line`() { + val code = "class\n" + val index = 0 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns true for phrase is with new line and space`() { + val code = """ + class + """ + val index = 3 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns true for phrase that starts with number`() { + val code = "9class" + val index = 1 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns false for phrase that ends with number`() { + val code = "class9" + val index = 0 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns false for phrase that is between numbers`() { + val code = "9class9" + val index = 1 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(false, result) + } + + @Test + fun `Returns true for phrase that starts special character`() { + val code = ".class" + val index = 1 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns true for phrase that ends with special character`() { + val code = "class." + val index = 0 + + val result = "class".isIndependentPhrase(code, index) + + assertEquals(true, result) + } + + @Test + fun `Returns true for phrase that is between special characters`() { + val code = ".class." + val index = 1 - val result = text.indicesOf(phrase) + val result = "class".isIndependentPhrase(code, index) - assertEquals(setOf(9), result) + assertEquals(true, result) + } } } \ No newline at end of file diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/language/JavaTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/language/JavaTest.kt new file mode 100644 index 0000000..f5bfb72 --- /dev/null +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/language/JavaTest.kt @@ -0,0 +1,21 @@ +import dev.snipme.highlights.Highlights +import dev.snipme.highlights.model.SyntaxLanguage +import kotlin.test.Test +import kotlin.test.assertEquals + +class JavaTest { + + @Test + fun test() { + val code = """ + this.class.abcd ) new + """.trimIndent() + + val result = Highlights.Builder( + language = SyntaxLanguage.JAVA, + code = code + ).build().getCodeStructure() + + assertEquals(3, result.keywords.size) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/language/KotlinTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/language/KotlinTest.kt new file mode 100644 index 0000000..a7c89b7 --- /dev/null +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/language/KotlinTest.kt @@ -0,0 +1,43 @@ +package dev.snipme.highlights.internal.language + +import dev.snipme.highlights.Highlights +import dev.snipme.highlights.model.SyntaxLanguage +import kotlin.test.Test +import kotlin.test.assertEquals + +class KotlinTest { + + @Test + fun test() { + val code = """ + val intent = 0 + copy(input = input(intent.value)) // highlights "value" + + // calling configure() is equivalent to: + + val new = 1 + + val initialPages = 0 + + // Visibility modifiers + internal val internalVar: Int = 10 + private fun privateFunction() { + println("This is a private function") + } + + // Data types + val number: Int = 42 + val pi: Double = 3.14 + val isValid: Boolean = true + """.trimIndent() + + val result = Highlights.Builder( + language = SyntaxLanguage.KOTLIN, + code = code + ).build().getCodeStructure() + + result.printStructure(code) + + assertEquals(11, result.keywords.size) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocatorTest.kt index 7cf902d..fbc10b4 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/AnnotationLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.Test import kotlin.test.assertEquals @@ -94,4 +95,4 @@ internal class AnnotationLocatorTest { assertEquals(PhraseLocation(79, 88), result[1]) assertEquals(PhraseLocation(56, 71), result[2]) } -} \ No newline at end of file +} diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/KeywordLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/KeywordLocatorTest.kt index c2d1fa1..f2413fb 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/KeywordLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/KeywordLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.Test import kotlin.test.assertEquals @@ -9,8 +10,8 @@ internal class KeywordLocatorTest { @Test fun `Returns empty list for no keywords`() { val testCode = "class NewClass" - val keywords = listOf() - val expectedResult = emptyList() + val keywords = setOf() + val expectedResult = emptySet() val result = KeywordLocator.locate(testCode, keywords) @@ -20,7 +21,7 @@ internal class KeywordLocatorTest { @Test fun `Returns location of first found keyword`() { val testCode = "class NewClass" - val keywords = listOf("static", "new", "class") + val keywords = setOf("static", "new", "class") val result = KeywordLocator.locate(testCode, keywords) @@ -31,7 +32,7 @@ internal class KeywordLocatorTest { @Test fun `Returns location of all found keyword in line`() { val testCode = "class NewClass extends { }" - val keywords = listOf("static", "new", "class", "extends") + val keywords = setOf("static", "new", "class", "extends") val result = KeywordLocator.locate(testCode, keywords) @@ -43,7 +44,7 @@ internal class KeywordLocatorTest { @Test fun `Returns location of all keyword next to each other`() { val testCode = "this.class.abcd ) new" - val keywords = listOf("this", "new", "class") + val keywords = setOf("this", "new", "class") val result = KeywordLocator.locate(testCode, keywords) @@ -56,7 +57,7 @@ internal class KeywordLocatorTest { @Test fun `Returns location of word that is proper keyword only`() { val testCode = "class 1class 1 class1 class& %class aclass" - val keywords = listOf("this", "new", "class") + val keywords = setOf("this", "new", "class") val result = KeywordLocator.locate(testCode, keywords) @@ -74,7 +75,7 @@ internal class KeywordLocatorTest { static class Example2 {} } """.trimIndent() - val keywords = listOf("static", "class", "extends") + val keywords = setOf("static", "class", "extends") val result = KeywordLocator.locate(testCode, keywords) @@ -90,7 +91,19 @@ internal class KeywordLocatorTest { val testCode = """ aclassa """.trimIndent() - val keywords = listOf("static", "class", "extends") + val keywords = setOf("static", "class", "extends") + + val result = KeywordLocator.locate(testCode, keywords) + + assertEquals(0, result.size) + } + + @Test + fun `Not returns location of keyword inside phrase from start`() { + val testCode = """ + val intent = 0 + """.trimIndent() + val keywords = setOf("int") val result = KeywordLocator.locate(testCode, keywords) @@ -102,9 +115,9 @@ internal class KeywordLocatorTest { val testCode = """ // This class is static and should extend another class """.trimIndent() - val keywords = listOf("static", "class", "extends") + val keywords = setOf("static", "class", "extends") - val result = KeywordLocator.locate(testCode, keywords, listOf(PhraseLocation(0, 55))) + val result = KeywordLocator.locate(testCode, keywords, setOf(IntRange(0, 55))) assertEquals(0, result.size) } @@ -116,9 +129,9 @@ internal class KeywordLocatorTest { This class is static and should extend another class */ """.trimIndent() - val keywords = listOf("static", "class", "extends") + val keywords = setOf("static", "class", "extends") - val result = KeywordLocator.locate(testCode, keywords, listOf(PhraseLocation(0, 56))) + val result = KeywordLocator.locate(testCode, keywords, setOf(IntRange(0, 56))) assertEquals(0, result.size) } @@ -126,11 +139,11 @@ internal class KeywordLocatorTest { @Test fun `Not returns keywords from string`() { val testCode = """ - val text = "This class is static and should extend another class" + "This class is static and should extend another class" """.trimIndent() - val keywords = listOf("static", "class", "extends") + val keywords = setOf("static", "class", "extends") - val result = KeywordLocator.locate(testCode, keywords, listOf(PhraseLocation(0, 54))) + val result = KeywordLocator.locate(testCode, keywords, setOf(IntRange(0, 54))) assertEquals(0, result.size) } diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MarkLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MarkLocatorTest.kt index 219d4f1..e1d4844 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MarkLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MarkLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.assertEquals import kotlin.test.Test diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocatorTest.kt index 16a7b94..cf03a09 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/MultilineCommentLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocatorTest.kt index 7aecbcd..bbdfb10 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/NumericLiteralLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.assertEquals import kotlin.test.Test diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocatorTest.kt index bbf8071..ed6f566 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/PunctuationLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/StringLocatorTest.kt b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/StringLocatorTest.kt index e854675..2cc542e 100644 --- a/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/StringLocatorTest.kt +++ b/src/commonTest/kotlin/dev/snipme/highlights/internal/locator/StringLocatorTest.kt @@ -1,5 +1,6 @@ package dev.snipme.highlights.internal.locator +import dev.snipme.highlights.internal.get import dev.snipme.highlights.model.PhraseLocation import kotlin.test.Test import kotlin.test.assertEquals