diff options
| author | Andrey Lihatskiy <118752495+marchcat@users.noreply.github.com> | 2026-01-15 13:18:02 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-15 13:18:02 +0200 |
| commit | c2e0c724868d0d36ead0dadf15d087501268b713 (patch) | |
| tree | 20b3adfd59f83c9080e6a437aec100f27759108a | |
| parent | 6639992a0fc822efd1d6deb1cadf20f6d9a8b037 (diff) | |
| parent | d86785b28a69d2a10808e5dc2f2d0588a7be5768 (diff) | |
Merge pull request #5279 from secondlife/marchcat/slua-thread-5278
Async SLua parsing
| -rw-r--r-- | indra/llui/llkeywords.cpp | 139 | ||||
| -rw-r--r-- | indra/llui/llkeywords.h | 28 | ||||
| -rw-r--r-- | indra/newview/llscripteditor.cpp | 406 | ||||
| -rw-r--r-- | indra/newview/llscripteditor.h | 40 |
4 files changed, 550 insertions, 63 deletions
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 2168ba2382..fd9991fd11 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -497,42 +497,29 @@ bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &o } LLTrace::BlockTimerStatHandle FTM_SYNTAX_COLORING("Syntax Coloring"); +constexpr size_t AVERAGE_SEGMENT_LENGTH = 8; -// Walk through a string, applying the rules specified by the keyword token list and -// create a list of color segments. -void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style) +void LLKeywords::collectSegmentOps(segment_ops_t& ops, const LLWString& wtext, bool disable_syntax_highlighting) const { - LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING); + ops.clear(); - if( wtext.empty() ) + if (wtext.empty()) { return; } - - // Clear the segment list - seg_list->clear(); - // Reserve capacity for segments based on an estimated average of 8 characters per segment. - constexpr size_t AVERAGE_SEGMENT_LENGTH = 8; - seg_list->reserve(wtext.size() / AVERAGE_SEGMENT_LENGTH); - - S32 text_len = static_cast<S32>(wtext.size()) + 1; - - seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); - - static LLCachedControl<bool> sDisableSyntaxHighlighting(gSavedSettings, "ScriptEditorDisableSyntaxHighlight", false); - const bool disable_syntax_highlighting = sDisableSyntaxHighlighting; + // Heuristic to reduce reallocation churn on large scripts. + ops.reserve(wtext.size() / AVERAGE_SEGMENT_LENGTH); const llwchar* base = wtext.c_str(); const llwchar* cur = base; + while( *cur ) { if( *cur == '\n' || cur == base ) { if( *cur == '\n' ) { - LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, (S32)(cur - base)); - text_segment->setToken( 0 ); - insertSegment( *seg_list, text_segment, text_len, style, editor); + ops.push_back({SegmentOp::OP_LINE_BREAK, (S32)(cur - base), 0, nullptr}); cur++; if( !*cur || *cur == '\n' ) { @@ -570,8 +557,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW } S32 seg_end = (S32)(cur - base); - //create segments from seg_start to seg_end - insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor); + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_end, cur_token}); line_done = true; // to break out of second loop. break; } @@ -594,9 +580,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW // Check if syntax highlighting is disabled if (disable_syntax_highlighting) { - if (*cur && *cur != '\n') + while (*cur && *cur != '\n') { - cur++; + ++cur; } continue; // skip processing any further syntax highlighting } @@ -646,7 +632,6 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW if (*p == '[') { p++; // skip the second [ - const llwchar* content_start = p; // Build the closing pattern: ] + level equals + ] // Search for it in the remaining text @@ -667,7 +652,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW // Found the matching close seg_end = (S32)(close_check + 1 - base); cur = close_check + 1; - insertSegments(wtext, *seg_list, cur_delimiter, text_len, seg_start, seg_end, style, editor); + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_end, cur_delimiter}); break; } } @@ -677,9 +662,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW if (!*p) { // No closing found, highlight to end of file - seg_end = text_len - 1; + seg_end = static_cast<S32>(wtext.size()); cur = base + seg_end; - insertSegments(wtext, *seg_list, cur_delimiter, text_len, seg_start, seg_end, style, editor); + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_end, cur_delimiter}); } continue; } @@ -761,7 +746,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead(); } - insertSegments(wtext, *seg_list, cur_delimiter, text_len, seg_start, seg_end, style, editor); + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_end, cur_delimiter}); // Note: we don't increment cur, since the end of one delimited seg may be immediately // followed by the start of another one. continue; @@ -794,13 +779,12 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW S32 seg_end = seg_start + seg_len; // First try to match the whole token (including dots for Lua namespaces) - word_token_map_t::iterator map_iter = mWordTokenMap.find(WStringMapIndex(word_start, seg_len)); + word_token_map_t::const_iterator map_iter = mWordTokenMap.find(WStringMapIndex(word_start, seg_len)); if (map_iter != mWordTokenMap.end()) { // Found a match for the complete token (including any namespace) - LLKeywordToken* cur_token = map_iter->second; - insertSegments(wtext, *seg_list, cur_token, text_len, seg_start, seg_end, style, editor); + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_end, map_iter->second}); } else if (namespace_dots > 0 && mLuauLanguage) { @@ -815,8 +799,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW if (map_iter != mWordTokenMap.end()) { // Found a match for the namespace prefix, highlight just that part - LLKeywordToken* cur_token = map_iter->second; - insertSegments(wtext, *seg_list, cur_token, text_len, seg_start, seg_start + prefix_len, style, editor); + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_start + prefix_len, map_iter->second}); // Now try to match the function part (after the dot) const llwchar* func_part = last_dot + 1; @@ -830,13 +813,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW if (map_iter != mWordTokenMap.end()) { // Found a match for the function part - LLKeywordToken* cur_token = map_iter->second; - insertSegments(wtext, *seg_list, cur_token, text_len, seg_start, seg_end, style, editor); - } - else - { - // No token found, continue without incrementing cur - // since we already advanced it while collecting the word + ops.push_back({SegmentOp::OP_TOKEN, seg_start, seg_end, map_iter->second}); } } } @@ -854,6 +831,84 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW } } +bool LLKeywords::applySegmentOpsRange(std::vector<LLTextSegmentPtr> *seg_list, + const LLWString& wtext, + const segment_ops_t& ops, + size_t& op_index, + size_t max_ops, + LLTextEditor& editor, + LLStyleConstSP style) +{ + if (wtext.empty()) + { + return true; + } + + if (op_index == 0) + { + // Clear the segment list + seg_list->clear(); + // Reserve capacity for segments based on an estimated average of 8 characters per segment. + seg_list->reserve(wtext.size() / AVERAGE_SEGMENT_LENGTH); + + S32 text_len = static_cast<S32>(wtext.size()) + 1; + seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); + } + + S32 text_len = static_cast<S32>(wtext.size()) + 1; + size_t end_index = op_index + max_ops; + if (end_index > ops.size()) + { + end_index = ops.size(); + } + + for (; op_index < end_index; ++op_index) + { + const auto& op = ops[op_index]; + if (op.type == SegmentOp::OP_LINE_BREAK) + { + LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, op.start); + text_segment->setToken( 0 ); + insertSegment( *seg_list, text_segment, text_len, style, editor); + } + else + { + insertSegments(wtext, *seg_list, op.token, text_len, op.start, op.end, style, editor); + } + } + + return op_index >= ops.size(); +} + +void LLKeywords::applySegmentOps(std::vector<LLTextSegmentPtr> *seg_list, + const LLWString& wtext, + const segment_ops_t& ops, + LLTextEditor& editor, + LLStyleConstSP style) +{ + size_t op_index = 0; + applySegmentOpsRange(seg_list, wtext, ops, op_index, ops.size(), editor, style); +} + +// Walk through a string, applying the rules specified by the keyword token list and +// create a list of color segments. +void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style) +{ + LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING); + + if( wtext.empty() ) + { + return; + } + + static LLCachedControl<bool> sDisableSyntaxHighlighting(gSavedSettings, "ScriptEditorDisableSyntaxHighlight", false); + const bool disable_syntax_highlighting = sDisableSyntaxHighlighting; + + segment_ops_t ops; + collectSegmentOps(ops, wtext, disable_syntax_highlighting); + applySegmentOps(seg_list, wtext, ops, editor, style); +} + void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor ) { std::string::size_type pos = wtext.find('\n',seg_start); diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h index 3a02f0e68e..70fc2c20d5 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -36,6 +36,8 @@ #include <map> #include <list> #include <deque> +#include <vector> +#include <cstddef> #include "llpointer.h" class LLTextSegment; @@ -124,6 +126,32 @@ public: const LLWString& text, class LLTextEditor& editor, LLStyleConstSP style); + struct SegmentOp + { + enum EOpType + { + OP_LINE_BREAK, + OP_TOKEN + }; + EOpType type; + S32 start; + S32 end; + LLKeywordToken* token; + }; + typedef std::vector<SegmentOp> segment_ops_t; + void collectSegmentOps(segment_ops_t& ops, const LLWString& text, bool disable_syntax_highlighting) const; + void applySegmentOps(std::vector<LLTextSegmentPtr> *seg_list, + const LLWString& text, + const segment_ops_t& ops, + class LLTextEditor& editor, + LLStyleConstSP style); + bool applySegmentOpsRange(std::vector<LLTextSegmentPtr> *seg_list, + const LLWString& text, + const segment_ops_t& ops, + size_t& op_index, + size_t max_ops, + class LLTextEditor& editor, + LLStyleConstSP style); void initialize(LLSD SyntaxXML, bool luau_language = false); void processTokens(); diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index d171d785a7..2a4c2b9d0b 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -31,6 +31,179 @@ #include "llsyntaxid.h" #include "lllocalcliprect.h" #include "llviewercontrol.h" +#include "llworkerthread.h" +#include <algorithm> +#include <utility> + +static LLWorkerThread& getSyntaxWorkerThread() +{ + static LLWorkerThread* sThread = new LLWorkerThread("SyntaxParse", true); + return *sThread; +} + +class LLScriptEditorSyntaxWorker final : public LLWorkerClass +{ +public: + struct Request + { + LLWString text; + S32 text_generation = 0; + U32 keywords_generation = 0; + bool disable_highlight = false; + LLKeywords* keywords = nullptr; + }; + + struct Result + { + LLWString text; + LLKeywords::segment_ops_t ops; + S32 text_generation = 0; + U32 keywords_generation = 0; + bool disable_highlight = false; + LLKeywords* keywords = nullptr; + }; + + LLScriptEditorSyntaxWorker(LLWorkerThread* thread, LLScriptEditor* editor) + : LLWorkerClass(thread, "ScriptEditorSyntax"), + mEditor(editor), + mHasPending(false), + mDropResults(false) + { + } + + void queueRequest(const Request& request) + { + bool should_add = false; + { + LLMutexLock lock(&mRequestMutex); + mPendingRequest = request; + mHasPending = true; + should_add = (!haveWork() && !isWorking()); + } + if (should_add) + { + addWork(0); + } + } + + void pump() + { + checkWork(); + bool should_add = false; + { + LLMutexLock lock(&mRequestMutex); + should_add = (!haveWork() && !isWorking() && mHasPending); + } + if (should_add) + { + addWork(0); + } + } + + void waitForIdle(bool drop_results) + { + mDropResults = drop_results; + { + LLMutexLock lock(&mRequestMutex); + mHasPending = false; + } + if (mRequestHandle != LLWorkerThread::nullHandle()) + { + mWorkerThread->waitForResult(mRequestHandle, false); + checkWork(); + } + mDropResults = false; + } + + void shutdown() + { + mEditor = nullptr; + waitForIdle(true); + scheduleDelete(); + } + +private: + void startWork(S32 param) override + { + LLMutexLock lock(&mRequestMutex); + if (!mHasPending) + { + return; + } + mActiveRequest = mPendingRequest; + mHasPending = false; + } + + bool doWork(S32 param) override + { + Request request; + { + LLMutexLock lock(&mRequestMutex); + request = mActiveRequest; + } + + Result result; + result.text = request.text; + result.text_generation = request.text_generation; + result.keywords_generation = request.keywords_generation; + result.disable_highlight = request.disable_highlight; + result.keywords = request.keywords; + + if (request.keywords) + { + request.keywords->collectSegmentOps(result.ops, request.text, request.disable_highlight); + } + + { + LLMutexLock lock(&mResultMutex); + mResult = std::move(result); + } + return true; + } + + void endWork(S32 param, bool aborted) override + { + if (aborted || mDropResults || !mEditor) + { + return; + } + + Result result; + { + LLMutexLock lock(&mResultMutex); + result = std::move(mResult); + } + + if (mEditor->getTextGeneration() != result.text_generation) + { + return; + } + if (mEditor->getKeywordsGeneration() != result.keywords_generation) + { + return; + } + if (&mEditor->getKeywords() != result.keywords) + { + return; + } + + mEditor->queueSyntaxApply(std::move(result.text), + std::move(result.ops), + result.text_generation, + result.keywords_generation, + result.disable_highlight, + result.keywords); + } + + LLScriptEditor* mEditor; + LLMutex mRequestMutex; + LLMutex mResultMutex; + Request mPendingRequest; + Request mActiveRequest; + Result mResult; + bool mHasPending; + bool mDropResults; +}; const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32; @@ -46,7 +219,20 @@ LLScriptEditor::LLScriptEditor(const Params& p) : LLTextEditor(p) , mShowLineNumbers(p.show_line_numbers), mUseDefaultFontSize(p.default_font_size), - mLuauLanguage(false) + mLuauLanguage(false), + mKeywordsGeneration(0), + mLastQueuedTextGeneration(-1), + mLastQueuedKeywordsGeneration(0), + mLastQueuedDisableHighlight(false), + mLastQueuedKeywords(nullptr), + mSyntaxWorker(nullptr), + mSyntaxApplyState(SyntaxApplyState::Idle), + mPendingApplyOpIndex(0), + mPendingApplySegmentIndex(0), + mPendingApplyTextGeneration(-1), + mPendingApplyKeywordsGeneration(0), + mPendingApplyDisableHighlight(false), + mPendingApplyKeywords(nullptr) { if (mShowLineNumbers) { @@ -55,10 +241,22 @@ LLScriptEditor::LLScriptEditor(const Params& p) } } +LLScriptEditor::~LLScriptEditor() +{ + if (mSyntaxWorker) + { + mSyntaxWorker->shutdown(); + getSyntaxWorkerThread().update(0.f); + mSyntaxWorker = nullptr; + } +} + bool LLScriptEditor::postBuild() { gSavedSettings.getControl("LSLFontSizeName")->getCommitSignal()->connect(boost::bind(&LLScriptEditor::onFontSizeChange, this)); - return LLTextEditor::postBuild(); + bool result = LLTextEditor::postBuild(); + setFont(getScriptFont()); + return result; } void LLScriptEditor::draw() @@ -154,41 +352,208 @@ void LLScriptEditor::initKeywords(bool luau_language) void LLScriptEditor::loadKeywords() { LL_PROFILE_ZONE_SCOPED; + ensureSyntaxWorker(); + mSyntaxWorker->waitForIdle(true); + resetPendingSyntaxApply(); getKeywords().processTokens(); + ++mKeywordsGeneration; - LLStyleConstSP style = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); - - segment_vec_t segment_list; - getKeywords().findSegments(&segment_list, getWText(), *this, style); - - mSegments.clear(); - segment_set_t::iterator insert_it = mSegments.begin(); - for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + LLKeywords::segment_ops_t ops; + const LLWString& text = getWText(); + const llwchar* base = text.c_str(); + for (const llwchar* cur = base; *cur; ++cur) { - insert_it = mSegments.insert(insert_it, *list_it); + if (*cur == '\n') + { + ops.push_back({LLKeywords::SegmentOp::OP_LINE_BREAK, (S32)(cur - base), 0, nullptr}); + } } + applySyntaxSegments(text, ops); + queueSyntaxParse(); } void LLScriptEditor::updateSegments() { - if (mReflowIndex < S32_MAX && getKeywords().isLoaded() && mParseOnTheFly) + if (getKeywords().isLoaded() && mParseOnTheFly) { LL_PROFILE_ZONE_SCOPED; + ensureSyntaxWorker(); + mSyntaxWorker->pump(); + if (mReflowIndex < S32_MAX) + { + queueSyntaxParse(); + } + processPendingSyntaxApply(); + } + + LLTextBase::updateSegments(); +} + +void LLScriptEditor::ensureSyntaxWorker() +{ + if (!mSyntaxWorker) + { + mSyntaxWorker = new LLScriptEditorSyntaxWorker(&getSyntaxWorkerThread(), this); + } +} + +void LLScriptEditor::queueSyntaxParse() +{ + static LLCachedControl<bool> sDisableSyntaxHighlighting(gSavedSettings, "ScriptEditorDisableSyntaxHighlight", false); + const bool disable_syntax_highlighting = sDisableSyntaxHighlighting; + + LLKeywords* keywords = &getKeywords(); + const S32 text_generation = getTextGeneration(); + + if (mLastQueuedTextGeneration == text_generation + && mLastQueuedKeywordsGeneration == mKeywordsGeneration + && mLastQueuedDisableHighlight == disable_syntax_highlighting + && mLastQueuedKeywords == keywords) + { + return; + } + + mLastQueuedTextGeneration = text_generation; + mLastQueuedKeywordsGeneration = mKeywordsGeneration; + mLastQueuedDisableHighlight = disable_syntax_highlighting; + mLastQueuedKeywords = keywords; + + LLScriptEditorSyntaxWorker::Request request; + request.text = getWText(); + request.text_generation = text_generation; + request.keywords_generation = mKeywordsGeneration; + request.disable_highlight = disable_syntax_highlighting; + request.keywords = keywords; + mSyntaxWorker->queueRequest(request); +} + +void LLScriptEditor::queueSyntaxApply(LLWString text, + LLKeywords::segment_ops_t ops, + S32 text_generation, + U32 keywords_generation, + bool disable_highlight, + LLKeywords* keywords) +{ + if (text_generation != getTextGeneration() + || keywords_generation != mKeywordsGeneration + || keywords != &getKeywords()) + { + return; + } + + resetPendingSyntaxApply(); + + // Small updates can apply synchronously; larger ones use background slices. + constexpr size_t kImmediateOpsThreshold = 500; + if (ops.size() <= kImmediateOpsThreshold) + { + applySyntaxSegments(text, ops); + return; + } + + mPendingApplyText = std::move(text); + mPendingApplyOps = std::move(ops); + mPendingApplyTextGeneration = text_generation; + mPendingApplyKeywordsGeneration = keywords_generation; + mPendingApplyDisableHighlight = disable_highlight; + mPendingApplyKeywords = keywords; + mPendingApplyOpIndex = 0; + mPendingApplySegmentIndex = 0; + mPendingApplySegments.clear(); + mPendingApplySegmentSet.clear(); + mPendingApplyStyle = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); + mSyntaxApplyState = SyntaxApplyState::Building; +} + +void LLScriptEditor::processPendingSyntaxApply() +{ + if (mSyntaxApplyState == SyntaxApplyState::Idle) + { + return; + } - LLStyleConstSP style = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); + static LLCachedControl<bool> sDisableSyntaxHighlighting(gSavedSettings, "ScriptEditorDisableSyntaxHighlight", false); + if (mPendingApplyTextGeneration != getTextGeneration() + || mPendingApplyKeywordsGeneration != mKeywordsGeneration + || mPendingApplyKeywords != &getKeywords() + || mPendingApplyDisableHighlight != sDisableSyntaxHighlighting) + { + resetPendingSyntaxApply(); + return; + } - // HACK: No non-ascii keywords for now - segment_vec_t segment_list; - getKeywords().findSegments(&segment_list, getWText(), *this, style); + // Slice sizes control UI budget while applying large highlight updates. + constexpr size_t kOpsPerSlice = 250; + constexpr size_t kSegmentsPerSlice = 250; - clearSegments(); - for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + if (mSyntaxApplyState == SyntaxApplyState::Building) + { + if (mPendingApplyStyle.isNull()) { - insertSegment(*list_it); + mPendingApplyStyle = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); } + bool done = getKeywords().applySegmentOpsRange(&mPendingApplySegments, + mPendingApplyText, + mPendingApplyOps, + mPendingApplyOpIndex, + kOpsPerSlice, + *this, + mPendingApplyStyle); + if (done) + { + mSyntaxApplyState = SyntaxApplyState::Inserting; + } + return; } - LLTextBase::updateSegments(); + size_t end_index = std::min(mPendingApplySegmentIndex + kSegmentsPerSlice, mPendingApplySegments.size()); + for (; mPendingApplySegmentIndex < end_index; ++mPendingApplySegmentIndex) + { + LLTextSegmentPtr segment = mPendingApplySegments[mPendingApplySegmentIndex]; + segment->linkToDocument(this); + mPendingApplySegmentSet.insert(segment); + } + + if (mPendingApplySegmentIndex >= mPendingApplySegments.size()) + { + S32 saved_scroll_index = mScrollIndex; + segment_set_t old_segments; + old_segments.swap(mSegments); + mSegments.swap(mPendingApplySegmentSet); + mScrollIndex = saved_scroll_index; + needsReflow(0); + resetPendingSyntaxApply(); + } +} + +void LLScriptEditor::resetPendingSyntaxApply() +{ + mSyntaxApplyState = SyntaxApplyState::Idle; + mPendingApplyText.clear(); + mPendingApplyOps.clear(); + mPendingApplySegments.clear(); + mPendingApplySegmentSet.clear(); + mPendingApplyOpIndex = 0; + mPendingApplySegmentIndex = 0; + mPendingApplyStyle = LLStyleConstSP(); + mPendingApplyTextGeneration = -1; + mPendingApplyKeywordsGeneration = 0; + mPendingApplyDisableHighlight = false; + mPendingApplyKeywords = nullptr; +} + +void LLScriptEditor::applySyntaxSegments(const LLWString& text, const LLKeywords::segment_ops_t& ops) +{ + LLStyleConstSP style = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); + + segment_vec_t segment_list; + getKeywords().applySegmentOps(&segment_list, text, ops, *this, style); + + clearSegments(); + for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + { + insertSegment(*list_it); + } } void LLScriptEditor::clearSegments() @@ -260,6 +625,7 @@ void LLScriptEditor::onFontSizeChange() { if (!mUseDefaultFontSize) { + setFont(getScriptFont()); needsReflow(); } } diff --git a/indra/newview/llscripteditor.h b/indra/newview/llscripteditor.h index 2d98110f75..dcbfdf3085 100644 --- a/indra/newview/llscripteditor.h +++ b/indra/newview/llscripteditor.h @@ -29,6 +29,7 @@ #define LL_SCRIPTEDITOR_H #include "lltexteditor.h" +#include <cstddef> class LLScriptEditor : public LLTextEditor { @@ -41,7 +42,7 @@ public: Params(); }; - ~LLScriptEditor() override {}; + ~LLScriptEditor() override; // LLView override void draw() override; @@ -55,6 +56,8 @@ public: LLKeywords& getKeywords(); bool getIsLuauLanguage() { return mLuauLanguage; } void setLuauLanguage(bool luau_language) { mLuauLanguage = luau_language; } + U32 getKeywordsGeneration() const { return mKeywordsGeneration; } + void applySyntaxSegments(const LLWString& text, const LLKeywords::segment_ops_t& ops); static std::string getScriptFontSize(); LLFontGL* getScriptFont(); @@ -62,12 +65,29 @@ public: protected: friend class LLUICtrlFactory; + friend class LLScriptEditorSyntaxWorker; LLScriptEditor(const Params& p); private: + enum class SyntaxApplyState + { + Idle, + Building, + Inserting + }; void drawLineNumbers(); void updateSegments() override; void drawSelectionBackground() override; + void ensureSyntaxWorker(); + void queueSyntaxParse(); + void queueSyntaxApply(LLWString text, + LLKeywords::segment_ops_t ops, + S32 text_generation, + U32 keywords_generation, + bool disable_highlight, + LLKeywords* keywords); + void processPendingSyntaxApply(); + void resetPendingSyntaxApply(); LLKeywords mKeywordsLua; LLKeywords mKeywordsLSL; @@ -75,6 +95,24 @@ private: bool mShowLineNumbers; bool mUseDefaultFontSize; + U32 mKeywordsGeneration; + S32 mLastQueuedTextGeneration; + U32 mLastQueuedKeywordsGeneration; + bool mLastQueuedDisableHighlight; + LLKeywords* mLastQueuedKeywords; + class LLScriptEditorSyntaxWorker* mSyntaxWorker; + SyntaxApplyState mSyntaxApplyState; + LLWString mPendingApplyText; + LLKeywords::segment_ops_t mPendingApplyOps; + segment_vec_t mPendingApplySegments; + segment_set_t mPendingApplySegmentSet; + size_t mPendingApplyOpIndex; + size_t mPendingApplySegmentIndex; + LLStyleConstSP mPendingApplyStyle; + S32 mPendingApplyTextGeneration; + U32 mPendingApplyKeywordsGeneration; + bool mPendingApplyDisableHighlight; + LLKeywords* mPendingApplyKeywords; }; #endif // LL_SCRIPTEDITOR_H |
