Skip to content

Add Haddock documentation snippets (fdoc, mdoc) #42

@tilin94

Description

@tilin94

Introduction

Hi! First of all I really appreciate Haskell Snippets, it would be amazing if I could (hopefully) bring some value.

I think that would be great to have snippets for generating Haddock documentation. I've put together an implementation that I'd love to contribute if you're interested. The snippets include smart auto-detection of function signatures and module names to make documentation writing faster and more convenient.

I'm completely open to feedback, suggestions, or alternative approaches. Please let me know if there's anything you'd like me to change or if you have any questions about the implementation. I'm happy to iterate on this based on your guidance.

Summary

Add two new snippets for generating Haddock documentation blocks with smart auto-detection capabilities, including support for multiline signatures, forall quantifiers, and type class constraints.

New Snippets

fdoc - Function Documentation

Generates a Haddock documentation block for functions with automatic type signature detection.

Image

Smart behavior: When the line below the cursor contains a type signature (single or multiline), the snippet automatically:

  • Detects the function name
  • Parses parameter types from the signature (stripping forall and constraints)
  • Generates example placeholders with the detected types

Supported signature patterns:

-- Simple signatures
f :: Int -> Int

-- Multiline signatures
longFunc :: Int
  -> String
  -> Bool

-- Name on separate line
fmap
  :: (a -> b)
  -> f a
  -> f b

-- With forall quantifiers
polymorphic :: forall a b. a -> b -> (a, b)

-- With type class constraints
showIt :: Show a => a -> String

-- Combined forall and constraints
complicated :: forall a. Show a => a -> String

-- Tuple constraints
multiConstraint :: (Show a, Eq b) => a -> b -> Bool

Example with detection:

-- Cursor here, line below is: add :: Int -> Int -> Int

Expands to:

-- | `add` description.
--
-- Examples:
--
-- >>> add Int Int
-- Int
add :: Int -> Int -> Int

Each type (Int) becomes an editable tab stop so users can replace them with actual example values.

Fallback (no signature detected):

-- | Brief description.
--
-- Examples:
--
-- >>> example
-- expected

mdoc - Module Documentation

Generates a Haddock module documentation header with automatic module name detection.

Image

Smart behavior: Uses LSP CodeLens (from haskell-language-server) or filename-based detection to auto-fill the module name.

Example:

-- |
-- Module      : MyApp.Utils
-- Description : Short description here
--
-- Longer description of the module.

When module name is detected, it appears as non-editable text. When not detected, it defaults to MyModule as an editable placeholder.

Implementation Details

New Utility Functions

Added to lua/haskell-snippets/util.lua:

  • strip_forall(signature) - Strips forall quantifiers from signatures, including nested foralls like forall a. forall b. ...

  • strip_constraints(signature) - Strips type class constraints, handling both single (Show a =>) and tuple ((Show a, Eq b) =>) constraints with proper bracket depth tracking

  • normalize_signature(signature) - Applies both forall and constraint stripping

  • parse_type_signature(signature) - Parses Haskell type signatures into parameter types and return type. Now automatically normalizes signatures first. Handles complex types including:

    • Parenthesized function types: (Int -> Int) -> Int
    • List types with arrows: [Int -> Int] -> Bool
    • Record types: {f :: Int -> Int} -> Bool
    • Nested parentheses: ((a -> b) -> c) -> d
  • parse_function_line(line) - Parses a function type declaration line and returns structured data with name, params, and return type.

  • is_signature_continuation(line) - Detects if a line is a continuation of a multiline type signature (indented, no ::, not a function definition/guard/where)

  • collect_multiline_signature(start_row) - Collects a potentially multiline type signature starting from a given row, joining continuation lines. Supports both name :: sig and name\n :: sig formats.

  • get_function_context() - Gets function information from the line below the cursor position, now with full multiline signature support

Test Coverage

Added 52 unit tests in tests/util_spec.lua:

  • 5 tests for strip_forall
  • 4 tests for strip_constraints
  • 7 tests for is_signature_continuation
  • 17 tests for parse_type_signature (including forall/constraint handling)
  • 7 tests for parse_function_line
  • 14 tests for get_function_context (including multiline signatures and name-on-separate-line format)

All tests pass on both neovim stable and nightly.

Environment

  • Neovim: v0.11.2
  • LuaSnip: v2.4.1
  • OS: Linux 6.12.47-1.qubes.fc37.x86_64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions