NSTextStorageDelegate的textStorage(_,willProcessEditing:,range:,changeInLength :)移动选择

时间:2017-07-16 09:15:39

标签: macos swift3 syntax-highlighting nstextview nstextstorage

我正在尝试实现一个语法着色文本编辑器,它也可以在新行的开头插入空格,或者用文本附件替换文本。

在之前的实现遇到撤消问题之后再次仔细阅读文档后,似乎推荐的瓶颈是NSTextStorageDelegate的textStorage(_,willProcessEditing:,range:,changeInLength :)方法(其中指出Delegates can change the characters or attributes. ,而didProcessEditing表示我更改属性)。这样工作正常,除了,每当我实际更改属性或文本时,文本插入标记移动到我修改的任何文本范围的末尾(所以如果我改变整行的样式,光标走在最后一行)。

有人知道我错过了哪些额外的电话,告诉NSTextStorage / NSTextView不要搞砸插入标记?此外,一旦我插入文本,我可能必须告诉它移动插入标记以考虑我插入的文本。

注意:我见过Modifying NSTextStorage causes insertion point to move to the end of the line,但是假设我是NSTextStorage的子类,所以我不能在那里使用解决方案(而不是NSTextStorage的子类,因为它是一个半抽象的子类,如果我将其子类化,我将失去Apple类的某些行为。)

1 个答案:

答案 0 :(得分:1)

我发现了问题的根源。

唯一可以基于Cocoa框架固有的原因而不仅仅是解决方案而稳健运行的解决方案。 (注意,可能至少还有一种基于 ton 快速修复的亚稳态方法会产生类似的结果,但随着亚稳态替代品的出现,这将非常脆弱并且需要大量的努力才能维持。)

  • TL; DR问题: NSTextStorage收集edited个来电并组合范围,从用户编辑的更改(例如插入)开始,然后添加{的所有范围在突出显示期间调用{1}}。

  • TL; DR解决方案:专门从addAttributes(_:range:)执行突出显示。

详细

What happens when you type and change style

这仅适用于textDidChange(_:)子类和processEditing()回调中的单NSTextStorage次运行。

我发现执行突出显示的唯一安全方法是挂钩NSTextStorageDelegate或实施NSText.didChangeNotification

根据@Willeke对OP问题的评论,这是布局通过后执行更改的最佳位置。但与评论主题相反,设置回NSTextDelegate.textDidChange(_:)是不够的。在插入符号移出

之后,您不会注意到修复后选择的问题
  • 突出显示整个文本块
  • 跨越多行,
  • 超出滚动视图的可见(NSText.selectedRange)边界。

在极少数情况下,大多数击键都会使滚动视图抖动或反弹。但是没有额外的快速解决方案。我试过了。既不阻止从NSClipView中的私有API发送滚动命令也不会通过使用"滚动"覆盖所有方法来避免滚动。来自NSLayoutManager子类的它们运行良好。你可以完全停止滚动到插入点,但是没有这样的运气得到一个只有在你执行高亮时不滚动的可靠算法。

NSTextView方法在我和我的应用程序的测试人员能够提出的所有情况下都能可靠地工作(包括像滚动文本那样奇怪的崩溃情况,然后在动画期间更换更短的字符串 - 是的,尝试从报告无效字形生成的崩溃日志中找出那种东西......)。

这种方法有效,因为它有2个字形生成过程:

  1. 编辑范围的一次传递,如果键入长度为didChangeNotification的每个键击,则NSRange通知edited,前者负责移动插入符号;
  2. 对于语法突出显示影响的任何范围的另一个传递,仅[.editedCharacters, .editedAttributes]仅发送edited通知,因此会影响插入符号的位置。
  3. 更多细节

    如果您想了解更多有关问题根源的信息,我会在更长的博客文章中提供更多我的研究,不同方法和解决方案的详细信息,以供参考。不过,这就是解决方案本身。 http://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/