Skip to content

Instantly share code, notes, and snippets.

@drhayes
Last active June 12, 2026 14:59
Show Gist options
  • Select an option

  • Save drhayes/2ebe938d09317ebbbebfbcc99c3ff192 to your computer and use it in GitHub Desktop.

Select an option

Save drhayes/2ebe938d09317ebbbebfbcc99c3ff192 to your computer and use it in GitHub Desktop.
A "JSON: Format" command for Silverbullet

#meta

Some utilities made for working with JSON.

This doesn’t turn lua into JSON and back again, this is strictly for working with JSON.

For doing stuff like that, check out js.tojs and js.stringify in the built-in API.

API

-- priority: 50
json = json or {}

parse

-- priority: 50
function json.parse(s)
  return js.window.JSON.parse(s)
end

stringify

-- priority: 50
function json.stringify(s)
  return js.window.JSON.stringify(s, null, 2)
end

Some useful JSON utilities.

Format

command.define({
  name = "JSON: Format",
  run = function()
    local position = editor.getCursor()
    local tree = markdown.parseMarkdown(editor.getText())
    local fencedCode = nodeParentOfType(tree, position, "FencedCode")
    if not fencedCode then
      editor.flashNotification('Not in a fenced code block!')
      return
    end
    -- Find the CodeInfo block.
    local codeInfo = recursiveFindFirstChild(fencedCode, 'CodeInfo')
    if not codeInfo then
      editor.flashNotification('Type not set in fenced code block!')
      return
    end
    -- nil for type means a text node, weirdly.
    local codeLanguage = recursiveFindFirstChild(codeInfo, nil)
    print(codeLanguage)
    if not codeLanguage or codeLanguage.text ~= 'json' then
      editor.flashNotification('Not in a JSON code block!')
      return
    end
    local codeTextBlock = recursiveFindFirstChild(fencedCode, 'CodeText')
    if not codeTextBlock then
      editor.flashNotification('Could not find text block in code block!')
      return
    end
    local codeTextNode = recursiveFindFirstChild(codeTextBlock, nil)
    if not codeTextNode or not codeTextNode.text then
      editor.flashNotification('Could not find actual code!')
      return
    end
    formatJsonInTree(codeTextNode)
  end,
})

-- Recurses down a ParseTree looking at children, trying to find
-- the child that matches the given type. Returns the child if found,
-- nil if not found.
local function recursiveFindFirstChild(tree, childType)
  if not tree then return nil end
  if tree.type == childType then
    return tree
  end
  if not tree.children then
    return nil
  end
  for child in each(tree.children) do
    local foundChild = recursiveFindFirstChild(child, childType)
    if foundChild then
      return foundChild
    end
  end
  return nil
end

local function formatJsonInTree(tree)
  -- Retrieve the contents of the tree.
  local jsonText = tree.text
  local formatted = json.stringify(json.parse(jsonText))
  editor.replaceRange(tree.from, tree.to, formatted)
end

-- Find outermost node of given type that contains the position
-- similar to plug-api/lib/tree.ts nodeAtPos
local function nodeParentOfType(tree, position, nodeType)
  if position < tree.from or position > tree.to then
    return nil
  end
  if tree.type == nodeType then
    return tree
  end
  if tree.children then
    for _, child in ipairs(tree.children) do
      if position >= child.from and position <= child.to then
        return nodeParentOfType(child, position, nodeType)
      end
    end
  end
  return nil
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment