Cocoa 脚本:从 "duplicate" 命令返回克隆的对象

Cocoa Scripting: Returning the cloned objects from a "duplicate" command

AppleScript duplicate 命令应该 return 复制的对象。

虽然使用基于 AE 的原始功能的应用程序似乎可以做到这一点,但基于 Cocoa 脚本框架的应用程序似乎从来没有 return 除了 缺失值 .

似乎 NSCloneCommand 的命令处理程序负责不 return 克隆对象的说明符。

我试图通过对命令进行子类化、收集克隆的对象说明符然后 returning 它们来在我的可编写脚本的应用程序中修复此问题。

如果只有一个项目被复制,这很有效。

如果克隆多个项目并在命令中使用 to 参数(如 duplicate every widget to end),它也有效:然后我可以 return 类型的说明符 NSRangeSpecifier 指定第一个和最后一个克隆项目。

但是,如果对多个项目使用复制命令 而没有 to 参数,那么这些项目将被排序以 非连续的方式 进入数组。例如,如果最初有 2 个 "x" 元素,id 为 1 和 2,duplicate every x 将在每个元素的原始元素之后插入一个副本,这样我们就可以按以下顺序获得它们:1 , 3, 2, 4.

现在,如何return为此指定说明符,即第 3 项和第 4 项的说明符?

NSScriptObjectSpecifier 的子 类 中没有 "list" 说明符,我也不能 return 每个个体 NSScriptObjectSpecifier 的 NSArray,它似乎。虽然 NSAppleEventDescriptor 支持创建列表,但我不知道如何将对象说明符转换为 NSAppleEventDescriptors。

除了强制执行克隆对象的连续顺序之外,我还能如何解决这个问题(恐怕这需要我完全重新实现 NSCloneCommand 的操作)。

顺便说一句,Mark Aldritt, author of Script Debugger, confirms the issue duplicate(也:moveopen)不return值,因为他们应该。

Thomas,我不确定这是否是一个拼写错误,但我在标准套件中没有看到重复命令的 "at" 参数:

<command name="duplicate" code="coreclon" description="Copy an object.">
    <cocoa class="NSCloneCommand"/>
    <direct-parameter type="specifier" .../>
    <parameter name="to" ...</parameter>
    <parameter name="with properties" ...</parameter>
</command>

也没有 <result ...> 元素,因此根据定义,命令不应 return 任何值。我错过了什么吗?

Mark Aldritt 进一步帮助了我,告诉我一些私有 API 方法:

@interface NSScriptObjectSpecifier (NSPrivate)
+ (id) _scriptingSpecifierWithDescriptor:(NSAppleEventDescriptor*) descriptor;
+ (id) _objectSpecifierFromDescriptor:(NSAppleEventDescriptor*) descriptor inCommandConstructionContext:(id) context;
- (NSAppleEventDescriptor*) _asDescriptor;
@end

_asDescriptor 是我一直在寻找的 - 一种将对象说明符转换为 NSAppleEventDescriptor 以便我可以将其添加到列表对象的方法。其代码如下所示:

- (NSAppleEventDescriptor*) objectSpecifiersAsList:(NSArray*) objectSpecifiers {
    NSAppleEventDescriptor* result = [NSAppleEventDescriptor listDescriptor];
    for (NSScriptObjectSpecifier* specifier in objectSpecifiersArray) {
        [result insertDescriptor:specifier._asDescriptor atIndex:0];
    }
    return  result;
}

当我尝试 return 非连续项目时,我发现这不起作用。事实上,它与 returning 一个 NSArray 相同的 NSScriptObjectSpecifier 具有相同的效果。这是一个例子:

set x to duplicate widgets 1 thru 2

使用自定义重复命令处理程序 return 为复制的项目 3 和 4 设置说明符列表,AppleScript 最终第二次调用相同的命令处理程序,之后它给出错误 -10006消息:

Can't set widgets 1 thru 2 to widgets 1 thru 2

请注意 - 它不会说 "widgets 3 thru 4" 或“{widget 3, widget 4}”。不,它总是将第一个参数中给出的项目报告给复制命令。

只要我将我的代码更改为 returning 单个说明符或范围说明符,命令就会再次正常运行。

所以这似乎是 Cocoa 脚本(或 AppleScript?)中的一个隐藏错误,它无法处理列表中的 returned 对象说明符。

更新与解决方案

经过反复试验,我找到了一个可行的方法:

结果的类型必须从 "descriptor" 更改,并且有两种可能性:

  • 要使用上面的代码 return 是 listDescriptor,结果类型需要是 "any",即:

    <result>
        <type type="any"/>
    </result>
    
  • 或者,如果结果类型更改为 "list of any",则可以 return 包含 NSAppleEventDescriptor 值的 NSArray:

    <result>
        <type type="any" list="yes"/>
    </result>
    

这两种解决方案都需要使用私有 _asDescriptor 方法,但是,因为没有其他已知方法可以将可编写脚本的对象转换为 NSAppleEventDescriptor.

(当然,如果您的应用仅支持一种元素类型的 duplicate 命令,那么您可以将类型更改为 "list of yourtype" 和 return 只是您的 NSArray对象,而不需要私有方法——只有 returning 类型 any 的结果才需要。)

Mark 说了关于使用私有方法:

If you are concerned about Mac App store issues, these private methods were give to me by Apple as there is no alternative API. I’m pretty sure you can get permission to use them.

我希望尽快将实现此解决方案的自己的应用程序提交到 App Store。然后我将使用私有函数的结果更新这个答案。