One place to pick a buffer's highlighter

Filetype detection (extension or shebang) was written out three times
(two open paths in Editor and the ImGui frontend's per-frame check) and
"highlighter for this filetype, else the null one" five times (Buffer's
copy constructor and assignment, Editor::SwitchTo, :set filetype).
Buffer::ApplyDetectedFiletype and InstallFiletypeHighlighter replace
them. The InvalidateFrom(0) calls after SetHighlighter are dropped:
SetHighlighter already clears the caches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
Claude
2026-09-24 12:19:59 -07:00
committed by Kyle Isom
parent 323e2377fe
commit 927accbca4
5 changed files with 50 additions and 135 deletions
+27 -30
View File
@@ -377,23 +377,8 @@ Buffer::Buffer(const Buffer &other)
undo_sys_ = std::make_unique<UndoSystem>(*this, *undo_tree_);
// Recreate a highlighter engine for this copy based on filetype/syntax state
if (syntax_enabled_) {
// Allocate engine and install an appropriate highlighter
highlighter_ = std::make_unique<kte::HighlighterEngine>();
if (!filetype_.empty()) {
auto hl = kte::HighlighterRegistry::CreateFor(filetype_);
if (hl) {
highlighter_->SetHighlighter(std::move(hl));
} else {
// Unsupported filetype -> NullHighlighter keeps syntax pipeline active
highlighter_->SetHighlighter(std::make_unique<kte::NullHighlighter>());
}
} else {
// No filetype -> keep syntax enabled but use NullHighlighter
highlighter_->SetHighlighter(std::make_unique<kte::NullHighlighter>());
}
// Fresh engine has empty caches; nothing to invalidate
}
if (syntax_enabled_)
InstallFiletypeHighlighter();
}
@@ -428,19 +413,8 @@ Buffer::operator=(const Buffer &other)
// Recreate highlighter engine consistent with syntax settings
highlighter_.reset();
if (syntax_enabled_) {
highlighter_ = std::make_unique<kte::HighlighterEngine>();
if (!filetype_.empty()) {
auto hl = kte::HighlighterRegistry::CreateFor(filetype_);
if (hl) {
highlighter_->SetHighlighter(std::move(hl));
} else {
highlighter_->SetHighlighter(std::make_unique<kte::NullHighlighter>());
}
} else {
highlighter_->SetHighlighter(std::make_unique<kte::NullHighlighter>());
}
}
if (syntax_enabled_)
InstallFiletypeHighlighter();
return *this;
}
@@ -768,6 +742,29 @@ Buffer::insert_text(int row, int col, std::string_view text)
}
void
Buffer::ApplyDetectedFiletype()
{
EnsureHighlighter();
const std::string first = Nrows() > 0 ? GetLineString(0) : std::string();
const std::string ft = kte::HighlighterRegistry::DetectForPath(filename_, first);
SetFiletype(ft);
SetSyntaxEnabled(true);
InstallFiletypeHighlighter();
}
void
Buffer::InstallFiletypeHighlighter()
{
EnsureHighlighter();
auto hl = filetype_.empty() ? nullptr : kte::HighlighterRegistry::CreateFor(filetype_);
if (!hl)
hl = std::make_unique<kte::NullHighlighter>();
highlighter_->SetHighlighter(std::move(hl));
}
// ===== Adapter helpers for PieceTable-backed Buffer =====
std::string
Buffer::Bytes() const
+9
View File
@@ -472,6 +472,15 @@ public:
}
// Enable syntax with the highlighter the filename (or a shebang on the
// first line) calls for, or none for an unknown type.
void ApplyDetectedFiletype();
// Install the highlighter for Filetype() (the null highlighter for an
// empty or unsupported one), creating the engine if needed.
void InstallFiletypeHighlighter();
// Swap journal integration (set by Editor)
void SetSwapRecorder(kte::SwapRecorder *rec)
{
+6 -24
View File
@@ -15,7 +15,6 @@
#include "RegexEngine.h"
#include "TermWidth.h"
#include "syntax/HighlighterRegistry.h"
#include "syntax/NullHighlighter.h"
#include "Editor.h"
#include "Buffer.h"
#include "UndoSystem.h"
@@ -1641,29 +1640,12 @@ apply_filetype(Buffer &buf, const std::string &ft)
buf.SetSyntaxEnabled(false);
return;
}
if (val.empty()) {
// Empty means unknown/unspecified -> use NullHighlighter but keep syntax enabled
buf.SetFiletype("");
buf.SetSyntaxEnabled(true);
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
eng->InvalidateFrom(0);
return;
}
// Normalize and create via registry
std::string norm = kte::HighlighterRegistry::Normalize(val);
auto hl = kte::HighlighterRegistry::CreateFor(norm);
if (hl) {
eng->SetHighlighter(std::move(hl));
buf.SetFiletype(norm);
buf.SetSyntaxEnabled(true);
eng->InvalidateFrom(0);
} else {
// Unknown -> install NullHighlighter and keep syntax enabled
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
buf.SetFiletype(val); // record what user asked even if unsupported
buf.SetSyntaxEnabled(true);
eng->InvalidateFrom(0);
}
// Empty means unspecified (no highlighting, syntax stays on); an
// unsupported type is recorded as asked, without highlighting.
const std::string norm = val.empty() ? val : kte::HighlighterRegistry::Normalize(val);
buf.SetFiletype(kte::HighlighterRegistry::CreateFor(norm) ? norm : val);
buf.SetSyntaxEnabled(true);
buf.InstallFiletypeHighlighter();
}
+6 -63
View File
@@ -6,9 +6,7 @@
#include "Editor.h"
#include "ErrorHandler.h"
#include "syntax/HighlighterRegistry.h"
#include "syntax/CppHighlighter.h"
#include "syntax/NullHighlighter.h"
namespace {
@@ -285,27 +283,8 @@ Editor::OpenFile(const std::string &path, std::string &err)
cur.SetSwapRecorder(Swap()->RecorderFor(&cur));
Swap()->NotifyFilenameChanged(cur);
}
// Setup highlighting using registry (extension + shebang)
cur.EnsureHighlighter();
std::string first = "";
if (cur.Nrows() > 0)
first = cur.GetLineString(0);
std::string ft = kte::HighlighterRegistry::DetectForPath(path, first);
if (!ft.empty()) {
cur.SetFiletype(ft);
cur.SetSyntaxEnabled(true);
if (auto *eng = cur.Highlighter()) {
eng->SetHighlighter(kte::HighlighterRegistry::CreateFor(ft));
eng->InvalidateFrom(0);
}
} else {
cur.SetFiletype("");
cur.SetSyntaxEnabled(true);
if (auto *eng = cur.Highlighter()) {
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
eng->InvalidateFrom(0);
}
}
// Highlighting by extension or shebang
cur.ApplyDetectedFiletype();
// Defensive: ensure any active prompt is closed after a successful open
CancelPrompt();
return true;
@@ -318,27 +297,8 @@ Editor::OpenFile(const std::string &path, std::string &err)
}
// NOTE: swap recorder/attach must happen after the buffer is stored in its
// final location (vector) because swap manager keys off Buffer*.
// Initialize syntax highlighting by extension + shebang via registry (v2)
b.EnsureHighlighter();
std::string first = "";
if (b.Nrows() > 0)
first = b.GetLineString(0);
std::string ft = kte::HighlighterRegistry::DetectForPath(path, first);
if (!ft.empty()) {
b.SetFiletype(ft);
b.SetSyntaxEnabled(true);
if (auto *eng = b.Highlighter()) {
eng->SetHighlighter(kte::HighlighterRegistry::CreateFor(ft));
eng->InvalidateFrom(0);
}
} else {
b.SetFiletype("");
b.SetSyntaxEnabled(true);
if (auto *eng = b.Highlighter()) {
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
eng->InvalidateFrom(0);
}
}
// Highlighting by extension or shebang
b.ApplyDetectedFiletype();
// Add as a new buffer and switch to it
std::size_t idx = AddBuffer(std::move(b));
if (Swap()) {
@@ -650,25 +610,8 @@ Editor::SwitchTo(std::size_t index)
curbuf_ = index;
// Robustness: ensure a valid highlighter is installed when switching buffers
Buffer &b = bufs[curbuf_];
if (b.SyntaxEnabled()) {
b.EnsureHighlighter();
if (auto *eng = b.Highlighter()) {
if (!eng->HasHighlighter()) {
// Try to set based on existing filetype; fall back to NullHighlighter
if (!b.Filetype().empty()) {
auto hl = kte::HighlighterRegistry::CreateFor(b.Filetype());
if (hl) {
eng->SetHighlighter(std::move(hl));
} else {
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
}
} else {
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
}
eng->InvalidateFrom(0);
}
}
}
if (b.SyntaxEnabled() && !(b.Highlighter() && b.Highlighter()->HasHighlighter()))
b.InstallFiletypeHighlighter();
return true;
}
+2 -18
View File
@@ -19,8 +19,6 @@
#include "fonts/Font.h" // embedded default font (DefaultFont)
#include "fonts/FontRegistry.h"
#include "fonts/IosevkaExtended.h"
#include "syntax/HighlighterRegistry.h"
#include "syntax/NullHighlighter.h"
#ifndef KTE_FONT_SIZE
@@ -54,22 +52,8 @@ apply_syntax_to_buffer(Buffer *b, const GUIConfig &cfg)
if (cfg.syntax && b->GetEditMode() != EditMode::Writing) {
b->SetSyntaxEnabled(true);
b->EnsureHighlighter();
if (auto *eng = b->Highlighter()) {
if (!eng->HasHighlighter()) {
const std::string first_line = b->Nrows() > 0 ? b->GetLineString(0) : std::string();
std::string ft = kte::HighlighterRegistry::DetectForPath(
b->Filename(), first_line);
if (!ft.empty()) {
eng->SetHighlighter(kte::HighlighterRegistry::CreateFor(ft));
b->SetFiletype(ft);
eng->InvalidateFrom(0);
} else {
eng->SetHighlighter(std::make_unique<kte::NullHighlighter>());
b->SetFiletype("");
eng->InvalidateFrom(0);
}
}
}
if (!b->Highlighter()->HasHighlighter())
b->ApplyDetectedFiletype();
} else {
b->SetSyntaxEnabled(false);
}