From 0cc0db81afec26842ea9887aa50afb072d13914c Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 25 Feb 2025 06:43:52 +0200 Subject: #3422 Better handling of Lua syntax --- indra/newview/llscripteditor.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 59cf3ac02b..0edaf43753 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -141,20 +141,24 @@ void LLScriptEditor::drawLineNumbers() } } -void LLScriptEditor::initKeywords() +void LLScriptEditor::initKeywords(bool luau_language) { - mKeywords.initialize(LLSyntaxIdLSL::getInstance()->getKeywordsXML()); + mKeywordsLua.initialize(LLSyntaxLua::getInstance()->getKeywordsXML(), true); + mKeywordsLSL.initialize(LLSyntaxIdLSL::getInstance()->getKeywordsXML(), false); + + mLuauLanguage = luau_language; + } void LLScriptEditor::loadKeywords() { LL_PROFILE_ZONE_SCOPED; - mKeywords.processTokens(); + getKeywords().processTokens(); LLStyleConstSP style = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); segment_vec_t segment_list; - mKeywords.findSegments(&segment_list, getWText(), *this, style); + getKeywords().findSegments(&segment_list, getWText(), *this, style); mSegments.clear(); segment_set_t::iterator insert_it = mSegments.begin(); @@ -166,7 +170,7 @@ void LLScriptEditor::loadKeywords() void LLScriptEditor::updateSegments() { - if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly) + if (mReflowIndex < S32_MAX && getKeywords().isLoaded() && mParseOnTheFly) { LL_PROFILE_ZONE_SCOPED; @@ -174,7 +178,7 @@ void LLScriptEditor::updateSegments() // HACK: No non-ascii keywords for now segment_vec_t segment_list; - mKeywords.findSegments(&segment_list, getWText(), *this, style); + getKeywords().findSegments(&segment_list, getWText(), *this, style); clearSegments(); for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) @@ -194,6 +198,21 @@ void LLScriptEditor::clearSegments() } } +LLKeywords::keyword_iterator_t LLScriptEditor::keywordsBegin() +{ + return mLuauLanguage ? mKeywordsLua.begin() : mKeywordsLSL.begin(); +} + +LLKeywords::keyword_iterator_t LLScriptEditor::keywordsEnd() +{ + return mLuauLanguage ? mKeywordsLua.end() : mKeywordsLSL.end(); +} + +LLKeywords& LLScriptEditor::getKeywords() +{ + return mLuauLanguage ? mKeywordsLua : mKeywordsLSL; +} + // Most of this is shamelessly copied from LLTextBase void LLScriptEditor::drawSelectionBackground() { -- cgit v1.3 From 668c8c61a12842d33cb83b79ba3d68ba3db7aa8b Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Fri, 7 Mar 2025 05:01:56 +0200 Subject: #3422 Code cleanup --- indra/newview/llpreviewscript.cpp | 6 +++--- indra/newview/llpreviewscript.h | 33 ++++++++++++++++----------------- indra/newview/llscripteditor.cpp | 8 ++++---- indra/newview/llscripteditor.h | 2 -- 4 files changed, 23 insertions(+), 26 deletions(-) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 28988f9564..9449768d79 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -396,9 +396,9 @@ LLScriptEdCore::LLScriptEdCore( LLScriptEdContainer* container, const std::string& sample, const LLHandle& floater_handle, - void (*load_callback)(void*), - void (*save_callback)(void*, bool), - void (*search_replace_callback) (void* userdata), + script_ed_callback_t load_callback, + save_callback_t save_callback, + script_ed_callback_t search_replace_callback, void* userdata, bool live, S32 bottom_pad) diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 430ca4cc76..117be03fc6 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -82,15 +82,19 @@ class LLScriptEdCore : public LLPanel friend class LLScriptEdContainer; friend class LLFloaterGotoLine; +public: + typedef boost::function script_ed_callback_t; + typedef boost::function save_callback_t; + protected: // Supposed to be invoked only by the container. LLScriptEdCore( LLScriptEdContainer* container, const std::string& sample, const LLHandle& floater_handle, - void (*load_callback)(void* userdata), - void (*save_callback)(void* userdata, bool close_after_save), - void (*search_replace_callback)(void* userdata), + script_ed_callback_t load_callback, + save_callback_t save_callback, + script_ed_callback_t search_replace_callback, void* userdata, bool live, S32 bottom_pad = 0); // pad below bottom row of buttons @@ -139,11 +143,11 @@ public: LLUUID getAssociatedExperience()const; void setAssociatedExperience( const LLUUID& experience_id ); - void setScriptName(const std::string& name){mScriptName = name;}; + void setScriptName(const std::string& name) { mScriptName = name; } - void setItemRemoved(bool script_removed){mScriptRemoved = script_removed;}; + void setItemRemoved(bool script_removed) { mScriptRemoved = script_removed; } - void setAssetID( const LLUUID& asset_id){ mAssetID = asset_id; }; + void setAssetID(const LLUUID& asset_id) { mAssetID = asset_id; } LLUUID getAssetID() { return mAssetID; } bool isFontSizeChecked(const LLSD &userdata); @@ -152,16 +156,15 @@ public: virtual bool handleKeyHere(KEY key, MASK mask); void selectAll() { mEditor->selectAll(); } + void enableSave(bool b) { mEnableSave = b; } + bool hasChanged(); + private: void onBtnDynamicHelp(); void onBtnUndoChanges(); - bool hasChanged(); - void selectFirstError(); - void enableSave(bool b) {mEnableSave = b;} - protected: void deleteBridges(); void setHelpPage(const std::string& help_string); @@ -176,9 +179,9 @@ private: std::string mSampleText; std::string mScriptName; LLScriptEditor* mEditor; - void (*mLoadCallback)(void* userdata); - void (*mSaveCallback)(void* userdata, bool close_after_save); - void (*mSearchReplaceCallback) (void* userdata); + script_ed_callback_t mLoadCallback; + save_callback_t mSaveCallback; + script_ed_callback_t mSearchReplaceCallback; void* mUserdata; LLComboBox *mFunctions; bool mForceClose; @@ -204,7 +207,6 @@ private: public: boost::signals2::connection mSyntaxIDConnection; - }; class LLScriptEdContainer : public LLPreview @@ -269,10 +271,8 @@ protected: S32 mPendingUploads; LLScriptMovedObserver* mItemObserver; - }; - // Used to view and edit an LSL script that is attached to an object. class LLLiveLSLEditor : public LLScriptEdContainer { @@ -280,7 +280,6 @@ class LLLiveLSLEditor : public LLScriptEdContainer public: LLLiveLSLEditor(const LLSD& key); - static void processScriptRunningReply(LLMessageSystem* msg, void**); virtual void callbackLSLCompileSucceeded(const LLUUID& task_id, diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 0edaf43753..2a9dd73936 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -45,7 +45,8 @@ LLScriptEditor::Params::Params() LLScriptEditor::LLScriptEditor(const Params& p) : LLTextEditor(p) , mShowLineNumbers(p.show_line_numbers), - mUseDefaultFontSize(p.default_font_size) + mUseDefaultFontSize(p.default_font_size), + mLuauLanguage(false) { if (mShowLineNumbers) { @@ -200,12 +201,12 @@ void LLScriptEditor::clearSegments() LLKeywords::keyword_iterator_t LLScriptEditor::keywordsBegin() { - return mLuauLanguage ? mKeywordsLua.begin() : mKeywordsLSL.begin(); + return getKeywords().begin(); } LLKeywords::keyword_iterator_t LLScriptEditor::keywordsEnd() { - return mLuauLanguage ? mKeywordsLua.end() : mKeywordsLSL.end(); + return getKeywords().end(); } LLKeywords& LLScriptEditor::getKeywords() @@ -237,7 +238,6 @@ void LLScriptEditor::drawSelectionBackground() ++rect_it) { LLRect selection_rect = *rect_it; - selection_rect = *rect_it; selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); gl_rect_2d(selection_rect, selection_color); } diff --git a/indra/newview/llscripteditor.h b/indra/newview/llscripteditor.h index ad61405917..1586a43160 100644 --- a/indra/newview/llscripteditor.h +++ b/indra/newview/llscripteditor.h @@ -67,8 +67,6 @@ private: void drawLineNumbers(); /* virtual */ void updateSegments(); /* virtual */ void drawSelectionBackground(); - void loadKeywords(const std::string& filename_keywords, - const std::string& filename_colors); LLKeywords mKeywordsLua; LLKeywords mKeywordsLSL; -- cgit v1.3 From 732b96958f0677674ca2756ce6a692f54bfd6a28 Mon Sep 17 00:00:00 2001 From: Darl Date: Wed, 31 Dec 2025 09:22:35 -0600 Subject: Update LLScriptEditor and friends to index line numbers from 1 when editing in Lua (#5190) * Update LLScriptEditor to index line numbers from 1 when editing in Lua This is to match server-generated runtime errors that reference lines. Signed-off-by: Darl * Update LLFloaterGotoLine and LLPreviewScript to support Lua's line/col expectations Existing behavior for LSL is preserved Fixes a bug in previous commit that causes the line number shown near the bottom of the script edit floater to be off by 1 when displaying a Lua script. Signed-off-by: Darl * Update script editor error console goto error to reflect Lua line nums Signed-off-by: Darl --------- Signed-off-by: Darl --- indra/newview/llfloatergotoline.cpp | 8 +++++--- indra/newview/llpreviewscript.cpp | 3 +++ indra/newview/llscripteditor.cpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/newview/llfloatergotoline.cpp b/indra/newview/llfloatergotoline.cpp index 09ca0ab530..6b3f16ecf5 100644 --- a/indra/newview/llfloatergotoline.cpp +++ b/indra/newview/llfloatergotoline.cpp @@ -109,8 +109,9 @@ void LLFloaterGotoLine::handleBtnGoto() { if (mEditorCore && mEditorCore->mEditor) { + S32 target_row = mEditorCore->mEditor->getIsLuauLanguage() ? (row - 1) : row; mEditorCore->mEditor->deselect(); - mEditorCore->mEditor->setCursor(row, column); + mEditorCore->mEditor->setCursor(target_row, column); mEditorCore->mEditor->setFocus(true); } } @@ -144,12 +145,13 @@ void LLFloaterGotoLine::onGotoBoxCommit() { if (mEditorCore && mEditorCore->mEditor) { - mEditorCore->mEditor->setCursor(row, column); + S32 target_row = mEditorCore->mEditor->getIsLuauLanguage() ? (row - 1) : row; + mEditorCore->mEditor->setCursor(target_row, column); S32 rownew = 0; S32 columnnew = 0; mEditorCore->mEditor->getCurrentLineAndColumn( &rownew, &columnnew, false ); // don't include wordwrap - if (rownew == row && columnnew == column) + if (rownew == target_row && columnnew == column) { mEditorCore->mEditor->deselect(); mEditorCore->mEditor->setFocus(true); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index b3e53df6d8..01167a43b8 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -751,6 +751,8 @@ void LLScriptEdCore::draw() S32 line = 0; S32 column = 0; mEditor->getCurrentLineAndColumn( &line, &column, false ); // don't include wordwrap + line = mEditor->getIsLuauLanguage() ? (line + 1) : line; + column = mEditor->getIsLuauLanguage() ? (column + 1) : column; LLStringUtil::format_map_t args; std::string cursor_pos; args["[LINE]"] = llformat ("%d", line); @@ -1211,6 +1213,7 @@ void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) sscanf(line.c_str(), "%d %d", &row, &column); //LL_INFOS() << "LLScriptEdCore::onErrorList() - " << row << ", " //<< column << LL_ENDL; + row = (self->mEditor->getIsLuauLanguage() ? row - 1 : row); self->mEditor->setCursor(row, column); self->mEditor->setFocus(true); } diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 2a9dd73936..d171d785a7 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -120,7 +120,7 @@ void LLScriptEditor::drawLineNumbers() // draw the line numbers if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop) { - const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum )); + const LLWString ltext = utf8str_to_wstring(llformat("%d", mLuauLanguage ? line.mLineNum + 1 : line.mLineNum)); bool is_cur_line = cursor_line == line.mLineNum; const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL; const LLColor4& fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor; -- cgit v1.3 From 5fa0910d6176f610de45548d8558fb53f00a0eda Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 12 Jan 2026 05:23:33 +0200 Subject: #5278 Move syntax tokenization off UI thread; apply results on main thread --- indra/llui/llkeywords.cpp | 109 ++++++++++------ indra/llui/llkeywords.h | 20 +++ indra/newview/llscripteditor.cpp | 275 ++++++++++++++++++++++++++++++++++++--- indra/newview/llscripteditor.h | 12 +- 4 files changed, 354 insertions(+), 62 deletions(-) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 2168ba2382..0ada54df06 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -498,41 +498,25 @@ bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &o LLTrace::BlockTimerStatHandle FTM_SYNTAX_COLORING("Syntax Coloring"); -// 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* 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(wtext.size()) + 1; - - seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); - - static LLCachedControl sDisableSyntaxHighlighting(gSavedSettings, "ScriptEditorDisableSyntaxHighlight", false); - const bool disable_syntax_highlighting = sDisableSyntaxHighlighting; - 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 +554,7 @@ void LLKeywords::findSegments(std::vector* 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; } @@ -646,7 +629,6 @@ void LLKeywords::findSegments(std::vector* 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 +649,7 @@ void LLKeywords::findSegments(std::vector* 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 +659,9 @@ void LLKeywords::findSegments(std::vector* seg_list, const LLW if (!*p) { // No closing found, highlight to end of file - seg_end = text_len - 1; + seg_end = static_cast(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 +743,7 @@ void LLKeywords::findSegments(std::vector* 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 +776,12 @@ void LLKeywords::findSegments(std::vector* 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 +796,7 @@ void LLKeywords::findSegments(std::vector* 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 +810,7 @@ void LLKeywords::findSegments(std::vector* 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 +828,61 @@ void LLKeywords::findSegments(std::vector* seg_list, const LLW } } +void LLKeywords::applySegmentOps(std::vector *seg_list, + const LLWString& wtext, + const segment_ops_t& ops, + LLTextEditor& editor, + LLStyleConstSP style) +{ + 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(wtext.size()) + 1; + + seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); + + for (const auto& op : ops) + { + 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); + } + } +} + +// 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* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style) +{ + LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING); + + if( wtext.empty() ) + { + return; + } + + static LLCachedControl 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& 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..b8689a0e88 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -36,6 +36,7 @@ #include #include #include +#include #include "llpointer.h" class LLTextSegment; @@ -124,6 +125,25 @@ 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 segment_ops_t; + void collectSegmentOps(segment_ops_t& ops, const LLWString& text, bool disable_syntax_highlighting) const; + void applySegmentOps(std::vector *seg_list, + const LLWString& text, + const segment_ops_t& 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..871210ea6d 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -31,6 +31,173 @@ #include "llsyntaxid.h" #include "lllocalcliprect.h" #include "llviewercontrol.h" +#include "llworkerthread.h" +#include + +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->applySyntaxSegments(result.text, result.ops); + } + + 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 +213,13 @@ 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) { if (mShowLineNumbers) { @@ -55,6 +228,16 @@ 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)); @@ -154,43 +337,93 @@ void LLScriptEditor::initKeywords(bool luau_language) void LLScriptEditor::loadKeywords() { LL_PROFILE_ZONE_SCOPED; + ensureSyntaxWorker(); + mSyntaxWorker->waitForIdle(true); 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; - - LLStyleConstSP style = new LLStyle(LLStyle::Params().font(getScriptFont()).color(mDefaultColor.get())); - - // HACK: No non-ascii keywords for now - segment_vec_t segment_list; - getKeywords().findSegments(&segment_list, getWText(), *this, style); - - clearSegments(); - for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + ensureSyntaxWorker(); + mSyntaxWorker->pump(); + if (mReflowIndex < S32_MAX) { - insertSegment(*list_it); + queueSyntaxParse(); } } LLTextBase::updateSegments(); } +void LLScriptEditor::ensureSyntaxWorker() +{ + if (!mSyntaxWorker) + { + mSyntaxWorker = new LLScriptEditorSyntaxWorker(&getSyntaxWorkerThread(), this); + } +} + +void LLScriptEditor::queueSyntaxParse() +{ + static LLCachedControl 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::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() { if (!mSegments.empty()) diff --git a/indra/newview/llscripteditor.h b/indra/newview/llscripteditor.h index 2d98110f75..1ca26fbcc9 100644 --- a/indra/newview/llscripteditor.h +++ b/indra/newview/llscripteditor.h @@ -41,7 +41,7 @@ public: Params(); }; - ~LLScriptEditor() override {}; + ~LLScriptEditor() override; // LLView override void draw() override; @@ -55,6 +55,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(); @@ -68,6 +70,8 @@ private: void drawLineNumbers(); void updateSegments() override; void drawSelectionBackground() override; + void ensureSyntaxWorker(); + void queueSyntaxParse(); LLKeywords mKeywordsLua; LLKeywords mKeywordsLSL; @@ -75,6 +79,12 @@ private: bool mShowLineNumbers; bool mUseDefaultFontSize; + U32 mKeywordsGeneration; + S32 mLastQueuedTextGeneration; + U32 mLastQueuedKeywordsGeneration; + bool mLastQueuedDisableHighlight; + LLKeywords* mLastQueuedKeywords; + class LLScriptEditorSyntaxWorker* mSyntaxWorker; }; #endif // LL_SCRIPTEDITOR_H -- cgit v1.3 From bb56e9dd9ff4b93c910bd1d72fb6cdba31be3ea9 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 12 Jan 2026 06:20:24 +0200 Subject: #5278 Async syntax apply in slices to avoid UI stalls --- indra/llui/llkeywords.cpp | 52 ++++++++++----- indra/llui/llkeywords.h | 8 +++ indra/newview/llscripteditor.cpp | 132 ++++++++++++++++++++++++++++++++++++++- indra/newview/llscripteditor.h | 28 +++++++++ 4 files changed, 204 insertions(+), 16 deletions(-) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 0ada54df06..8c34e4ab9c 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -828,29 +828,41 @@ void LLKeywords::collectSegmentOps(segment_ops_t& ops, const LLWString& wtext, b } } -void LLKeywords::applySegmentOps(std::vector *seg_list, - const LLWString& wtext, - const segment_ops_t& ops, - LLTextEditor& editor, - LLStyleConstSP style) +bool LLKeywords::applySegmentOpsRange(std::vector *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; + return true; } - // 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); + 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. + constexpr size_t AVERAGE_SEGMENT_LENGTH = 8; + seg_list->reserve(wtext.size() / AVERAGE_SEGMENT_LENGTH); - S32 text_len = static_cast(wtext.size()) + 1; + S32 text_len = static_cast(wtext.size()) + 1; + seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); + } - seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); + S32 text_len = static_cast(wtext.size()) + 1; + size_t end_index = op_index + max_ops; + if (end_index > ops.size()) + { + end_index = ops.size(); + } - for (const auto& op : ops) + 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); @@ -862,6 +874,18 @@ void LLKeywords::applySegmentOps(std::vector *seg_list, insertSegments(wtext, *seg_list, op.token, text_len, op.start, op.end, style, editor); } } + + return op_index >= ops.size(); +} + +void LLKeywords::applySegmentOps(std::vector *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 diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h index b8689a0e88..70fc2c20d5 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -37,6 +37,7 @@ #include #include #include +#include #include "llpointer.h" class LLTextSegment; @@ -144,6 +145,13 @@ public: const segment_ops_t& ops, class LLTextEditor& editor, LLStyleConstSP style); + bool applySegmentOpsRange(std::vector *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 871210ea6d..93244107ca 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -32,6 +32,7 @@ #include "lllocalcliprect.h" #include "llviewercontrol.h" #include "llworkerthread.h" +#include #include static LLWorkerThread& getSyntaxWorkerThread() @@ -186,7 +187,12 @@ private: return; } - mEditor->applySyntaxSegments(result.text, result.ops); + mEditor->queueSyntaxApply(std::move(result.text), + std::move(result.ops), + result.text_generation, + result.keywords_generation, + result.disable_highlight, + result.keywords); } LLScriptEditor* mEditor; @@ -219,7 +225,14 @@ LLScriptEditor::LLScriptEditor(const Params& p) mLastQueuedKeywordsGeneration(0), mLastQueuedDisableHighlight(false), mLastQueuedKeywords(nullptr), - mSyntaxWorker(nullptr) + mSyntaxWorker(nullptr), + mSyntaxApplyState(SyntaxApplyState::Idle), + mPendingApplyOpIndex(0), + mPendingApplySegmentIndex(0), + mPendingApplyTextGeneration(-1), + mPendingApplyKeywordsGeneration(0), + mPendingApplyDisableHighlight(false), + mPendingApplyKeywords(nullptr) { if (mShowLineNumbers) { @@ -339,6 +352,7 @@ void LLScriptEditor::loadKeywords() LL_PROFILE_ZONE_SCOPED; ensureSyntaxWorker(); mSyntaxWorker->waitForIdle(true); + resetPendingSyntaxApply(); getKeywords().processTokens(); ++mKeywordsGeneration; @@ -367,6 +381,7 @@ void LLScriptEditor::updateSegments() { queueSyntaxParse(); } + processPendingSyntaxApply(); } LLTextBase::updateSegments(); @@ -410,6 +425,119 @@ void LLScriptEditor::queueSyntaxParse() 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(); + + 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; + } + + static LLCachedControl sDisableSyntaxHighlighting(gSavedSettings, "ScriptEditorDisableSyntaxHighlight", false); + if (mPendingApplyTextGeneration != getTextGeneration() + || mPendingApplyKeywordsGeneration != mKeywordsGeneration + || mPendingApplyKeywords != &getKeywords() + || mPendingApplyDisableHighlight != sDisableSyntaxHighlighting) + { + resetPendingSyntaxApply(); + return; + } + + constexpr size_t kOpsPerSlice = 250; + constexpr size_t kSegmentsPerSlice = 250; + + if (mSyntaxApplyState == SyntaxApplyState::Building) + { + if (mPendingApplyStyle.isNull()) + { + 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; + } + + 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())); diff --git a/indra/newview/llscripteditor.h b/indra/newview/llscripteditor.h index 1ca26fbcc9..dcbfdf3085 100644 --- a/indra/newview/llscripteditor.h +++ b/indra/newview/llscripteditor.h @@ -29,6 +29,7 @@ #define LL_SCRIPTEDITOR_H #include "lltexteditor.h" +#include class LLScriptEditor : public LLTextEditor { @@ -64,14 +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; @@ -85,6 +101,18 @@ private: 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 -- cgit v1.3 From f1f89b2a21468a9a6f90bfd77a5dfcbcb0b3c0c6 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 12 Jan 2026 06:42:56 +0200 Subject: #5278 Use correct font for unhighlighted segments --- indra/newview/llscripteditor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 93244107ca..68c4077b89 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -254,7 +254,9 @@ LLScriptEditor::~LLScriptEditor() 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() @@ -621,6 +623,7 @@ void LLScriptEditor::onFontSizeChange() { if (!mUseDefaultFontSize) { + setFont(getScriptFont()); needsReflow(); } } -- cgit v1.3 From 04db46f0b12c894983fdb1bb783fbad9d5a11943 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 15 Jan 2026 06:24:41 +0200 Subject: #5278 Reduce reallocs --- indra/llui/llkeywords.cpp | 2 ++ indra/newview/llscripteditor.cpp | 2 ++ 2 files changed, 4 insertions(+) (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index bbcb45f100..c4272401b9 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -506,6 +506,8 @@ void LLKeywords::collectSegmentOps(segment_ops_t& ops, const LLWString& wtext, b { return; } + // Heuristic to reduce reallocation churn on large scripts. + ops.reserve(wtext.size() / 8); const llwchar* base = wtext.c_str(); const llwchar* cur = base; diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 68c4077b89..2a4c2b9d0b 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -443,6 +443,7 @@ void LLScriptEditor::queueSyntaxApply(LLWString text, resetPendingSyntaxApply(); + // Small updates can apply synchronously; larger ones use background slices. constexpr size_t kImmediateOpsThreshold = 500; if (ops.size() <= kImmediateOpsThreshold) { @@ -481,6 +482,7 @@ void LLScriptEditor::processPendingSyntaxApply() return; } + // Slice sizes control UI budget while applying large highlight updates. constexpr size_t kOpsPerSlice = 250; constexpr size_t kSegmentsPerSlice = 250; -- cgit v1.3 From 364ff6e6d45087653a302b96ccd8c9e02db2b9d1 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 28 Apr 2026 10:23:44 -0700 Subject: Refactor the LLSyntaxIDLSL/Lua into single LLSyntaxDefCache, use new cap when available to retrieve all language definition files and cache them locally. Retrieve the default definition files from the LSL definitions precompiled library. --- indra/llmessage/llcorehttputil.cpp | 33 +- indra/llmessage/llcorehttputil.h | 1 - indra/newview/app_settings/types_lua_default.xml | 5262 ---------------------- indra/newview/llpreviewscript.cpp | 5 +- indra/newview/llscripteditor.cpp | 4 +- indra/newview/llscripteditorws.cpp | 10 +- indra/newview/llsyntaxid.cpp | 517 ++- indra/newview/llsyntaxid.h | 127 +- indra/newview/llviewerregion.cpp | 1 + indra/newview/viewer_manifest.py | 12 +- 10 files changed, 438 insertions(+), 5534 deletions(-) delete mode 100644 indra/newview/app_settings/types_lua_default.xml (limited to 'indra/newview/llscripteditor.cpp') diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index b24e5e4fcc..f18af1f18e 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -39,7 +39,7 @@ #include "llfilesystem.h" #include "message.h" // for getting the port - +#include "llstl.h" using namespace LLCore; @@ -112,15 +112,17 @@ bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd) { // Convert response to LLSD BufferArray * body(response->getBody()); - if (!body || !body->size()) + size_t body_size(body ? body->size() : 0); + if (!body || !body_size) { return false; } LLCore::BufferArrayStream bas(body); LLSD body_llsd; - S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); - if (LLSDParser::PARSE_FAILURE == parse_status){ + + if (!LLSDSerialize::deserialize(body_llsd, bas, body_size)) + { return false; } out_llsd = body_llsd; @@ -409,28 +411,6 @@ LLSD HttpCoroLLSDHandler::handleSuccess(LLCore::HttpResponse * response, LLCore: result = parseBody(response, success); -#if 0 - bool parsed = !((response->getBodySize() == 0) || - !LLCoreHttpUtil::responseToLLSD(response, emit_parse_errors, result)); - - if (!parsed) - { - // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' - LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); - const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; - - if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) - { - std::string thebody = LLCoreHttpUtil::responseToString(response); - LL_WARNS("CoreHTTP") << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " - << " body: " << thebody << LL_ENDL; - - // Replace the status with a new one indicating the failure. - status = LLCore::HttpStatus(499, "Failed to deserialize LLSD."); - } - } -#endif - if (!success) { #if 1 @@ -955,7 +935,6 @@ LLSD HttpCoroutineAdapter::getJsonAndSuspend(LLCore::HttpRequest::ptr_t request, return getAndSuspend_(request, url, options, headers, httpHandler); } - LLSD HttpCoroutineAdapter::getAndSuspend_(LLCore::HttpRequest::ptr_t &request, const std::string & url, LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h index 3dbfd6f00d..8fa638fd6e 100644 --- a/indra/llmessage/llcorehttputil.h +++ b/indra/llmessage/llcorehttputil.h @@ -483,7 +483,6 @@ public: headers); } - /// Execute a DELETE transaction on the supplied URL and yield execution of /// the coroutine until a result is available. /// diff --git a/indra/newview/app_settings/types_lua_default.xml b/indra/newview/app_settings/types_lua_default.xml deleted file mode 100644 index cb40ec3d06..0000000000 --- a/indra/newview/app_settings/types_lua_default.xml +++ /dev/null @@ -1,5262 +0,0 @@ - - - - version - 1.0.0 - typeAliases - - - name - rotation - definition - - kind - reference - name - quaternion - - comment - 'rotation' is an alias for 'quaternion' - - - name - numeric - definition - - kind - union - types - - boolean - number - - - - - name - list - definition - - kind - array - elementType - - kind - union - types - - string - number - vector - uuid - quaternion - boolean - - - - - - name - LLDetectedEventName - definition - - kind - literal-union - values - - - - - name - LLNonDetectedEventName - definition - - kind - literal-union - values - - - - - name - LLEventName - definition - - kind - union - types - - LLDetectedEventName - LLNonDetectedEventName - - - - - name - LLEventHandler - definition - - kind - function - parameters - - - type - any - variadic - true - - - returnType - () - - - - name - LLDetectedEventHandler - definition - - kind - function - parameters - - - name - detected - type - - kind - array - elementType - LLDetectedEvent - - - - returnType - () - - - - name - LLTimerEveryCallback - definition - - kind - function - parameters - - - name - scheduled - type - number - - - name - interval - type - number - - - returnType - () - - comment - Callback type for LLTimers.every() - receives scheduled time and interval - - - name - LLTimerOnceCallback - definition - - kind - function - parameters - - - name - scheduled - type - number - - - returnType - () - - comment - Callback type for LLTimers.once() - receives scheduled time - - - name - LLTimerCallback - definition - - kind - union - types - - LLTimerEveryCallback - LLTimerOnceCallback - (...any) -> ...any - - - comment - Union of timer callback types - - - name - OsDateTime - definition - - kind - table - properties - - - name - year - type - number - - - name - month - type - number - - - name - day - type - number - - - name - hour - type - number - optional - true - - - name - min - type - number - optional - true - - - name - sec - type - number - optional - true - - - name - wday - type - number - optional - true - - - name - yday - type - number - optional - true - - - name - isdst - type - boolean - optional - true - - - - comment - Date/time table structure used by os.date and os.time - - - classes - - - name - quaternion - comment - Luau Type Definition for LSL UserData Type: quaternion - properties - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - methods - - - name - __add - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __sub - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __mul - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __div - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - quaternion - - - name - __unm - parameters - - - name - self - - - returnType - quaternion - - - name - __eq - parameters - - - name - self - - - name - other - type - quaternion - - - returnType - boolean - - - name - __tostring - parameters - - - name - self - - - returnType - string - - - - - name - uuid - comment - Luau Type Definition for LSL UserData Type: uuid - properties - - - name - istruthy - type - boolean - comment - Returns true if the UUID is not the null UUID (all zeros) - - - name - bytes - type - string? - comment - Returns the raw 16-byte binary string of the UUID, or nil if the UUID is not in a compressed state - - - methods - - - name - __tostring - parameters - - - name - self - - - returnType - string - - - - - name - vector - comment - Luau Type Definition for LSL UserData Type: vector - properties - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - methods - - - name - __add - parameters - - - name - self - - - name - other - type - vector - - - returnType - vector - comment - Native component-wise addition - - - name - __sub - parameters - - - name - self - - - name - other - type - vector - - - returnType - vector - comment - Native component-wise subtraction - - - name - __unm - parameters - - - name - self - - - returnType - vector - comment - Unary negation - - - name - __mul - parameters - - - name - self - - - name - other - type - number | vector | quaternion - - - returnType - vector - comment - Multiplication: vector * number or vector * vector -> vector, vector * quaternion -> vector (Rotation) - - - name - __div - parameters - - - name - self - - - name - other - type - number | vector | quaternion - - - returnType - vector - comment - Division: vector / number or vector / vector -> vector (Scale), vector / quaternion -> vector (Rotation by inverse) - - - name - __mod - parameters - - - name - self - - - name - other - type - vector - - - returnType - vector - comment - LSL-style modulo: vector % vector -> vector (Cross Product) - - - name - __tostring - parameters - - - name - self - - - returnType - string - - - - - name - LLDetectedEvent - comment - Event detection class providing access to detected object/avatar information - properties - - - name - index - type - number - - - name - valid - type - boolean - - - name - canAdjustDamage - type - boolean - - - methods - - - - name - LLEvents - comment - Event registration and management class for Second Life events - methods - - - name - on - parameters - - - name - self - - - name - event - type - LLEventName - - - name - callback - type - LLEventHandler - - - returnType - LLEventHandler - comment - Registers a callback for an event. Returns the callback. - - - name - off - parameters - - - name - self - - - name - event - type - LLEventName - - - name - callback - type - LLEventHandler - - - returnType - boolean - comment - Unregisters a callback. Returns true if found and removed. - - - name - once - parameters - - - name - self - - - name - event - type - LLEventName - - - name - callback - type - LLEventHandler - - - returnType - LLEventHandler - comment - Registers a one-time callback. Returns the wrapper function. - - - name - listeners - parameters - - - name - self - - - name - event - type - LLEventName - - - returnType - {LLEventHandler} - comment - Returns a list of all listeners for a specific event. - - - name - eventNames - parameters - - - name - self - - - returnType - {string} - comment - Returns a list of all event names that have listeners. - - - - - name - LLTimers - comment - Timer management class for scheduling periodic and one-time callbacks - methods - - - name - every - parameters - - - name - self - - - name - seconds - type - number - - - name - callback - type - LLTimerEveryCallback - - - returnType - LLTimerCallback - comment - Registers a callback to be called every N seconds. Returns the callback. - - - name - once - parameters - - - name - self - - - name - seconds - type - number - - - name - callback - type - LLTimerOnceCallback - - - returnType - LLTimerCallback - comment - Registers a callback to be called once after N seconds. Returns the callback. - - - name - off - parameters - - - name - self - - - name - callback - type - LLTimerCallback - - - returnType - boolean - comment - Unregisters a timer callback. Returns true if found and removed. - - - - - globalVariables - - - name - quaternion - type - - kind - callable-table - tableType - - properties - - - name - create - type - - kind - function - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - returnType - quaternion - - comment - Constructor - - - name - identity - type - quaternion - comment - Identity quaternion constant - - - name - normalize - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - quaternion - - comment - Returns normalized quaternion - - - name - magnitude - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - number - - comment - Returns magnitude of quaternion - - - name - dot - type - - kind - function - parameters - - - name - a - type - quaternion - - - name - b - type - quaternion - - - returnType - number - - comment - Dot product of two quaternions - - - name - slerp - type - - kind - function - parameters - - - name - a - type - quaternion - - - name - b - type - quaternion - - - name - t - type - number - - - returnType - quaternion - - comment - Spherical linear interpolation - - - name - conjugate - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - quaternion - - comment - Returns conjugate of quaternion - - - name - tofwd - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - vector - - comment - Returns forward vector from quaternion rotation - - - name - toleft - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - vector - - comment - Returns left vector from quaternion rotation - - - name - toup - type - - kind - function - parameters - - - name - q - type - quaternion - - - returnType - vector - - comment - Returns up vector from quaternion rotation - - - - callSignature - - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - - - name - s - type - number - - - returnType - quaternion - - - comment - Global 'quaternion' library table with callable metatable - - - name - rotation - type - - kind - typeof - target - quaternion - - comment - 'rotation' global is an alias to the 'quaternion' library - - - name - uuid - type - - kind - callable-table - tableType - - properties - - - name - create - type - - kind - function - parameters - - - name - value - type - - kind - union - types - - string - buffer - uuid - - - - - returnType - uuid? - - comment - Creates a new uuid from a string, buffer, or existing uuid - - - - callSignature - - parameters - - - name - value - type - - kind - union - types - - string - buffer - uuid - - - - - returnType - uuid? - - - comment - Global 'uuid' library table - - - name - vector - type - - kind - callable-table - tableType - - properties - - - name - create - type - - kind - function - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - optional - true - - - returnType - vector - - comment - Creates a new vector with the given component values - - - name - magnitude - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - number - - comment - Calculates the magnitude of a given vector - - - name - normalize - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Computes the normalized version (unit vector) of a given vector - - - name - cross - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - returnType - vector - - comment - Computes the cross product of two vectors - - - name - dot - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - returnType - number - - comment - Computes the dot product of two vectors - - - name - angle - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - name - axis - type - vector - optional - true - - - returnType - number - - comment - Computes the angle between two vectors in radians - - - name - floor - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.floor to every component of the input vector - - - name - ceil - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.ceil to every component of the input vector - - - name - abs - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.abs to every component of the input vector - - - name - sign - type - - kind - function - parameters - - - name - v - type - vector - - - returnType - vector - - comment - Applies math.sign to every component of the input vector - - - name - clamp - type - - kind - function - parameters - - - name - v - type - vector - - - name - min - type - vector - - - name - max - type - vector - - - returnType - vector - - comment - Applies math.clamp to every component of the input vector - - - name - max - type - - kind - function - parameters - - - name - v - type - vector - - - type - vector - variadic - true - - - returnType - vector - - comment - Applies math.max to the corresponding components of the input vectors - - - name - min - type - - kind - function - parameters - - - name - v - type - vector - - - type - vector - variadic - true - - - returnType - vector - - comment - Applies math.min to the corresponding components of the input vectors - - - name - lerp - type - - kind - function - parameters - - - name - a - type - vector - - - name - b - type - vector - - - name - t - type - number - - - returnType - vector - - comment - Linear interpolation between two vectors - - - name - zero - type - vector - comment - A zero vector <0,0,0> - - - name - one - type - vector - comment - A one vector <1,1,1> - - - - callSignature - - parameters - - - name - x - type - number - - - name - y - type - number - - - name - z - type - number - optional - true - - - returnType - vector - - - comment - Global 'vector' library table - - - name - LLEvents - type - LLEvents - comment - Second Life event management and registration - - - name - LLTimers - type - LLTimers - comment - Second Life timer management and scheduling - - - name - loadstring - type - nil - comment - loadstring is removed in SLua - - - name - getfenv - type - nil - comment - getfenv is removed in SLua - - - name - setfenv - type - nil - comment - setfenv is removed in SLua - - - modules - - - name - bit32 - comment - Bitwise operations library - functions - - - name - arshift - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Arithmetic right shift - - - name - band - parameters - - - type - number - variadic - true - - - returnType - number - comment - Bitwise AND of all arguments - - - name - bnot - parameters - - - name - x - type - number - - - returnType - number - comment - Bitwise NOT - - - name - bor - parameters - - - type - number - variadic - true - - - returnType - number - comment - Bitwise OR of all arguments - - - name - bxor - parameters - - - type - number - variadic - true - - - returnType - number - comment - Bitwise XOR of all arguments - - - name - btest - parameters - - - type - number - variadic - true - - - returnType - boolean - comment - Returns true if bitwise AND of all arguments is not zero - - - name - extract - parameters - - - name - n - type - number - - - name - field - type - number - - - name - width - type - number - optional - true - - - returnType - number - comment - Extracts bits from n at position field with width - - - name - lrotate - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Left rotate - - - name - lshift - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Left shift - - - name - replace - parameters - - - name - n - type - number - - - name - v - type - number - - - name - field - type - number - - - name - width - type - number - optional - true - - - returnType - number - comment - Replaces bits in n at position field with width using value v - - - name - rrotate - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Right rotate - - - name - rshift - parameters - - - name - x - type - number - - - name - disp - type - number - - - returnType - number - comment - Right shift - - - name - countlz - parameters - - - name - n - type - number - - - returnType - number - comment - Count leading zeros - - - name - countrz - parameters - - - name - n - type - number - - - returnType - number - comment - Count trailing zeros - - - name - byteswap - parameters - - - name - n - type - number - - - returnType - number - comment - Swap byte order - - - - - name - buffer - comment - Buffer manipulation library for binary data - functions - - - name - create - parameters - - - name - size - type - number - - - returnType - buffer - comment - Creates a new buffer of the specified size - - - name - fromstring - parameters - - - name - str - type - string - - - returnType - buffer - comment - Creates a buffer from a string - - - name - tostring - parameters - - - name - b - type - buffer - - - returnType - string - comment - Converts buffer to string - - - name - readi8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read signed 8-bit integer - - - name - readu8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read unsigned 8-bit integer - - - name - readi16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read signed 16-bit integer - - - name - readu16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read unsigned 16-bit integer - - - name - readi32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read signed 32-bit integer - - - name - readu32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read unsigned 32-bit integer - - - name - readf32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read 32-bit float - - - name - readf64 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - returnType - number - comment - Read 64-bit float - - - name - writei8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write signed 8-bit integer - - - name - writeu8 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write unsigned 8-bit integer - - - name - writei16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write signed 16-bit integer - - - name - writeu16 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write unsigned 16-bit integer - - - name - writei32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write signed 32-bit integer - - - name - writeu32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write unsigned 32-bit integer - - - name - writef32 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write 32-bit float - - - name - writef64 - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - returnType - () - comment - Write 64-bit float - - - name - readstring - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - count - type - number - - - returnType - string - comment - Read string from buffer - - - name - writestring - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - string - - - name - count - type - number - optional - true - - - returnType - () - comment - Write string to buffer - - - name - len - parameters - - - name - b - type - buffer - - - returnType - number - comment - Returns the length of the buffer - - - name - copy - parameters - - - name - target - type - buffer - - - name - targetOffset - type - number - - - name - source - type - buffer - - - name - sourceOffset - type - number - optional - true - - - name - count - type - number - optional - true - - - returnType - () - comment - Copy data from source buffer to target buffer - - - name - fill - parameters - - - name - b - type - buffer - - - name - offset - type - number - - - name - value - type - number - - - name - count - type - number - optional - true - - - returnType - () - comment - Fill buffer with a value - - - name - readbits - parameters - - - name - b - type - buffer - - - name - bitOffset - type - number - - - name - bitCount - type - number - - - returnType - number - comment - Read bits from buffer - - - name - writebits - parameters - - - name - b - type - buffer - - - name - bitOffset - type - number - - - name - bitCount - type - number - - - name - value - type - number - - - returnType - () - comment - Write bits to buffer - - - - - name - coroutine - comment - Coroutine manipulation library - functions - - - name - create - parameters - - - name - f - type - (...any) -> ...any - - - returnType - thread - comment - Creates a new coroutine from a function - - - name - resume - parameters - - - name - co - type - thread - - - type - any - variadic - true - - - returnType - (boolean, ...any) - comment - Resumes a coroutine, returns success and results - - - name - running - parameters - - returnType - thread? - comment - Returns the running coroutine, or nil if called from main thread - - - name - status - parameters - - - name - co - type - thread - - - returnType - "running" | "suspended" | "normal" | "dead" - comment - Returns the status of a coroutine - - - name - wrap - parameters - - - name - f - type - (...any) -> ...any - - - returnType - (...any) -> ...any - comment - Creates a coroutine and returns a function that resumes it - - - name - yield - parameters - - - type - any - variadic - true - - - returnType - ...any - comment - Suspends the coroutine and returns values to resume - - - name - isyieldable - parameters - - returnType - boolean - comment - Returns true if the coroutine can yield - - - name - close - parameters - - - name - co - type - thread - - - returnType - (boolean, string?) - comment - Closes a coroutine, returns success and optional error message - - - - - name - debug - comment - Debug library for introspection - functions - - - name - info - parameters - - - name - thread - type - thread - - - name - level - type - number - - - name - options - type - string - - - returnType - ...any - comment - Returns information about a function or stack level - overloads - - - parameters - - - name - level - type - number - - - name - options - type - string - - - returnType - ...any - comment - Returns information about a stack level - - - parameters - - - name - func - type - (...any) -> ...any - - - name - options - type - string - - - returnType - ...any - comment - Returns information about a function - - - - - name - traceback - parameters - - - name - thread - type - thread - - - name - message - type - string - optional - true - - - name - level - type - number - optional - true - - - returnType - string - comment - Returns a string with a traceback of the call stack - overloads - - - parameters - - - name - message - type - string - optional - true - - - name - level - type - number - optional - true - - - returnType - string - comment - Returns a string with a traceback of the current call stack - - - - - - - name - llbase64 - comment - Base64 encoding/decoding library - functions - - - name - encode - parameters - - - name - data - type - string | buffer - - - returnType - string - comment - Encodes a string or buffer to base64 - - - name - decode - parameters - - - name - data - type - string - - - returnType - string - comment - Decodes a base64 string to a string - overloads - - - parameters - - - name - data - type - string - - - name - asBuffer - type - boolean - optional - true - - - returnType - string | buffer - comment - Decodes a base64 string to a string or buffer - - - - - - - name - lljson - comment - JSON encoding/decoding library for Second Life - properties - - - name - null - type - any - comment - A constant to pass for null to json encode - - - name - empty_array_mt - type - { [any]: any } - comment - Metatable for declaring table as an empty array for json encode - - - name - array_mt - type - { [any]: any } - comment - Metatable for declaring table as an array for json encode - - - name - empty_array - type - any - comment - A constant to pass for an empty array to json encode - - - name - _NAME - type - string - comment - Name of the lljson library - - - name - _VERSION - type - string - comment - Version of the lljson library - - - functions - - - name - encode - parameters - - - name - value - type - any - - - returnType - string - comment - Encodes a Lua value as JSON - - - name - decode - parameters - - - name - json - type - string - - - returnType - any - comment - Decodes a JSON string to a Lua value - - - name - slencode - parameters - - - name - value - type - any - - - name - tight - type - boolean - optional - true - - - returnType - string - comment - Encodes a Lua value as JSON preserving SL types. Use tight to encode more compactly. - - - name - sldecode - parameters - - - name - json - type - string - - - returnType - any - comment - Decodes a JSON string to a Lua value preserving SL types - - - - - name - math - comment - Mathematical functions library - properties - - - name - pi - type - number - comment - The value of pi - - - name - huge - type - number - comment - A value larger than any other numeric value (infinity) - - - functions - - - name - abs - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the absolute value of x - - - name - acos - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the arc cosine of x (in radians) - - - name - asin - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the arc sine of x (in radians) - - - name - atan - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the arc tangent of x (in radians) - - - name - atan2 - parameters - - - name - y - type - number - - - name - x - type - number - - - returnType - number - comment - Returns the arc tangent of y/x (in radians), using the signs to determine the quadrant - - - name - ceil - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the smallest integer larger than or equal to x - - - name - clamp - parameters - - - name - n - type - number - - - name - min - type - number - - - name - max - type - number - - - returnType - number - comment - Returns n clamped between min and max - - - name - cos - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the cosine of x (x in radians) - - - name - cosh - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the hyperbolic cosine of x - - - name - deg - parameters - - - name - x - type - number - - - returnType - number - comment - Converts x from radians to degrees - - - name - exp - parameters - - - name - x - type - number - - - returnType - number - comment - Returns e^x - - - name - floor - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the largest integer smaller than or equal to x - - - name - fmod - parameters - - - name - x - type - number - - - name - y - type - number - - - returnType - number - comment - Returns the remainder of x/y that rounds towards zero - - - name - frexp - parameters - - - name - x - type - number - - - returnType - (number, number) - comment - Returns m and e such that x = m * 2^e - - - name - ldexp - parameters - - - name - m - type - number - - - name - e - type - number - - - returnType - number - comment - Returns m * 2^e - - - name - lerp - parameters - - - name - a - type - number - - - name - b - type - number - - - name - t - type - number - - - returnType - number - comment - Linear interpolation between a and b by t - - - name - log - parameters - - - name - x - type - number - - - name - base - type - number - optional - true - - - returnType - number - comment - Returns the logarithm of x in the given base (default e) - - - name - log10 - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the base-10 logarithm of x - - - name - map - parameters - - - name - x - type - number - - - name - inMin - type - number - - - name - inMax - type - number - - - name - outMin - type - number - - - name - outMax - type - number - - - returnType - number - comment - Maps x from input range to output range - - - name - max - parameters - - - name - x - type - number - - - type - number - variadic - true - - - returnType - number - comment - Returns the maximum value among the arguments - - - name - min - parameters - - - name - x - type - number - - - type - number - variadic - true - - - returnType - number - comment - Returns the minimum value among the arguments - - - name - modf - parameters - - - name - x - type - number - - - returnType - (number, number) - comment - Returns the integer and fractional parts of x - - - name - noise - parameters - - - name - x - type - number - - - name - y - type - number - optional - true - - - name - z - type - number - optional - true - - - returnType - number - comment - Returns Perlin noise value for the given coordinates - - - name - pow - parameters - - - name - base - type - number - - - name - exponent - type - number - - - returnType - number - comment - Returns base^exponent - - - name - rad - parameters - - - name - x - type - number - - - returnType - number - comment - Converts x from degrees to radians - - - name - random - parameters - - - name - m - type - number - optional - true - - - name - n - type - number - optional - true - - - returnType - number - comment - Returns a pseudo-random number - - - name - randomseed - parameters - - - name - seed - type - number - - - returnType - () - comment - Sets the seed for the pseudo-random generator - - - name - round - parameters - - - name - x - type - number - - - returnType - number - comment - Returns x rounded to the nearest integer - - - name - sign - parameters - - - name - x - type - number - - - returnType - number - comment - Returns -1, 0, or 1 depending on the sign of x - - - name - sin - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the sine of x (x in radians) - - - name - sinh - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the hyperbolic sine of x - - - name - sqrt - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the square root of x - - - name - tan - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the tangent of x (x in radians) - - - name - tanh - parameters - - - name - x - type - number - - - returnType - number - comment - Returns the hyperbolic tangent of x - - - name - isnan - parameters - - - name - x - type - number - - - returnType - boolean - comment - Returns true if x is NaN - - - name - isinf - parameters - - - name - x - type - number - - - returnType - boolean - comment - Returns true if x is infinite - - - name - isfinite - parameters - - - name - x - type - number - - - returnType - boolean - comment - Returns true if x is finite - - - - - name - os - comment - Operating system facilities library - functions - - - name - clock - parameters - - returnType - number - comment - Returns CPU time used by the program in seconds - - - name - date - parameters - - - name - format - type - string - optional - true - - - name - time - type - number - optional - true - - - returnType - string | OsDateTime | nil - comment - Returns a string or table containing date and time - - - name - difftime - parameters - - - name - t2 - type - number - - - name - t1 - type - number - optional - true - - - returnType - number - comment - Returns the difference in seconds between two times - - - name - time - parameters - - - name - time - type - OsDateTime - optional - true - - - returnType - number? - comment - Returns the current time or converts a table to time - - - - - name - string - comment - String manipulation library - functions - - - name - byte - parameters - - - name - s - type - string - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - ...number - comment - Returns the internal numeric codes of the characters - - - name - char - parameters - - - type - number - variadic - true - - - returnType - string - comment - Returns a string from character codes - - - name - find - parameters - - - name - s - type - string - - - name - pattern - type - string - - - name - init - type - number - optional - true - - - name - plain - type - boolean - optional - true - - - returnType - (number?, number?, ...string) - comment - Finds first match of pattern in string - - - name - format - parameters - - - name - formatstring - type - string - - - type - any - variadic - true - - - returnType - string - comment - Returns a formatted string - - - name - gmatch - parameters - - - name - s - type - string - - - name - pattern - type - string - - - returnType - () -> ...string - comment - Returns an iterator function for pattern matches - - - name - gsub - parameters - - - name - s - type - string - - - name - pattern - type - string - - - name - repl - type - string | { [string]: string } | (...string) -> string - - - name - n - type - number - optional - true - - - returnType - (string, number) - comment - Global substitution of pattern matches - - - name - len - parameters - - - name - s - type - string - - - returnType - number - comment - Returns the length of the string - - - name - lower - parameters - - - name - s - type - string - - - returnType - string - comment - Converts string to lowercase - - - name - match - parameters - - - name - s - type - string - - - name - pattern - type - string - - - name - init - type - number - optional - true - - - returnType - ...string - comment - Returns captures from pattern match - - - name - pack - parameters - - - name - fmt - type - string - - - type - any - variadic - true - - - returnType - string - comment - Packs values into a binary string - - - name - packsize - parameters - - - name - fmt - type - string - - - returnType - number - comment - Returns the size of a packed string for the given format - - - name - rep - parameters - - - name - s - type - string - - - name - n - type - number - - - returnType - string - comment - Returns a string repeated n times - - - name - reverse - parameters - - - name - s - type - string - - - returnType - string - comment - Reverses a string - - - name - split - parameters - - - name - s - type - string - - - name - separator - type - string - optional - true - - - returnType - {string} - comment - Splits a string by separator - - - name - sub - parameters - - - name - s - type - string - - - name - i - type - number - - - name - j - type - number - optional - true - - - returnType - string - comment - Returns a substring - - - name - unpack - parameters - - - name - fmt - type - string - - - name - s - type - string - - - name - pos - type - number - optional - true - - - returnType - ...any - comment - Unpacks values from a binary string - - - name - upper - parameters - - - name - s - type - string - - - returnType - string - comment - Converts string to uppercase - - - - - name - table - comment - Table manipulation library - functions - - - name - concat - parameters - - - name - list - type - {string} - - - name - sep - type - string - optional - true - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - string - comment - Concatenates table elements into a string - - - name - foreach - parameters - - - name - t - type - {[any]: any} - - - name - f - type - (key: any, value: any) -> any - - - returnType - any? - comment - Iterates over table key-value pairs (deprecated) - - - name - foreachi - parameters - - - name - t - type - {any} - - - name - f - type - (index: number, value: any) -> any - - - returnType - any? - comment - Iterates over array indices (deprecated) - - - name - getn - parameters - - - name - t - type - {any} - - - returnType - number - comment - Returns the length of a table (deprecated, use # operator) - - - name - maxn - parameters - - - name - t - type - {any} - - - returnType - number - comment - Returns the largest positive numeric index - - - name - insert - parameters - - - name - list - type - {any} - - - name - value - type - any - - - returnType - () - comment - Inserts an element at the end of a list - overloads - - - parameters - - - name - list - type - {any} - - - name - pos - type - number - - - name - value - type - any - - - returnType - () - comment - Inserts an element at a specific position - - - - - name - remove - parameters - - - name - list - type - {any} - - - name - pos - type - number - optional - true - - - returnType - any? - comment - Removes and returns an element from a list - - - name - sort - parameters - - - name - list - type - {any} - - - name - comp - type - (a: any, b: any) -> boolean - optional - true - - - returnType - () - comment - Sorts list elements in place - - - name - pack - parameters - - - type - any - variadic - true - - - returnType - { n: number, [number]: any } - comment - Packs arguments into a table with length field n - - - name - unpack - parameters - - - name - list - type - {any} - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - ...any - comment - Unpacks table elements as multiple return values - - - name - move - parameters - - - name - a1 - type - {any} - - - name - f - type - number - - - name - e - type - number - - - name - t - type - number - - - name - a2 - type - {any} - optional - true - - - returnType - {any} - comment - Moves elements from one table to another - - - name - create - parameters - - - name - count - type - number - - - name - value - type - any - optional - true - - - returnType - {any} - comment - Creates a new table with pre-allocated array slots - - - name - find - parameters - - - name - t - type - {any} - - - name - value - type - any - - - name - init - type - number - optional - true - - - returnType - number? - comment - Finds the index of a value in an array - - - name - clear - parameters - - - name - t - type - {[any]: any} - - - returnType - () - comment - Removes all elements from a table - - - name - freeze - parameters - - - name - t - type - {[any]: any} - - - returnType - {[any]: any} - comment - Makes a table read-only - - - name - isfrozen - parameters - - - name - t - type - {[any]: any} - - - returnType - boolean - comment - Returns true if a table is frozen - - - name - clone - parameters - - - name - t - type - {[any]: any} - - - returnType - {[any]: any} - comment - Creates a shallow copy of a table - - - - - name - utf8 - comment - UTF-8 support library - properties - - - name - charpattern - type - string - comment - Pattern that matches exactly one UTF-8 byte sequence - - - functions - - - name - char - parameters - - - type - number - variadic - true - - - returnType - string - comment - Returns a string from UTF-8 codepoints - - - name - codes - parameters - - - name - s - type - string - - - returnType - ((string, number) -> (number, number), string, number) - comment - Returns an iterator for UTF-8 codepoints in a string - - - name - codepoint - parameters - - - name - s - type - string - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - ...number - comment - Returns the codepoints of characters in a string - - - name - len - parameters - - - name - s - type - string - - - name - i - type - number - optional - true - - - name - j - type - number - optional - true - - - returnType - (number?, number?) - comment - Returns the number of UTF-8 characters in a string, or nil and error position - - - name - offset - parameters - - - name - s - type - string - - - name - n - type - number - - - name - i - type - number - optional - true - - - returnType - number? - comment - Returns the byte position of the n-th character - - - - - globalFunctions - - - name - dangerouslyexecuterequiredmodule - parameters - - - name - f - type - (...any) -> ...any - - - returnType - ...any - comment - Dangerously executes a required module function - - - name - touuid - parameters - - - name - val - type - string | buffer | uuid - - - returnType - uuid? - comment - Converts a string, buffer, or uuid to a uuid, returns nil if invalid - - - name - tovector - parameters - - - name - val - type - any - - - returnType - vector? - comment - Converts a value to a vector, returns nil if invalid - - - name - toquaternion - parameters - - - name - val - type - any - - - returnType - quaternion? - comment - Converts a value to a quaternion, returns nil if invalid - - - name - torotation - parameters - - - name - val - type - any - - - returnType - quaternion? - comment - Converts a value to a rotation (quaternion), returns nil if invalid - - - constants - - - diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index a9e3afbc58..544473ba77 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -507,11 +507,10 @@ bool LLScriptEdCore::postBuild() initMenu(); - mSyntaxIDConnection = LLSyntaxIdLSL::getInstance()->addSyntaxIDCallback(boost::bind(&LLScriptEdCore::processKeywords, this)); + mSyntaxIDConnection = LLSyntaxDefCache::getInstance()->addSyntaxIDCallback(boost::bind(&LLScriptEdCore::processKeywords, this)); // Intialise keyword highlighting for the current simulator's version of LSL - LLSyntaxIdLSL::getInstance()->initialize(); - LLSyntaxLua::getInstance()->initialize(); + LLSyntaxDefCache::getInstance(); processKeywords(); mCommitCallbackRegistrar.add("FontSize.Set", boost::bind(&LLScriptEdCore::onChangeFontSize, this, _2)); diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 2a4c2b9d0b..ac6b81f677 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -342,8 +342,8 @@ void LLScriptEditor::drawLineNumbers() void LLScriptEditor::initKeywords(bool luau_language) { - mKeywordsLua.initialize(LLSyntaxLua::getInstance()->getKeywordsXML(), true); - mKeywordsLSL.initialize(LLSyntaxIdLSL::getInstance()->getKeywordsXML(), false); + mKeywordsLua.initialize(LLSyntaxDefCache::getInstance()->getLuaKeywords(), true); + mKeywordsLSL.initialize(LLSyntaxDefCache::getInstance()->getLSLKeywords(), false); mLuauLanguage = luau_language; diff --git a/indra/newview/llscripteditorws.cpp b/indra/newview/llscripteditorws.cpp index 30a1ca53f6..11aacd791e 100644 --- a/indra/newview/llscripteditorws.cpp +++ b/indra/newview/llscripteditorws.cpp @@ -73,7 +73,7 @@ LLWebsocketMgr::WSConnection::ptr_t LLScriptEditorWSServer::connectionFactory(LL void LLScriptEditorWSServer::onStarted() { - LLSyntaxIdLSL& syntax_id_mgr = LLSyntaxIdLSL::instance(); + LLSyntaxDefCache& syntax_id_mgr = LLSyntaxDefCache::instance(); wptr_t that(std::static_pointer_cast(shared_from_this())); mLastSyntaxId = syntax_id_mgr.getSyntaxID(); @@ -302,7 +302,7 @@ void LLScriptEditorWSServer::setupConnectionMethods(LLJSONRPCConnection::ptr_t c void LLScriptEditorWSServer::broadcastLanguageChange() { - LLUUID syntax_id = LLSyntaxIdLSL::instance().getSyntaxID(); + LLUUID syntax_id = LLSyntaxDefCache::instance().getSyntaxID(); if (syntax_id != mLastSyntaxId) { @@ -340,12 +340,12 @@ LLSD LLScriptEditorWSServer::handleSyntaxRequest(const LLSD& params) const response["id"] = mLastSyntaxId; if (category == "defs.lua") { - response["defs"] = LLSyntaxLua::instance().getTypesXML(); + response["defs"] = LLSyntaxDefCache::instance().getLuaKeywords(); response["success"] = response["defs"].isDefined(); } else if (category == "defs.lsl") { - response["defs"] = LLSyntaxIdLSL::instance().getKeywordsXML(); + response["defs"] = LLSyntaxDefCache::instance().getLSLKeywords(); response["success"] = response["defs"].isDefined(); } else @@ -710,7 +710,7 @@ void LLScriptEditorWSConnection::onOpen() languages.append("lsl"); languages.append("luau"); handshake["languages"] = languages; - handshake["syntax_id"] = LLSyntaxIdLSL::instance().getSyntaxID(); + handshake["syntax_id"] = LLSyntaxDefCache::instance().getSyntaxID(); // Features object LLSD features; diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 16274f59bc..3d0a9d80b4 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -38,37 +38,53 @@ //----------------------------------------------------------------------------- // LLSyntaxIdLSL //----------------------------------------------------------------------------- -const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; -const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; -const std::string FILENAME_DEFAULT_LSL = "keywords_lsl_default.xml"; -const std::string FILENAME_DEFAULT_LUA = "keywords_lua_default.xml"; - -/** - * @brief LLSyntaxIdLSL constructor - */ -LLSyntaxIdLSL::LLSyntaxIdLSL() -: mKeywordsXml(LLSD()) -, mCapabilityURL(std::string()) -, mFilePath(LL_PATH_APP_SETTINGS) -, mSyntaxId(LLUUID()) -, mInitialized(false) +namespace +{ + const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; + const std::string SYNTAX_DEF_CAPABILITY_NAME = "ScriptDefinitions"; + const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; + const std::string FILENAME_INTERNAL_LSL = "lsl_keywords.xml"; + const std::string FILENAME_INTERNAL_LUA = "slua_keywords.xml"; + + constexpr U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; + const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); + + const std::unordered_set MEMCACHED_LLSD = { + FILENAME_INTERNAL_LSL, + FILENAME_INTERNAL_LUA + }; +} // namespace + +//======================================================================== +void LLSyntaxDefCache::initSingleton() { - loadDefaultKeywordsIntoLLSD(); - mRegionChangedCallback = gAgent.addRegionChangedCallback(boost::bind(&LLSyntaxIdLSL::handleRegionChanged, this)); + buildDefaultCache(); + loadKeywordsIntoLLSD(); + mRegionChangedCallback = gAgent.addRegionChangedCallback(boost::bind(&LLSyntaxDefCache::handleRegionChanged, this)); handleRegionChanged(); // Kick off an initial caps query and fetch } -void LLSyntaxIdLSL::buildFullFileSpec() +void LLSyntaxDefCache::cleanupSingleton() { - ELLPath path = mSyntaxId.isNull() ? LL_PATH_APP_SETTINGS : LL_PATH_CACHE; - const std::string filename = mSyntaxId.isNull() ? FILENAME_DEFAULT_LSL : "keywords_lsl_" + mSyntaxId.asString() + ".llsd.xml"; - mFullFileSpec = gDirUtilp->getExpandedFilename(path, filename); + gAgent.removeRegionChangedCallback(mRegionChangedCallback); + mLSLKeywords = LLSD(); + mLuaKeywords = LLSD(); + mCapabilityURL = std::string(); + mSyntaxId = LLUUID(); + mFileCachePaths.clear(); } -//----------------------------------------------------------------------------- -// syntaxIdChange() -//----------------------------------------------------------------------------- -bool LLSyntaxIdLSL::syntaxIdChanged() +boost::signals2::connection LLSyntaxDefCache::addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb) +{ + return mSyntaxIDChangedSignal.connect(cb); +} + +//======================================================================== +// checkSyntaxId() +// Checks the current region for the LSLSyntaxId feature and capability, and +// if found checks the syntax ID against the one we have. If they differ, +// updates the syntax ID and returns true. Otherwise returns false. +bool LLSyntaxDefCache::updateSyntaxId() { LLViewerRegion* region = gAgent.getRegion(); @@ -83,51 +99,97 @@ bool LLSyntaxIdLSL::syntaxIdChanged() { // get and check the hash LLUUID new_syntax_id = sim_features[SYNTAX_ID_SIMULATOR_FEATURE].asUUID(); - mCapabilityURL = region->getCapability(SYNTAX_ID_CAPABILITY_NAME); + + // *Note* the syntax ID may not have changed, but the region almost certainly has. + // update the cap URL + mCapabilityURL = region->getCapability(SYNTAX_DEF_CAPABILITY_NAME); + mUseDefsCap = !mCapabilityURL.empty(); + if (!mUseDefsCap) + { + mCapabilityURL = region->getCapability(SYNTAX_ID_CAPABILITY_NAME); + } LL_DEBUGS("SyntaxLSL") << SYNTAX_ID_SIMULATOR_FEATURE << " capability URL: " << mCapabilityURL << LL_ENDL; + if (new_syntax_id != mSyntaxId) { LL_DEBUGS("SyntaxLSL") << "New SyntaxID '" << new_syntax_id << "' found." << LL_ENDL; mSyntaxId = new_syntax_id; return true; } - else - LL_DEBUGS("SyntaxLSL") << "SyntaxID matches what we have." << LL_ENDL; + + LL_DEBUGS("SyntaxLSL") << "SyntaxID has not changed. Still " << mSyntaxId << LL_ENDL; } } else { - region->setCapabilitiesReceivedCallback(boost::bind(&LLSyntaxIdLSL::handleCapsReceived, this, _1)); + region->setCapabilitiesReceivedCallback(boost::bind(&LLSyntaxDefCache::handleCapsReceived, this, _1)); LL_DEBUGS("SyntaxLSL") << "Region has not received capabilities. Waiting for caps..." << LL_ENDL; } } return false; } +void LLSyntaxDefCache::handleRegionChanged() +{ + if (updateSyntaxId()) + { + if (!checkCacheAndLoad(mSyntaxId)) + { + fetchKeywords(); + } + } +} + +void LLSyntaxDefCache::handleCapsReceived(const LLUUID& region_uuid) +{ + LLViewerRegion* current_region = gAgent.getRegion(); + + if (region_uuid.notNull() && current_region->getRegionID() == region_uuid) + { + updateSyntaxId(); + if (!checkCacheAndLoad(mSyntaxId)) + { + fetchKeywords(); + } + } +} + //----------------------------------------------------------------------------- -// fetchKeywordsFile -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::fetchKeywordsFile(const std::string& filespec) +// fetchKeywords +// Initiates a fetch of the current language definitions from the region caps. +void LLSyntaxDefCache::fetchKeywords() { - LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsFileCoro", - boost::bind(&LLSyntaxIdLSL::fetchKeywordsFileCoro, this, mCapabilityURL, filespec)); - LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is: " << mCapabilityURL << ". Filename to use is: '" << filespec << "'." << LL_ENDL; + if (mCapabilityURL.empty()) + { + LL_WARNS("SyntaxLSL") << "No capability URL for fetching syntax definitions." << LL_ENDL; + return; + } + if (mUseDefsCap) + { + LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsDefsCoro", + boost::bind(&LLSyntaxDefCache::fetchKeywordsDefsCoro, this, mCapabilityURL, mSyntaxId)); + } + else + { + LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsFileCoro", + boost::bind(&LLSyntaxDefCache::fetchKeywordsFileCoro, this, mCapabilityURL, mSyntaxId)); + } } //----------------------------------------------------------------------------- // fetchKeywordsFileCoro -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) +// This uses the legacy languge cap which only sends the LSL keywords file. +void LLSyntaxDefCache::fetchKeywordsFileCoro(std::string url, LLUUID syntax_id) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter = std::make_shared("fetchKeywordsFileCoro", httpPolicy); LLCore::HttpRequest::ptr_t httpRequest = std::make_shared(); - std::pair::iterator, bool> insrt = mInflightFetches.insert(fileSpec); + auto insrt = mInflightFetches.insert(syntax_id); if (!insrt.second) { - LL_WARNS("SyntaxLSL") << "Already downloading keyword file called \"" << fileSpec << "\"." << LL_ENDL; + LL_WARNS("SyntaxLSL") << "Already downloading keyword file for syntax ID \"" << syntax_id << "\"." << LL_ENDL; return; } @@ -136,11 +198,11 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - mInflightFetches.erase(fileSpec); + mInflightFetches.erase(syntax_id); if (!status) { - LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file \"" << fileSpec << "\"" << LL_ENDL; + LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file for syntax ID \"" << syntax_id << "\"" << LL_ENDL; return; } @@ -148,13 +210,38 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) if (isSupportedVersion(result)) { - // Shuttle this task to the main coro/worker. - // loadKeywordsIntoLLSD will attempt to get a mutex which is not coro aware. - LLAppViewer::instance()->postToMainCoro([this, result, fileSpec]() + std::string path = buildCacheDirectoryName(syntax_id); + + if (!LLFile::exists(path)) + { + LL_DEBUGS("SyntaxLSL") << "Cache directory '" << path << "' does not exist. Attempting to create." << LL_ENDL; + if (LLFile::mkdir(path)) { - setKeywordsXml(result); - cacheFile(fileSpec, result); - loadKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Failed to create cache directory '" << path << "'. Cannot cache syntax defs file." << LL_ENDL; + return; + } + } + + // Note that after this call the file cache will have all well known files pointing + // to the default versions, so below, where we get the path to the lua keywords + // well be loading the default version. + buildDefaultCache(); + + // The LSL keywords we just received + std::string full_path = gDirUtilp->add(path, FILENAME_INTERNAL_LSL); + if (writeCacheFile(full_path, result)) + { + mFileCachePaths.addNamePath(FILENAME_INTERNAL_LSL, full_path); + } + // We need to manually load the Lua keywords + full_path = mFileCachePaths.getPath(FILENAME_INTERNAL_LUA); + LLSD lua_defs = loadDeserializedCacheFile(full_path); + + setKeywords(result, lua_defs); + + // Shuttle this task to the main coro/worker. + LLAppViewer::instance()->postToMainCoro([this]() { + mSyntaxIDChangedSignal(); }); } else @@ -164,73 +251,196 @@ void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) } -//----------------------------------------------------------------------------- -// cacheFile -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::cacheFile(const std::string &fileSpec, const LLSD& content_ref) +void LLSyntaxDefCache::fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id) { - std::stringstream str; - LLSDSerialize::toXML(content_ref, str); - const std::string xml = str.str(); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter = + std::make_shared("fetchKeywordsDefsCoro", httpPolicy); + LLCore::HttpRequest::ptr_t httpRequest = std::make_shared(); - // save the str to disk, usually to the cache. - llofstream file(fileSpec.c_str(), std::ios_base::out); - file.write(xml.c_str(), str.str().size()); - file.close(); + static std::set inflightDefsFetches; + auto insrt = inflightDefsFetches.insert(syntax_id); + //auto insrt = mInflightFetches.insert(syntax_id); + if (!insrt.second) + { + LL_WARNS("SyntaxLSL") << "Already downloading keyword defs for \"" << syntax_id << "\"." << LL_ENDL; + return; + } - LL_DEBUGS("SyntaxLSL") << "Syntax file received, saving as: '" << fileSpec << "'" << LL_ENDL; -} + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); -//----------------------------------------------------------------------------- -// initialize -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::initialize() -{ - if(mInitialized) return; - if (mSyntaxId.isNull()) + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + inflightDefsFetches.erase(syntax_id); + + if (!status) { - loadDefaultKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file \"" << syntax_id << "\"" << LL_ENDL; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + return; } - else if (!mCapabilityURL.empty()) + + // Now we need to walk through the returned LLSD. It consists of a map keyed on the file name containing a binary + // blob of the actual file contents. + LLSD files = result["files"]; + LLSD memcached_keywords; + if (files.isMap()) { - LL_DEBUGS("SyntaxLSL") << "LSL version has changed, getting appropriate file." << LL_ENDL; + std::string path = buildCacheDirectoryName(syntax_id); - // Need a full spec regardless of file source, so build it now. - buildFullFileSpec(); - if (mSyntaxId.notNull()) + if (!LLFile::exists(path)) { - if (!gDirUtilp->fileExists(mFullFileSpec)) - { // Does not exist, so fetch it from the capability - LL_DEBUGS("SyntaxLSL") << "LSL syntax not cached, attempting download." << LL_ENDL; - fetchKeywordsFile(mFullFileSpec); - } - else + LL_DEBUGS("SyntaxLSL") << "Cache directory '" << path << "' does not exist. Attempting to create." << LL_ENDL; + if (LLFile::mkdir(path)) { - LL_DEBUGS("SyntaxLSL") << "Found cached Syntax file: " << mFullFileSpec << " Loading keywords." << LL_ENDL; - loadKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Failed to create cache directory '" << path << "'. Cannot cache syntax defs file." << LL_ENDL; + return; } } - else + + buildDefaultCache(); + + for (const auto &[filename, contents] : llsd::inMap(files)) { - LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId is null. Loading default values" << LL_ENDL; - loadDefaultKeywordsIntoLLSD(); + std::string full_path = gDirUtilp->add(path, filename); + + if (MEMCACHED_LLSD.find(filename) != MEMCACHED_LLSD.end()) + { // Maintain some keyword LLSDs internally, LSL and Lua + memcached_keywords[filename] = contents; + } + + if (writeCacheFile(full_path, contents)) + { + mFileCachePaths.addNamePath(filename, full_path); + } } } else { - LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is empty." << LL_ENDL; - loadDefaultKeywordsIntoLLSD(); + LL_WARNS("SyntaxLSL") << "Malformed syntax defs response, missing 'files' map." << LL_ENDL; } - mInitialized = true; + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + + setKeywords(memcached_keywords["lsl_keywords.xml"], memcached_keywords["lua_keywords.xml"]); + LLAppViewer::instance()->postToMainCoro( + [this]() + { + mSyntaxIDChangedSignal(); + }); } +//----------------------------------------------------------------------------- +// buildCache +// Constructs the cache file paths for the given syntax ID. If the syntax ID is null, +// constructs the default cache file paths. +void LLSyntaxDefCache::buildCachePaths(const LLUUID &syntax_id) +{ + if (syntax_id.notNull()) + { // Initialize the cache files to point to the default files + buildCachePaths(LLUUID::null); + } + else + { + mFileCachePaths.clear(); + } + + std::string cache_dir = buildCacheDirectoryName(syntax_id); + if (!LLFile::exists(cache_dir)) + { + LL_DEBUGS("SyntaxLSL") << "Cache directory '" << cache_dir << "' does not exist." << LL_ENDL; + return; + } + + auto files = gDirUtilp->getFilesInDir(cache_dir); + for (const auto &file : files) + { + mFileCachePaths.addNamePath(file, gDirUtilp->add(cache_dir, file)); + } +} + +bool LLSyntaxDefCache::writeCacheFile(const std::string &fileSpec, const LLSD& content_ref) +{ + bool binary(content_ref.isBinary()); + std::ofstream file(fileSpec.c_str(), (binary)? std::ios_base::binary : 0); + + if (!file.is_open()) + { + LL_WARNS("SyntaxLSL") << "Failed to open file for writing: '" << fileSpec << "'" << LL_ENDL; + return false; + } + + if (binary) + { + LL_DEBUGS("SyntaxLSL") << "Caching raw content to '" << fileSpec << "'" << LL_ENDL; + file.write((const char*)content_ref.asBinary().data(), content_ref.asBinary().size()); + } + else + { + LL_DEBUGS("SyntaxLSL") << "Caching XML content to '" << fileSpec << "'" << LL_ENDL; + LLSDSerialize::serialize(content_ref, file, LLSDSerialize::LLSD_XML, LLSDFormatter::OPTIONS_PRETTY); + } + file.close(); + + if (!file.good()) + { + LL_WARNS("SyntaxLSL") << "Failed to write content to file: '" << fileSpec << "'" << LL_ENDL; + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// checkCacheAndLoad +// Tests the local cache for the given syntax ID. If found it loads the keywords +// from the cache into LLSD and returns true. Otherwise returns false. +bool LLSyntaxDefCache::checkCacheAndLoad(const LLUUID& syntax_id) +{ + if (checkLocalCache(syntax_id)) + { + buildCachePaths(syntax_id); + loadKeywordsIntoLLSD(); + return true; + } + return false; +} + +std::string LLSyntaxDefCache::buildCacheDirectoryName(const LLUUID& syntax_id) +{ + if (syntax_id.isNull()) + { + LL_DEBUGS("SyntaxLSL") << "No SyntaxID, using app settings directory." << LL_ENDL; + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "syntax_default"); + } + else + { + LL_DEBUGS("SyntaxLSL") << "Using cache directory for SyntaxID '" << syntax_id << "'." << LL_ENDL; + std::string cache_dir_name = "syntax_" + syntax_id.asString(); + + return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, cache_dir_name); + } +} + +bool LLSyntaxDefCache::checkLocalCache(const LLUUID& syntax_id) const +{ + if (syntax_id.isNull()) + { // Cache check will always fail if we don't have a valid SyntaxID, so skip it in that case. + LL_DEBUGS("SyntaxLSL") << "No SyntaxID, skipping local cache check." << LL_ENDL; + return false; + } + + // Check for the existence of the cache directory for this syntax ID. If it doesn't exist, then we don't have a cached file. + std::string cache_dir = buildCacheDirectoryName(syntax_id); + return gDirUtilp->fileExists(cache_dir); +} + + //----------------------------------------------------------------------------- // isSupportedVersion //----------------------------------------------------------------------------- -const U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; -const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); -bool LLSyntaxIdLSL::isSupportedVersion(const LLSD& content) +bool LLSyntaxDefCache::isSupportedVersion(const LLSD& content) { bool is_valid = false; /* @@ -256,128 +466,61 @@ bool LLSyntaxIdLSL::isSupportedVersion(const LLSD& content) } //----------------------------------------------------------------------------- -// loadDefaultKeywordsIntoLLSD() -//----------------------------------------------------------------------------- -void LLSyntaxIdLSL::loadDefaultKeywordsIntoLLSD() -{ - mSyntaxId.setNull(); - buildFullFileSpec(); - loadKeywordsIntoLLSD(); -} - -//----------------------------------------------------------------------------- -// loadKeywordsFileIntoLLSD +// loadKeywordsIntoLLSD //----------------------------------------------------------------------------- /** * @brief Load xml serialized LLSD - * @desc Opens the specified filespec and attempts to deserializes the - * contained data to the specified LLSD object. indicate success/failure with - * sLoaded/sLoadFailed members. + * @desc Open the internal lsl keywords files and deserialize them into the correct + * members. */ -void LLSyntaxIdLSL::loadKeywordsIntoLLSD() +void LLSyntaxDefCache::loadKeywordsIntoLLSD() { - LLSD content; - llifstream file; - file.open(mFullFileSpec.c_str()); - if (file.is_open()) + for (auto& filename : MEMCACHED_LLSD) { - if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + // Note, in the case of the legacy language cap (it only delivers the LSL keywords file) + // The mFileCachePaths will have been initialized in such a way that the Lua keywords + // point to the default file. + std::string full_path = mFileCachePaths.getPath(filename); + if (!full_path.empty()) { - if (isSupportedVersion(content)) + LLSD content = loadDeserializedCacheFile(full_path); + if (!content.isUndefined() && isSupportedVersion(content)) { - LL_DEBUGS("SyntaxLSL") << "Deserialized: " << mFullFileSpec << LL_ENDL; + LL_DEBUGS("SyntaxLSL") << "Deserialized cached file: " << full_path << LL_ENDL; + if (filename == FILENAME_INTERNAL_LSL) + { + mLSLKeywords = content; + } + else if (filename == FILENAME_INTERNAL_LUA) + { + mLuaKeywords = content; + } } else { - LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file." << LL_ENDL; + LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file " << full_path << "." << LL_ENDL; } } } - else - { - LL_WARNS("SyntaxLSL") << "Failed to open: " << mFullFileSpec << LL_ENDL; - } - mKeywordsXml = content; - mSyntaxIDChangedSignal(); -} - -bool LLSyntaxIdLSL::keywordFetchInProgress() -{ - return !mInflightFetches.empty(); -} - -void LLSyntaxIdLSL::handleRegionChanged() -{ - if (syntaxIdChanged()) - { - buildFullFileSpec(); - fetchKeywordsFile(mFullFileSpec); - mInitialized = false; - } -} - -void LLSyntaxIdLSL::handleCapsReceived(const LLUUID& region_uuid) -{ - LLViewerRegion* current_region = gAgent.getRegion(); - - if (region_uuid.notNull() - && current_region->getRegionID() == region_uuid) - { - syntaxIdChanged(); - } -} - -boost::signals2::connection LLSyntaxIdLSL::addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb) -{ - return mSyntaxIDChangedSignal.connect(cb); -} - - - -//----------------------------------------------------------------------------- -// LLSyntaxLua -//----------------------------------------------------------------------------- -LLSyntaxLua::LLSyntaxLua() - : mKeywordsXml(LLSD()) - , mInitialized(false) -{ -} - -void LLSyntaxLua::initialize() -{ - if (mInitialized) return; - loadDefaultKeywordsIntoLLSD(); - loadLuaTypesIntoLLSD(); - mInitialized = true; + mSyntaxIDChangedSignal(); } -void LLSyntaxLua::loadDefaultKeywordsIntoLLSD() +LLSD LLSyntaxDefCache::loadDeserializedCacheFile(const std::string& file_path) { - std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, FILENAME_DEFAULT_LUA); - llifstream file(fullFileSpec.c_str()); - + std::ifstream file(file_path.c_str()); if (file.good()) { LLSD content; - if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + if (LLSDSerialize::deserialize(content, file, -1)) { - mKeywordsXml = content; + return content; } } -} - -void LLSyntaxLua::loadLuaTypesIntoLLSD() -{ - std::string fullFileSpec = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "types_lua_default.xml"); - llifstream file(fullFileSpec.c_str()); - - if (file.good()) + else { - LLSD content; - if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) - { - mTypesXml = content; - } + LL_WARNS("SyntaxLSL") << "Failed to open cached file: " << file_path << LL_ENDL; } + return LLSD(); } + diff --git a/indra/newview/llsyntaxid.h b/indra/newview/llsyntaxid.h index 51103e3396..3bd49378b5 100644 --- a/indra/newview/llsyntaxid.h +++ b/indra/newview/llsyntaxid.h @@ -34,66 +34,101 @@ #include "lleventcoro.h" #include "llcoros.h" -class fetchKeywordsFileResponder; - -class LLSyntaxIdLSL : public LLSingleton +class LLSyntaxDefCache : public LLSingleton { - LLSINGLETON(LLSyntaxIdLSL); - friend class fetchKeywordsFileResponder; + LLSINGLETON(LLSyntaxDefCache) = default; public: using syntax_id_changed_signal_t = boost::signals2::signal; using syntax_id_changed_h = boost::signals2::connection; - - void initialize(); - bool keywordFetchInProgress(); - LLSD getKeywordsXML() const { return mKeywordsXml; }; + class cache_names_t + { + public: + using name_path_map_t = std::unordered_map; + using iterator = name_path_map_t::iterator; + using const_iterator = name_path_map_t::const_iterator; + + cache_names_t() = default; + ~cache_names_t() = default; + + std::string getPath(const std::string &name) const + { + auto it = mNamePathMap.find(name); + if (it != mNamePathMap.end()) + { + return it->second; + } + return std::string(); + } + + bool hasName(const std::string &name) const + { + return mNamePathMap.find(name) != mNamePathMap.end(); + } + + void addNamePath(const std::string &name, const std::string &path) + { + mNamePathMap[name] = path; + } + + iterator begin() { return mNamePathMap.begin(); } + const_iterator begin() const { return mNamePathMap.begin(); } + iterator end() { return mNamePathMap.end(); } + const_iterator end() const { return mNamePathMap.end(); } + + size_t size() const { return mNamePathMap.size(); } + void clear() { mNamePathMap.clear(); } + bool empty() const { return mNamePathMap.empty(); } + + private: + name_path_map_t mNamePathMap; + }; + + bool keywordFetchInProgress() const { return !mInflightFetches.empty(); } + LLSD getLSLKeywords() const { return mLSLKeywords; }; + LLSD getLuaKeywords() const { return mLuaKeywords; }; LLUUID getSyntaxID() const { return mSyntaxId; } syntax_id_changed_h addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb); + bool checkCacheAndLoad(const LLUUID& syntax_id); + + static std::string buildCacheDirectoryName(const LLUUID& syntax_id); + +protected: + void initSingleton() override; + void cleanupSingleton() override; + private: - std::set mInflightFetches; - syntax_id_changed_signal_t mSyntaxIDChangedSignal; - syntax_id_changed_h mRegionChangedCallback; - - bool syntaxIdChanged(); - bool isSupportedVersion(const LLSD& content); - void handleRegionChanged(); - void handleCapsReceived(const LLUUID& region_uuid); - void setKeywordsXml(const LLSD& content) { mKeywordsXml = content; }; - void buildFullFileSpec(); - void fetchKeywordsFile(const std::string& filespec); - void loadDefaultKeywordsIntoLLSD(); - void loadKeywordsIntoLLSD(); - - void fetchKeywordsFileCoro(std::string url, std::string fileSpec); - void cacheFile(const std::string &fileSpec, const LLSD& content_ref); - - std::string mCapabilityURL; - std::string mFullFileSpec; - ELLPath mFilePath; - LLUUID mSyntaxId; - LLSD mKeywordsXml; - bool mInitialized; -}; + bool updateSyntaxId(); + static bool isSupportedVersion(const LLSD& content); + void handleRegionChanged(); + void handleCapsReceived(const LLUUID& region_uuid); + void setKeywords(const LLSD& lsl, const LLSD& lua) { mLSLKeywords = lsl; mLuaKeywords = lua; }; + void loadKeywordsIntoLLSD(); -class LLSyntaxLua : public LLSingleton -{ - LLSINGLETON(LLSyntaxLua); + void fetchKeywords(); + void fetchKeywordsFileCoro(std::string url, LLUUID syntax_id); + void fetchKeywordsDefsCoro(std::string url, LLUUID syntax_id); -public: - void initialize(); - LLSD getKeywordsXML() const { return mKeywordsXml; } - LLSD getTypesXML() const { return mTypesXml; } + bool checkLocalCache(const LLUUID& syntax_id) const; + void buildDefaultCache() { buildCachePaths(LLUUID::null); } + void buildCachePaths(const LLUUID &syntax_id); + bool writeCacheFile(const std::string &fileSpec, const LLSD& content_ref); -private: - void loadDefaultKeywordsIntoLLSD(); - void loadLuaTypesIntoLLSD(); - LLSD mKeywordsXml; - LLSD mTypesXml; - bool mInitialized; + static LLSD loadDeserializedCacheFile(const std::string& file_path); + + std::set mInflightFetches; + syntax_id_changed_signal_t mSyntaxIDChangedSignal; + syntax_id_changed_h mRegionChangedCallback; + + std::string mCapabilityURL; + cache_names_t mFileCachePaths; + LLUUID mSyntaxId; + LLSD mLSLKeywords; + LLSD mLuaKeywords; + bool mUseDefsCap{ false }; }; #endif // LLSYNTAXID_H diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 42a587f376..01ce2a7a21 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3325,6 +3325,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); + capabilityNames.append("ScriptDefinitions"); // Please add new capabilities alphabetically to reduce // merge conflicts. diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 21e50f4dcb..524077f1b2 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -64,6 +64,16 @@ class ViewerManifest(LLManifest): super(ViewerManifest, self).construct() self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") + # ... and the lsl/lua script definition files (needed at build time and packaging) + pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') + with self.prefix(src=os.path.join(pkgdir, 'lsl_definitions'), dst="app_settings/syntax_default"): + self.path("*.txt") + self.path("*.yaml") + self.path("*.xml") + self.path("*.json") + self.path("*.luau") + self.path("*.yml") + if self.is_packaging_viewer(): with self.prefix(src_dst="app_settings"): self.exclude("logcontrol.xml") @@ -92,7 +102,7 @@ class ViewerManifest(LLManifest): with self.prefix(src=pkgdir): self.path("dictionaries") - # include the extracted packages information (see BuildPackagesInfo.cmake) + # include the extracted packages information self.path(src=os.path.join(self.args['build'],"packages-info.txt"), dst="packages-info.txt") # CHOP-955: If we have "sourceid" or "viewer_channel" in the # build process environment, generate it into -- cgit v1.3