formit

package module
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2026 License: GPL-3.0 Imports: 11 Imported by: 0

README

formit

A simple go library that transforms .formit files into rendered html forms

⚠️Beta warning!⚠️

This project is in its early stages and the final structure is still being developed. Expect breaking changes before the 1.0.0 release.

Format

Lines are prefixed with a prefix to determine their type. The following types are included by default:

  • @: create an input field
  • #: create a heading where the number of # defines the level of the heading
  • $: create a line of text
  • >: create an external line to dynamically insert content into the document

Blocks are prefixed and suffixed with an identifier. The following identifiers are included by default:

  • $$$: create a block of text
  • >>>, <<<: create an external block that dynamically wraps the content with the inserted content

Inline types are prefixed and suffixed with an identifier to replace them. The following are included by default:

  • ~~: strikethrough the content

Context

The formit parser uses a context given to it at render time for configuration. The format of the context is structured as follows:

context
-> @.classes = string
-> #.classes = string
-> >.externals = map[string]string

Each key is prefixed with the prefix of its associated handler. So the first one passes html classes to inputs, the second to headings and the third passes a mapping of external content to the external handler. There are a few special cases where context keys are handled slightly out of the ordinary:

  • $.classes and $$$.classes are merged and then applied since both output a single paragraph
  • Besides @.classes which applies to both inputs and labels, there are also @.input.classes and @.label.classes to add styles to them separately. These are merged so that both the global and type specific classes are applied
  • >.externals accepts a map of keys that have a complete block of content as a string as their value
  • >>>.externals accepts a map of keys that have a block of content as a template string containing one %s as their value. The %s is used to render the content of the block inside the template.
  • All handlers except the external handlers have the content of the context as optional. The external handlers will panic if they cannot find the content.

Input format

The format for inputs is the following: @name:text(Name) [required]
The format is the name of the field followed by the type optionally followed by parentheses with a text to use for a label. After that the parameters for the field between square brackets, seperated by a semicolon.

Supported field types are: text, number, date, email, file, password, reset, submit, tel, url, checkbox, textarea

The following parameters are supported:

  • All default html parameters, since those are what are passed as the parameters (docs)
  • No special ones are supported at the moment since they are not required by the currently supported field types

Example:

# formit format
@name:text(Name) [required;minlength=2]

# render to:
<label for="formit-name">Name</label>
<input type="text" name="formit-name" id="formit-name" required minlength="2" />
Render with values

You can also render the form with values by passing them in the context. The format for this is @.values with a map of field names to their values as strings as its value. By default, the values will be escaped to prevent XSS attacks. If you want to disable this, you can set @.escapeHtml to false in the context.

⚠️Note on type="file"⚠️

When using the file type, and you want to render its value by passing it value in the context, you need to pass its value as a JSON string with the following format:

{
  "fileName": string,
  "contentType": string,
  "data": string (base64 encoded)
}

Heading format

The format for headings is the following: #Text for heading (space in the beginning is optional). You can increase the number of # for different heading levels, such as # -> h1, ## -> h2, ..., ###### -> h6 where 6 is the max.

Text format

Text comes in 2 flavors: single-line or multi-line. Single-line is prefixed with a $ and will be rendered as a single paragraph. The format is $ line of text (space in the beginning is required).

Multi-line is prefixed with $$$ and suffixed with $$$. All the lines within will be rendered as a single paragraph. Two spaces behind a line is an instruction to insert a newline, or you can leave a blank line for a newline.
Example:

# valid
$$$ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. $$$

# valid
$$$ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 
$$$

# valid
$$$ 
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 
$$$

External format

Formit supports external content being passed in on render-time. This can be used to for example wrap inputs in a form, or insert control buttons for flow. This feature has both a line and block version.

The line version is used to dynamically insert lines into the content, e.g:

# formit format
> controls

# ctx: ">.externals" = ["controls"]"<button>Control</button>"
# renders to:
<button>Control</button>

The matcher is > that signifies that external content needs to inserted here. It then matches the word behind it and uses that as a lookup to find the matching content in the externals key. It will panic if it cannot find a match.

The block version is used to dynamically wrap a block in a external wrapper:

# formit format
>>> form
@name:text(Name) [required]
@password:password(Password) [required]
<<<

# ctx: ">>>.externals" = ["form"]`<form>\n%s\n<input type="submit" />\n</form>`
# renders to:
<form>
<label for="formit-name">Name</label>
<input type="text" name="formit-name" id="formit-name" />
<label for="formit-password">Password</label>
<input type="password" name="formit-password" id="formit-password" />
<input type="submit" />
</form>

The logic here is the same as the single line version, except that a %s needs to be present in order for the template to render. But as you see, the writer of the content does not need to worry about passing the right parameters to the form or even think about a submit button.

Strikethrough handler

If you prefix and suffix a piece of text in a line with ~~ it will be striped trough by surrounding it with <del> tags.

# formit format
$ I'm a line of ~~important~~ text

# renders to:
<p>I'm a line of <del>important</del> text</p>

Write your own extension

You can write your own handler by writing one of two handlers: line or block handlers.
The file reader will pass occurrences of your handler to your handler based on the prefix without stripping them, that you need to do yourself.

You can check out the existing handlers for examples and register them in your code using the RegisterNewLineHandler and RegisterNewBlockHandler methods. They will add them to the list of handlers that are referenced when parsing.

Planned changes

Formit
V0.6.0
  • Move HandlerSettings to ParseText as a parameter (breaking)
  • Add support for both regular links and wikilinks
  • Add support for both images and wikilink-images
  • Add support for underlined, italic and bold text
  • Add support for extracting metadata, not parsing (breaking)
  • Pass matched regexes to handlers to prevent double regex matching (almost all handlers currently do this, potentially breaking for extensions)
  • Require a space after the # in a heading to be more in line with regular markdown (breaking)
  • Add support for a custom variable parser for the externals, such that custom resolution techniques can be used
  • Add support for HTML style comments
Templ component

No changes planned yet

CLI

No changes planned yet

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterNewBlockHandler

func RegisterNewBlockHandler(name, prefix, suffix string, handler BlockHandler) error

func RegisterNewLineHandler

func RegisterNewLineHandler(name, matcher string, handler LineHandler) error

Types

type BlockHandler

type BlockHandler interface {
	Init(ctx context.Context)
	Handle(match []string, ctx context.Context) string
}

type BlockMatcher

type BlockMatcher struct {
	Name          string
	PrefixMatcher string
	SuffixMatcher string
	Handler       BlockHandler
}

type Formit

type Formit struct {
	// HandlerSettings is a context that contains settings for the handlers in the format: prefix.setting-name = value.
	// A default setting for all built-in handlers is: prefix.class = [array of html classes]
	HandlerSettings context.Context
	// contains filtered or unexported fields
}

func (*Formit) Init

func (f *Formit) Init() *Formit

func (*Formit) ParseText

func (f *Formit) ParseText(text []string) string

type LineHandler

type LineHandler interface {
	Init(ctx context.Context)
	Handle(match string, ctx context.Context) string
}

type LineMatcher

type LineMatcher struct {
	Name    string
	Matcher string
	Handler LineHandler
}

type Utils

type Utils struct {
}

func (*Utils) ReverseString

func (u *Utils) ReverseString(s string) string

Directories

Path Synopsis
handlers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL