gelisam / hawk

Haskell text processor for the command-line
Apache License 2.0
361 stars 20 forks source link

-i for in-place edit #171

Open gelisam opened 7 years ago

Rahel-A commented 6 years ago

We'll be looking at this issue in this fork: https://github.com/lars-olsson/hawk/tree/master

gelisam commented 6 years ago

Any progress? Can I help with anything?

Rahel-A commented 6 years ago

I'm currently trying to understand the api for OptionParser. Is there a way for the parser to not consume the arguments (so that the same argument could be used to create both the input and output spec.) I'm trying to write my syntax is as follows: hawk -i[suffix] "1+1" -f"file.txt" The input spec consumes the file argument, which prevents output spec from retrieving the input file name.

Rahel-A commented 6 years ago

A snippet of what I'm trying to do:

inputSpec :: (Functor m, Monad m)
          ⇒  CommonSeparators →  OptionParserT HawkOption m InputSpec
inputSpec (rSep, fSep) = InputSpec ↥ source ⊛ format
  where
    source = do 
        file ←  consumeLast Option.InputFile "" $ consumeNullable "" consumeString
        η $ if null file
            then UseStdin
            else InputFile file

outputSpec :: (Functor m, Monad m)
           ⇒  CommonSeparators →  OptionParserT HawkOption m OutputSpec
outputSpec (r, f) = OutputSpec ↥ sink ⊛ format
  where
    sink = do
        backupSuffix ←  consumeLast Option.InPlaceEdit "" $ consumeNullable "" consumeString
        file ←  consumeLast Option.InputFile "" $ consumeNullable "" consumeString
        if null file
            then if null backupSuffix
                then η UseStdout
                else fail "in-place edit requires input file"
            else η $ OutputFile file $ file ⧺ backupSuffix
gelisam commented 6 years ago

Is there a way for the parser to not consume the arguments

It would be possible to write a variants of consumeAll, consumeLast and consumeExtra which observe but do not modify the state. I don't recommend it though.

so that the same argument could be used to create both the input and output spec

The --field-delimiter and --record-delimiter options are also used by both the input and the output spec. In parseArgs, this is accomplished by first calling commonSeparators to consume those two options before passing the result to both the input and the output specs. This is the approach I would recommend.

gelisam commented 6 years ago

I'm trying to write my syntax is as follows:

hawk -i[suffix] "1+1" -f"file.txt"

Why -f? Is there something wrong with hawk's current approach of always putting the file name after the expression if any?

gelisam commented 6 years ago

A snippet of what I'm trying to do:

[...]
if null file
[...]

Is there a reason why you are using the empty string as a distinguished value instead of using a Maybe FilePath?

Rahel-A commented 6 years ago

Why -f? Is there something wrong with hawk's current approach of always putting the file name after the expression if any?

This was just helping me understand how to explicitly parse arguments. But it still seems like I might need it because I believe you can input a list of files? So I'd would like to just be able to work with single files for now.

Is there a reason why you are using the empty string as a distinguished value instead of using a Maybe FilePath?

Adding consumeNullable changes the type from Maybe String to String

The --field-delimiter and --record-delimiter options are also used by both the input and the output spec. In parseArgs, this is accomplished by first calling commonSeparators to consume those two options before passing the result to both the input and the output specs. This is the approach I would recommend.

Yeah this approach seems to work well for me, thanks! I'll post an update later.

gelisam commented 6 years ago

I believe you can input a list of files?

No, that feature is not yet implemented, see #13 and #50. Currently you can only specify zero files, in which case you'll get stdin, or one file, in which case you'd get that file.

I wonder if using -i with stdin should be an error, or if writing to stdout should be considered close enough to "modifying stdin in-place"?

gelisam commented 6 years ago

Oh, and even if if was possible to use a list of files, I still don't think adding a -f option would be necessary; instead, it would be an error to use -i with more than one file.

gelisam commented 6 years ago

Adding consumeNullable changes the type from Maybe String to String

Ah, you're right! I don't remember why I designed it that way, it looks like a mistake now.