Skip to content

Commit

Permalink
Merge pull request #16 from hashibuto/prepare_v2
Browse files Browse the repository at this point in the history
update to v1
  • Loading branch information
hashibuto authored Sep 28, 2024
2 parents 670662e + 323f1df commit bb90483
Show file tree
Hide file tree
Showing 30 changed files with 1,845 additions and 790 deletions.
53 changes: 35 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,48 @@ What it doesn't do

- Any sort of argument parsing / tokenization

For a full CLI parser implementation using nilshell, check out [Artillery](https://github.com/hashibuto/artillery)
For a full CLI parser implementation using nilshell, check out [Commander](https://github.com/hashibuto/commander)

## Usage

```
import "github.com/hashibuto/nilshell"
import (
ns "github.com/hashibuto/nilshell"
)
ns := NewNilShell(
"» ",
func(beforeCursor, afterCursor string, full string) []*ns.AutoComplete {
// Autocompletion happens here, perhaps tokenization, and the last token before the cursor is
// fed to a lookup to find potential matches
return nil
},
func(ns *ns.NilShell, cmd string) {
// Perform tokenization, command lookup, and execution
config := ns.ReaderConfig{
CompletionFunction: func(beforeCursor string, afterCursor string, full string) *ns.Suggestions {
// This is where you would return tab completion suggestions based on the input before the cursor, perhaps after the
// cursor, or even the entire line buffer.
return &ns.Suggestions{
Total: 0
Items: []*ns.Suggestion{}
}
},
)
// Attach saved command history
ns.History = NewHistory(myLoadedHistory)
ProcessFunction: func(text string) error {
// text contains the command to be processed by your own command interpreter
return nil
}
// implement your own history manager if you want to persist history across multiple invocations, or use the default (nil)
HistoryManager: nil,
PromptFunction: func() string {
// Return a prompt with terminal escape chars to add style
return "$ "
}
Debug: false,
// enable the log file to dump debugging info to a tailable log file
LogFile: "",
}
ns.ReadUntilTerm()
r := NewReader(config)
// Save command history
myHistory := ns.History.Export()
// write myHistory it to disk
// block until the process captures SIGINT or SIGTERM
r.ReadLoop()
```
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.2.7
v1.0.0
94 changes: 94 additions & 0 deletions basichistorymanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package ns

import (
"sort"

"github.com/hashibuto/nimble"
)

type BasicHistoryManager struct {
index *nimble.IndexedDequeue
maxKeep int
prev string
}

type BasicHistoryIterator struct {
iter *nimble.IndexedDequeIterator
}

func (bhi *BasicHistoryIterator) Forward() string {
if bhi.iter == nil {
return ""
}

bhi.iter.Next()
return bhi.iter.Value()
}

func (bhi *BasicHistoryIterator) Backward() string {
if bhi.iter == nil {
return ""
}

bhi.iter.ReverseNext()
return bhi.iter.Value()
}

func NewBasicHistoryManager(maxKeep int) *BasicHistoryManager {
return &BasicHistoryManager{
index: nimble.NewIndexedDequeue(),
maxKeep: maxKeep,
}
}

func (h *BasicHistoryManager) Push(value string) {
if value == h.prev {
return
}
h.prev = value
h.index.Push(value)
if h.index.Size() > h.maxKeep {
h.index.Pop()
}
}

func (h *BasicHistoryManager) GetIterator() HistoryIterator {
if h.index.Size() > 0 {
return &BasicHistoryIterator{
iter: h.index.GetIter(),
}
}

return &BasicHistoryIterator{}
}

func (h *BasicHistoryManager) Search(pattern string) []string {
if h.index.Size() == 0 {
return nil
}

if len(pattern) == 0 {
return nil
}

links := h.index.Find(pattern)
if len(links) == 0 {
return nil
}

// sort from most recent to oldest
sort.Slice(links, func(i, j int) bool {
return links[i].CreatedAt.After(links[j].CreatedAt)
})

strs := make([]string, len(links))
for i, link := range links {
strs[i] = link.Value
}

return strs
}

func (h *BasicHistoryManager) Exit() {

}
91 changes: 55 additions & 36 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,86 @@ import (
"strings"

ns "github.com/hashibuto/nilshell"
"github.com/hashibuto/nimble"
)

var ac []*ns.AutoComplete = []*ns.AutoComplete{
var suggestions = []*ns.Suggestion{
{
Value: "helpur",
Display: "helpur (-h / --help)",
Value: "carrot",
Display: "carrot (is an orange vegetable)",
},
{
Value: "helpinhand",
Display: "helpinhand (-g / --helper) awesome helpster",
Value: "cucumber",
Display: "cucumber (green and refreshing)",
},
{
Value: "helping",
Display: "helping (-g / --helper) helping of fiber a day",
Value: "zucchini",
Display: "zuccini (a kind of squash)",
},
{
Value: "dog",
Display: "dog",
Value: "tomato",
Display: "tomato (great on salad)",
},
{
Value: "doggy",
Display: "doggy",
Value: "pommodori",
Display: "pommodori (a kind of tomato)",
},
{
Value: "doggo",
Display: "doggo",
Value: "pepper",
Display: "pepper (green or red)",
},
{
Value: "big1",
Display: "big column is a big column, let's see how much we can fit into it to make it work",
Value: "paprika",
Display: "paprika",
},
{
Value: "big2",
Display: "big column is a big column, let's see how much we can fit into it to make it work",
Value: "tom-and-jerry",
Display: "tom-and-jerry (cat and mouse)",
},
{
Value: "big3",
Display: "big column is a big column, let's see how much we can fit into it to make it work",
Value: "zoo",
Display: "zoo",
},
{
Value: "big4",
Display: "big column is a big column, let's see how much we can fit into it to make it work",
Value: "papa",
Display: "papa (father)",
},
{
Value: "cuckoo-clock",
Display: "cuckoo-clock (a clock with bird sound)",
},
{
Value: "cartographer",
Display: "cartographer (one who does mapping?)",
},
}

func main() {
shell := ns.NewShell(
"\033[33m » \033[0m",
func(beforeCursor, afterCursor string, full string) []*ns.AutoComplete {
newAc := []*ns.AutoComplete{}
for _, acItem := range ac {
if strings.HasPrefix(acItem.Value, beforeCursor) {
newAc = append(newAc, acItem)
}
}
func completer(beforeCursor, afterCursor, full string) *ns.Suggestions {
x := strings.ToLower(beforeCursor)
suggs := nimble.Filter[*ns.Suggestion](func(index int, v *ns.Suggestion) bool {
return strings.HasPrefix(strings.ToLower(v.Value), x)
}, suggestions...)

return &ns.Suggestions{
Total: len(suggs),
Items: suggs,
}
}

return newAc
func main() {
r := ns.NewReader(ns.ReaderConfig{
Debug: true,
LogFile: "/tmp/log.txt",
ProcessFunction: func(s string) error {
fmt.Println("got command")
return nil
},
func(ns *ns.NilShell, cmd string) {
fmt.Println("Executed a command")
CompletionFunction: completer,
HistoryManager: ns.NewPersistedHistoryManager(10, "/tmp/example.hist"),
PromptFunction: func() string {
return "$ "
//return fmt.Sprintf("%s$%s ", termutils.CreateFgColor(0, 255, 255), termutils.STYLE_RESET)
},
)
shell.ReadUntilTerm()
})
r.ReadLoop()
}
45 changes: 45 additions & 0 deletions filelock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ns

import (
"os"

"golang.org/x/sys/unix"
)

type FileLock struct {
filename string
f *os.File
}

func NewFileLock(filename string) *FileLock {
return &FileLock{
filename: filename,
}
}

func (fl *FileLock) Lock() error {
f, err := os.OpenFile(fl.filename, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return err
}

fl.f = f

err = unix.Flock(int(fl.f.Fd()), unix.LOCK_EX)
if err != nil {
return err
}

return nil
}

func (fl *FileLock) Unlock() error {
defer fl.f.Close()

err := unix.Flock(int(fl.f.Fd()), unix.LOCK_UN)
if err != nil {
return err
}

return nil
}
38 changes: 38 additions & 0 deletions filelock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ns

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestFileLockSeq(t *testing.T) {
l := NewFileLock("/tmp/test.lock")
err := l.Lock()
assert.NoError(t, err)
err = l.Unlock()
assert.NoError(t, err)

err = l.Lock()
assert.NoError(t, err)
err = l.Unlock()
assert.NoError(t, err)
}

func TestFileLockConcur(t *testing.T) {
l := NewFileLock("/tmp/test.lock")
err := l.Lock()
assert.NoError(t, err)
go func() {
time.Sleep(1 * time.Second)
err = l.Unlock()
assert.NoError(t, err)
}()

l2 := NewFileLock("/tmp/test.lock")
err = l2.Lock()
assert.NoError(t, err)
err = l2.Unlock()
assert.NoError(t, err)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.19
require golang.org/x/term v0.2.0

require (
github.com/hashibuto/nimble v0.6.2
github.com/stretchr/testify v1.9.0
golang.org/x/sys v0.2.0
)
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashibuto/nimble v0.6.0 h1:a4XPlqx+AyLZg0krZyclJP7FC0ujOGP0Onkfh9VgtIc=
github.com/hashibuto/nimble v0.6.0/go.mod h1:Y0OSUCdlIjuEiwHV0lFE5fWsadw3fB8X83LhGhKpq6s=
github.com/hashibuto/nimble v0.6.1 h1:D6RfE0Ts9xjN/OVyBvk8V3d2tIyDRM46nT1PpSOXIBc=
github.com/hashibuto/nimble v0.6.1/go.mod h1:Y0OSUCdlIjuEiwHV0lFE5fWsadw3fB8X83LhGhKpq6s=
github.com/hashibuto/nimble v0.6.2 h1:uL1vUCGCooxFCai2GNmDrGGVAK3ky2Q5lFH4foD7MCY=
github.com/hashibuto/nimble v0.6.2/go.mod h1:Y0OSUCdlIjuEiwHV0lFE5fWsadw3fB8X83LhGhKpq6s=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
Expand Down
Loading

0 comments on commit bb90483

Please sign in to comment.