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

Brace expansions #366

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Brace expansions #366

wants to merge 5 commits into from

Conversation

georgebrock
Copy link
Collaborator

e.g. :echo h{i,o}p produces hip hop.

The implementation differs from GNU Bash and other general purpose shells in a few ways. First, there are a few deliberate things that I think improve the consistency of the language:

  • Single-option expansions behave like strings, e.g. {x} is like 'x'.
  • Zero-option expansions behave like empty strings, e.g. {} is like ''.
  • Unclosed braces cause a parse error, not a literal brace.
  • Spaces inside of expansions don't need to be escaped.
  • Quotes inside of expansions are literals not string delimiters, which mirrors the fact that braces inside of quoted strings are literals not expansion delimiters.

There are also a couple of differences that we might want to consider changing:

  • Expansions can't be used inside of variable names, e.g. in GNU Bash echo $FOO{1,2} is like echo $FOO1 $FOO2 (but echo ${FOO1,FOO2} is an error).

To support this new behaviour this PR also updates all of the various Gitsh::Arguments::* classes'
#value methods to produce Arrays instead of single values. The Gitsh::ArgumentList class takes on the responsibility of flattening the results.

Begins to address #145

spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/arguments/brace_expansion_spec.rb Show resolved Hide resolved
:LEFT_BRACE
end
end
rule(/#{BRACE_EXPANSION_ESCAPABLES.to_negative_regexp}+/, :brace_expansion) do |t|

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [86/80]

lib/gitsh/lexer.rb Show resolved Hide resolved
@georgebrock georgebrock added this to the v0.15 milestone Apr 6, 2019
Copy link
Contributor

@sharplet sharplet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it! Left a comments for things I noticed

lib/gitsh/argument_list.rb Outdated Show resolved Hide resolved
lib/gitsh/arguments/brace_expansion.rb Outdated Show resolved Hide resolved
gitsh.type(':echo f{{e,i,o}e,um}')

expect(gitsh).to output_no_errors
expect(gitsh).to output(/fee fie foe fum/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

spec/units/lexer_spec.rb Outdated Show resolved Hide resolved
spec/units/lexer_spec.rb Outdated Show resolved Hide resolved
@georgebrock
Copy link
Collaborator Author

I've been using this version for a few days, and I think I want to revisit some of the deliberate differences from how GNU Bash et al. handle things. Specifically these two:

  • Single-option expansions behave like strings, e.g. {x} is like 'x'.
  • Zero-option expansions behave like empty strings, e.g. {} is like ''.

I'll frequently diff my local branch against upstream, e.g. after a complex interactive rebase to make sure I didn't introduce accidental changes. The easiest way I know of doing that is diff @{u} @. After this change, that's pretty cumbersome (either diff '@{u}' @ or diff @\{u\}).

There are other similar shorthands, which I use less often but others might not, like @{-1}.

Three solutions I can think of:

  1. Do the Bash-like thing, and treat {} or {x} as literals, only doing expansion when there's at least one comma.
  2. Introduce a magic variable for upstream, e.g. $_u, and advise gitsh users to use that.
  3. Leave things as they are, alias diff @{u} @ to diffu for my own use case, and trust others will find similar work arounds.

Following the principal of least astonishment, I'm inclined to do the first one.

What do other gitsh users things? /cc @sharplet @mike-burns

@mike-burns
Copy link
Contributor

Huh I had no idea:

~% echo a.{b,c}
a.b a.c
~% echo a.{b}
a.{b}
~% echo a.{} 
a.{}

(zsh)

I'm in favor of that, option (1).

@sharplet
Copy link
Contributor

sharplet commented Apr 9, 2019

I’m also in favour of option 1. I regularly use this syntax to inspect my stash:

git show stash@{1} # show me the next stash below the top

@georgebrock georgebrock changed the base branch from arg0-aint-special to master August 2, 2019 17:55
spec/units/parser_spec.rb Outdated Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Outdated Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Outdated Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Outdated Show resolved Hide resolved
lib/gitsh/parser.rb Show resolved Hide resolved
lib/gitsh/parser.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
spec/units/parser_spec.rb Show resolved Hide resolved
lib/gitsh/parser.rb Show resolved Hide resolved
@georgebrock
Copy link
Collaborator Author

Finally got back to this and (pairing with @seanpdoyle) figured out how to make this behave more like general-purpose shells. As of now:

  • Two or more items in braces will expand, e.g. {a,b}1 is equivalent to a1 b1
  • One item in braces will not expand, e.g. @{u} is a literal argument
  • Empty braces will not expand, e.g. {} is a literal argument

We had to make some minor compromises to get there, specifically an unmatched brace is now a parse error, but it's possible to work around by using an escaped brace e.g.

gitsh@ :echo }
gitsh: parse error
gitsh@ :echo \}
}

Expect the first param of a multi-line call to be indented one step more
than the previous line.
Let's stick with `[:foo, :bar]`; I find it easier to read than `%i[foo bar]`
This commit updates all of the various `Gitsh::Arguments::*` classes'
`#value` methods to produce Arrays instead of single values. The
`Gitsh::ArgumentList` class takes on the responsibility of flattening the
results.

This change paves the way for various kinds of globbing and argument
expansion which could allow a single argument to produce multiple values.
For example, in GNU Bash the argument `h{i,o}p` would be expanded to the two
arguments `hip hop`.
mv foo_{old,new}.txt
mv foo_old.txt foo_new.txt
.Ed
.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention the special cases in here?

Copy link
Contributor

@mike-burns mike-burns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything in here makes sense to me.

e.g. `:echo h{i,o}p` produces `hip hop`.

This is similar to GNU Bash and other general-purpose shells:

- Expansions can be nested.
- Braces with one option do not expand, e.g. `{x}` is a literal.
- Empty braces to not expand, i.e. `{}` is a literal.

And different in a few ways:

- Spaces inside of expansions don't need to be escaped.
- Quotes inside of expansions are literals, not string delimiters.
- $s inside of expansions are literals, not variable delimiters.
- Expansions can't be used inside of variable names.
- Un-matched braces must be escaped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants