# Hello & a Gradle integration plugin for Kakoune

**URL:** <https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214>\
**Category:** Plugins\
**Created:** [August 16, 2020, 9:56pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214 "2020-08-16T21:56:24Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [August 16, 2020, 9:56pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/1 "2020-08-16T21:56:25Z")

</div>

Since discovering and switching to Kakoune from NeoVim about 2 months ago, I have noticed that Kakoune’s extension model is so much simpler than Vim’s. The complexity of VimL put me off attempting to implement any sort of plugins in Vim, but since Kakoune has a simpler extension model, I decided to give it a shot.

[This](https://github.com/jdugan6240/kak-gradle.git) is one of my first attempts at creating a Kakoune plugin - a plugin that integrates the Gradle build system into Kakoune, so gradle tasks can be run without having to leave Kakoune. It’s essentially a glorified wrapper for gradle that runs gradle tasks using the `:terminal` command, apart from the `gradle-tasks` command that shows the available Gradle tasks in a Kakoune buffer, so the user can press Enter/Return to execute the desired task.

Again, this is one of my first attempts to create a Kakoune plugin, so there’s likely room for improvement. Any feedback would be much appreciated! 🙂

---

<div class="post-metadata">

**Author:** ![robertmeta](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/robertmeta/32/833_2.png) [@robertmeta](https://discuss.kakoune.com/u/robertmeta)\
**Post date:** [August 17, 2020, 6:38pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/2 "2020-08-17T18:38:52Z")

</div>

Oh, I have a very complex gradle app I constantly work with in AndroidStudio – will be great to get that out into Kakoune. I will be testing this ASAP.

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [February 15, 2021, 9:30am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/3 "2021-02-15T09:30:50Z")

</div>

Hi jdugan,  
I also use ‘gradle’ as my build tool and like your plugin.

_‘Suggestion Box Time’_:

- a flag or bool to use a projects `./gradlew` instead of the system-wide `gradle` command.
- ability to verify gradle-wrapper.jar or grab one with additional positional arguments (see my script below).

* * *

> **verify-gradle-wrapper.zsh**
>
> ```auto
> #! /usr/bin/env zsh
> # ----------------------------------------------------------------------------------------------------------- #
> # Version control repositories that allow users to include binary files:
> # gradle/wrapper/gradle-wrapper.jar
> #
> # Gradle 2021, Verifying the integrity of the Gradle Wrapper JAR, The Gradle Wrapper, viewed 04 February 2021,
> # <https://docs.gradle.org/current/userguide/gradle_wrapper.html#wrapper_checksum_verification>
> # ----------------------------------------------------------------------------------------------------------- #
> function print_help(){
> cat << EOF
> 
> Usage: ${${(%):-%x}:t} [-h|-help|--help]|[[-g|-get|--get] <version> <checksum>]|[[-c|-check|--check] <version>]
> 
> -g, -get, --get
> 
> VERSION="\$2" CHECKSUM="\$3"
> 
> $ gradle wrapper \\
> --gradle-version "\$VERSION" \\
> --distribution-type all \\
> --gradle-distribution-sha256-sum "\$CHECKSUM"
> 
> # ------------------------------------------------------------------- #
> Gradle 2021, Gradle distribution and wrapper JAR checksum reference,
> viewed 04 February 2021, <https://gradle.org/release-checksums/>
> # ------------------------------------------------------------------- #
> Integrated Development Environment (IDE) pulls:
> Complete (-all|-bin) ZIP Checksum
> gradle/wrapper/gradle.wrapper.properties
> # ------------------------------------------------------------------- #
> Command Line Interface (CLI) pulls:
> Wrapper JAR Checksum
> $ ./gradlew tasks
> # ------------------------------------------------------------------- #
> 
> -c, -check, --check
> 
> VERSION="\$2"
> 
> $ cd \$PWD/gradle/wrapper
> 
> $ curl --fail --no-progress-meter --show-error --location \\
> --output gradle-wrapper.jar.sha256 \\
> https://services.gradle.org/distributions/gradle-"\$VERSION"-wrapper.jar.sha256
> 
> $ echo " gradle-wrapper.jar" >> gradle-wrapper.jar.sha256
> $ sha256sum --check gradle-wrapper.jar.sha256
> 
> EOF
> }
> # -------------------------------------------------------------------------------------------------- #
> if [$# -eq 0]; then print_help; exit 1; fi
> # -------------------------------------------------------------------------------------------------- #
> function check_dependency() {
> if ! (builtin command -V "$1" > /dev/null 2>& 1); then
> echo "ERROR: missing dependency can't find $1" 1>& 2
> exit 1
> fi
> }
> # -------------------------------------------------------------------------------------------------- #
> function curl_check_gradle_wrapper() {
> local version="$1"
> # ------------------------------------------------------------------------------------------------ #
> cd gradle/wrapper || { echo "cd: no such file or directory: gradle/wrapper"; exit 1; }
> # ------------------------------------------------------------------------------------------------ #
> echo "Downloading $version of gradle-wrapper.jar.sha256..."
> # ------------------------------------------------------------------------------------------------ #
> curl --fail --no-progress-meter \
> --show-error --location \
> --output gradle-wrapper.jar.sha256 \
> https://services.gradle.org/distributions/gradle-"$version"-wrapper.jar.sha256
> }
> # -------------------------------------------------------------------------------------------------- #
> function gradle_get_wrapper() {
> local version="$1"
> local checksum="$2"
> # ------------------------------------------------------------------------------------------------ #
> echo "Wrapper JAR Checksum: $checksum"
> echo "Downloading $version of gradle-wrapper.jar..."
> # ------------------------------------------------------------------------------------------------ #
> gradle wrapper \
> --gradle-version "$version" \
> --distribution-type all \
> --gradle-distribution-sha256-sum "$checksum"
> }
> # -------------------------------------------------------------------------------------------------- #
> function shasum_gradle_wrapper() {
> echo "checking sha256 of gradle-wrapper.jar..."
> # ------------------------------------------------------------------------------------------------ #
> echo " gradle-wrapper.jar" >> gradle-wrapper.jar.sha256
> # ------------------------------------------------------------------------------------------------ #
> sha256sum --check gradle-wrapper.jar.sha256
> # ------------------------------------------------------------------------------------------------ #
> SHA256=$(cat gradle-wrapper.jar.sha256)
> SHA256SUM=$(sha256sum gradle-wrapper.jar)
> # ------------------------------------------------------------------------------------------------ #
> printf %s%s\\n "1) sha256: " "$SHA256"
> printf %s%s\\n "2) sha256sum: " "$SHA256SUM"
> # ------------------------------------------------------------------------------------------------ #
> rm gradle-wrapper.jar.sha256
> }
> # -------------------------------------------------------------------------------------------------- #
> case "$1" in
> -h | -help | --help | -\? )
> print_help >&2
> ;;
> -g | -get | --get )
> if ["$2"] && ["$3"]; then
> printf %s\\n "running: get gradle wrapper" >&2
> check_dependency gradle
> gradle_get_wrapper "$2" "$3"
> else
> printf %s\\n "Option requires two arguments: $1 <version> <checksum>" >&2
> fi
> ;;
> -c | -check | --check )
> if ["$2"]; then
> printf %s\\n "running: curl check gradle wrapper" >&2
> check_dependency curl
> check_dependency sha256sum
> curl_check_gradle_wrapper "$2"
> shasum_gradle_wrapper
> else
> printf %s\\n "Option requires an argument: $1 <version>" >&2
> fi
> ;;
> *)
> printf %s\\n "Option $1 invalid argument." >&2
> exit 1
> ;;
> esac
> # -------------------------------------------------------------------------------------------------- #
> exit $?
> 
> ```

* * *

No, I have no idea how to implement this either in kak. But I do plan to tackle it. If you get there first let me know and I’ll do like wise. Bye 👋

* * *

* * *

TODO:  
upgrade the gradle-wrapper.jar  
`$./gradlew wrapper --gradle-version VERSION --distribution-type TYPE --gradle-distribution-sha256-sum CHECKSUM`

Thinking out loud with no research done. Could we use .editorconfig or .git to find the root directories. Maybe, maybe not duncan. Why don’t you go and do some frig’n research duncan. Your a hard man jdugan, I’ll be back with answers 😅

* * *

A bit of research and something to dwell on.

Lenormf has _‘kakmerge’_ which is a great example of a shell script interfacing with a local binary and kakoune. Likewise a Gradle integration with the shell could be implemented via a _‘gradle task’_ from a _‘build.gradle’_ file similar to Lenormf and your `gradle_wrap.sh` does with kakoune. See example below of a Gradle task with shell script.

This could potentially work for a verify-gradle-wrapper.sh task.

> **login.sh**
>
> ```auto
> #!/bin/bash
> 
> echo Enter username:
> read username
> echo Enter password:
> if [-t 0] ; then # if connected to a terminal, do not echo the password input
> stty -echo
> read password
> stty echo
> echo
> else
> read password
> fi
> 
> if ["$username" = "secret-user"] && ["$password" = "secret-password"] ; then
> echo "Welcome, $username!"
> else
> echo "Bad credentials!"
> exit 1
> fi
> 
> ```

> **build.gradle**
>
> ```auto
> def login = tasks.register('login', Exec) {
> def loginProvider = providers.credentials(PasswordCredentials, 'login')
> inputs.property('credentials', loginProvider)
> 
> commandLine = ['sh', 'login.sh']
> doFirst {
> def loginCredentials = loginProvider.get()
> standardInput = new ByteArrayInputStream("$loginCredentials.username\n$loginCredentials.password".getBytes())
> }
> }
> 
> tasks.register('doAuthenticated') {
> dependsOn(login)
> doLast {
> println 'Doing authenticated task'
> }
> }
> 
> ```

Finally, the gradle-completion repository states: ‘[The] `./gradlew` may not work on Linux if you don’t have `.` on your $PATH, so I recommend adding it in your [kakoune script] file: `export PATH=".:$PATH"`’. Setting a simple environment variable inside kak-gradle could be the answer.

Lenormf 2021, _‘kakmerge: merge tool for git’_, Github, viewed 26 February 2021, [GitHub - lenormf/kakmerge: A Kakoune-based mergetool for Git](https://github.com/lenormf/kakmerge)

Gradle 2021, _‘Supply credentials to external tool Sample’_, Gradle v6.8.3, viewed 26 February 2021, [Supply credentials to external tool Sample](https://docs.gradle.org/current/samples/sample_credentials_for_external_tool_via_stdin.html)

Gradle 2021, _‘Troubleshooting’_, Gradle Completion, viewed 26 February 2021, [GitHub - gradle/gradle-completion: Gradle tab completion for bash and zsh](https://github.com/gradle/gradle-completion#troubleshooting)

NOTE Gradle also does: C++, Swift, etc…

Gradle 2021, Sample Index, Gradle v6.8.3, viewed 26 February 2021, [Sample Index](https://docs.gradle.org/current/samples/index.html)

* * *

I’m going to see if I can get the task working with the verify script in the next week or so. Cya.

* * *

> [@jdugan6240](#):
>
> Feel free to try it out and see if there’s anything else that you’d like to suggest.

Perfect timing jdugan, I’m getting back into the projects and hitting the gradle manual for custom task creation. Definitely will give you some feedback based on my usage.

Cool buddy, bye 👋

> **build.gradle.kts - jlink task**
>
> ```auto
> /* ilya-g 2020, build.gradle.kts, kotlin-jlink-examples, gradle, viewed 01 March 2021,
> * https://github.com/ilya-g/kotlin-jlink-examples/blob/master/gradle/app/build.gradle.kts
> */
> register<Exec>("jlink") {
> description = "Build kowsky.kak module jar with an optimised custom runtime image"
> val outputDir by extra("$buildDir/jrt-kowsky-kak")
> inputs.files(configurations.runtimeClasspath)
> inputs.files(jar)
> outputs.dir(outputDir)
> doFirst {
> val modulePath = files(jar) + configurations.runtimeClasspath.get()
> logger.lifecycle(modulePath.joinToString("\n", "jlink module path:\n"))
> delete(outputDir)
> commandLine("$javaHome/bin/jlink",
> "--module-path",
> listOf("$javaHome/jmods/", modulePath.asPath).joinToString(File.pathSeparator),
> "--add-modules", moduleName,
> "--output", outputDir
> )
> }
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [February 15, 2021, 3:43pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/4 "2021-02-15T15:43:04Z")

</div>

Those both sound like great suggestions. I think a boolean option called “gradle-use-gradlew” would satisfy the first, although the “root” of the project will likely need to be found (AKA a directory with build.gradle in it), so the ./gradlew executable can be found.

As for the second - I’m going to need to play around with your script to see exactly how it works.

Edit: just noticed your edit. I wouldn’t rely on using the .git directory to find the project root, since a different version control system may be used, such as Mercurial. Good idea though - keep 'em coming… 🙂

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [February 27, 2021, 11:14pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/5 "2021-02-27T23:14:33Z")

</div>

I finally was able to find time to sit down and implement some of these ideas.

kak-gradle now has a `gradle_use_gradlew` option, which if set to `true` will cause kak-gradle to attempt to use the project’s wrapper to perform gradle operations instead of the systemwide installation. This doesn’t seem to support gradle subprojects yet, since the way the ‘find the root directory’ algorithm works is rather crude.

Feel free to try it out and see if there’s anything else that you’d like to suggest.

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [March 4, 2021, 1:36pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/6 "2021-03-04T13:36:53Z")

</div>

Hey jdugan,  
been working on `kak-gradle.kak` just a refactor and some idea’s thrown in, it’s not finished or finalised.

Take a look, tell me what you like, what you don’t like, and any ideas you might have.

It’s an public _‘snippet’_ repository under my account. If any problems just let me know. Talk later, bye 👋

#### SNIPPET:

`kak-gradle-WIP.sh`

Change file extension: \*.sh → \*.kak

`git clone https://KJ_Duncan@bitbucket.org/snippets/KJ_Duncan/kxpAbn/jdugan6240-kak-gradle-wipkak.git`

> **kak-gradle-WIP.(sh|kak)**
>
> ```shell
> # References:
> # -----------
> # Alexherbo2 2020, plug.kak, require-module, ModuleLoaded, viewed 04 March 2021, https://github.com/alexherbo2/plug.kak/blob/master/rc/plug.kak
> # Gradle 2020, Always define a settings file, Organizing Gradle Projects, Authoring Gradle Builds, Gradle User Manual, v6.8.3, p.378, viewed 04 March 2021,
> # Gradle-Completion 2020, _gradle-set-project-root-dir, _gradle, viewed 04 March 2021, https://github.com/gradle/gradle-completion/blob/master/_gradle#L3
> # Kakoune 2020, git.kak, mawww/kakaoune/rc/tools, viewed 04 March 2021, https://github.com/mawww/kakoune/blob/master/rc/tools/git.kak
> #
> # SOURCE FILE:
> # ------------
> # JDugan6240 2021, kak-gradle, rc/kak-gradle.kak, viewed 04 March 2021, https://github.com/jdugan6240/kak-gradle
> #
> # SNIPPET:
> # --------
> # kak-gradle-WIP.sh
> # git clone https://KJ_Duncan@bitbucket.org/snippets/KJ_Duncan/kxpAbn/jdugan6240-kak-gradle-wipkak.git
> #
> provide-module gradle-build-tool %{
> 
> declare-option -hidden str gradle_system %sh{ printf "%s/../src/%s" "${kak_source%/*}" "gradle_system.sh" }
> declare-option -hidden str gradlew_wrapper %sh{ printf "%s/../src/%s" "${kak_source%/*}" "gradlew_wrapper.sh" }
> 
> declare-option -hidden str gradle_build_root_dir "nay"
> 
> declare-option global str gradle_command ""
> declare-option global str gradle_wrapper_command ""
> # ------------------------------------------------------------------------------------------------- #
> declare-option -docstring %{
> Determines whether to use the project's gradle
> wrapper or the system wide gradle installation
> } bool use_gradlew false
> # ------------------------------------------------------------------------------------------------- #
> # Enable the highlighters for the gradle-tasks filetype
> hook -group gradle-tasks-syntax global WinSetOption filetype=gradle-tasks %{
> add-highlighter buffer/gradle_tasks_buffer ref gradle_tasks_buffer
> hook -always -once window WinSetOption filetype=.* %{
> remove-highlighter buffer/gradle_tasks_buffer
> }
> }
> # ------------------------------------------------------------------------------------------------- #
> # Enable the highlighters for the gradle-deps filetype
> hook -group gradle-deps-syntax global WinSetOption filetype=gradle-deps %{
> add-highlighter buffer/gradle_dep_buffer ref gradle_dep_buffer
> hook -always -once window WinSetOption filetype=.* %{
> remove-highlighter buffer/gradle_dep_buffer
> }
> }
> # ------------------------------------------------------------------------------------------------- #
> # Declare highlighters for the tasks buffer
> add-highlighter global/gradle_tasks_buffer group
> add-highlighter global/gradle_tasks_buffer/task regex "(^[a-zA-Z0-9]*)( - [^\n]*)" 0:string 1:type
> 
> # Declare highlighters for the dependencies buffer
> add-highlighter global/gradle_dep_buffer group
> add-highlighter global/gradle_dep_buffer/category regex "(^[a-zA-Z0-9]*)( - [^\n]*)" 0:string 1:type
> add-highlighter global/gradle_dep_buffer/dependency regex "([+\\]---)( [^\n]*)" 0:keyword 1:string
> add-highlighter global/gradle_dep_buffer/symbol regex "(\|)" 0:string
> add-highlighter global/gradle_dep_buffer/no_deps regex "^No dependencies$" 0:keyword
> add-highlighter global/gradle_dep_buffer/legend regex "(\([*n]\))( [^\n]*)" 0:string 1:type
> # ------------------------------------------------------------------------------------------------ #
> # ------------------------------------------------------------------------------------------------ #
> # ENTRY POINT FOR KAK-GRADLE?
> # A USE-CASE FOR THE USER KNOWS BEST:
> # define-command -menu %{ }
> # case "$1" in
> # reckless) recursively-set-project-root-dir ;;
> # cautious) set-project-root-dir ;;
> # *) "occupational health and safety" ;;
> # esac
> #
> # If we found a (build|settings).gradle file, then we found the gradle root directory
> define-command -hidden gradle-build-root-dir %{ evaluate-commands %sh{
> 
> # NOTE: uncontrolled recursion from leaf or node to users top level directory,
> # kakoune does not enforce a directory ceiling/floor,
> # allot of information on that path and no guarantees.
> #
> # .
> # ├── gradle.properties
> # └── settings.gradle
> # ├── subproject-a
> # │ └── build.gradle
> # └── subproject-b
> # └──build.gradle
> #
> # ------------------------------------------------------------------------------------------------ #
> function recursively-set-project-root-dir() {
> local dir=`pwd`
> project_root_dir=`pwd`
> while [[$dir != '/']]; do
> if [[-f "$dir/settings.gradle" || -f "$dir/settings.gradle.kts" || -f "$dir/gradlew"]]; then
> project_root_dir=$dir
> return 0
> fi
> dir="$(dirname "$dir")"
> done
> return 1
> }
> # ------------------------------------------------------------------------------------------------ #
> function set-project-root-dir() {
> out=$(find -P -mount -maxdepth 1 -type f -regex '^.*/build.gradle[.kts]*?' | sed 's/^.\///')
> 
> if [["$out" == *"build.gradle"*]]; then
> cur_dir=$(realpath $(dirname "$kak_source"))
> printf "set-option global gradle_build_root_dir %s\n" "$cur_dir"
> fi
> }
> 
> }}
> # ------------------------------------------------------------------------------------------------- #
> # Determine if we need to use the gradle wrapper, or use the systemwide gradle command
> define-command -hidden -docstring "variable initialisation on ModuleLoad" \
> gradle-module-initialisation %{ try %{
> gradle-build-root-dir
> evaluate-commands %opt{gradle-build-root-dir}
> try %{
> evaluate-commands %opt{use_gradlew}
> set-option global gradle_wrapper_command %opt{gradle_build_root_dir}
> set-option -add global gradle_wrapper_command "/gradlew"
> set-option global gradle_command "gradlew"
> } catch %{
> set-option global gradle_command "gradle"
> }
> } catch %{
> fail "No build.gradle(.kts) file found in working directory"
> }
> }
> # ------------------------------------------------------------------------------------------------- #
> define-command -docstring "Execute arbitrary gradle command" \
> -params .. gradle %{ evaluate-commands %sh{
> # Determine if we need to use the gradle wrapper, or use the systemwide gradle command
> if [["$kak_opt_use_gradlew" = "true"]]; then
> echo "terminal ${kak_opt_gradlew_wrapper} $kak_opt_gradle_build_root_dir $@"
> else
> echo "terminal ${kak_opt_gradle_system} $@"
> fi
> }
> }
> # ------------------------------------------------------------------------------------------------- #
> # +-------------------------------------------+
> # | DISTRIBUTION URL |
> # +----------+---------+---------+------------+
> # | Constant | Version | Type | Extension |
> # +----------+---------+---------+------------+
> # | gradle 6.8.3 wrapper jar.sha256 |
> # | gradle 6.8.3 docs zip |
> # | gradle 6.8.3 docs zip.sha256 |
> # | gradle 6.8.3 src zip |
> # | gradle 6.8.3 src zip.sha256 |
> # | gradle 6.8.3 bin zip |
> # | gradle 6.8.3 bin zip.sha256 |
> # | gradle 6.8.3 all zip |
> # | gradle 6.8.3 all zip.sha256 |
> # +-------------------------------------------+----------------------------------------------------- #
> define-command --params 1.. gradle-commands %{ evaluate-commands %sh{
> # ------------------------------------------------------------------------------------------------ #
> # HELPER FUNCTION -------------------------------------------------------------------------------- #
> function make_named_pipe() {
> # directory creation MODE: 100700
> tmp=$(mktemp -d "${TMPDIR:-/tmp}/kak-gradle.XXXXXXXX")
> fifo=$tmp/fifo
> # MODE: rw-r-00640, rw-r-r-00644, rw-rw-00660, rw-rw-r-00664
> mkfifo --mode 00640 ${fifo}
> 
> # TEST: does this work?
> printf %s\n "evaluate-commands -client "$kak_client" %{
> hook -always -once buffer BufCloseFifo .* %{ nop %sh{ rm -rf ${tmp} } }
> }"
> 
> return ${fifo}
> }
> # ------------------------------------------------------------------------------------------------ #
> # CONSUMER FUNCTIONS ----------------------------------------------------------------------------- #
> function consume_gradle_init() {
> }
> # ------------------------------------------------------------------------------------------------ #
> function consume_gradle_wrapper() {
> # TODO: make it work with positional arguments
> version="$1"
> type=""
> case "$2" in
> "all") type="all" ;;
> "bin") type="bin" ;;
> "docs") type="docs" ;;
> "src") type="src" ;;
> "wrapper") type="wrapper" ;;
> *) ;;
> esac
> 
> distributionUrlZip="https://services.gradle.org/distributions/gradle-${version}-${type}.zip"
> distributionUrlZipSha256="https://services.gradle.org/distributions/gradle-${version}-${type}.zip.sha256"
> distributionUrlJarSha256="https://services.gradle.org/distributions/gradle-${version}-${type}.jar.sha256"
> }
> # ------------------------------------------------------------------------------------------------ #
> # PRODUCER FUNCTIONS ----------------------------------------------------------------------------- #
> function show_gradle_task_custom() {
> }
> # ------------------------------------------------------------------------------------------------ #
> function show_gradle_test() {
> }
> # ------------------------------------------------------------------------------------------------ #
> function show_gradle_projects() {
> # TEST: info -title
> projects=$($kak_opt_gradle_command projects | grep -E "Root project[^\n]+|Project")
> printf %s\n%s\n "info -title \"Subprojects\"" "${projects}"
> }
> # ------------------------------------------------------------------------------------------------ #
> function show_gradle_task_help() {
> pipe=$(make_named_pipe)
> # ---------------------------------------------------------------------------------------------- #
> # Run "gradle help --task <taskname>" in the background and extract strictly the task help
> # FIXME: grep regex
> ( $kak_opt_gradle_command help --task "$@" | grep '^[a-zA-Z0-9]* -' > ${pipe} 2>&1 & ) > /dev/null 2>&1 < /dev/null
> # ---------------------------------------------------------------------------------------------- #
> printf %s "evaluate-commands -try-client '$kak_opt_docsclient' %{
> edit! -fifo ${pipe} *gradle*
> set-option buffer filetype gradle-task-help
> }"
> }
> # ------------------------------------------------------------------------------------------------ #
> function show_gradle_tasks() {
> pipe=$(make_named_pipe)
> # ---------------------------------------------------------------------------------------------- #
> # Run "gradle tasks" in the background and extract strictly the task names
> ( $kak_opt_gradle_command tasks | grep '^[a-zA-Z0-9]* -' > ${pipe} 2>&1 & ) > /dev/null 2>&1 < /dev/null
> # ---------------------------------------------------------------------------------------------- #
> printf %s "evaluate-commands -try-client '$kak_opt_docsclient' %{
> edit! -fifo ${pipe} *gradle*
> set-option buffer filetype gradle-tasks
> map buffer normal '<ret>' ':<space>gradle-fifo-operate<ret>'
> }"
> }
> # ------------------------------------------------------------------------------------------------ #
> function show_gradle_dependencies() {
> pipe=$(make_named_pipe)
> # ---------------------------------------------------------------------------------------------- #
> # Run "gradle dependencies" in the background and extract the dependencies and "legend"
> ( $kak_opt_gradle_command dependencies | grep -E '^[a-zA-Z0-9]* -|[+\]--- |No dependencies|\([*n]\)|^$' > ${pipe} 2>&1 & ) > /dev/null 2>&1 < /dev/null
> # ---------------------------------------------------------------------------------------------- #
> printf %s "evaluate-commands -try-client '$kak_opt_docsclient' %{
> edit! -fifo ${pipe} *gradle*
> set-option buffer filetype gradle-deps
> }"
> }
> # ------------------------------------------------------------------------------------------------ #
> # GRADLE CONSTANTS DECISION TREE ----------------------------------------------------------------- #
> case "$1" in
> init) consume_gradle_init "$@" ;;
> wrapper) consume_gradle_wrapper "$@" ;;
> dependencies) show_gradle_dependencies "$@" ;;
> custom) show_gradle_task_custom "$@" ;;
> help) show_gradle_task_help "$@" ;;
> tasks) show_gradle_tasks "$@" ;;
> test) show_gradle_test "$@" ;;
> esac
> }}
> # ------------------------------------------------------------------------------------------------- #
> define-command -hidden gradle-fifo-operate %{ evaluate-commands -save-regs t %{
> execute-keys -save-regs '' "ghw"
> set-register t %val{selection}
> evaluate-commands %sh{
> task="${kak_reg_t%:*}"
> echo "terminal ${kak_opt_gradle_system} $task"
> }
> }}
> }
> # ------------------------------------------------------------------------------------------------- #
> 
> ```

* * *

All fair comments,  
I should have clearly stated to view the file as _‘template only’_ it is a none functioning script implementation. The acronym WIP - Work In Progress, was an assumption on my part.

Back to the discussion, we are looking at the file’s (kak-gradle.kak) code base from differing points off view. Which is great!

Let me explain, as Gradle provides like `git` does, refer to `git.kak`, a bunch of utility constants. These constants provide a single point of entry/maintenance. I look at `kak-gradle.kak` and see duplication of logic that can be abstracted into a single point, the case statement (decision tree).

As with the previous paragraph the `make_named_pipe` function also provides an easily maintainable call site as there is no code duplication.

The `%sh{}` is called multiple times in `kak-gradle.kak`, for me this means it needs to be only called once, hence the decision tree rests inside this single `%sh{}` call. Again for me this provides a flaunt code base for maintenance, extension, and contraction. A requirement in response to kakoune’s awesome pace of continuous development.

Next, kak’s Module system provides lazy loading so when the user sparks up a kak session the file (kak-gradle.kak) is not loaded each and every time, only on upon the users request.

`define-command -menu` is something that I have never explored. Also I’ve just run out off things to say.

Till next time, thanks for you feedback and I will work out what the hell I was thinking with the below method call.

> [@jdugan6240](#):
>
> `gradle-module-initialisation`

It had a purpose, perhaps this was it. FIXME.

Bye 👋

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [March 4, 2021, 4:15pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/7 "2021-03-04T16:15:14Z")

</div>

I don’t have a lot of time to look at this right now, so here are my initial thoughts:

- The `function` construct doesn’t appear to be POSIX-compliant. This is a minor nitpick, but one that may prevent the plugin from working for some.
- I really like the direction you chose to implement the “find the root directory” functionality. That’s a really elegant way to go about it.
- I’m really not a fan of the `gradle-commands` command as you implemented it.Using a case statement to select between all of these shell functions seems a bit over-engineered, and this could just as easily have been implemented as Kakoune commands, negating the need for the case statement entirely.

Those are my initial thoughts - I’ll have more when I come back from work.

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [March 5, 2021, 2:08am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/8 "2021-03-05T02:08:28Z")

</div>

OK, now that I’ve had a better chance to look at it, here’s my full review:

```auto
provide-module gradle-build-tool %{

```

To be honest, I’m not sure this plugin makes sense as a module. The only thing this enables is running a hook on module load, which I don’t think this plugin needs. I’m certainly not opposed to the idea, but I don’t see any real benefit to wrapping this inside a module and requiring a `require-module` call.

```auto
define-command -hidden gradle-build-root-dir %{ evaluate-commands %sh{

    # NOTE: uncontrolled recursion from leaf or node to users top level directory,
    # kakoune does not enforce a directory ceiling/floor,
    # allot of information on that path and no guarantees.
    #
    # .
    # ├── gradle.properties
    # └── settings.gradle
    # ├── subproject-a
    # │ └── build.gradle
    # └── subproject-b
    # └──build.gradle
    #
    # ------------------------------------------------------------------------------------------------ #
    function recursively-set-project-root-dir() {
      local dir=`pwd`
      project_root_dir=`pwd`
      while [[$dir != '/']]; do
        if [[-f "$dir/settings.gradle" || -f "$dir/settings.gradle.kts" || -f "$dir/gradlew"]]; then
          project_root_dir=$dir
          return 0
        fi
        dir="$(dirname "$dir")"
      done
      return 1
    }
    # ------------------------------------------------------------------------------------------------ #
    function set-project-root-dir() {
      out=$(find -P -mount -maxdepth 1 -type f -regex '^.*/build.gradle[.kts]*?' | sed 's/^.\///')

      if [["$out" == *"build.gradle"*]]; then
        cur_dir=$(realpath $(dirname "$kak_source"))
        printf "set-option global gradle_build_root_dir %s\n" "$cur_dir"
      fi
    }

  }}

```

Nice job on this - this seems to be a great way to approach the “find the root directory” problem. This currently doesn’t do anything when the `gradle-build-root-dir` command is called, though, since none of the shell functions you define are actually called anywhere. Also, the `function` and `return` keywords appear to be zsh-only as far as I can tell, so the function declarations and return command would need to be changed to adhere to POSIX standards (not everyone uses zsh).

```auto
define-command -hidden -docstring "variable initialisation on ModuleLoad" \
   gradle-module-initialisation %{ try %{
     gradle-build-root-dir
     evaluate-commands %opt{gradle-build-root-dir}
     try %{
       evaluate-commands %opt{use_gradlew}
       set-option global gradle_wrapper_command %opt{gradle_build_root_dir}
       set-option -add global gradle_wrapper_command "/gradlew"
       set-option global gradle_command "gradlew"
     } catch %{
       set-option global gradle_command "gradle"
     }
   } catch %{
     fail "No build.gradle(.kts) file found in working directory"
   }
 }

```

Assuming this is meant to be called when the module gets loaded, this will call `gradle-build-root-dir` whenever the module is loaded, regardless of if the user plans on using the gradle wrapper or the systemwide install. The root directory isn’t needed when the systemwide install is used, so computing it is useless in that case.

```auto
define-command --params 1.. gradle-commands %{ evaluate-commands %sh{
    # ------------------------------------------------------------------------------------------------ #
    # HELPER FUNCTION -------------------------------------------------------------------------------- #
    function make_named_pipe() {
      # directory creation MODE: 100700
      tmp=$(mktemp -d "${TMPDIR:-/tmp}/kak-gradle.XXXXXXXX")
      fifo=$tmp/fifo
      # MODE: rw-r-00640, rw-r-r-00644, rw-rw-00660, rw-rw-r-00664
      mkfifo --mode 00640 ${fifo}

      # TEST: does this work?
      printf %s\n "evaluate-commands -client "$kak_client" %{
                    hook -always -once buffer BufCloseFifo .* %{ nop %sh{ rm -rf ${tmp} } }
                  }"

      return ${fifo}
    }
    # ------------------------------------------------------------------------------------------------ #
    # CONSUMER FUNCTIONS ----------------------------------------------------------------------------- #
    function consume_gradle_init() {
    }
    # ------------------------------------------------------------------------------------------------ #
    function consume_gradle_wrapper() {
      # TODO: make it work with positional arguments
      version="$1"
      type=""
      case "$2" in
        "all") type="all" ;;
        "bin") type="bin" ;;
        "docs") type="docs" ;;
        "src") type="src" ;;
        "wrapper") type="wrapper" ;;
        *) ;;
      esac

      distributionUrlZip="https://services.gradle.org/distributions/gradle-${version}-${type}.zip"
      distributionUrlZipSha256="https://services.gradle.org/distributions/gradle-${version}-${type}.zip.sha256"
      distributionUrlJarSha256="https://services.gradle.org/distributions/gradle-${version}-${type}.jar.sha256"
    }
    # ------------------------------------------------------------------------------------------------ #
    # PRODUCER FUNCTIONS ----------------------------------------------------------------------------- #
    function show_gradle_task_custom() {
    }
    # ------------------------------------------------------------------------------------------------ #
    function show_gradle_test() {
    }
    # ------------------------------------------------------------------------------------------------ #
    function show_gradle_projects() {
      # TEST: info -title
      projects=$($kak_opt_gradle_command projects | grep -E "Root project[^\n]+|Project")
      printf %s\n%s\n "info -title \"Subprojects\"" "${projects}"
    }
    # ------------------------------------------------------------------------------------------------ #
    function show_gradle_task_help() {
      pipe=$(make_named_pipe)
      # ---------------------------------------------------------------------------------------------- #
      # Run "gradle help --task <taskname>" in the background and extract strictly the task help
      # FIXME: grep regex
      ( $kak_opt_gradle_command help --task "$@" | grep '^[a-zA-Z0-9]* -' > ${pipe} 2>&1 & ) > /dev/null 2>&1 < /dev/null
      # ---------------------------------------------------------------------------------------------- #
      printf %s "evaluate-commands -try-client '$kak_opt_docsclient' %{
                  edit! -fifo ${pipe} *gradle*
                  set-option buffer filetype gradle-task-help
                }"
    }
    # ------------------------------------------------------------------------------------------------ #
    function show_gradle_tasks() {
      pipe=$(make_named_pipe)
      # ---------------------------------------------------------------------------------------------- #
      # Run "gradle tasks" in the background and extract strictly the task names
      ( $kak_opt_gradle_command tasks | grep '^[a-zA-Z0-9]* -' > ${pipe} 2>&1 & ) > /dev/null 2>&1 < /dev/null
      # ---------------------------------------------------------------------------------------------- #
      printf %s "evaluate-commands -try-client '$kak_opt_docsclient' %{
                  edit! -fifo ${pipe} *gradle*
                  set-option buffer filetype gradle-tasks
                  map buffer normal '<ret>' ':<space>gradle-fifo-operate<ret>'
                }"
    }
    # ------------------------------------------------------------------------------------------------ #
    function show_gradle_dependencies() {
      pipe=$(make_named_pipe)
      # ---------------------------------------------------------------------------------------------- #
      # Run "gradle dependencies" in the background and extract the dependencies and "legend"
      ( $kak_opt_gradle_command dependencies | grep -E '^[a-zA-Z0-9]* -|[+\]--- |No dependencies|\([*n]\)|^$' > ${pipe} 2>&1 & ) > /dev/null 2>&1 < /dev/null
      # ---------------------------------------------------------------------------------------------- #
      printf %s "evaluate-commands -try-client '$kak_opt_docsclient' %{
                  edit! -fifo ${pipe} *gradle*
                  set-option buffer filetype gradle-deps
                }"
    }
    # ------------------------------------------------------------------------------------------------ #
    # GRADLE CONSTANTS DECISION TREE ----------------------------------------------------------------- #
    case "$1" in
      init) consume_gradle_init "$@" ;;
      wrapper) consume_gradle_wrapper "$@" ;;
      dependencies) show_gradle_dependencies "$@" ;;
      custom) show_gradle_task_custom "$@" ;;
      help) show_gradle_task_help "$@" ;;
      tasks) show_gradle_tasks "$@" ;;
      test) show_gradle_test "$@" ;;
    esac
  }}

```

Honestly, I don’t understand why you structured this command like this. Combining all of these commands into a single entity adds considerable complexity - complexity that I don’t believe is warranted. The case statement could be completely eliminated by simply extracting each of these shell functions into a separate Kakoune command like things were before. If there’s a benefit to this structure that I’m not seeing, please let me know.

You could very well take all of this feedback with a grain of salt - this plugin is feature-complete for what I need it to do, so I don’t have a lot of motivation to continue developing it further. If you would like to take over, let me know and I’ll point my README to your repo.

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [March 6, 2021, 3:47pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/9 "2021-03-06T15:47:06Z")

</div>

This might be an entry point into kak-gradle.kak with:  
`:menu [<switches>] <label1> <commands1> <label2> <commands2> ...`

> gradle events: init or task  
> init does not imply gradle or gradlew  
> need human choice: gradle or gradlew
> 
> 1. gradle init or task
> 2. gradlew or gradle

```shell
$ gradle init [...]
 .
 ├── gradle
 │ └── wrapper
 │ ├── gradle-wrapper.jar
 │ └── gradle-wrapper.properties
 ├── build.gradle.kts
 ├── gradlew
 ├── gradlew.bat
 └── settings.gradle.kts

 2 directories, 6 files

```

I have updated the snippet repo with more documented ideas and references.

Till next time, bye 🛫

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [March 7, 2021, 1:28am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/10 "2021-03-07T01:28:34Z")

</div>

OK, I’ve seen your snippet repository. While I like your ideas in general, they seem to go against the original vision I had for this plugin, which was to be a simple wrapper around the “gradle” command to make using Gradle without leaving Kakoune easier. This may very well mean that my original vision - well - lacked vision 😄. It’s hard to know at this point. Here’s my thoughts on your new snippet:

- Your reasoning of why you consolidated the prior functionality into a single Bash function makes sense now that you’ve explained it. That doesn’t really mean I agree with it, though, unfortunately. The reason that `git.kak` is implemented the way it is (at least as far as I can tell) is that all the plugin does is piping the results of various `git` commands into a highlighted FIFO buffer. In this case, implementing it as a single function makes sense, as there **is** a lot of duplicated logic there.  
However, in `kak-gradle`, I don’t really see the same kind of duplication of logic that would warrant consolidation of these functions into one. The `gradle-init` and `gradle-wrapper` commands are one-liners that simply translate to `gradle` commands under the hood - no duplicated logic there. The `gradle-tasks` and `gradle-dependencies` commands are similar in that they both pipe the result of their respective operations into a FIFO buffer, but this can be resolved by creating a `gradle-create-fifo` command and passing in “tasks” or “dependencies”, depending on which command we are running. This would nicely abstract out the FIFO functionality without the need for a single large, complex command.  
In short, although you made some good points about code duplication and DRY, I’m still not convinced that your approach is the best one for resolving it.

- It may be that I don’t understand what you’re trying to do with it, but I don’t get what your “menu” feature would add to the plugin. Selecting whether the plugin uses the project’s gradle wrapper or the systemwide gradle installation should be done with the `use_gradlew` option IMO, and for determining the project root, I think it would be nicer to either have the plugin compute it or have the user input it directly into the `gradle_project_path` option - no menu required in either case.

- In addition, you seem to have a `require-module gradle-build-tool` call in the module itself. This would only be executed once the module is loaded, which would require another `require-module gradle-build-tool` call - which would seem to make this line meaningless. A minor nitpick, but something to keep in mind nonetheless.

If it sounds like I’m being too condescending, that isn’t intentional. I love the enthusiasm - I just believe that some of what you are suggesting may make this plugin more complex than it needs to be.

I propose the following as a base to work off of:

> **New kak-gradle.kak**
>
> ```auto
> # These are defined outside the module.
> # Otherwise, they aren't guaranteed to be resolved properly.
> decl -hidden str gradle_wrap %sh{ printf "%s/../src/%s" "${kak_source%/*}" "gradle_wrap.sh" }
> decl -hidden str gradlew_wrap %sh{ printf "%s/../src/%s" "${kak_source%/*}" "gradlew_wrap.sh" }
> 
> provide-module gradle %{
> 
> decl -docstring "Determines the root directory of the current gradle project" str gradle_root_dir ""
> 
> decl -docstring "Determines whether to use the project's gradle wrapper or the systemwide gradle installation" bool gradle_use_gradlew false
> 
> decl -hidden str gradle_command "gradle"
> 
> try %{
> # Declare highlighters for the tasks buffer
> add-highlighter global/gradle_tasks_buffer group
> add-highlighter global/gradle_tasks_buffer/task regex "(^[a-zA-Z0-9]*)( - [^\n]*)" 0:string 1:type
> 
> # Declare highlighters for the dependencies buffer
> add-highlighter global/gradle_dep_buffer group
> add-highlighter global/gradle_dep_buffer/category regex "(^[a-zA-Z0-9]*)( - [^\n]*)" 0:string 1:type
> add-highlighter global/gradle_dep_buffer/dependency regex "([+\\]---)( [^\n]*)" 0:keyword 1:string
> add-highlighter global/gradle_dep_buffer/symbol regex "(\|)" 0:string
> add-highlighter global/gradle_dep_buffer/no_deps regex "^No dependencies$" 0:keyword
> add-highlighter global/gradle_dep_buffer/legend regex "(\([*n]\))( [^\n]*)" 0:string 1:type
> } catch %{
> echo -debug "kak-gradle: Can't declare highlighters for *gradle* buffer."
> echo -debug " Detailed error: %val{error}"
> }
> 
> # Enable the highlighters for the gradle-tasks filetype
> hook -group gradle-tasks-syntax global WinSetOption filetype=gradle-tasks %{
> add-highlighter buffer/gradle_tasks_buffer ref gradle_tasks_buffer
> hook -always -once window WinSetOption filetype=.* %{
> remove-highlighter buffer/gradle_tasks_buffer
> }
> }
> 
> # Enable the highlighters for the gradle-deps filetype
> hook -group gradle-deps-syntax global WinSetOption filetype=gradle-deps %{
> add-highlighter buffer/gradle_dep_buffer ref gradle_dep_buffer
> hook -always -once window WinSetOption filetype=.* %{
> remove-highlighter buffer/gradle_dep_buffer
> }
> }
> 
> ## UTILITY FUNCTIONS ##
> 
> define-command -hidden gradle-root-dir %{
> evaluate-commands %sh{
> cur_dir=$(pwd)
> # Scour the filesystem, looking for a build.gradle file
> while [$cur_dir != "/"]; do
> out=$(ls | grep "build.gradle")
> if ["$out" = *"build.gradle"*]; then
> printf "set-option global gradle_root_dir %s\n" "$cur_dir"
> break
> fi
> cd ..
> cur_dir=$(pwd)
> done
> }
> }
> 
> define-command -hidden gradle-check-root-dir %{ evaluate-commands %sh{
> # If we haven't populated the gradle_root_dir option yet, then do so
> if ["$kak_opt_gradle_root_dir" == '']; then
> echo "gradle-root-dir"
> fi
> }}
> 
> define-command -hidden gradle-fifo -params 1 %{ evaluate-commands %sh{
> # Determine if we need to use the gradle wrapper, or use the systemwide gradle command
> if ["$kak_opt_gradle_use_gradlew" = "true"]; then
> echo "set-option global gradle_command $kak_opt_gradle_root_dir/gradlew"
> else
> echo "set-option global gradle_command gradle"
> fi
>        
> tmp=$(mktemp -d "${TMPDIR:-/tmp}/kak-gradle.XXXXXXXX")
> fifo=$tmp/fifo
> mkfifo ${fifo}
> # Run the desired command in the background and extract strictly the right values
> # Depending on the command, the regex required is different
> if [$1 = "tasks"]; then
> ( $kak_opt_gradle_command tasks | grep '^[a-zA-Z0-9]* -' > ${fifo} 2>&1 & ) > /dev/null 2>&1 < /dev/null
> else
> ( $kak_opt_gradle_command dependencies | grep -E '^[a-zA-Z0-9]* -|[+\]--- |No dependencies|\([*n]\)|^$' > ${fifo} 2>&1 & ) > /dev/null 2>&1 < /dev/null
> fi
> # Open the FIFO buffer
> printf "%s\n" "edit! -fifo ${fifo} *gradle*
> set-option buffer filetype gradle-$1
> hook -always -once buffer BufCloseFifo .* %{ nop %sh{ rm -rf ${tmp} } }"
> 
> # If this is the tasks command, we need to add <ret> functionality
> if [$1 = "tasks"]; then
> echo "map buffer normal '<ret>' ':<space>gradle-fifo-operate<ret>'"
> fi
> }}
> 
> define-command -hidden gradle-fifo-operate %{ evaluate-commands -save-regs t %{
> execute-keys -save-regs '' "ghw"
> set-register t %val{selection}
> evaluate-commands %sh{
> task="${kak_reg_t%:*}"
> # Determine if we need to use the gradle wrapper, or use the systemwide gradle command
> if ["$kak_opt_gradle_use_gradlew" = "true"]; then
> echo "terminal ${kak_opt_gradlew_wrap} $kak_opt_gradle_root_dir $task"
> else
> echo "terminal ${kak_opt_gradle_wrap} $task"
> fi
> }
> }}
> 
> ## USER FUNCTIONS ##
> 
> define-command -docstring "Execute arbitrary gradle command" -params .. gradle %{
> gradle-check-root-dir
> evaluate-commands %sh{
> # Determine if we need to use the gradle wrapper, or use the systemwide gradle command
> if ["$kak_opt_gradle_use_gradlew" = "true"]; then
> echo "terminal ${kak_opt_gradlew_wrap} $kak_opt_gradle_root_dir $@"
> else
> echo "terminal ${kak_opt_gradle_wrap} $@"
> fi
> }
> }
> 
> define-command -docstring "Initialize gradle project" gradle-init %{
> gradle "init"
> }
> 
> define-command -docstring "Generate gradle wrapper files" -params .. gradle-wrapper %{
> gradle "wrapper" %arg{@}
> }
> 
> define-command -docstring "List subprojects" gradle-projects %{
> gradle-check-root-dir
> evaluate-commands %sh{
> # Determine if we need to use the gradle wrapper, or use the systemwide gradle command
> if ["$kak_opt_gradle_use_gradlew" = "true"]; then
> echo "set-option global gradle_command $kak_opt_gradle_root_dir/gradlew"
> else
> echo "set-option global gradle_command gradle"
> fi
> }
> info -title "Subprojects" %sh{ $kak_opt_gradle_command projects | grep -E "Root project[^\n]+|Project" }
> }
> 
> define-command -docstring "List available gradle tasks" gradle-tasks %{
> gradle-check-root-dir
> gradle-fifo tasks
> }
> 
> define-command -docstring "List gradle project dependencies" gradle-dependencies %{
> gradle-check-root-dir
> gradle-fifo dependencies
> }
> }
> 
> ```

The above has the utility of modules and reuses code wherever possible, thus removing the duplicated logic the original had. I believe this is a clean base to build off of, should we chose to do so. Any further suggestions are much appreciated.

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [March 7, 2021, 3:23am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/11 "2021-03-07T03:23:45Z")

</div>

Cool, I’ll read `New kak-gradle.kak` this coming week and give some feedback and adjust my snippet.  
Have a look at kakoune wiki [_“Writing Plugins”_](https://github.com/mawww/kakoune/wiki/Writing-Plugins) great for topic development.

If all goes well back again next weekend. Bye 👋

![topic-development](https://discuss.kakoune.com/uploads/default/original/1X/9cbcbba74b0a01f68f0a2b9626cea476557c8e18.png)

[kakoune-plantuml.kak](https://bitbucket.org/KJ_Duncan/kakoune-plantuml.kak/src/master/)

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [March 14, 2021, 2:17pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/12 "2021-03-14T14:17:41Z")

</div>

This might take some reading but please take the time as I have.

Also a **community help request** , from learned ICT professionals. _Is this or could this be an issue?_ It’s an _‘honest’_ question from an honest person, nothing more. All sources are referenced at the end.

A new snippet, but same title, due to png: _jdugan6240/kak-gradle-WIP.kak_  
`git clone https://bitbucket.org/snippets/KJ_Duncan/A9AyR8/jdugan6240-kak-gradle-wipkak.git`

Or in the browser:  
`https://bitbucket.org/KJ_Duncan/workspace/snippets/A9AyR8/jdugan6240-kak-gradle-wipkak`

There is no `#!sha-bang` on the file just change `kak-gradle-WIP.sh` -\> `kak-gradle-WIP.kak` to get correct highlighting in the kakoune editor.

## Issue

#### Secure Coding Guidelines for Java SE

Guideline 5-1 / INPUT-1: Validate inputs

Input from untrusted sources must be validated before use. Maliciously crafted inputs may cause problems, whether coming through method arguments or external streams. Examples include overflow of integer values and **directory traversal attacks** by including “…/” sequences in filenames. Ease-of-use features should be separated from programmatic interfaces.

#### Package org.gradle.api

Interface Project

This interface is the main API you use to interact with Gradle from your build file. From a Project, you have programmatic access to all of Gradle’s features.

#### Gradle-completion

```auto
_gradle-set-project-root-dir() {
  local dir=`pwd`
  project_root_dir=`pwd`
  while [[$dir != '/']]; do
    if [[-f "$dir/settings.gradle" || -f "$dir/settings.gradle.kts" || -f "$dir/gradlew"]]; then
      project_root_dir=$dir
      return 0
    fi
    dir="$(dirname "$dir")"
  done
  return 1
}

```

#### Kakoune Wiki

When you require the side-effects of the module, you want to use the shared  
highlighters it defines, or read the value of the option it sets, etc. use the  
require-module command. The first time Kakoune executes a require-module command, it looks up the  
contents of the corresponding module and executes them, then marks that module  
as loaded (kakoune wiki, Writing Plugins).

NOTE: Kakoune does **not** provide user module isolation.

* * *

## Feature Request

### kak-gradle

Given the above issue do we need to _‘tempt fate’_ with:

```auto
## UTILITY FUNCTIONS ##

define-command -hidden gradle-root-dir %{
  evaluate-commands %sh{
    cur_dir=$(pwd)
    # Scour the filesystem, looking for a build.gradle file
    while [$cur_dir != "/"]; do
      out=$(ls | grep "build.gradle")
      if ["$out" = *"build.gradle"*]; then
        printf "set-option global gradle_root_dir %s\n" "$cur_dir"
        break
      fi
      cd ..
      cur_dir=$(pwd)
    done
  }
}

```

Or can we just do something like:

```auto
# The gradle(w) wrapper universe
readonly FILE_LIST=(
  $PWD/gradlew
  $PWD/gradlew.bat
  $PWD/gradle/wrapper/gradle-wrapper.jar
)

```

Or with some flexable guarantees:

```auto
project_root_dir=$(find -P -mount -maxdepth 1 -mindepth 1 -type f -regex '^.*/settings.gradle[.kts]*?' | sed 's/^.\///')
package_root_dir=$(find -P -mount -maxdepth 1 -mindepth 1 -type f -regex '^.*/build.gradle[.kts]*?' | sed 's/^.\///')

```

Or a user mode/menu for project path or new project:

```auto
# Andrey does: 
fzf_project_file "%val{config}/.fzf-projects

#As a user knows best solution for quick and clean: 
-kak-cmd change-directory

```

#### References

- Dugan, J 2020, kak-gradle.kak, kak-gradle/rc/, viewed 14 March 2021, `https://github.com/jdugan6240/kak-gradle/blob/master/rc/kak-gradle.kak#L47`
- gng 2021, install.sh, gdubw/gng/, github com, viewed 11 March 2021, `https://github.com/gdubw/gng/blob/master/install.sh#L114`
- Gradle 2021, Interface Project, org.gradle.api, version 6.8.3, viewed 07 March 2021, `https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html`
- Gradle-completion 2017, \_gradle, gradle-completion/, gradle/gradle-completion is licensed under the MIT License, viewed 10 March 2021, `https://github.com/gradle/gradle-completion/blob/master/_gradle#L3`
- Kakoune 2020, Put slow dependencies in modules, Writing Plugins, kakoune wiki, viewed 11 March 2021, `https://github.com/mawww/kakoune/wiki/Writing-Plugins#put-slow-dependencies-in-modules`
- Listopadov, A 2021, fzf-save-path-as-project, fzf-project.kak, fzf.kak/rc/modules/, viewed 06 March 2021, `https://github.com/andreyorst/fzf.kak/blob/master/rc/modules/fzf-project.kak#L27`
- Secure Coding Guidelines for Java SE 2020, Guideline 5-1 / INPUT-1: Validate inputs, 5 Input Validation, viewed 12 March 2021, `https://www.oracle.com/java/technologies/javase/seccodeguide.html`

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [March 15, 2021, 2:29am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/13 "2021-03-15T02:29:49Z")

</div>

I’ve had a look at your new snippet, and while I like the idea of a “gradle” user mode, I have some concerns about some of it.

I’m going to try to address your concerns, one by one:

> [@duncan](#):
>
> Given the above issue do we need to _‘tempt fate’_ with:
> 
> ```auto
> ## UTILITY FUNCTIONS ##
> 
> define-command -hidden gradle-root-dir %{
> evaluate-commands %sh{
> cur_dir=$(pwd)
> # Scour the filesystem, looking for a build.gradle file
> while [$cur_dir != "/"]; do
> out=$(ls | grep "build.gradle")
> if ["$out" = *"build.gradle"*]; then
> printf "set-option global gradle_root_dir %s\n" "$cur_dir"
> break
> fi
> cd ..
> cur_dir=$(pwd)
> done
> }
> }
> 
> ```

In a word: yes. Yes, we do. The “find” commands you have only searches the current working directory, so if the project root is farther up the directory heirarchy, we won’t find it. This means that some sort of reverse recursion is necessary to find the project root in this circumstance. Any system with the concept of a “project root” will need to find the project root in this manner, and Gradle is no exception. Fortunately, from a security standpoint, this is a nonissue, as attempted directory traversal attacks will simply result in the parent directory being referenced by the filename on \*nix systems. Since the only use of this in `gradle.kak` is to search for a build.gradle[.kts] file, which won’t be preceded by `../` anyway, this is a nonissue. I hope that makes sense.

Furthermore, your comment:

> [@duncan](#):
>
> The first time Kakoune executes a require-module command, it looks up the  
> contents of the corresponding module and executes them, then marks that module  
> as loaded (kakoune wiki, Writing Plugins).

is exactly why I believe the extra `require-module` call in the `ModuleLoaded` hook is redundant - the module’s contents have already been executed at this point, and there’s no need to execute them again.

I still believe the `gradle-commands` command is best split up. While I agree that the original `gradle.kak` implementation had duplication of logic, Kakoune already provides a mechanism to define separate functions for different tasks by defining different commands - we don’t need to reinvent the wheel with POSIX functions and a case statement. The main benefit to defining it as one Kakoune command is the invocation of a single shell process as opposed to two, but in the context of running gradle-\* commands, this seems negligible. My point here is that I’d like to avoid excess reliance of shell scripting in this plugin, as POSIX shell is (IMO) a terrible scripting language, and as I’ve already discussed, we don’t necessarily need to use the shell for selecting which task to execute.

In summary, I’d like this plugin to follow the KISS approach, being as simple as is reasonable while still offering the core functionality required of a simple Gradle wrapper.

I really like your gradle usermode idea, though. Here’s my vision of what keybindings we might have for this usermode:

```auto
t - gradle-tasks
d - gradle-dependencies
p - gradle-projects
i - gradle-init
w - gradle-wrapper (prompt for arguments?)
g - generic gradle command (prompt for arguments)

```

Say the user binds this usermode to Alt-G. Then, the user can press Alt-G-T to execute the `gradle-tasks` command, for example, without needing to type it out in the prompt. We can allow the user to bind this usermode to whatever keybinding they want, which would allow for great flexibility.

Again, I really appreciate your feedback, and apologies if I come off as condescending, since I really like some of your ideas, but the way you have them implemented in your snippet at the moment seem to be a bit convoluted for my liking. I had envisioned this plugin as a simple wrapper for the `gradle` command, and I’d like to stick to that for simplicity. Keep the ideas coming, though! 😀

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [March 16, 2021, 1:23pm UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/14 "2021-03-16T13:23:58Z")

</div>

> [@jdugan6240](#):
>
> Fortunately, from a security standpoint, this is a nonissue, as attempted directory traversal attacks will simply result in the parent directory being referenced by the filename on \*nix systems. Since the only use of this in `gradle.kak` is to search for a build.gradle[.kts] file, which won’t be preceded by `../` anyway, this is a nonissue. I hope that makes sense.

From a security standpoint: _“Hearsay.”_

> [@jdugan6240](#):
>
> I’d like this plugin to follow the KISS approach

This is where we differ, I prefer the research methods in which I am learned. This is not to say _“I’m right and your wrong.”_ It means I’m not done yet and please slow down its not a `*.kak` or `*.sh` file to be scrutinized. It’s merely a _‘work in progress’_ document to be viewed, read, and matured.

> [@jdugan6240](#):
>
> My point here is that I’d like to avoid excess reliance of shell scripting in this plugin,

Ok, every `%sh{}` is a call to the shell. Keep that in mind when we are done to contrast the alternate approaches. It’s twigged my interest.

> [@jdugan6240](#):
>
> gradle usermode idea

Cool, that is something we can work on.

> [@jdugan6240](#):
>
> I had envisioned this plugin as a simple wrapper for the `gradle` command, and I’d like to stick to that for simplicity.

That’s fine. What I’m thinking moving forward is keeping the communication up with this back and forth as its productive due to our differences. Let’s do it over internal discuss.kakoune messages so not to upset the locals. Finally, my `gradle.kak` will not be your idea of `kak-gradle.kak` and that’s perfect.

> [@jdugan6240](#):
>
> apologies if I come off as condescending

Nothing on the internet hurts my feelings. Thanks youtube.

Before I go keep checking the snippet once a week for new ground. Bye 👋

---

<div class="post-metadata">

**Author:** ![jdugan6240](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/jdugan6240/32/637_2.png) [@jdugan6240](https://discuss.kakoune.com/u/jdugan6240)\
**Post date:** [March 18, 2021, 1:08am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/15 "2021-03-18T01:08:50Z")

</div>

OK, @duncan I just messaged you. To avoid polluting this thread any further, let’s keep our discussion in that private message thread.

---

<div class="post-metadata">

**Author:** ![duncan](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/duncan/32/95_2.png) [@duncan](https://discuss.kakoune.com/u/duncan)\
**Post date:** [March 19, 2021, 8:01am UTC](https://discuss.kakoune.com/t/hello-a-gradle-integration-plugin-for-kakoune/1214/16 "2021-03-19T08:01:44Z")

</div>

> [@duncan](#):
>
> From a security standpoint: _“Hearsay.”_

An unpretentious explanation to _‘Hearsay’_.

Industry peers who provide conclusive evidence, either via a case-study or peer-reviewed-journal.

An example of a current case-study of industry professionals:

- Red Hat 2020, _Boot Hole Vulnerability - GRUB 2 boot loader - CVE-2020-10713_, revision 12 March 2021, viewed 19 March 2021, `https://access.redhat.com/security/vulnerabilities/grub2bootloader`

This document might also be useful to others.

Introduction: _‘Independent software suppliers to create applications which are FHS compliant’_

- Russell, R Quinlan, D Yeoh, C 2004, _Filesystem Hierarchy Standard_, Filesystem Hierarchy Standard Group, viewed 19 March 2021, `https://www.pathname.com/fhs/pub/fhs-2.3.pdf`

* * *

Knowledge time 📝.

Directory traversal attack (Path traversal).

- Common Weakness Enumeration 2021, _CWE-22: Improper Limitation of a Pathname to a Restricted Directory (‘Path Traversal’)_, viewed 22 March 2021, `https://cwe.mitre.org/data/definitions/22.html`

This search result shows the severity is wide in scope and ongoing.

- CVE 2021, _directory traversal attack_, Search Results, viewed 22 March 2021, `https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=directory+traversal+attack`

Hope others find this information as useful as I did. Bye 👋

* * *

So I did a little more digging on the topic and…

Phase 2: Knowledge time.

A peer-reviewed-journal:

- Coker, Z. et al., 2015. _Evaluating the Flexibility of the Java Sandbox_. Proceedings of the 31st Annual Computer Security Applications Conference. Available at: [http://dx.doi.org/10.1145/2818000.2818003](http://dx.doi.org/10.1145/2818000.2818003).

_‘Gradle runs on the Java Virtual Machine (JVM) and uses several supporting libraries that require a non-trivial initialization time’_ (The Gradle Daemon).

- Gradle Inc 2021, The Gradle Daemon, Gradle User Manual, v6.8.3, viewed 28 March 2021, [https://docs.gradle.org/current/userguide/gradle\_daemon.html](https://docs.gradle.org/current/userguide/gradle_daemon.html)

It’s a jigsaw puzzle, not an answer. Bye 👋
