修改NSTextStorage会导致插入点移动到行尾

时间:2012-08-28 00:35:02

标签: macos cocoa nstextview nstextstorage

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

  1. 以某种方式突出显示文字
  2. 评估文本,然后将答案附加到textview。
  3. 我在两个不同的方法中执行此操作,均由- (void)textStorageWillProcessEditing:(NSNotification *)notification委托回调调用。

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

    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];
    

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

    我试过了:

    1. 包含上述内容会在[textStorage beginEditing]-endEditing中调用。
    2. 在更改文本存储之前保存选择范围(即插入点),以便我可以在之后重置它,但没有骰子。
    3. 我这样做了吗?我试图以最少的hackish方式做到这一点,我也不确定这是否是进行解析/突出显示的理想场所。文档让我相信这一点,但也许这是错误的。

3 个答案:

答案 0 :(得分:4)

我知道这个问题早已回答,但我确实完全同样的问题。在我的NSTextStorage子类中,我正在执行以下操作:

- (void)processEditing {
    //Process self.editedRange and apply styles first
    [super processEditing];
}

然而,正确的做法是:

- (void)processEditing {
    [super processEditing];
    //Process self.editedRange and apply styles after calling superclass method
}

答案 1 :(得分:3)

插入点移动的原因

令人惊讶的是,我从来没有找到为什么这些建议能够(或不能)发挥作用的实际解释。

深入研究,插入点移动的原因是:.editedCharacters(ObjC中的NSTextStorageEditedCharacters)会影响{{1}的插入点位置}。

如果仅发送NSLayoutManager.processEditing(from:editedMask:...) / .editedAttributes,则不会触及插入点。这是您在突出显示时想要实现的目标:仅更改属性。

为什么突出显示会影响插入点

此处突出显示的问题NSTextStorageEditedAttributes在单个处理运行期间收集所有NSTextStorage个调用并组合范围,从用户编辑的更改开始(例如输入时插入),然后形成edited报告的此范围和所有范围的并集。这会导致一次addAttributes(_:range:)次调用 - NSLayoutManager.processEditing(from:editedMask:...) editedMask。{/ p>

因此,希望为突出显示的范围发送[.editedCharacters, .editedAttributes],但最终会与.editedAttributes形成联盟。那个联盟将插入点waaaaaaaay移动到应该去的地方。

更改.editedCharacters中的顺序以调用超级优先,因为布局管理器将收到已完成编辑的通知。但是这种方法仍会因某些边缘情况而中断,导致无效的布局或在您输入非常大的段落时摇晃滚动视图。

顺便说一下,

This is true for hooking into NSTextStorageDelegate

布局完全触发突出显示而不是processEditing

后挂钩回调

基于Cocoa框架固有的原因,唯一可以稳健工作的解决方案是从processEditing专门执行突出显示,即在布局处理确实完成之后。也可以订阅textDidChange(_:)

下行:您必须触发突出显示传递以对基础字符串进行编程更改,因为这些不会调用NSTextDidChangeNotification回调。

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

答案 2 :(得分:0)

很简单!我最终把这个问题分成两部分。由于textStorage委托回调,我仍然会进行语法突出显示,但现在我进行评估并附加到其他地方。

我最终覆盖了-insertText:-deleteBackwards:(我可能也希望对-deleteForwards:执行相同操作)。两个覆盖都如下所示:

- (void)insertText:(id)insertString {
    [super insertText:insertString];
    NSRange selectedRange = [self selectedRange];
    [self doEvaluationAndAppendResult];
    [self setSelectedRange:selectedRange];
}

我最终必须在这里手动重置插入点。我仍然想知道为什么这是必要的,但至少这感觉不像是黑客。