Include Cursor Movement in Keystroke Alias

Jan 11, 2022
20
1
Hello!

Is there any way to include cursor movement in a keystroke alias? As an example:

Code:
alias @Alt-i=%@UserFunc1[]

but I'd like the cursor to end up between the square brackets rather than at the end?

Thanks,
Jesse
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
4,576
97
Albuquerque, NM
prospero.unm.edu
Seems like it ought to be possible with ANSI sequences, but I haven't been able to make it work. Vincent?

It would be pretty easy in a plugin. A plugin key handler is passed a structure containing the command line and the cursor position, which it can modify at will.
 
May 20, 2008
11,840
120
Syracuse, NY, USA
I doubt CONHOST will act on ANSI sequences in the input (or maybe even know about them). Even if it did, TCC wouldn't know about it and KEYINFO's idea of the current position will be incorrect.

And I'm not sure how easy/hard it'd be in a plugin. If you were going to do it anywhere except the end of a command line, I suppose the plugin would have to check for insert/overstrike and act accordingly.
 
Jan 11, 2022
20
1
Thanks for your replies - I can probably live with typing the close bracket myself, heh heh.
 

ben

Jan 3, 2012
48
6
UK
I used to have some keystroke aliases that included cursor movement.

I assigned control characters to line editing commands, in:
OPTION | Keyboard | Category: Editing

The assignments appeared in in TCMD.INI under [Keys], for example:
BeginLine = Home Ctrl-Q EndLine = End Ctrl-T Left = Left Ctrl-U

In versions from the distant past you could include those control characters literally in aliases, and when executing those aliases, TCC would interpret the control characters according to those assignments. Sadly, that doesn't work any more: TCC now enters the control characters into the command literally.
 
May 20, 2008
11,840
120
Syracuse, NY, USA
Thanks for your replies - I can probably live with typing the close bracket myself, heh heh.
I'm in INSERT mode everywhere. When I type @FUNCTIONs names (command line or editing a BTM) I almost always type both brackets and a <Left>. That helps keep track of things if the line is long and (especially) if there are nested functions.
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
4,576
97
Albuquerque, NM
prospero.unm.edu
How about this: Typing an open bracket or an open parenthesis automatically inserts the matching close mark to the right of the cursor? Something like that would be trivial to implement.
 
May 20, 2008
11,840
120
Syracuse, NY, USA
How about this: Typing an open bracket or an open parenthesis automatically inserts the matching close mark to the right of the cursor? Something like that would be trivial to implement.
Won't it be tedious if not at the end of the command line (dealing with insert/overstrike mode as needed)? And if end of command line is required, that will preclude building nested thingies from the outside in.

If I'm missing something and it really would be trivial, you could do the same for double quotes, backticks, and braces.
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
4,576
97
Albuquerque, NM
prospero.unm.edu
Won't it be tedious if not at the end of the command line (dealing with insert/overstrike mode as needed)? And if end of command line is required, that will preclude building nested thingies from the outside in.

If I'm missing something and it really would be trivial, you could do the same for double quotes, backticks, and braces.

I think we can safely ignore overstrike mode. Anyone who uses overstrike by default would not use such a feature in the first place. ("Please automatically supply a close bracket, so I can overwrite it with my next keystroke"?)

I'm attaching a ditzy demo plugin, with no documentation. There is almost nothing to it, though I did add a check for overstrike mode so you can see the doomed punctuation mark for the fraction of a second before you hit the next key.

Whether this feature would actually be useful to anyone is open to question.
 

Attachments

  • autobrackets.zip
    35.7 KB · Views: 8
May 20, 2008
11,840
120
Syracuse, NY, USA
Autobrackets ... interesting ... simple ... and it works I've started one that will use SendInput (letting TCC worry about the tail). If I'm still so inclined after dinner, I'll get back to it.
 
May 20, 2008
11,840
120
Syracuse, NY, USA
Autobrackets ... interesting ... simple ... and it works I've started one that will use SendInput (letting TCC worry about the tail). If I'm still so inclined after dinner, I'll get back to it.
That was going to be too hard. So I did it as you did, Charles. And, I did it for all of these (below). I'll just leave it in place and see if it enhances my life.

Code:
struct OPEN_CLOSE_PAIR { WCHAR opener; WCHAR closer; };

OPEN_CLOSE_PAIR  oc[5] = { {L'[', L']'} , {L'(', L')'} , {L'\"', L'\"'} , {L'{', L'}'} , {L'`', L'`'} };
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
4,576
97
Albuquerque, NM
prospero.unm.edu
I'm a bit leery of quotes because the open quote and the close quote are the same character. If you want to determine whether the quote the user typed is an opener or a closer, you have to look at the context.

I'm thinking maybe: If the user typed a quote, look at the previous character. If it's whitespace, or a colon, equals sign, or open bracket, or if you're at the start of the line (there is no previous character), then it's an opener and should be closed. Anything else, ignore it. Does that make sense to you? Too simpleminded?
 
May 20, 2008
11,840
120
Syracuse, NY, USA
I'm a bit leery of quotes because the open quote and the close quote are the same character. If you want to determine whether the quote the user typed is an opener or a closer, you have to look at the context.

I'm thinking maybe: If the user typed a quote, look at the previous character. If it's whitespace, or a colon, equals sign, or open bracket, or if you're at the start of the line (there is no previous character), then it's an opener and should be closed. Anything else, ignore it. Does that make sense to you? Too simpleminded?
Hmmm! I don't know. Presumably, the user wouldn't be typing a close quote if it had already been done for him. Yes/no?

A problem would arise if I wanted to quote a portion of an existing command line (maybe a recalled one that failed because something needed quoting?).

Maybe I'll add the single quote. It's nothing special (?). I'm pretty sure I only use it to quote a string inside a WMI query.

And maybe something like @jpavel's original idea ... use Alt-(, Alt-[ (et cetera) when you want an automatic closer.
 
May 20, 2008
11,840
120
Syracuse, NY, USA
And maybe something like @jpavel's original idea ... use Alt-(, Alt-[ (et cetera) when you want an automatic closer.
Nah! That would be too hard to use. The user would have to press (and the plugin handle) all these.

Code:
pszKey = Alt-`
pszKey = Alt-[
pszKey = Alt-'
pszKey = Alt-Shift-9
pszKey = Alt-Shift-[
pszKey = Alt-Shift-'
 
May 20, 2008
11,840
120
Syracuse, NY, USA
Quite true.

I haven't programmed in a while so I'm enjoying fooling around. I don't get to use the comma operator often; when I do I feel good about it. This has got to be at least a little more efficient that a counted loop and an array of open/close pairs.

Code:
    WCHAR c;
    if  (   lpki->nKey == ( c=L')'  , L'('  )    ||
            lpki->nKey == ( c=L']'  , L'['  )    ||
            lpki->nKey == ( c=L'}'  , L'{'  )    ||
            lpki->nKey == ( c=L'`'  , L'`'  )    ||
            lpki->nKey == ( c=L'\"' , L'\"' )    ||
            lpki->nKey == ( c=L'\'' , L'\'' )
        )
    {
        AddCloser(lpki, c);
        return 0;
    }
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
4,576
97
Albuquerque, NM
prospero.unm.edu
Code:
    WCHAR c;
    if  (   lpki->nKey == ( c=L')'  , L'('  )    ||
            lpki->nKey == ( c=L']'  , L'['  )    ||
            lpki->nKey == ( c=L'}'  , L'{'  )    ||
            lpki->nKey == ( c=L'`'  , L'`'  )    ||
            lpki->nKey == ( c=L'\"' , L'\"' )    ||
            lpki->nKey == ( c=L'\'' , L'\'' )
        )
    {
        AddCloser(lpki, c);
        return 0;
    }

So let me check my understanding. You're assigning a value to c for each comparison. But if the comparison is true, it short-circuits any remaining comparisons, and therefore any further assignments. The last assignment was done by the first (only) comparison which returned true.

And if none are true, then you've done six (wrong) assignments, but it doesn't matter because c is only used if one of your comparisons was true?

Kinda makes my head hurt.
 
May 20, 2008
11,840
120
Syracuse, NY, USA
Yup, that's how it works. It's certainly no worse than a counted loop (where you keep incrementing a counter, possibly for no good reason). The only reason I think it might be more efficient is that in evaluating ( lpki->nKey == oc[n] ) (vs. lpki->nKey == constant) is that you've got to add to the oc pointer each time).
 
Last edited:
May 20, 2008
11,840
120
Syracuse, NY, USA
Hey Charles ... speaking of headaches, how about this.

Code:
VOID AddCloser(LPKEYINFO lpki, WCHAR closer)
{
    WCHAR    *p = lpki->pszCurrent;

    while ( *p++ != UNICODE_NULL );

    while ( p > lpki->pszCurrent )
    {
        *p = *(p-1);
        p -= 1;
    }

    *p = closer;
}
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
4,576
97
Albuquerque, NM
prospero.unm.edu
Hey Charles ... speaking of headaches, how about this.

Code:
VOID AddCloser(LPKEYINFO lpki, WCHAR closer)
{
    WCHAR    *p = lpki->pszCurrent;

    while ( *p++ != UNICODE_NULL );

    while ( p > lpki->pszCurrent )
    {
        *p = *(p-1);
        p -= 1;
    }

    *p = closer;
}

Maybe I'm missing something, but I think you need to add a null to the new end of your string. It looks like you're moving everything up one place, with the last non-null character overwriting the original null.

Code:
for ( wchar_t *p = lpki->pszCurrent + wcslen( lpki->pszCurrent ); p >= lpki->pszCurrent; p-- )
     p[1] = *p;

lpki->pszCurrent[0] = closer;

Not tested, may be utterly wrong....
 
May 20, 2008
11,840
120
Syracuse, NY, USA
Hmmm! It works. And putting in some comments made me feel pretty good about it. Here it is again with a few comments.

Code:
VOID AddCloser(LPKEYINFO lpki, WCHAR closer)
{
    WCHAR    *p = lpki->pszCurrent;

    // this loop will stop when *p == 0 (and p will be incremented)
    while ( *p++ != UNICODE_NULL );
    // now p points one past the terminating null

    while ( p > lpki->pszCurrent )
    {
        *p = *(p-1); // move each character one to the right (starting with the terminating null)
        p -= 1;
    }

    // insert the closer
    *p = closer;
}

Yours looks good. I'm finding the end whereas you get it with wcslen() (pretty much the same thing).
 

Similar threads