Skip to content

Commit

Permalink
Merge pull request #290 from knqyf263/multiple-default-param-support
Browse files Browse the repository at this point in the history
Added multiple parameter support via two character separators
  • Loading branch information
RamiAwar authored Mar 5, 2024
2 parents a1ba29e + 95c69fe commit 19dc6b2
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 25 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ So I made it possible to register snippets with description and search them easi
# TOC

- [Main features](#main-features)
- [Parameters] (#parameters)
- [Examples](#examples)
- [Register the previous command easily](#register-the-previous-command-easily)
- [bash](#bash-prev-function)
Expand Down Expand Up @@ -70,12 +71,27 @@ So I made it possible to register snippets with description and search them easi
`pet` has the following features.

- Register your command snippets easily.
- Use variables in snippets.
- Use variables (with one or several default values) in snippets.
- Search snippets interactively.
- Run snippets directly.
- Edit snippets easily (config is just a TOML file).
- Sync snippets via Gist or GitLab Snippets automatically.

# Parameters
There are `<n_ways>` ways of entering parameters.

They can contain default values: Hello `<subject=world>`
defined by the equal sign.

They can even contain `<content=spaces & = signs>` where the default value would be \<content=<mark>spaces & = signs</mark>\>.

Default values just can't \<end with spaces \>.

They can also contain multiple default values:
Hello `<subject=|_John_||_Sam_||_Jane Doe = special #chars_|>`

The values in this case would be :Hello \<subject=\|\_<mark>John</mark>\_\|\|\_<mark>Sam</mark>\_\|\|\_<mark>Jane Doe = special #chars</mark>\_\|\>

# Examples
Some examples are shown below.

Expand Down
6 changes: 3 additions & 3 deletions dialog/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ var (
// This matches most encountered patterns
// Skips match if there is a whitespace at the end ex. <param='my >
// Ignores <, > characters since they're used to match the pattern
patternRegex = `<([^<>]*[^\s])>`
parameterStringRegex = `<([^<>]*[^\s])>`
)

func insertParams(command string, filledInParams map[string]string) string {
r := regexp.MustCompile(patternRegex)
r := regexp.MustCompile(parameterStringRegex)

matches := r.FindAllStringSubmatch(command, -1)
if len(matches) == 0 {
Expand All @@ -48,7 +48,7 @@ func insertParams(command string, filledInParams map[string]string) string {

// SearchForParams returns variables from a command
func SearchForParams(command string) [][2]string {
r := regexp.MustCompile(patternRegex)
r := regexp.MustCompile(parameterStringRegex)

params := r.FindAllStringSubmatch(command, -1)
if len(params) == 0 {
Expand Down
15 changes: 15 additions & 0 deletions dialog/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,21 @@ func TestSearchForParams_EqualsInDefaultValueIgnored(t *testing.T) {
}
}

func TestSearchForParams_MultipleDefaultValuesDoNotBreakFunction(t *testing.T) {
command := "echo \"<param=|_Hello_||_Hello world_||_How are you?_|> <second=Hello>, <third>\""
want := [][2]string{
{"param", "|_Hello_||_Hello world_||_How are you?_|"},
{"second", "Hello"},
{"third", ""},
}

got := SearchForParams(command)

if diff := deep.Equal(want, got); diff != nil {
t.Fatal(diff)
}
}

func TestInsertParams(t *testing.T) {
command := "<a=1> <a> <b> hello"

Expand Down
137 changes: 116 additions & 21 deletions dialog/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,102 @@ package dialog
import (
"fmt"
"log"
"regexp"

"github.com/awesome-gocui/gocui"
)

var (
layoutStep = 3
curView = -1

// This is for matching multiple default values in parameters
parameterMultipleValueRegex = `(\|_.*?_\|)`
)

func generateView(g *gocui.Gui, desc string, fill string, coords []int, editable bool) error {
if StringInSlice(desc, views) {
// createView sets up a new view with the given parameters.
func createView(g *gocui.Gui, name string, coords []int, editable bool) (*gocui.View, error) {
if StringInSlice(name, views) {
return nil, nil
}

v, err := g.SetView(name, coords[0], coords[1], coords[2], coords[3], 0)
if err != nil && err != gocui.ErrUnknownView {
return nil, err
}

v.Title = name
v.Wrap = true
v.Autoscroll = true
v.Editable = editable

views = append(views, name)

return v, nil
}

func generateSingleParameterView(g *gocui.Gui, name string, defaultParam string, coords []int, editable bool) error {
view, err := createView(g, name, coords, editable)
if err != nil {
return err
}

g.SetKeybinding(view.Name(), gocui.KeyCtrlK, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
v.Clear()
return nil
})

fmt.Fprint(view, defaultParam)
return nil
}

func generateMultipleParameterView(g *gocui.Gui, name string, defaultParams []string, coords []int, editable bool) error {
view, err := createView(g, name, coords, editable)
if err != nil {
return err
}

currentOpt := 0
maxOpt := len(defaultParams)

fmt.Fprint(view, defaultParams[currentOpt])

viewTitle := name
// Adjust view title to hint the user about the available
// options if there are more than one
if maxOpt > 1 {
viewTitle = name + " (UP/DOWN => Select default value)"
}
if v, err := g.SetView(desc, coords[0], coords[1], coords[2], coords[3], 0); err != nil {
if err != gocui.ErrUnknownView {
return err

view.Title = viewTitle

g.SetKeybinding(view.Name(), gocui.KeyArrowDown, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
if maxOpt == 0 {
return nil
}
fmt.Fprint(v, fill)
}
view, _ := g.View(desc)
view.Title = desc
view.Wrap = true
view.Autoscroll = true
view.Editable = editable
next := currentOpt + 1
if next >= maxOpt {
next = 0
}
v.Clear()
fmt.Fprint(v, defaultParams[next])
currentOpt = next
return nil
})

views = append(views, desc)
g.SetKeybinding(view.Name(), gocui.KeyArrowUp, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
if maxOpt == 0 {
return nil
}
prev := currentOpt - 1
if prev < 0 {
prev = maxOpt - 1
}
v.Clear()
fmt.Fprint(v, defaultParams[prev])
currentOpt = prev
return nil
})

return nil
}
Expand All @@ -51,20 +121,45 @@ func GenerateParamsLayout(params [][2]string, command string) {
leftX := (maxX / 2) - (maxX / 3)
rightX := (maxX / 2) + (maxX / 3)

generateView(g, "Command(TAB => Select next, ENTER => Execute command):",
generateSingleParameterView(g, "Command(TAB => Select next, ENTER => Execute command):",
command, []int{leftX, maxY / 10, rightX, maxY/10 + 5}, false)
idx := 0

// Create a view for each param
for _, pair := range params {
// Unpack parameter key and value
k, v := pair[0], pair[1]
generateView(g, k, v,
[]int{leftX,
(maxY / 4) + (idx+1)*layoutStep,
rightX,
(maxY / 4) + 2 + (idx+1)*layoutStep},
true)
parameterKey, parameterValue := pair[0], pair[1]

// Check value for multiple defaults
r := regexp.MustCompile(parameterMultipleValueRegex)
matches := r.FindAllStringSubmatch(parameterValue, -1)

if len(matches) > 0 {
// Extract the default values and generate multiple params view
parameters := []string{}
for _, p := range matches {
_, matchedGroup := p[0], p[1]
// Remove the separators
matchedGroup = matchedGroup[2 : len(matchedGroup)-2]
parameters = append(parameters, matchedGroup)
}
generateMultipleParameterView(
g, parameterKey, parameters, []int{
leftX,
(maxY / 4) + (idx+1)*layoutStep,
rightX,
(maxY / 4) + 2 + (idx+1)*layoutStep},
true)
} else {
// Generate single param view using the single value
generateSingleParameterView(g, parameterKey, parameterValue,
[]int{
leftX,
(maxY / 4) + (idx+1)*layoutStep,
rightX,
(maxY / 4) + 2 + (idx+1)*layoutStep},
true)
}
idx++
}

Expand Down

0 comments on commit 19dc6b2

Please sign in to comment.