Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addition of path substitution #61

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ The following is given by running `reflex -h`:
Usage: reflex [OPTIONS] [COMMAND]

COMMAND is any command you'd like to run. Any instance of {} will be replaced
with the filename of the changed file. (The symbol may be changed with the
--substitute flag.)
with the filename of the changed file and || will be replaced with the path to the changed file. (The symbol may be changed with the
--substitute or --pathsubstitute flag.)

OPTIONS are given below:
--all=false:
Expand All @@ -50,6 +50,9 @@ OPTIONS are given below:
Only match directories (not files).
--only-files=false:
Only match files (not directories).
--pathsubstitute="||":
The substitution symbol that is replace with the path to the file
in a command.
-r, --regex=[]:
A regular expression to match filenames. (May be repeated.)
-e, --sequential=false:
Expand Down Expand Up @@ -117,8 +120,8 @@ changed.
### Substitution

Reflex provides a way for you to determine, inside your command, what file
changed. This is via a substitution symbol. The default is `{}`. Every instance
of the substitution symbol inside your command is replaced by the filename.
changed and the path to the file. This is via a substitution symbol. The default is `{}` for files and `||` for paths. Every instance
of the substitution symbol inside your command is replaced by the filename or path.

As a simple example, suppose you're writing Coffeescript and you wish to compile
the CS files to Javascript when they change. You can do this with:
Expand Down Expand Up @@ -332,3 +335,4 @@ background on this issue.
* Rich Liebling ([rliebling](https://github.com/rliebling))
* Seth W. Klein ([sethwklein](https://github.com/sethwklein))
* Vincent Vanackere ([vanackere](https://github.com/vanackere))
* Richard Cox ([Khabi](https://github.com/Khabi))
4 changes: 4 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Config struct {
inverseRegexes []string
inverseGlobs []string
subSymbol string
subPathSymbol string
startService bool
shutdownTimeout time.Duration
onlyFiles bool
Expand All @@ -42,6 +43,9 @@ func (c *Config) registerFlags(f *flag.FlagSet) {
f.StringVar(&c.subSymbol, "substitute", defaultSubSymbol, `
The substitution symbol that is replaced with the filename
in a command.`)
f.StringVar(&c.subPathSymbol, "pathsubstitute", defaultSubPathSymbol, `
The substitution symbol that is replace with the path to the file
in a command.`)
f.BoolVarP(&c.startService, "start-service", "s", false, `
Indicates that the command is a long-running process to be
restarted on matching changes.`)
Expand Down
9 changes: 7 additions & 2 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func TestReadConfigs(t *testing.T) {
const in = `-g '*.go' echo {}
const in = `-g '*.go' echo {} ||

# Some comment here
-r '^a[0-9]+\.txt$' --only-dirs --substitute='[]' echo []
Expand All @@ -28,17 +28,19 @@ world"
}
want := []*Config{
{
command: []string{"echo", "{}"},
command: []string{"echo", "{}", "||"},
source: "test input, line 1",
globs: []string{"*.go"},
subSymbol: "{}",
subPathSymbol: "||",
shutdownTimeout: 500 * time.Millisecond,
},
{
command: []string{"echo", "[]"},
source: "test input, line 4",
regexes: []string{`^a[0-9]+\.txt$`},
subSymbol: "[]",
subPathSymbol: "||",
shutdownTimeout: 500 * time.Millisecond,
onlyDirs: true,
},
Expand All @@ -47,6 +49,7 @@ world"
source: "test input, line 6",
globs: []string{"*.go"},
subSymbol: "{}",
subPathSymbol: "||",
startService: true,
shutdownTimeout: 500 * time.Millisecond,
onlyFiles: true,
Expand All @@ -59,6 +62,7 @@ world"
inverseRegexes: []string{"baz"},
inverseGlobs: []string{"b", "c"},
subSymbol: "{}",
subPathSymbol: "||",
shutdownTimeout: 500 * time.Millisecond,
},
}
Expand All @@ -76,6 +80,7 @@ func TestReadConfigsBad(t *testing.T) {
"--substitute='' echo hi",
"-s echo {}",
"--only-files --only-dirs echo hi",
"--pathsubstitute='' echo hi",
} {
r := strings.NewReader(in)
if configs, err := readConfigsFromReader(r, "test input"); err == nil {
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
)

const defaultSubSymbol = "{}"
const defaultSubPathSymbol = "||"

var (
reflexes []*Reflex
Expand Down
63 changes: 36 additions & 27 deletions reflex.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"
Expand All @@ -18,16 +19,17 @@ import (

// A Reflex is a single watch + command to execute.
type Reflex struct {
id int
source string // Describes what config/line defines this Reflex
startService bool
backlog Backlog
matcher Matcher
onlyFiles bool
onlyDirs bool
command []string
subSymbol string
done chan struct{}
id int
source string // Describes what config/line defines this Reflex
startService bool
backlog Backlog
matcher Matcher
onlyFiles bool
onlyDirs bool
command []string
subSymbol string
subPathSymbol string
done chan struct{}

mu *sync.Mutex // protects killed and running
killed bool
Expand Down Expand Up @@ -56,9 +58,13 @@ func NewReflex(c *Config) (*Reflex, error) {
return nil, errors.New("substitution symbol must be non-empty")
}

if c.subPathSymbol == "" {
return nil, errors.New("path substitution symbol must be non-empty")
}

substitution := false
for _, part := range c.command {
if strings.Contains(part, c.subSymbol) {
if strings.Contains(part, c.subSymbol) || strings.Contains(part, c.subPathSymbol) {
substitution = true
break
}
Expand All @@ -83,18 +89,19 @@ func NewReflex(c *Config) (*Reflex, error) {
}

reflex := &Reflex{
id: reflexID,
source: c.source,
startService: c.startService,
backlog: backlog,
matcher: matcher,
onlyFiles: c.onlyFiles,
onlyDirs: c.onlyDirs,
command: c.command,
subSymbol: c.subSymbol,
done: make(chan struct{}),
timeout: c.shutdownTimeout,
mu: &sync.Mutex{},
id: reflexID,
source: c.source,
startService: c.startService,
backlog: backlog,
matcher: matcher,
onlyFiles: c.onlyFiles,
onlyDirs: c.onlyDirs,
command: c.command,
subSymbol: c.subSymbol,
subPathSymbol: c.subPathSymbol,
done: make(chan struct{}),
timeout: c.shutdownTimeout,
mu: &sync.Mutex{},
}
reflexID++

Expand All @@ -115,8 +122,9 @@ func (r *Reflex) String() string {
}
if !r.startService {
fmt.Fprintln(&buf, "| Substitution symbol", r.subSymbol)
fmt.Fprintln(&buf, "| Path Substitution Symbol", r.subPathSymbol)
}
replacer := strings.NewReplacer(r.subSymbol, "<filename>")
replacer := strings.NewReplacer(r.subSymbol, "<filename>", r.subPathSymbol, "<path>")
command := make([]string, len(r.command))
for i, part := range r.command {
command[i] = replacer.Replace(part)
Expand Down Expand Up @@ -245,8 +253,9 @@ func (r *Reflex) terminate() {
}
}

func replaceSubSymbol(command []string, subSymbol string, name string) []string {
replacer := strings.NewReplacer(subSymbol, name)
func replaceSubSymbol(command []string, subSymbol string, subPathSymbol string, name string) []string {
p := fmt.Sprintf("%s/", filepath.Dir(name))
replacer := strings.NewReplacer(subSymbol, name, subPathSymbol, p)
newCommand := make([]string, len(command))
for i, c := range command {
newCommand[i] = replacer.Replace(c)
Expand All @@ -259,7 +268,7 @@ var seqCommands = &sync.Mutex{}
// runCommand runs the given Command. All output is passed line-by-line to the
// stdout channel.
func (r *Reflex) runCommand(name string, stdout chan<- OutMsg) {
command := replaceSubSymbol(r.command, r.subSymbol, name)
command := replaceSubSymbol(r.command, r.subSymbol, r.subPathSymbol, name)
cmd := exec.Command(command[0], command[1:]...)
r.cmd = cmd

Expand Down