On 04.06.25 10:45, Will Godfrey wrote:
There is a bit of a puzzle when looking at the CLI for control.

...functions, sendDirect and sendNormal ...

These would be the obvious place for monitoring changes,

otherwise it
becomes a line-by-line examination :(


Regarding the topic of CLI : how to warn from CLI actions?
==========================================================

This is a tricky one.
Most notably, we should consider CLI scripts.
If the CLI now starts asking questions, we will *break existing scripts*.

Next, adding such a question-and-confirmation process, is not trivial,
given the way how the logic of the existing CLI is implemented, with
rather tight integration with the CLI parser.

Another consideration is "technical debt".
It might be compelling to save on effort now and just hook it into
some central dispatch function. I would rather refrain from going the
easy route though. Because this is the way how much of the complexity
in Yoshimi was generated over time.
Such a change can probably be cooked up in some hours, but will
be a constant pain for everyone trying to figure out how the
CLI works later on.

And on top, you will not save much work. In the central dispatch
functions, you will need some way to limit the effect to the real
changes. This will require to figure out which branches these are.



First question: do we really need that warning at the CLI ?

It is a trade-off: benefit for the user vs extra effort
and complexity in the code base.



What follows are some ideas how such a warning could be approached

- the structure should be very similar to what we use in the UI
- so it should be broken down into several basic functions
  + access InterChange with a read invocation to get the changed-state
  + maybe trigger a confirmation response
  + actually forward to the dispatch

The main difficulty we're facing is the way the CLI-Loop works.
It is point-and-shot. You enter something, you get a response
you enter the next command.

And we do that interwoven with the parsing of the input, which means,
we go down into a huge fan-out of conditions, and as soon as we can
figure out a response in one branch, we "fire" and break out of the
decision-fan and return to the top-level of the CLI loop.

We need now to insert an additional prompt-response and
retrieve an answer. In fact, there is a query() function, which
already does that; and it shows the problems, since it uses readline
directly and thus sneaks past the top-level CLI-loop -- notably this
means that shut-down of Yoshimi will be blocked by such a prompt/answer.


Anyway, how could that be integrated into the code with moderate effort?

What I would do: I would package the actual dispatch functions into a
Lambda function. Then we could write a special handler for such a situation.
That special handler
 - takes the actual dispatch as an (opaque) lambda
 - must check if we are running in Script evaluation and *not* prompt
   in that case
 - can then invoke a dedicated function (yes, please) which defines
   the logic which cases are actually writing changes to parts
 - and then it can invoke another dedicated function to query
   the InterChange if there is a pending "dirty" state in some part
 - and if so, then this function would go into query() and await
   the user's confirmation
 - and after that, it would either (or not) invoke the lambda for
   the actual response.

Then, we'd have to go through all of InterChange.cpp and piggy-back
the existing invocation of either sendDirect or sendNormal and place
it into a body of a lambda function.

This is a change, that can be made with a cleverly crafted regular
expression, with manual watch and possibly some adjustments here and there.
So that should be doable with some hours of focussed attention.


To give an example (disclaimer: this is just a draft....)

the following code (InterChange.cpp, line 5467)


    if (input.matchnMove(2, "clear"))
    {
        if (controlType != TOPLEVEL::type::Write)
            return REPLY::writeOnly_msg;
        if (input.matchnMove(3, "all")) // clear entire part
            return sendNormal(synth, 0, npart,
                              controlType,
                              MAIN::control::defaultPart,
                              TOPLEVEL::section::main);
        return sendNormal(synth, 0, npart,
                          controlType,
                          MAIN::control::defaultInstrument,
                          TOPLEVEL::section::main);
    }



...would be rewritten to:



    if (input.matchnMove(2, "clear"))
    {
        if (controlType != TOPLEVEL::type::Write)
            return REPLY::writeOnly_msg;
        if (input.matchnMove(3, "all")) // clear entire part
            return promptSend("really clear complete part?"
                             ,needsPrompt(controlType, npart)
                             ,[&]{return sendNormal(synth, 0, npart,
                                             controlType,
                                             MAIN::control::defaultPart,
                                             TOPLEVEL::section::main);
                             });
                          }
        return promptSend("really clear?"
                         ,needsPrompt(controlType, npart)
                         ,[&]{return sendNormal(synth, 0, npart,
                                         controlType,
                                         MAIN::control::defaultInstrument,
                                         TOPLEVEL::section::main);
                         });
    }


...and that function would have the following skeleton:

template<class FUN>
int promptSend(string msg, bool shouldPrompt, FUN&& dispatch)
{
    if (shouldPrompt and not isScriptRun)
       if (not promptAndConfirm(msg))
           return REPLY::failed_msg;

    return dispatch();
}



Note: I have added a function "needsPrompt(...)" with the logic to decide
if we need to ask. Depending on the practical circumstances, it may or
may not be simpler just to bite the bullet and add that change only to
those invocations where it is necessary.




_______________________________________________
Yoshimi-devel mailing list
Yoshimi-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/yoshimi-devel

Reply via email to