Note: This may not be needed in practice at all. We have to test this.
One feature that Emacs Lisp provides is to jump around in a buffer and read and write text, then restore the context exactly as it was before so that the user doesn't perceive anything. This is called save-excursion.
// Imagine we're in the middle of a long document here, like
// line 500 of 1000.
SaveExcursion {
Select(LineRange(at: 0)) {
// ... do stuff on the first line ...
}
Select(LineRange(at: buffer.endLocation)) {
// ... do stuff on the last line line ...
}
}
// At this point, we're at line 500 again and the scroll offset is the same as before
Unlike our Buffer.character(at:), Emacs buffers can't access substrings without moving them into view. There, the buffer is the model, and moving the reading head also moves the scrolled viewport.
NSTextView can show similar behavior when you change characters in one place and attributes in a larger place and the effective range is enlargened; then the NSTextStorage.processEditing() run will move the insertion point:

Source and fix: https://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/
UITextView doesn't have this problem.
One feature that Emacs Lisp provides is to jump around in a buffer and read and write text, then restore the context exactly as it was before so that the user doesn't perceive anything. This is called
save-excursion.Unlike our
Buffer.character(at:), Emacs buffers can't access substrings without moving them into view. There, the buffer is the model, and moving the reading head also moves the scrolled viewport.NSTextViewcan show similar behavior when you change characters in one place and attributes in a larger place and the effective range is enlargened; then theNSTextStorage.processEditing()run will move the insertion point:Source and fix: https://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/
UITextViewdoesn't have this problem.