{"id":13610789,"url":"https://github.com/mfwgenerics/kapshot","last_synced_at":"2026-01-14T02:27:40.480Z","repository":{"id":65261578,"uuid":"579957420","full_name":"mfwgenerics/kapshot","owner":"mfwgenerics","description":"Kotlin Compiler Plugin for source capture","archived":false,"fork":false,"pushed_at":"2024-05-27T10:53:28.000Z","size":105,"stargazers_count":76,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T01:33:10.732Z","etag":null,"topics":["kotlin","kotlin-jvm","kotlin-library","kotlin-plugin","literate-programming"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mfwgenerics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-19T11:14:56.000Z","updated_at":"2025-02-08T16:09:37.000Z","dependencies_parsed_at":"2024-08-01T19:44:25.448Z","dependency_job_id":"118a27ac-911a-471a-9041-6a109e7aa150","html_url":"https://github.com/mfwgenerics/kapshot","commit_stats":{"total_commits":22,"total_committers":1,"mean_commits":22.0,"dds":0.0,"last_synced_commit":"3ba8426d3267fe1cc55c133c9285595abd32004a"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mfwgenerics/kapshot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfwgenerics%2Fkapshot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfwgenerics%2Fkapshot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfwgenerics%2Fkapshot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfwgenerics%2Fkapshot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mfwgenerics","download_url":"https://codeload.github.com/mfwgenerics/kapshot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfwgenerics%2Fkapshot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kotlin","kotlin-jvm","kotlin-library","kotlin-plugin","literate-programming"],"created_at":"2024-08-01T19:01:48.009Z","updated_at":"2026-01-14T02:27:40.466Z","avatar_url":"https://github.com/mfwgenerics.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"# Kapshot\n\nKapshot is a simple Kotlin compiler plugin for capturing\nsource code text from closure blocks and declarations.\n\n## Usage\n\nInclude the `io.koalaql.kapshot-plugin` Gradle plugin in your `build.gradle.kts`:\n\n```kotlin\nplugins {\n    /* ... */\n\n    id(\"io.koalaql.kapshot-plugin\") version \"0.2.0\"\n}\n```\n\n### Capturing Blocks\n\nNow your Kotlin code can use `CapturedBlock\u003cT\u003e` as a source enriched replacement for `() -\u003e T`.\nYou can use the `source` property on any instance of\n`CapturedBlock` to access the source for that block.\n\n```kotlin\nimport io.koalaql.kapshot.CapturedBlock\n\nval captured = CapturedBlock {\n    println(\"Hello!\")\n}\n\ncheck(captured.source.text == \"\"\"println(\"Hello!\")\"\"\")\n```\n\nYou can invoke the block similar to a regular function:\n\n```kotlin\nimport io.koalaql.kapshot.CapturedBlock\n\nfun equation(block: CapturedBlock\u003cInt\u003e): String {\n    val result = block() // invoke the block\n\n    return \"${block.source} = $result\"\n}\n\ncheck(equation { 2 + 2 } == \"2 + 2 = 4\")\n```\n\n### Parameterized Blocks\n\nThe default `CapturedBlock` interface doesn't accept any\narguments to `invoke` and is only generic on the return type. This\nmeans the captured source block must depend only on state from the\nenclosing scope. To write source capturing versions of builder blocks\nor common higher-order functions like `map` and `filter` you will\nneed to define your own capture interface that extends `Capturable`.\n\n```kotlin\n/* must be a fun interface to support SAM conversion from blocks */\nfun interface CustomCapturable\u003cT, R\u003e : Capturable\u003cCustomCapturable\u003cT, R\u003e\u003e {\n    /* invoke is not special. this could be any single abstract method */\n    operator fun invoke(arg: T): R\n\n    /* withSource is called by the plugin to add source information */\n    override fun withSource(source: Source): CustomCapturable\u003cT, R\u003e =\n        object : CustomCapturable\u003cT, R\u003e by this { override val source = source }\n}\n```\n\nOnce you have declared your own `Capturable` you can use it\nin a similar way to `CapturedBlock` from above.\n\n```kotlin\nfun \u003cT\u003e List\u003cT\u003e.mapped(block: CustomCapturable\u003cT, T\u003e): String {\n    return \"$this.map { ${block.source} } = ${map { block(it) }}\"\n}\n\ncheck(\n    listOf(1, 2, 3).mapped { x -\u003e x*2 } ==\n    \"[1, 2, 3].map { x -\u003e x*2 } = [2, 4, 6]\"\n)\n```\n\nIf it is present, the block's argument list is considered part of its source text.\n\n### Declarations\n\nYou can capture declaration sources using the `@CaptureSource`\nannotation. The source of annotated declarations can then be retrieved using\n`sourceOf\u003cT\u003e` for class declarations or `sourceOf(::declaration)` for method and property\ndeclarations. The source capture starts at the end of the `@CaptureSource`\nannotation.\n\n```kotlin\n@CaptureSource\nclass MyClass {\n    @CaptureSource\n    fun twelve() = 12\n}\n\ncheck(\n    sourceOf\u003cMyClass\u003e().text ==\n    \"\"\"\n    class MyClass {\n        @CaptureSource\n        fun twelve() = 12\n    }\n    \"\"\".trimIndent()\n)\n\ncheck(\n    sourceOf(MyClass::twelve).text ==\n    \"fun twelve() = 12\"\n)\n```\n\n### Source Location\n\nThe `Source::location` property\ncontains information about the location of captured source code\nincluding the file path (relative to the project root directory)\nand the char, line and column offsets for both the start\nand end of the captured source. Offsets are 0-indexed.\n\n```kotlin\nval source = CapturedBlock { 2 + 2 }.source\nval location = source.location\n\nprintln(\n    \"`${source.text}`\"\n    + \" found in ${location.path}\"\n    + \" @ line ${location.from.line+1}\"\n)\n```\n\nThe code above will print the following:\n\n```\n`2 + 2` found in src/main/kotlin/Main.kt @ line 176\n```\n\n## Purpose\n\nThe purpose of this plugin is to support experimental literate\nprogramming and documentation generation techniques in Kotlin\n\nAn example of this is the code used to generate this README.md.\nCapturing source from blocks allows sample code to be run and\ntested during generation.\n\nView the source here: [readme/src/main/kotlin/Main.kt](readme/src/main/kotlin/Main.kt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfwgenerics%2Fkapshot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmfwgenerics%2Fkapshot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfwgenerics%2Fkapshot/lists"}