Examples of user custom modules

Example of a barebones module

---@brief Let's go through the most boilerplate, simple example of a module you may use.
---@brief This module will have no real functionality,
local mod = require "down.mod"


---@brief Let's say you want to create a module for Jupyter notebooks to run in Neovim.
---@brief We'll start by just creating a barebones module, with no functionality, just to show you how.
---@brief We will name this module "jupyter".
---@type down.Mod
local J = mod.create("jupyter", {

  ---@brief This is where we would automatically call any submodules underneath "jupyter" to be called in
  ---@brief simultaneously as it is loaded. Since we do not have any such submodules, we will leave this empty.

})

--[[           1. Flow of functions:

      +-------+  load mod   +------------------+ +------+ +----------+
      | setup |>----------->| cmds, opts, maps |>| load |>| postload |
      +-------+  set data   +------------------+ +------+ +----------+

--]]

---@brief This is where the module will first be `setup`. As you can see above, this occurs before a module has
---@brief fully loaded its data it needs to function. It may, however, perform important functionality during
---@brief this step, such as specifying dependencies it will need, setting up configuration, defining variables,
---@brief or even defining commands and autocommands that will invoke its functionality.
---@brief It must only return a table generally containing a confirmation it has loaded, as well as its
---@brief dependencies, except on rare occasions.
---@return down.mod.Setup
function J.setup()
  ---@class down.mod.Setup
  return {
    loaded = true,
    ---@brief For a jupyter module, we will likely need several dependencies, perhaps too many to list
    ---@brief through in such an early stage. Taking a guess, however, and knowing we can always change,
    ---@brief we'll just choose a few which we will likely need regardless.
    requires = {
      "data",
      "workspace",
      "data.code",
      "ui.progress",
      "ui.status",
      "ui.notify",
      "ui.vtext"
    }
  }
end

---@brief This is where we will set up the module's data and any methods it will call.
---@class down.jupyter.Data
J.data = {

  ---@brief One such piece of data you may wish to store is the ongoing collection of cells, as well
  ---@brief as their contents and type in the Juypyter notebook. You may even wish to leverage the
  ---@brief down.lua `lsp.notebook` module to hook into the LSP for Jupyter notebooks.
  cells = {

  },

  ---@brief To keep track of the notebook currently being interacted with
  notebook = {

    path = nil,

    name = nil,

    kernel = "python3",

  }

}

---@brief Technically, we have now created a proper module that can be loaded into Neovim through down.lua.
---@brief However, we will be typically be best off at the beginning characterizing the module with any
---@brief setup, dependencies, configuration, commands, and even keymaps you believe it may one day need.

---@brief Each modue has a config table specified, which is where the user may set any configuration options
---@brief changing the behaviour of the module.

---@class down.jupyter.Config
---@field kernal string: The kernel to use for the Jupyter user interface. Default is `python3
J.config = {

  ---@brief The default directory you might want to specify for Jupyter notebooks to be stored in.
  ---@brief You may also wish to leverage the required "workspace" module to allow users to specify both
  ---@brief a specific workspace tey would like to associate with Jupyter notebooks, as well as a default
  ---@brief relative directory within that workspace.
  ---
  ---@brief Configuration details about created notebooks. While you are specifying default values here,
  ---@brief consider that a user will likely want to change several of the values.
  notebook = {

    default = "notebook.ipynb",

    dir = {

      workspace = "default",

      default = "notes",
    }

  },

  service = "jupyter",

  command = "jupyterlab",

  kernel = "python3",

  kernels = {
    "python3"
  }

}

---@brief There are many more aspects to a module that can and should be defined as you begin to flesh it out,
---@brief even before you begin to test any major functionality. These include defining commands, options,
---@brief mappings, not to mention learning the interdependencies between your module and other modules,
---@brief whether builtin or custom-made by the community.
---
---@brief Regardless, I hope this has provided a good starting point to help you to take the very first steps
---@brief in creating an awesome module to extend and bless the down.lua ecosystem. Good luck and godspeed!
return J

Example custom module

Note that the below example is out of date and needs to be updated. Thanks!

local down = require("down")

local mod, config, util = down.mod, down.config, down.util

local M = mod.create("user.example", {
  --- @brief submodules
  ---         child directories containing
  ---         modules to load in tandem, relative
  ---         to this (parent) module.
  --- "subexample",
  --- "pre_example",
  --- ...
})

M.setup = function()
  return {
    requires = {
      ---@brief required modules
      ---         modules from builtin or custom
      ---         modules that can be loaded in (same as
      ---         if calling `require 'module'`) as a dependency
      ---         for the module.
      --- "ui.popup",
      --- "edit.link",
      --- "integration.treesitter",
      --- ...
    },
    loaded = true,
  }
end

---@class (exact) example.Config
M.config = {
  --- @brief module config
  ---          the public facing config for this module that can be
  ---          set by the user from its default values here.
  --- ...
}

---@class example.Data
M.data = {
  --- @brief module data
  ---          the home of the module's internal data and methods
  ---          TODO: split up concerns
  --- ...
}

M.load = function()
  --- @brief module load
  ---          a set of functions to run whenever the
  ---          module is fist loaded upon startup.
  ---          TODO: maybe just merge in with setup()
  --- ...
end

return M