Plug-in "TokenString" field

Hi,
I built myself a plug-in to get the line no. of a string in the source string.

When I put a global variable in the "TokenString" field, it works. When I put a Local Variable (with or without %) there, it's not working.
Get Line Number of String in Source.zip (4.9 KB)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Name</key>
	<string>Get Line Number of String in Source</string>
	<key>Script</key>
	<string>Action.sh</string>
	<key>Icon</key>
	<string>Icon.png</string>
	<key>Title</key>
	<string>Get Line Number of %Param%The String% in %Param%The Source String%</string>
	<key>Results</key>
	<string>Window|Variable|Clipboard</string>
	<key>Parameters</key>
	<array>
		<dict>
			<key>Label</key>
			<string>The String</string>
			<key>Type</key>
			<string>TokenString</string>
			<key>Default</key>
			<string>String</string>
		</dict>
		<dict>
			<key>Label</key>
			<string>The Source String</string>
			<key>Type</key>
			<string>TokenString</string>
			<key>Default</key>
			<string>Source String</string>
		</dict>
	</array>
</dict>
</plist>
#!/usr/bin/osascript -l JavaScript
var kme = Application("Keyboard Maestro Engine");

var theString = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_The_String"))
var str = kme.getvariable( theString );

var theSourceString = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_The_Source_String"))
var sourceStr = kme.getvariable( theSourceString );



if (str == "" || sourceStr == "") {
	"Error: The String or the Source String is empty. \n\nTry the Variable Name without %. \n\nLocal Variables are not supported!";
} else {
lineNo = getLineNo(str, sourceStr);
}


function getLineNo(str, sourceStr){
    let arr = sourceStr.split("\n");
    for (let i = 0; i < arr.length; ++i) {
        if (str == arr[i]) {
            return i + 1;
            break;
        }
    }
}

Using global Variables works:
image

Using local variables does not work.
With out %:
image

With %:
image

I have another plugin to get the content of the Nth from the source string.
It works with local variables (with %).
image

I use "TokenString" for all these fields.

The only difference is that the first use javascript executed via osascript and the latter use shell script.
Can anyone explain why and how to fix?

Thanks!

I didn't read the entire post, but if the issue is just retrieving a local variable in JXA (aka "Instance" variable), check out this Wiki article: https://wiki.keyboardmaestro.com/action/Execute_a_JavaScript_For_Automation

1 Like

Hi @DanThomas,

Thanks.
I'm aware of that. But I assume getting a variable works differently in a plug-in.
I actually followed the example given in @peternlewis's reply to your post years ago.

This is the only resource I could find on the internet about using JXA for KM plug-ins.

Oh! I didn't realize it was different. My bad, and this is the second new KM thing I've learned today. :smirk:

1 Like

And of course, the post you mentioned was directed to me, so I guess I should have known it. Old age sucks. :grinning_face_with_smiling_eyes:

1 Like

But that was in 2016, when Local and Instance variables didn’t exist. I’ve used the information referred to by @DanThomas to get and set Local and Instance variables with no problems. I suggest you try using the info from the wiki, as Dan rightly suggests. Plugins run in the same KM environment...

1 Like

Thanks, @tiffle.

I'm not dealing with JXA in an action.
I don't understand exactly how it works, but it appears that the plugin gets the variable value from "The String" and "The Source String" that I set in the .plist file.
KM in someway passes the value of variables to these two variables.

"The String" and "The Source String" do not exist in the variable list.

		<dict>
			<key>Label</key>
			<string>The String</string>
			<key>Type</key>
			<string>TokenString</string>
			<key>Default</key>
			<string>String</string>
		</dict>
		<dict>
			<key>Label</key>
			<string>The Source String</string>
			<key>Type</key>
			<string>TokenString</string>
			<key>Default</key>
			<string>Source String</string>
var kme = Application("Keyboard Maestro Engine");

var theString = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_The_String"))
var str = kme.getvariable( theString );

var theSourceString = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_The_Source_String"))
var sourceStr = kme.getvariable( theSourceString );

Ok - well, the TokenString in the plist definition is a normal string that, if it contains KM tokens, then the KM engine will replace the tokens with their values.

In my experience with writing and using plugins, I’ve always referred to variables as %Variable%InstanceVariableName% and %Variable%LocalVariableName% when using them as plugin parameters. In other words, I’ve never dropped the %Variable% part, which I notice you’ve been doing. Perhaps there’s a clue there?

I've also tried this. Not working.
As I mentioned in the OP, I have another plug-in, in which I use %Local__SourceStr% and %Local__LineNo%, both without %Variable. and it works fine.
The only difference is that that plug-in uses purely Shell. The one I have issue with is JS executed via osascript.

One more thing:
In the other plugin, I need to use the % sign to indicate that it is a variable.
image

In the plugin written with JS, I don't need the % sign to indicate that it is a variable.
image

If I use the % sign in the latter, it will NOT work as intended.
image

OK, I just tested one of my old plugins, XML Escape. When I use %Variable% syntax, it works regardless of whether it's a local or global variable.

image

Here's my plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Author</key>
	<string>@DanThomas</string>
	<key>Help</key>
	<string>Escape a string for use in XML</string>
	<key>Icon</key>
	<string>Icon.png</string>
	<key>Name</key>
	<string>XML Escape</string>
	<key>Parameters</key>
	<array>
		<dict>
			<key>Label</key>
			<string>Escape Text</string>
			<key>Type</key>
			<string>TokenString</string>
		</dict>
	</array>
	<key>Results</key>
	<string>Variable|Clipboard|Window|Briefly|Typing|Pasting</string>
	<key>Script</key>
	<string>Action.scpt</string>
	<key>Title</key>
	<string>XML Escape '%Param%Escape Text%'</string>
</dict>
</plist>

Here's my script:

function escapeXml(unsafe) {
    return unsafe.replace(/[<>&'"]/g, function (c) {
        switch (c) {
            case '<': return '&lt;';
            case '>': return '&gt;';
            case '&': return '&amp;';
            case '\'': return '&apos;';
            case '"': return '&quot;';
        }
    });
}

var kme = Application("Keyboard Maestro Engine");
var text = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_Escape_Text"))
var xml = escapeXml(text);
xml;

I hope this helps.

2 Likes

@martin I forgot to mention that I compiled the script in Script Editor and saved it, and used the compiled version in the .zip file. I'm not sure if that matters or not.

1 Like

When the KM engine interprets the variables passed to it using the plist definition, it can't know whether those values are being passed to JS, SH or AppleScript so I don't understand why the JS plugin appears to fail while your plugin that employs a shell script works.

So, as long as you're using the methods given to retrieve those variable values into your JS and sh code, all I can think is there must be a difference between, and an error in, your plist files because that's where the interpretation of the TokenStrings is determined.

@DanThomas has confirmed that it all works as expected so it must be that you're doing something unexpected?

1 Like

Scrub that - this is the problem:

@peternlewis post helps @DanThomas to "get the value of a named variable" so the code he offers (a) gets the name of the variable and then (b) gets the value of the variable with that name.

Your code does the same. Notice, though, that Dan's latest example doesn't have the (b) part - the bit with kme.getvariable as that is not required because the KM engine is supplying the value directly through its interpretation of the TokenString.

It appears to work when you provide the name of a KM variable without %-signs, because your code (a) first gets the name of the variable and then (b) gets its value.

This is a p*** poor explanation, but I hope you can extract some sense from it.

Cheers and thanks to Dan for making it clearer to me!

1 Like

Hi @DanThomas,

It definitely helped!
Turns out we don't need these:

var kme = Application("Keyboard Maestro Engine");
var str = kme.getvariable( theString );

Instead of:

var kme = Application("Keyboard Maestro Engine");
var theString = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_The_String"))
var str = kme.getvariable( theString );

We only need:

var str = ObjC.unwrap($.NSProcessInfo.processInfo.environment.objectForKey("KMPARAM_The_String"))

It's working now, with Local variables as well!
image

Yes. I get it now. Thanks for the explanation.
It explains why it must be global variables. kme.getvariable( VarName ); is used to get the value of global variables. Local & Instant variables require a different method.

For plug-ins, we normally pass variable values through the "Label" names. kme.getvariable( VarName ); is usually used in executing JXA script actions, but not in plug-ins.

Dan's post was the only thing I could find for creating a plug-in with JXA. I think it might be helpful to add some examples in the wiki page on using JXA for making plug-ins.

1 Like

Yes, that makes perfect sense. "kme" wasn't even being used.

Glad you got it working!