Skip to content

Project Viewer: SLua editor #3537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 44 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7f88aed
#3423 Add Lua as Compile Target in Viewer Script Editor
LLGuru Jan 23, 2025
fd82aa0
#3423 Adapt the code to the changes on a server
marchcat Feb 5, 2025
a54ac5a
#3423 Gray out Lua selection on unsupported regions
marchcat Feb 11, 2025
f650597
#3422 Fix two-sided delimiter tail processing
marchcat Feb 11, 2025
34c1627
#3422 Process Lua-style comments
marchcat Feb 11, 2025
ad7dee8
#3422 Add Lua keywords
marchcat Feb 12, 2025
23ac8f6
Merge pull request #3535 from secondlife/marchcat/lua_editor
marchcat Feb 12, 2025
8f0e0fc
Fix xcode16 build errors
marchcat Feb 13, 2025
a0e31b1
Bump macos runner to os version 15 with xcode 16.1
marchcat Feb 13, 2025
bf8a9e8
Merge pull request #3560 from secondlife/marchcat/lua_editor
marchcat Feb 14, 2025
385838c
#3423 Handle region properly
marchcat Feb 21, 2025
10de131
#3423 Use correct 'lsl-luau' compile target
marchcat Feb 21, 2025
0cc0db8
#3422 Better handling of Lua syntax
marchcat Feb 25, 2025
0aaf2e4
Merge pull request #3609 from secondlife/marchcat/lua_editor
marchcat Feb 25, 2025
c663b3f
#3614 Tidy up Script editor UI
marchcat Feb 27, 2025
7c5297d
#3615 WIP: Improve script editor code for the inventory
marchcat Mar 1, 2025
359f604
#3615 WIP: Store correct script compile target in inventory
marchcat Mar 6, 2025
7aad509
Merge pull request #3662 from secondlife/marchcat/lua_editor
marchcat Mar 6, 2025
668c8c6
#3422 Code cleanup
marchcat Mar 7, 2025
f5a6851
#3691 Fix Reset button
marchcat Mar 8, 2025
66faf3d
#3422 Improve handling of Lua functions
marchcat Mar 8, 2025
8c7a5bd
#3730 Change compiler dropdown options to "SLua"
marchcat Mar 14, 2025
5b23e28
#3731 Remove unused code
marchcat Mar 14, 2025
da0a819
Temporary hack to determine script language when opened from inventory
marchcat Mar 14, 2025
e2333b4
Merge pull request #3701 from secondlife/marchcat/lua_editor
marchcat Mar 14, 2025
a0e8b5f
#3731 Temporary viewer-side workaround to create SLua scripts by default
marchcat Mar 17, 2025
dba481f
Merge pull request #3752 from secondlife/marchcat/lua_editor
marchcat Mar 17, 2025
fe5d7d4
#3731 Follow-up: Use SLua defaults only on SLua-enabled regions (#3753)
marchcat Mar 17, 2025
39dda55
#3731 Delete temporary script from server
marchcat Mar 17, 2025
e59d44d
Merge pull request #3756 from secondlife/marchcat/lua_editor
marchcat Mar 17, 2025
19cdf73
#3747 Use correct extension for temporary script files
marchcat Mar 17, 2025
aab20f5
#3639 Update keywords_lsl_default.xml
marchcat Mar 20, 2025
9efd188
#3639 Sync keywords_lua_default.xml with keywords_lsl_default.xml
marchcat Mar 20, 2025
078170a
#3639 Add Luau functions from https://luau.org/library
marchcat Mar 20, 2025
62a196b
Merge pull request #3781 from secondlife/marchcat/lua_editor
marchcat Mar 20, 2025
0b9458b
secondlife/viewer-private#408 Add Luau License to the About Section
marchcat Mar 25, 2025
bb2447c
#3801 Update "types" section in keywords_lua_default.xml
marchcat Mar 25, 2025
372aa3c
#3800 Use regex to support multi-line strings and nested comments
marchcat Mar 30, 2025
57dfeaf
Merge pull request #3807 from secondlife/marchcat/lua_editor
marchcat Mar 31, 2025
42355fa
#3800 Use range-based for loop for regex token iteration
marchcat Mar 31, 2025
3c2933b
#3800 Add support for single-quoted strings
marchcat Mar 31, 2025
a1f682b
#3800 Add support for interpolated strings
marchcat Apr 2, 2025
403fcb2
#3867 Print script errors to disk when external editor is used
marchcat Apr 3, 2025
18ead6a
Merge pull request #3860 from secondlife/marchcat/lua_editor
marchcat Apr 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
needs: setup
strategy:
matrix:
runner: [windows-large, macos-12-large]
runner: [windows-large, macos-15-xlarge]
configuration: ${{ fromJSON(needs.setup.outputs.configurations) }}
runs-on: ${{ matrix.runner }}
outputs:
Expand All @@ -64,7 +64,7 @@ jobs:
# autobuild-package.xml.
AUTOBUILD_VCS_INFO: "true"
AUTOBUILD_VSVER: "170"
DEVELOPER_DIR: "/Applications/Xcode_14.0.1.app/Contents/Developer"
DEVELOPER_DIR: "/Applications/Xcode_16.1.app/Contents/Developer"
# Ensure that Linden viewer builds engage Bugsplat.
BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }}
build_coverity: false
Expand Down
5 changes: 5 additions & 0 deletions indra/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ else()
set( USE_AUTOBUILD_3P ON )
endif()

if (NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(Variables)
include(BuildVersion)

Expand Down
6 changes: 5 additions & 1 deletion indra/llcommon/fsyspath.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ class fsyspath: public std::filesystem::path
}

// shadow base-class string() method with UTF-8 aware method
std::string string() const { return super::u8string(); }
std::string string() const
{
auto u8 = super::u8string();
return std::string(u8.begin(), u8.end());
}
// On Posix systems, where value_type is already char, this operator
// std::string() method shadows the base class operator string_type()
// method. But on Windows, where value_type is wchar_t, the base class
Expand Down
4 changes: 2 additions & 2 deletions indra/llcommon/llqueuedthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ size_t LLQueuedThread::updateQueue(F32 max_time_ms)
// schedule a call to threadedUpdate for every call to updateQueue
if (!isQuitting())
{
mRequestQueue.post([=]()
mRequestQueue.post([=, this]()
{
LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update");
mIdleThread = false;
Expand Down Expand Up @@ -474,7 +474,7 @@ void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req)
#else
using namespace std::chrono_literals;
auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms;
mRequestQueue.post([=]
mRequestQueue.post([=, this]
{
LL_PROFILE_ZONE_NAMED("processRequest - retry");
if (LL::WorkQueue::TimePoint::clock::now() < retry_time)
Expand Down
3 changes: 3 additions & 0 deletions indra/llmessage/message_prehash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,9 @@ char const* const _PREHASH_FirstName = LLMessageStringTable::getInstance()->getS
char const* const _PREHASH_AttachedSoundGainChange = LLMessageStringTable::getInstance()->getString("AttachedSoundGainChange");
char const* const _PREHASH_LocationID = LLMessageStringTable::getInstance()->getString("LocationID");
char const* const _PREHASH_Running = LLMessageStringTable::getInstance()->getString("Running");
char const* const _PREHASH_Mono = LLMessageStringTable::getInstance()->getString("Mono");
char const* const _PREHASH_Luau = LLMessageStringTable::getInstance()->getString("Luau");
char const* const _PREHASH_LuauLanguage = LLMessageStringTable::getInstance()->getString("LuauLanguage");
char const* const _PREHASH_AgentThrottle = LLMessageStringTable::getInstance()->getString("AgentThrottle");
char const* const _PREHASH_NeighborList = LLMessageStringTable::getInstance()->getString("NeighborList");
char const* const _PREHASH_PathTaperX = LLMessageStringTable::getInstance()->getString("PathTaperX");
Expand Down
3 changes: 3 additions & 0 deletions indra/llmessage/message_prehash.h
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,9 @@ extern char const* const _PREHASH_FirstName;
extern char const* const _PREHASH_AttachedSoundGainChange;
extern char const* const _PREHASH_LocationID;
extern char const* const _PREHASH_Running;
extern char const* const _PREHASH_Mono;
extern char const* const _PREHASH_Luau;
extern char const* const _PREHASH_LuauLanguage;
extern char const* const _PREHASH_AgentThrottle;
extern char const* const _PREHASH_NeighborList;
extern char const* const _PREHASH_PathTaperX;
Expand Down
2 changes: 1 addition & 1 deletion indra/llrender/llimagegl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,7 @@ void LLImageGL::syncToMainThread(LLGLuint new_tex_name)
ref();
LL::WorkQueue::postMaybe(
mMainQueue,
[=]()
[=, this]()
{
LL_PROFILE_ZONE_NAMED("cglt - delete callback");
syncTexName(new_tex_name);
Expand Down
17 changes: 16 additions & 1 deletion indra/llui/llcombobox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,26 @@ void LLComboBox::resetDirty()
}
}

bool LLComboBox::itemExists(const std::string& name)
bool LLComboBox::itemExists(const std::string& name) const
{
return mList->getItemByLabel(name);
}

bool LLComboBox::valueExists(const std::string& value) const
{
return mList->getItemByValue(value);
}

LLScrollListItem* LLComboBox::findItemByValue(const std::string& value) const
{
return mList->getItemByValue(value);
}

std::vector<LLScrollListItem*> LLComboBox::getAllData() const
{
return mList->getAllData();
}

// add item "name" to menu
LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, bool enabled)
{
Expand Down
5 changes: 4 additions & 1 deletion indra/llui/llcombobox.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ class LLComboBox
LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM);
bool remove( S32 index ); // remove item by index, return true if found and removed
void removeall() { clearRows(); }
bool itemExists(const std::string& name);
bool itemExists(const std::string& name) const;
bool valueExists(const std::string& value) const;
LLScrollListItem* findItemByValue(const std::string& value) const;
std::vector<LLScrollListItem*> getAllData() const;

void sortByName(bool ascending = true); // Sort the entries in the combobox by name

Expand Down
112 changes: 90 additions & 22 deletions indra/llui/llkeywords.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,10 @@ LLUIColor LLKeywords::getColorGroup(std::string_view key_in)
return LLUIColorTable::instance().getColor(color_group);
}

void LLKeywords::initialize(LLSD SyntaxXML)
void LLKeywords::initialize(LLSD SyntaxXML, bool luau_language)
{
mSyntax = SyntaxXML;
mLuauLanguage = luau_language;
mLoaded = true;
}

Expand All @@ -235,10 +236,22 @@ void LLKeywords::processTokens()
// Add 'standard' stuff: Quotes, Comments, Strings, Labels, etc. before processing the LLSD
std::string delimiter;
addToken(LLKeywordToken::TT_LABEL, "@", getColorGroup("misc-flow-label"), "Label\nTarget for jump statement", delimiter );
addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "//", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (single-line)\nNon-functional commentary or disabled code", delimiter );
addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "/*", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (multi-line)\nNon-functional commentary or disabled code", "*/" );
addToken(LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS, "\"", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal", "\"" );

if (mLuauLanguage)
{
// Add Lua-style comments
addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "--", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (Lua-style single-line)\nNon-functional commentary or disabled code", delimiter);
addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "--[[", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (Lua-style multi-line)\nNon-functional commentary or disabled code", "]]");
}
else
{
// Add LSL-style comments
addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "//", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (single-line)\nNon-functional commentary or disabled code", delimiter);
addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "/*", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (multi-line)\nNon-functional commentary or disabled code", "*/");
}


LLSD::map_iterator itr = mSyntax.beginMap();
for ( ; itr != mSyntax.endMap(); ++itr)
{
Expand Down Expand Up @@ -346,8 +359,11 @@ void LLKeywords::processTokensGroup(const LLSD& tokens, std::string_view group)
break;
case LLKeywordToken::TT_FUNCTION:
tooltip = getAttribute("return") + " " + outer_itr->first + "(" + getArguments(arguments) + ");";
tooltip.append("\nEnergy: ");
tooltip.append(getAttribute("energy").empty() ? "0.0" : getAttribute("energy"));
if (std::stod(getAttribute("energy")) >= 0)
{
tooltip.append("\nEnergy: ");
tooltip.append(getAttribute("energy").empty() ? "0.0" : getAttribute("energy"));
}
if (!getAttribute("sleep").empty())
{
tooltip += ", Sleep: " + getAttribute("sleep");
Expand Down Expand Up @@ -627,7 +643,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW

if( *cur )
{
cur += cur_delimiter->getLengthHead();
cur += cur_delimiter->getLengthTail();
seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead() + cur_delimiter->getLengthTail();
}
else
Expand Down Expand Up @@ -662,34 +678,86 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW

// check against words
llwchar prev = cur > base ? *(cur-1) : 0;
if( !iswalnum( prev ) && (prev != '_') )
if (!iswalnum(prev) && prev != '_' && prev != '.')
{
const llwchar* p = cur;
while( iswalnum( *p ) || (*p == '_') )
const llwchar* word_start = cur;
S32 namespace_dots = 0;
const llwchar* last_dot = nullptr;

// Find the full extent of the word, potentially including namespace dots
while (iswalnum(*cur) || *cur == '_' || (mLuauLanguage && *cur == '.' && iswalnum(*(cur+1))))
{
p++;
if (mLuauLanguage && *cur == '.')
{
namespace_dots++;
last_dot = cur;
}
cur++;
}
S32 seg_len = (S32)(p - cur);
if( seg_len > 0 )

S32 seg_len = (S32)(cur - word_start);
if (seg_len > 0)
{
WStringMapIndex word( cur, seg_len );
word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
if( map_iter != mWordTokenMap.end() )
S32 seg_start = (S32)(word_start - base);
S32 seg_end = seg_start + seg_len;

// First try to match the whole token (including dots for Lua namespaces)
WStringMapIndex whole_token(word_start, seg_len);
word_token_map_t::iterator map_iter = mWordTokenMap.find(whole_token);

if (map_iter != mWordTokenMap.end())
{
// Found a match for the complete token (including any namespace)
LLKeywordToken* cur_token = map_iter->second;
S32 seg_start = (S32)(cur - base);
S32 seg_end = seg_start + seg_len;
insertSegments(wtext, *seg_list, cur_token, text_len, seg_start, seg_end, style, editor);
}
else if (namespace_dots > 0 && mLuauLanguage)
{
// If using Lua and we have namespace dots but didn't match the whole token,
// check if we have a match for just the namespace prefix (e.g., "ll")
if (last_dot > word_start)
{
// Get the namespace prefix (part before the first dot)
S32 prefix_len = (S32)(last_dot - word_start);
WStringMapIndex prefix_token(word_start, prefix_len);
map_iter = mWordTokenMap.find(prefix_token);

// LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL;
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);

insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
// Now try to match the function part (after the dot)
const llwchar* func_part = last_dot + 1;
S32 func_len = (S32)(cur - func_part);

if (func_len > 0)
{
// Look for complete function matches
WStringMapIndex func_token(func_part, func_len);
map_iter = mWordTokenMap.find(func_token);

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
}
}
}
}
}
cur += seg_len;
continue;
continue; // Continue to next token regardless of match
}
}

if( *cur && *cur != '\n' )
if (*cur && *cur != '\n')
{
cur++;
}
Expand Down
3 changes: 2 additions & 1 deletion indra/llui/llkeywords.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class LLKeywords
const LLWString& text,
class LLTextEditor& editor,
LLStyleConstSP style);
void initialize(LLSD SyntaxXML);
void initialize(LLSD SyntaxXML, bool luau_language = false);
void processTokens();

// Add the token as described
Expand Down Expand Up @@ -189,6 +189,7 @@ class LLKeywords

bool mLoaded;
LLSD mSyntax;
bool mLuauLanguage;
word_token_map_t mWordTokenMap;
typedef std::deque<LLKeywordToken*> token_list_t;
token_list_t mLineTokenList;
Expand Down
13 changes: 13 additions & 0 deletions indra/llui/llscrolllistctrl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,19 @@ LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, boo
return NULL;
}

LLScrollListItem* LLScrollListCtrl::getItemByValue(const std::string& value)
{
for (LLScrollListItem* item : mItemList)
{
if (item->getValue().asString() == value)
{
return item;
}
}

return NULL;
}

LLScrollListItem* LLScrollListCtrl::getItemByIndex(S32 index)
{
if (index >= 0 && index < (S32)mItemList.size())
Expand Down
3 changes: 2 additions & 1 deletion indra/llui/llscrolllistctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
bool selectItemByLabel(const std::string& item, bool case_sensitive = true, S32 column = 0); // false if item not found
bool selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1);
bool selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1);
LLScrollListItem* getItemByLabel(const std::string& item, bool case_sensitive = true, S32 column = 0);
LLScrollListItem* getItemByLabel(const std::string& label, bool case_sensitive = true, S32 column = 0);
LLScrollListItem* getItemByValue(const std::string& value);
LLScrollListItem* getItemByIndex(S32 index);
std::string getSelectedItemLabel(S32 column = 0) const;
LLSD getSelectedValue();
Expand Down
8 changes: 4 additions & 4 deletions indra/llwindow/llwindowmacosx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,7 @@ F32 LLWindowMacOSX::getGamma()
&greenGamma,
&blueMin,
&blueMax,
&blueGamma) == noErr)
&blueGamma) == kCGErrorSuccess)
{
// So many choices...
// Let's just return the green channel gamma for now.
Expand Down Expand Up @@ -1111,7 +1111,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma)
&greenGamma,
&blueMin,
&blueMax,
&blueGamma) != noErr)
&blueGamma) != kCGErrorSuccess)
{
return false;
}
Expand All @@ -1126,7 +1126,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma)
gamma,
blueMin,
blueMax,
gamma) != noErr)
gamma) != kCGErrorSuccess)
{
return false;
}
Expand Down Expand Up @@ -1178,7 +1178,7 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
newPosition.y = screen_pos.mY;

CGSetLocalEventsSuppressionInterval(0.0);
if(CGWarpMouseCursorPosition(newPosition) == noErr)
if(CGWarpMouseCursorPosition(newPosition) == kCGErrorSuccess)
{
result = true;
}
Expand Down
Loading
Loading