Hello & a Gradle integration plugin for Kakoune

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 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! :slightly_smiling_face:

5 Likes

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.

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
#! /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 :wave:



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 :sweat_smile:


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
#!/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
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

Gradle 2021, ‘Supply credentials to external tool Sample’, Gradle v6.8.3, viewed 26 February 2021, Supply credentials to external tool Sample

Gradle 2021, ‘Troubleshooting’, Gradle Completion, viewed 26 February 2021, GitHub - gradle/gradle-completion: Gradle tab completion for bash and zsh

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

Gradle 2021, Sample Index, Gradle v6.8.3, viewed 26 February 2021, Sample Index


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


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 :wave:

build.gradle.kts - jlink task
  /* 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
      )
    }
  }
1 Like

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… :slightly_smiling_face:

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.

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 :wave:

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)
# 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.

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

Bye :wave:

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.

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

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.

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).

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.

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.

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
$ 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 :flight_departure:

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 :smile:. 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
# 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.

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” great for topic development.

If all goes well back again next weekend. Bye :wave:

topic-development

kakoune-plantuml.kak

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

_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:

## 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:

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

Or with some flexable guarantees:

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:

# 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

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:

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:

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:

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! :grinning:

From a security standpoint: “Hearsay.”

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.

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.

Cool, that is something we can work on.

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.

Nothing on the internet hurts my feelings. Thanks youtube.

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

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

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 :memo:.

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 :wave:


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

Phase 2: Knowledge time.

A peer-reviewed-journal:

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

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