editor

package module
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Dec 20, 2022 License: Apache-2.0 Imports: 8 Imported by: 2

README

go-editor

Go Reference

Allow your CLI users to edit arbitrary data in their preferred editor.

Just like editing messages in git commit or resources with kubectl edit.

Install

go get github.com/confluentinc/go-editor

Usage

Existing File

The most basic usage is to prompt the user to edit an existing file. This may be useful to edit the application configuration or a system file, for example.

edit := editor.NewEditor()
err := edit.Launch("/etc/bashrc")
Arbitrary Data

Most of the time, the data you want your user to edit isn't in an local file. In these cases, if you can represent your data in a human editable format (txt, yaml, hcl, json, etc), then go-editor will enable the user to edit it.

Provide any io.Reader with the initial contents:

original := bytes.NewBufferString("something to be edited\n")

edit := editor.NewEditor()
edited, path, err := edit.LaunchTempFile("example", original)
defer os.Remove(path)
if err != nil {
    // handle it
}

The library leaves it up to you to cleanup the temp file.

This enables your CLI to validate the edited data and prompt the user to continue editing where they left off, rather than starting over. And if that's what you want...

Input Validation

If you would like to validate the edited data, use a ValidatingEditor instead. This will prompt the user to continue editing until validation succeeds or the edit is cancelled.

Simply create a schema and pass it to the editor:

schema := &mySchema{}
edit := editor.NewValidatingEditor(schema)

A schema is any object that implements the Schema interface. This interface has a single method, ValidateBytes([]byte) error.

You can see working examples in the examples directory.

Happy editing!

Acknowledgements

Thanks to these other projects and groups for pointing the way.

Documentation

Overview

Package editor allows your CLI users to edit arbitrary data in their preferred editor.

It's just like editing messages in "git commit" or resources with "kubectl edit".

Existing File

The most basic usage is to prompt the user to edit an existing file. This may be useful to edit the application configuration or a system file, for example.

edit := editor.NewEditor()
err := edit.Launch("/etc/bashrc")

Arbitrary Data

Most of the time, the data you want your user to edit isn't in an local file. In these cases, if you can represent your data in a human editable format (txt, yaml, hcl, json, etc), then go-editor will enable the user to edit it.

Provide any "io.Reader" with the initial contents:

original := bytes.NewBufferString("something to be edited\n")

edit := editor.NewEditor()
edited, file, err := edit.LaunchTempFile("example", original)
defer os.Remove(file)
if err != nil {
    // handle it
}

The library leaves it up to you to cleanup the temp file.

This enables your CLI to validate the edited data and prompt the user to continue editing where they left off, rather than starting over. And if that's what you want...

Input Validation

If you would like to validate the edited data, use a ValidatingEditor instead. This will prompt the user to continue editing until validation succeeds or the edit is cancelled.

Simply create a schema and pass it to the editor:

schema := &mySchema{}
edit := editor.NewValidatingEditor(schema)

A schema must implement the Schema interface: https://godoc.org/github.com/confluentinc/go-editor#Schema

Example (Basic)
edit := editor.NewEditor()

// Simulate user making changes
edit.LaunchFn = func(command, file string) error {
	return os.WriteFile(file, []byte("something else here"), 0777)
}

contents, file, err := edit.LaunchTempFile("example", bytes.NewBufferString("something to be edited\n"))
defer os.Remove(file)
if err != nil {
	fmt.Println("error: " + err.Error())
	os.Exit(1)
}

fmt.Println(string(contents))
Output:

something else here
Example (Validating)
package main

import (
	"bytes"
	"fmt"
	"os"
	"strings"

	"github.com/confluentinc/go-editor"
)

// Example schema that expects a string prefix match
type prefixSchema struct {
	prefix string
}

func (s *prefixSchema) ValidateBytes(data []byte) error {
	if !strings.HasPrefix(string(data), s.prefix) {
		return fmt.Errorf("data missing prefix")
	}
	return nil
}

func main() {
	schema := &prefixSchema{prefix: "something"}
	edit := editor.NewValidatingEditor(schema)

	// Simulate user making changes
	edit.LaunchFn = func(command, file string) error {
		return os.WriteFile(file, []byte("something else here"), 0777)
	}

	obj := bytes.NewBufferString("something else")

	contents, file, err := edit.LaunchTempFile("example", obj)
	defer os.Remove(file)
	if err != nil {
		fmt.Println("error: " + err.Error())
		os.Exit(1)
	}

	fmt.Println(string(contents))
}
Output:

something else here

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BasicEditor

type BasicEditor struct {
	Command string
	// this is only for testing
	LaunchFn func(command, file string) error
}

BasicEditor launches an editor given by a specific command.

func NewEditor

func NewEditor() *BasicEditor

NewEditor launches an instance of the users preferred editor. The editor to use is determined by reading the $VISUAL and $EDITOR environment variables. If neither of these are present, vim or notepad (on Windows) is used.

func (*BasicEditor) Launch

func (e *BasicEditor) Launch(file string) error

Launch opens the given file path in the external editor or returns an error.

func (*BasicEditor) LaunchTempFile

func (e *BasicEditor) LaunchTempFile(prefix string, r io.Reader) ([]byte, string, error)

LaunchTempFile launches the users preferred editor on a temporary file. This file is initialized with contents from the provided stream and named with the given prefix.

Returns the modified data, the path to the temporary file so the caller can clean it up, and an error.

A file may be present even when an error is returned. Please clean it up.

type CancelEditingFn

type CancelEditingFn func() (bool, error)

CancelEditingFn is a function with which you can cancel editing and provide a suitable error message.

type ErrEditing

type ErrEditing error

ErrEditing represents an editing error

type PreserveFileFn

type PreserveFileFn func(data []byte, file string, err error) ([]byte, string, error)

PreserveFileFn is a function with which you can inspect the preserved file, edited data, and resulting error.

type Schema

type Schema interface {
	ValidateBytes(data []byte) error
}

Schema is an interface for validating data.

type ValidatingEditor

type ValidatingEditor struct {
	*BasicEditor

	// Schema is used to validate the edited data.
	Schema Schema

	// InvalidFn is called when a Schema fails to validate data.
	InvalidFn ValidationFailedFn
	// OriginalUnchangedFn is called when no changes were made from the original data.
	OriginalUnchangedFn CancelEditingFn
	// EmptyFileFn is called when the edited data is (effectively) empty; the file doesn't have any uncommented lines (ignoring whitespace)
	EmptyFileFn CancelEditingFn
	// PreserveFileFn is called when a non-recoverable error has occurred and the users edits have been preserved in a temp file.
	PreserveFileFn PreserveFileFn

	// CommentChars is a list of comment string prefixes for determining "empty" files. Defaults to "#" and "//".
	CommentChars []string
}

ValidatingEditor is an Editor which validates data against a schema. This will prompt the user to continue editing until validation succeeds or the edit is cancelled.

func NewValidatingEditor

func NewValidatingEditor(schema Schema) *ValidatingEditor

NewValidatingEditor returns a new ValidatingEditor.

This extends the BasicEditor with schema validation capabilities.

func (*ValidatingEditor) LaunchTempFile

func (e *ValidatingEditor) LaunchTempFile(prefix string, obj io.Reader) ([]byte, string, error)

LaunchTempFile launches the users preferred editor on a temporary file. This file is initialized with contents from the provided stream and named with the given prefix.

Returns the modified data, the path to the temporary file so the caller can clean it up, and an error.

A file may be present even when an error is returned. Please clean it up.

The last byte of "obj" must be a newline to cancel editing if no changes are made. (This is because many editors like vim automatically add a newline when saving.)

type ValidationFailedFn

type ValidationFailedFn func(error) error

ValidationFailedFn is a function with which you can handle a validation error.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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