How can I add a footer to Kotlin Dokka docs? - kotlin

I am looking for a way to include text in the footer of all Dokka generated docs. I am not seeing this option being advertised by the Gradle or Maven plugins for Dokka.
Is this possible? Can you point me to a sample?

You can set your own footer by overriding the Dokka footer message.
{module}/build.gradle
tasks.named("dokkaHtml").configure {
pluginsMapConfiguration.set(
[
"org.jetbrains.dokka.base.DokkaBase": """{
"footerMessage": "Your New Footer!"
}"""
]
)
}
This will replace the Copyright 20xx in the current footer.
For further details on multi-module / css support, recommend checking out the source below.
Source: Raywenderlich

There are two instance methods in dokka package – one for footer, one for header:
fun appendFooter(to:) { }
fun appendHeader(to:, title:, basePath:) { }
Here's a real code how it looks like:
package org.jetbrains.dokka
import java.io.File
interface HtmlTemplateService {
fun appendHeader(to: StringBuilder, title: String?, basePath: File)
fun appendFooter(to: StringBuilder)
companion object {
fun default(css: String? = null): HtmlTemplateService {
return object : HtmlTemplateService {
override fun appendFooter(to: StringBuilder) {
if (!to.endsWith('\n')) {
to.append('\n')
}
to.appendln("</BODY>")
to.appendln("</HTML>")
}
override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {
to.appendln("<HTML>")
to.appendln("<HEAD>")
to.appendln("<meta charset=\"UTF-8\">")
if (title != null) {
to.appendln("<title>$title</title>")
}
if (css != null) {
val cssPath = basePath.resolve(css)
to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">")
}
to.appendln("</HEAD>")
to.appendln("<BODY>")
}
}
}
}
}
I think it must be working even in dokka.playground.
Hope this helps.

Related

Custom lint rule kotlin object

I'm trying to create a custom lint rule in order to avoid using a kotlin object class.
object AColors {
val white: Color = Color(0xFFFFFFFF)
}
fun main() {
val myColor = AColors.white //Lint error : Do not use AColors
}
How can I manage to get a lint issue when AColors is used?
In my case, AColors can't be private because I need it for a specific case.
I tried to create a custom lint rule to check the import but this is not a bullet-proof solution as you can use AColors without importing anything
class InvalidImportHandler(private val context: JavaContext) : UElementHandler() {
override fun visitImportStatement(node: UImportStatement) {
//check the import
}
}
For this specific case, you may check for USimpleNameReferenceExpressions and then check if the reference is to the AColors class. Like so:
class AColorsReferenceDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes(): List<Class<out UElement>> {
return listOf(USimpleNameReferenceExpression::class.java)
}
override fun createUastHandler(context: JavaContext): UElementHandler {
return object : UElementHandler() {
override fun visitSimpleNameReferenceExpression(
node: USimpleNameReferenceExpression
) {
val element = node.resolve()?.unwrapped
if (element is KtObjectDeclaration && element.name == "AColors") {
context.report(
ISSUE,
node,
context.getLocation(node.uastParent)
)
}
}
}
}
companion object {
val ISSUE = Issue.create(
AColorsReferenceDetector::class.simpleName.orEmpty(),
"Do not use AColors",
"Do not use AColors",
Category.CORRECTNESS,
10,
Severity.ERROR,
Implementation(
AColorsReferenceDetector::class.java,
EnumSet.of(Scope.JAVA_FILE)
)
)
}
}
Example IssueRegistry:
class IssueRegistry : IssueRegistry() {
override val api = CURRENT_API
override val minApi: Int
get() = 8
override val vendor: Vendor = Vendor()
override val issues
get() = listOf(AColorsReferenceDetector.ISSUE)
}
Example result:

Which are the advantatges of using Template in Ktor in HTML DSL?

I've just started learning Ktor, and I really like the use of the HTML DSL. Reading the docs (https://ktor.io/docs/html-dsl.html#templates) I've seen that there is a Templates engine. However, either there is something that I can't see, or I don't find the advantages of using it. Isn't cleaner and easier to use standard Kotlin extension functions, like the Div, P,... instead?
I post an example of how I would write the example on the tutorial using functions (the template example can be found here):
fun Application.configureRouting() {
configureArticleRouting()
routing {
get("/") {
val name = "Ktor"
call.respondHtml(HttpStatusCode.OK) {
mainLayout(title = name){
articleContent(Article("Article Title", "My article content"))
}
}
}
}
}
fun FlowContent.articleContent(article: Article) {
article {
h2 {
+article.title
}
p {
+article.content
}
}
}
fun HTML.mainLayout(title: String, content: FlowContent.() -> Unit) {
head {
title {
+title
}
}
body {
content()
}
}
data class Article(val title: String, val content: String)
The question is, what is the advantage of using templates?
Thanks,

Unresolved reference in Kotlin optic data class reference

Was playing a bit with arrow library for Kotlin and found this error right out of the documentation https://arrow-kt.io/docs/optics/ . What am I doing wrong?
Unresolved reference: company
the code is next, so it is not compiling due to an error in reference
package com.app
import arrow.optics.Optional
import arrow.optics.optics
#optics
data class Street(val number: Int, val name: String) {
companion object
}
#optics
data class Address(val city: String, val street: Street) {
companion object
}
#optics
data class Company(val name: String, val address: Address) {
companion object
}
#optics
data class Employee(val name: String, val company: Company) {
companion object
}
fun main() {
// an immutable value with very nested components
val john = Employee("John Doe", Company("Kategory", Address("Functional city", Street(42, "lambda street"))))
// an Optional points to one place in the value
val optional: Optional<Employee, String> = Employee.company.address.street.name
// and now you can modify into a new copy without nested 'copy's!
optional.modify(john, String::toUpperCase)
}
my dependencies are next
//region Arrow
implementation("io.arrow-kt:arrow-core:$arrow_version")
implementation("io.arrow-kt:arrow-fx-coroutines:$arrow_version")
implementation("io.arrow-kt:arrow-optics:$arrow_version")
//endregion
Your Gradle configuration seems to be missing some of the required Google KSP setup. You can find it in the Arrow Optics Setup section of the website, and below.
plugins {
id("com.google.devtools.ksp") version "$googleKspVersion"
}
dependencies {
ksp("io.arrow-kt:arrow-optics-ksp-plugin:$arrowVersion")
}
You also need to make IDEA aware of the generated sources, or it will not be able to correctly pick up the code for code highlighting and syntax reporting. The setup is explained on the official Kotlin Website, and shown below.
kotlin {
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
sourceSets.test {
kotlin.srcDir("build/generated/ksp/test/kotlin")
}
}
Add the code below (*.gradle.kts) in your build script.
It will register a new source dir so that your IDE will see generated extensions. This works regardless of the number of flavors and build types you have.
android {
// ...
androidComponents.onVariants { variant ->
val name = variant.name
sourceSets {
getByName(name).kotlin.srcDir("${buildDir.absolutePath}/generated/ksp/${name}/kotlin")
}
}
}

Make use of web component library in Kotlin Compose for Web

I want to tinker with Kotlin Compose for Web a bit.
In some of my past web projects, I made use of some web components of the Clarity Design System (CDS).
In a JavaScript or TypeScript project,
you first need to install both npm packages#cds/core and #cds/city.
Secondly, you have to include some global stylesheets, e.g. via HTML or sass-import.
For each component you want to use, you need to import the corresponding register.js.
Lastly, you can include the component in your HTML like any other tag:
<cds-button>Click me!</cds-button>
I tried to replicate the steps with Kotlin Compose for Web, but wasn't able to get it to work.
Any help appreciated!
Okay, I've got it to work now, which included several steps.
Install npm dependencies
kotlin {
...
sourceSets {
val jsMain by getting {
dependencies {
// dependencies for Compose for Web
implementation(compose.web.core)
implementation(compose.runtime)
// dependencies for Clarity Design System
implementation(npm("#cds/core", "5.6.0"))
implementation(npm("#cds/city", "1.1.0"))
// dependency for webpack - see step 3
implementation(npm("file-loader", "6.2.0"))
}
}
...
}
}
Enable css support
This seems to be required, in order to include the global stylesheets.
kotlin {
js(IR) {
browser {
...
commonWebpackConfig {
cssSupport.enabled = true
}
}
...
}
...
}
Add support for .woff2 files included in stylesheet of Clarity
The stylesheet of CDS include font files of type .woff2, whose support in webpack must be configured.
This can be achieved by creating the file webpack.config.d/support-fonts.js
at the project root with the following content:
config.module.rules.push({
test: /\.(woff(2)?|ttf|eot|svg|gif|png|jpe?g)(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}]
});
Include global stylesheets
external fun require(module: String): dynamic
fun main() {
require("modern-normalize/modern-normalize.css")
require("#cds/core/global.min.css")
require("#cds/core/styles/module.shims.min.css")
require("#cds/city/css/bundles/default.min.css")
...
}
Import register.js for desired web component
external fun require(module: String): dynamic
fun main() {
...
require("#cds/core/button/register.js")
...
}
Create #Composable for the web component
Sadly this solution makes use of APIs marked as #OptIn(ComposeWebInternalApi::class),
which stands for "This API is internal and is likely to change in the future".
Any hints on how this may be implemented without relying on internal APIs are appreciated.
#Composable
fun CdsButton(
status: CdsButtonStatus = CdsButtonStatus.Primary,
attrs: AttrBuilderContext<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(
elementBuilder = CdsElementBuilder("cds-button"),
applyAttrs = {
if (attrs != null) apply(attrs)
attr("status", status.attributeValue)
},
content = content
)
/**
* This is a copy of the private class org.jetbrains.compose.web.dom.ElementBuilderImplementation
*/
internal class CdsElementBuilder<TElement : Element>(private val tagName: String) : ElementBuilder<TElement> {
private val element: Element by lazy {
document.createElement(tagName)
}
override fun create(): TElement = element.cloneNode() as TElement
}
sealed interface CdsButtonStatus {
object Primary : CdsButtonStatus
...
}
internal val CdsButtonStatus.attributeValue
get() = when (this) {
CdsButtonStatus.Primary -> "primary"
...
}
Make us of your #Composable!
fun main() {
...
renderComposable(rootElementId = "root") {
CdsButton(
status = CdsButtonStatus.Success
) {
Text("It works! :-)")
}
}
}

How to use Internationalization in tornadofx

I am trying to use internationalization in a Kotlin application using the tornadofx framework.
I have created a properties file and depending on the selected language the correct file is loaded. But when I want to change the language in the running application the UI does not update accordingly.
For internationalization you should use a companion object to get the related translation anywhere in your application.
First of all your translation class should know which is the actual selected language/locale. For this I use an enum with the possible locales for the application:
fun setLocale(locale: SupportedLocale) {
if (SupportedLocale.supportedLocals.contains(locale)) {
Locale.setDefault(locale.local)
actualLocal = locale.local
//Good practice would be to store it in a properties file to have the information after restart
} else {
//Throw a warning or sth with your preferred logger
}
}
Then we need a method which gets the particular string value from your resource bundle like:
operator fun get(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): String {
val bundle = ResourceBundle.getBundle(BUNDLE_NAME, actualLocal)
return MessageFormat.format(bundle.getString(key), *args)
}
In JavaFx applications (also TornadoFX) you should use StringBindings (https://docs.oracle.com/javase/8/javafx/api/javafx/beans/binding/StringBinding.html) for example to bind a label text property to your translated string. For that we will implement a special method:
fun createStringBinding(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): StringBinding {
return Bindings.createStringBinding(Callable { get(key, *args) }, Settings.languageProperty())
}
Now you can use your object like this:
textProperty().bind(MyLang.createStringBinding("MyApp.MyTranslation"))
Here an runnable example:
MyLang.kt
enum class SupportedLocale(val local:Locale) {
ENGLISH(Locale.ENGLISH),
GERMAN(Locale.GERMAN);
companion object {
val supportedLocals: List<SupportedLocale>
get() = SupportedLocale.values().toList()
}
}
class MyLang {
companion object {
private const val BUNDLE_NAME = "Language" //prefix of your resource bundle
private var actualLocal = Locale.getDefault()
fun setLocale(locale: SupportedLocale) {
if (SupportedLocale.supportedLocals.contains(locale)) {
Locale.setDefault(locale.local)
actualLocal = locale.local
//Good practice would be to store it in a properties file to have the information after restart
} else {
//Throw a warning or sth with your preferred logger
}
}
operator fun get(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): String {
val bundle = ResourceBundle.getBundle(BUNDLE_NAME, actualLocal)
return MessageFormat.format(bundle.getString(key), *args)
}
fun createStringBinding(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): StringBinding {
return Bindings.createStringBinding(Callable { get(key, *args) }, Settings.languageProperty())
}
}
}
fun main() {
println("My translation: " + MyLang.createStringBinding("MyApp.MyTranslation").get())
//The get() here is only to get the string for assign a property its not needed like in the example
}
If you need any explanations or its unclear. Just ask! Its just written down maybe I forgot something to explain.