本文介绍了修改NSTextStorage会导致插入点移动到行尾的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个NSTextView子类作为其NSTextStorage委托.我正在尝试做两件事:

I've got an NSTextView subclass acting as its NSTextStorage delegate. I'm trying to do 2 things:

  1. 以某些方式突出显示文本
  2. 评估文本,然后将答案附加到textview.

我正在通过两种不同的方法执行此操作,这两种方法均由- (void)textStorageWillProcessEditing:(NSNotification *)notification委托回调调用.

I'm doing this in two different methods, both invoked by the - (void)textStorageWillProcessEditing:(NSNotification *)notification delegate callback.

我可以很好地突出显示语法,但是在添加答案时,插入点跳到行尾,我真的不知道为什么.我的评估方法如下所示:

I can do the syntax highlighting just fine, but when it comes to appending my answer, the insertion point jumps to the end of the line and I don't really know why. My evaluate method looks like the following:

NSString *result = ..;
NSRange lineRange = [[textStorage string] lineRangeForRange:[self selectedRange]];
NSString *line = [[textStorage string] substringWithRange:lineRange];
line = [self appendResult:result toLine:line]; // appends the answer

[textStorage replaceCharactersInRange:lineRange withString:line];

这样做会很好地附加我的结果,但是如上所述,问题是插入点跳到了最后.

Doing that will append my result just fine, but the problem is, as mentioned, the insertion point jumps to the end.

我尝试过:

  1. 将上述内容包装在[textStorage beginEditing]-endEditing中.
  2. 在更改文本存储之前保存选择范围(即插入点),以便以后可以重置它,但没有骰子.
  1. Wrapping those above calls up in [textStorage beginEditing] and -endEditing.
  2. Saving the selection range (i.e., the insertion point) before changing the text storage so I can reset it afterwards, but no dice.

我这样做正确吗?我正在尝试以最少的方式进行此操作,并且我也不确定这是否是进行解析/突出显示的理想位置.文档使我相信了这一点,但也许是错误的.

Am I doing this right? I'm trying to do this the least hackish way, and I'm also unsure if this is the ideal place to be doing my parsing/highlighting. The docs lead me to believe this, but maybe it's wrong.

推荐答案

插入点移动的原因

令人惊讶的是,我从未找到关于这些建议为何起作用(或不起作用)的实际解释.

Reason for the insertion point to move

Suprisingly, I never found an actual explanation to why these suggestion do (or do not) work.

深入其中,使插入点移动的原因为:.editedCharacters(在ObjC中为NSTextStorageEditedCharacters)会影响NSLayoutManager.processEditing(from:editedMask:...)中插入点的位置.

Digging into it, the reason for the insertion point to move is: .editedCharacters (NSTextStorageEditedCharacters in ObjC)affects the position of the insertion point from NSLayoutManager.processEditing(from:editedMask:...).

如果仅发送.editedAttributes/NSTextStorageEditedAttributes,将不会触摸插入点.这是您突出显示时要实现的:仅更改属性.

If only .editedAttributes/NSTextStorageEditedAttributes is sent, the insertion point will not be touched. This is what you will want to achieve when you highlight: change attributes only.

突出显示问题是,NSTextStorage在单个处理运行期间收集所有edited调用并合并范围,从用户编辑的更改开始(例如,键入时插入) ,然后形成该值与addAttributes(_:range:)报告的所有范围的并集.这样会导致一个NSLayoutManager.processEditing(from:editedMask:...)调用-两个[.editedCharacters, .editedAttributes]editedMask.

The problem with highlighting here is that NSTextStorage collects all edited calls during a single processing run and combines the ranges, starting with the user-edited change (e.g. the insertion when typing), then forming a union of this and all ranges reported by addAttributes(_:range:). This results in one single NSLayoutManager.processEditing(from:editedMask:...) call -- with an editedMask of both [.editedCharacters, .editedAttributes].

因此,您想要向突出显示的范围发送.editedAttributes,但最终与.editedCharacters组成了并集.该联合将插入点waaaaaaaay移至其应到达的位置之外.

So you want to send .editedAttributes for the highlighted ranges but end up forming a union with .editedCharacters instead. That union moves the insertion point waaaaaaaay beyond where it should go.

processEditing中更改顺序以调用超级优先操作,因为布局编辑器将收到完成的编辑通知.但是这种方法在某些情况下仍然会失效,导致在您键入非常大的段落时导致无效的布局或滚动的滚动视图.

Changing the order in processEditing to call super first works because the layout manager will be notified of a finished edit. But this approach will still break for some edge cases, resulting in invalid layout or jiggling scroll views while you type in very large paragraphs.

顺便提一下,这也确实适用于NSTextStorageDelegate

基于Cocoa框架固有的原因,唯一能够稳定运行的解决方案是专门从textDidChange(_:)中进行突出显示,即在布局处理真正完成之后.订阅NSTextDidChangeNotification也可以.

The only solution that will work robustly based on reasons inherent to the Cocoa framework is to perform highlighting from textDidChange(_:) exclusively, i.e. after the layout processing really has been finished. Subscribing to NSTextDidChangeNotification work just as well.

缺点:您必须触发对基础字符串进行编程更改的高亮显示通道,因为这些更改不会调用textDidChange(_:)回调.

Downside: you have to trigger highlighting passes for programmatic changes to the underlying string as these will not invoke the textDidChange(_:) callback.

如果您想更多地了解问题的根源,我将在更长的博客文章中投入更多的研究,不同的方法以及解决方案的详细信息,以供参考.该帖子本身仍然是一个独立的解决方案: http://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/

In case you want to know more about the source of the problem, I put more my research, different approaches, and details of the solution in a much longer blog post for reference. This post is still a self-contained solution in itself: http://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/

这篇关于修改NSTextStorage会导致插入点移动到行尾的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 09:51