jbr*_*nan 10 macos cocoa nstextview nstextstorage
我有一个NSTextView子类充当其NSTextStorage委托.我正在尝试做两件事:
我在两种不同的方法中执行此操作,这两种方法都由- (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];
Run Code Online (Sandbox Code Playgroud)
这样做会很好地追加我的结果,但问题是,如上所述,插入点会跳到最后.
我试过了:
[textStorage beginEditing]和-endEditing.我这样做了吗?我试图以最少的hackish方式做到这一点,我也不确定这是否是进行解析/突出显示的理想场所.文档让我相信这一点,但也许这是错的.
令人惊讶的是,我从来没有找到对这些建议为什么有效(或无效)的实际解释。
挖掘到它,所述原因为插入点移动是:.editedCharacters(NSTextStorageEditedCharacters在ObjC)影响从插入点的位置NSLayoutManager.processEditing(from:editedMask:...)。
如果只发送.editedAttributes/ NSTextStorageEditedAttributes,则不会触及插入点。当您突出显示时,这就是您想要实现的目标:仅更改属性。
这里突出显示的问题是在单个处理运行期间NSTextStorage收集所有edited调用并组合范围,从用户编辑的更改开始(例如键入时的插入),然后形成此和报告的所有范围的联合addAttributes(_:range:)。这会导致一次NSLayoutManager.processEditing(from:editedMask:...)调用 -editedMask两者都调用[.editedCharacters, .editedAttributes]。
所以你想发送.editedAttributes突出显示的范围,但最终却形成了一个联合.editedCharacters。该联合将插入点 waaaaaaaay 移到它应该去的地方。
更改顺序processEditing以调用 super first 有效,因为布局管理器将收到完成编辑的通知。但是这种方法在某些边缘情况下仍然会中断,导致在您输入非常大的段落时无效的布局或摇晃的滚动视图。
NSTextStorageDelegate顺便说一下,这对于钩入 也是如此。
processEditing基于 Cocoa 框架固有的原因,唯一能够稳健运行的解决方案是从textDidChange(_:)独占方式执行突出显示,即在布局处理真正完成之后。订阅NSTextDidChangeNotification工作也一样。
缺点:您必须触发突出显示传递以对底层字符串进行编程更改,因为这些不会调用textDidChange(_:)回调。
如果您想更多地了解问题的根源,我将更多我的研究、不同的方法和解决方案的详细信息放在更长的博客文章中以供参考。这篇文章本身仍然是一个独立的解决方案:http : //christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/
这很简单!我最终把这个问题分成两部分。我仍然进行语法突出显示,因为textStorage,但现在我进行评估并附加到其他地方。
我最终覆盖了-insertText:和-deleteBackwards:(我可能也想对 做同样的事情-deleteForwards:)。两个覆盖如下所示:
- (void)insertText:(id)insertString {
[super insertText:insertString];
NSRange selectedRange = [self selectedRange];
[self doEvaluationAndAppendResult];
[self setSelectedRange:selectedRange];
}
Run Code Online (Sandbox Code Playgroud)
我最终不得不在这里手动重置插入点。我仍然很想弄清楚为什么这是必要的,但至少这感觉不像黑客。