From f938f62e318ce83bb1b6ca869035712166fc9490 Mon Sep 17 00:00:00 2001 From: Dmytro Maluka Date: Sun, 27 Jul 2025 00:24:02 +0200 Subject: [PATCH] Make isModified reflect actual modified/unmodified state of buffer Instead of calculating the hash of the buffer every time Modified() is called, do that every time b.isModified is updated (i.e. every time the buffer is modified) and set b.isModified value accordingly. This change means that the hash will be recalculated every time the user types or deletes a character. But that is what already happens anyway, since inserting or deleting characters triggers redrawing the display, in particular redrawing the status line, which triggers Modified() in order to show the up-to-date modified/unmodified status in the status line. And with this change, we will be able to check this status more than once during a single "handle event & redraw" cycle, while still recalculating the hash only once. --- internal/buffer/backup.go | 2 +- internal/buffer/buffer.go | 37 ++++++++++++++++++++----------------- internal/buffer/save.go | 4 +--- internal/buffer/settings.go | 4 ++-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/internal/buffer/backup.go b/internal/buffer/backup.go index cda7a0eb..32180c0a 100644 --- a/internal/buffer/backup.go +++ b/internal/buffer/backup.go @@ -125,7 +125,7 @@ func (b *Buffer) ApplyBackup(fsize int64) (bool, bool) { if choice%3 == 0 { // recover b.LineArray = NewLineArray(uint64(fsize), FFAuto, backup) - b.isModified = true + b.setModified() return true, true } else if choice%3 == 1 { // delete diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index ef9d2331..00056fc0 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -126,20 +126,36 @@ type SharedBuffer struct { } func (b *SharedBuffer) insert(pos Loc, value []byte) { - b.isModified = true b.HasSuggestions = false b.LineArray.insert(pos, value) + b.setModified() inslines := bytes.Count(value, []byte{'\n'}) b.MarkModified(pos.Y, pos.Y+inslines) } + func (b *SharedBuffer) remove(start, end Loc) []byte { - b.isModified = true b.HasSuggestions = false + defer b.setModified() defer b.MarkModified(start.Y, end.Y) return b.LineArray.remove(start, end) } +func (b *SharedBuffer) setModified() { + if b.Type.Scratch { + return + } + + if b.Settings["fastdirty"].(bool) { + b.isModified = true + } else { + var buff [md5.Size]byte + + b.calcHash(&buff) + b.isModified = buff != b.origHash + } +} + // calcHash calculates md5 hash of all lines in the buffer func (b *SharedBuffer) calcHash(out *[md5.Size]byte) { h := md5.New() @@ -653,18 +669,7 @@ func (b *Buffer) Shared() bool { // Modified returns if this buffer has been modified since // being opened func (b *Buffer) Modified() bool { - if b.Type.Scratch { - return false - } - - if b.Settings["fastdirty"].(bool) { - return b.isModified - } - - var buff [md5.Size]byte - - b.calcHash(&buff) - return buff != b.origHash + return b.isModified } // Size returns the number of bytes in the current buffer @@ -1233,7 +1238,6 @@ func (b *Buffer) FindMatchingBrace(start Loc) (Loc, bool, bool) { func (b *Buffer) Retab() { toSpaces := b.Settings["tabstospaces"].(bool) tabsize := util.IntOpt(b.Settings["tabsize"]) - dirty := false for i := 0; i < b.LinesNum(); i++ { l := b.LineBytes(i) @@ -1254,10 +1258,9 @@ func (b *Buffer) Retab() { b.Unlock() b.MarkModified(i, i) - dirty = true } - b.isModified = dirty + b.setModified() } // ParseCursorLocation turns a cursor location like 10:5 (LINE:COL) diff --git a/internal/buffer/save.go b/internal/buffer/save.go index f3e05ad2..3c79089a 100644 --- a/internal/buffer/save.go +++ b/internal/buffer/save.go @@ -206,9 +206,7 @@ func (b *Buffer) Save() error { // AutoSave saves the buffer to its default path func (b *Buffer) AutoSave() error { - // Doing full b.Modified() check every time would be costly, due to the hash - // calculation. So use just isModified even if fastdirty is not set. - if !b.isModified { + if !b.Modified() { return nil } return b.saveToFile(b.Path, false, true) diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index ed22eae8..9c8b3ce1 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -91,7 +91,7 @@ func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) { case "dos": b.Endings = FFDos } - b.isModified = true + b.setModified() } else if option == "syntax" { if !nativeValue.(bool) { b.ClearMatches() @@ -105,7 +105,7 @@ func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) { b.Settings["encoding"] = "utf-8" } b.encoding = enc - b.isModified = true + b.setModified() } else if option == "readonly" && b.Type.Kind == BTDefault.Kind { b.Type.Readonly = nativeValue.(bool) } else if option == "hlsearch" {