Skip to content

Commit 57dfeaf

Browse files
authored
Merge pull request #3807 from secondlife/marchcat/lua_editor
SLua editor: multi-line strings and comments
2 parents 62a196b + 372aa3c commit 57dfeaf

File tree

13 files changed

+179
-33
lines changed

13 files changed

+179
-33
lines changed

indra/llui/llkeywords.cpp

Lines changed: 139 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include <iostream>
3030
#include <fstream>
31+
#include <regex>
3132

3233
#include "llkeywords.h"
3334
#include "llsdserialize.h"
@@ -80,6 +81,8 @@ LLKeywords::~LLKeywords()
8081
mLineTokenList.clear();
8182
std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer());
8283
mDelimiterTokenList.clear();
84+
std::for_each(mRegexTokenList.begin(), mRegexTokenList.end(), DeletePointer());
85+
mRegexTokenList.clear();
8386
}
8487

8588
// Add the token as described
@@ -123,6 +126,10 @@ void LLKeywords::addToken(LLKeywordToken::ETokenType type,
123126
mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, delimiter));
124127
break;
125128

129+
case LLKeywordToken::TT_REGEX_MATCH:
130+
mRegexTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, delimiter));
131+
break;
132+
126133
default:
127134
llassert(0);
128135
}
@@ -235,25 +242,27 @@ void LLKeywords::processTokens()
235242

236243
// Add 'standard' stuff: Quotes, Comments, Strings, Labels, etc. before processing the LLSD
237244
std::string delimiter;
238-
addToken(LLKeywordToken::TT_LABEL, "@", getColorGroup("misc-flow-label"), "Label\nTarget for jump statement", delimiter );
239-
addToken(LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS, "\"", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal", "\"" );
245+
addToken(LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS, "\"", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal", "\"");
240246

241247
if (mLuauLanguage)
242248
{
243249
// Add Lua-style comments
244250
addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "--", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (Lua-style single-line)\nNon-functional commentary or disabled code", delimiter);
245-
addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "--[[", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (Lua-style multi-line)\nNon-functional commentary or disabled code", "]]");
251+
// Add Lua multi-line comments
252+
addToken(LLKeywordToken::TT_REGEX_MATCH, "^--\\[(=*)\\[", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (Lua-style multi-line)\nNon-functional commentary or disabled code", "\\]\\1\\]");
253+
// Add Lua multi-line strings
254+
addToken(LLKeywordToken::TT_REGEX_MATCH, "^\\[(=*)\\[", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal (Lua-style multi-line)", "\\]\\1\\]");
246255
}
247256
else
248257
{
258+
addToken(LLKeywordToken::TT_LABEL, "@", getColorGroup("misc-flow-label"), "Label\nTarget for jump statement", delimiter);
249259
// Add LSL-style comments
250260
addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "//", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (single-line)\nNon-functional commentary or disabled code", delimiter);
251261
addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "/*", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (multi-line)\nNon-functional commentary or disabled code", "*/");
252262
}
253263

254-
255264
LLSD::map_iterator itr = mSyntax.beginMap();
256-
for ( ; itr != mSyntax.endMap(); ++itr)
265+
for (; itr != mSyntax.endMap(); ++itr)
257266
{
258267
if (itr->first == "llsd-lsl-syntax-version")
259268
{
@@ -578,7 +587,121 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
578587

579588
while( *cur && *cur != '\n' )
580589
{
581-
// Check against delimiters
590+
// Check for regex matches first
591+
bool regex_matched = false;
592+
if (!mRegexTokenList.empty())
593+
{
594+
S32 seg_start = (S32)(cur - base);
595+
596+
std::string text_to_search(wtext.begin() + seg_start, wtext.end());
597+
598+
for (token_list_t::iterator iter = mRegexTokenList.begin();
599+
iter != mRegexTokenList.end(); ++iter)
600+
{
601+
LLKeywordToken* regex_token = *iter;
602+
std::string start_pattern(regex_token->getToken().begin(), regex_token->getToken().end());
603+
std::string end_pattern(regex_token->getDelimiter().begin(), regex_token->getDelimiter().end());
604+
605+
try
606+
{
607+
std::regex start_regex_pattern(start_pattern);
608+
std::smatch start_match;
609+
610+
if (std::regex_search(text_to_search, start_match, start_regex_pattern) && !start_match.empty())
611+
{
612+
if (start_match.position() == 0) // Match starts at current position
613+
{
614+
// Calculate segment boundaries for start pattern
615+
S32 start_match_length = static_cast<S32>(start_match.str().length());
616+
S32 start_seg_end = seg_start + start_match_length;
617+
618+
if (end_pattern.empty())
619+
{
620+
// If no end pattern is provided, treat the entire regex match as a single segment
621+
// Move cursor past the matched segment
622+
cur = base + start_seg_end;
623+
624+
// Insert the matched segment
625+
insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, start_seg_end, style, editor);
626+
}
627+
else
628+
{
629+
// Look for the end pattern after the start pattern
630+
std::string remaining_text = text_to_search.substr(start_match_length);
631+
632+
// Process end pattern - replace any capture group references
633+
std::string actual_end_pattern = end_pattern;
634+
635+
// Handle capture groups in the end pattern (replace \1, \2, etc. with their matched content)
636+
for (size_t i = 1; i < start_match.size(); ++i)
637+
{
638+
std::string capture = start_match[i].str();
639+
std::string placeholder = "\\" + std::to_string(i);
640+
641+
// Replace all occurrences of the placeholder with the captured content
642+
size_t pos = 0;
643+
while ((pos = actual_end_pattern.find(placeholder, pos)) != std::string::npos)
644+
{
645+
actual_end_pattern.replace(pos, placeholder.length(), capture);
646+
pos += capture.length();
647+
}
648+
}
649+
650+
try
651+
{
652+
std::regex end_regex_pattern(actual_end_pattern);
653+
std::smatch end_match;
654+
655+
S32 seg_end = start_seg_end;
656+
657+
if (std::regex_search(remaining_text, end_match, end_regex_pattern) && !end_match.empty())
658+
{
659+
// Calculate position of end match relative to the original text
660+
S32 end_match_position = static_cast<S32>(end_match.position());
661+
S32 end_match_length = static_cast<S32>(end_match.str().length());
662+
663+
// Calculate the total length including both patterns and text between
664+
seg_end += end_match_position + end_match_length;
665+
}
666+
else
667+
{
668+
// End pattern not found, treat everything up to EOF as the segment
669+
seg_end += static_cast<S32>(remaining_text.length());
670+
}
671+
672+
// Move cursor past the entire matched segment (start + content + end)
673+
cur = base + seg_end;
674+
675+
// Insert the matched segment
676+
insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, seg_end, style, editor);
677+
}
678+
catch (const std::regex_error& e)
679+
{
680+
LL_WARNS() << "Regex error in end pattern: " << e.what() << " in pattern: " << actual_end_pattern << LL_ENDL;
681+
// Fall back to treating the start match as the entire segment
682+
cur = base + start_seg_end;
683+
insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, start_seg_end, style, editor);
684+
}
685+
}
686+
687+
regex_matched = true;
688+
break;
689+
}
690+
}
691+
}
692+
catch (const std::regex_error& e)
693+
{
694+
LL_WARNS() << "Regex error in start pattern: " << e.what() << " in pattern: " << start_pattern << LL_ENDL;
695+
}
696+
}
697+
698+
if (regex_matched)
699+
{
700+
continue;
701+
}
702+
}
703+
704+
// If no regex match, check against delimiters
582705
{
583706
S32 seg_start = 0;
584707
LLKeywordToken* cur_delimiter = NULL;
@@ -602,7 +725,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
602725
cur += cur_delimiter->getLengthHead();
603726

604727
LLKeywordToken::ETokenType type = cur_delimiter->getType();
605-
if( type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS )
728+
if(type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS)
606729
{
607730
while( *cur && !cur_delimiter->isTail(cur))
608731
{
@@ -664,12 +787,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
664787
seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
665788
}
666789

667-
insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor);
668-
/*
669-
LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
670-
text_segment->setToken( cur_delimiter );
671-
insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
672-
*/
790+
insertSegments(wtext, *seg_list, cur_delimiter, text_len, seg_start, seg_end, style, editor);
673791
// Note: we don't increment cur, since the end of one delimited seg may be immediately
674792
// followed by the start of another one.
675793
continue;
@@ -866,6 +984,14 @@ void LLKeywords::dump()
866984
LLKeywordToken* delimiter_token = *iter;
867985
delimiter_token->dump();
868986
}
987+
988+
LL_INFOS() << "LLKeywords::sRegexTokenList" << LL_ENDL;
989+
for (token_list_t::iterator iter = mRegexTokenList.begin();
990+
iter != mRegexTokenList.end(); ++iter)
991+
{
992+
LLKeywordToken* regex_token = *iter;
993+
regex_token->dump();
994+
}
869995
}
870996

871997
void LLKeywordToken::dump()

indra/llui/llkeywords.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ class LLKeywordToken
5353
* - TT_ONE_SIDED_DELIMITER are for open-ended delimiters which are terminated by EOL.
5454
* - TT_TWO_SIDED_DELIMITER are for delimiters that end with a different delimiter than they open with.
5555
* - TT_DOUBLE_QUOTATION_MARKS are for delimiting areas using the same delimiter to open and close.
56+
* - TT_REGEX_MATCH are for pattern-based matching using regular expressions.
57+
* For TT_REGEX_MATCH: mToken contains the start pattern, mDelimiter contains the end pattern (if any).
58+
* If mDelimiter is empty, the entire match is considered one segment.
59+
* If mDelimiter contains capture group references (e.g. \1, \2), these will be replaced with
60+
* the corresponding capture groups from the start pattern match.
5661
*/
5762
typedef enum e_token_type
5863
{
@@ -62,6 +67,7 @@ class LLKeywordToken
6267
TT_TWO_SIDED_DELIMITER,
6368
TT_ONE_SIDED_DELIMITER,
6469
TT_DOUBLE_QUOTATION_MARKS,
70+
TT_REGEX_MATCH,
6571
// Following constants are more specific versions of the preceding ones
6672
TT_CONSTANT, // WORD
6773
TT_CONTROL, // WORD
@@ -194,6 +200,7 @@ class LLKeywords
194200
typedef std::deque<LLKeywordToken*> token_list_t;
195201
token_list_t mLineTokenList;
196202
token_list_t mDelimiterTokenList;
203+
token_list_t mRegexTokenList;
197204

198205
typedef std::map<std::string, std::string, std::less<>> element_attributes_t;
199206
typedef element_attributes_t::const_iterator attribute_iterator_t;

indra/newview/app_settings/keywords_lua_default.xml

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,47 +104,42 @@
104104
<key>boolean</key>
105105
<map>
106106
<key>tooltip</key>
107-
<string>Lua boolean: represents a true or false value.</string>
107+
<string>Boolean: represents a true or false value.</string>
108108
</map>
109-
<key>float</key>
109+
<key>number</key>
110110
<map>
111111
<key>tooltip</key>
112-
<string>32 bit floating point value.\nThe range is 1.175494351E-38 to 3.402823466E+38.</string>
112+
<string>Double‑precision floating point number.</string>
113113
</map>
114-
<key>integer</key>
115-
<map>
116-
<key>tooltip</key>
117-
<string>32 bit integer value.\n−2,147,483,648 and +2,147,483,647 (that is 0x80000000 to 0x7FFFFFFF in hex).</string>
118-
</map>
119-
<key>key</key>
114+
<key>string</key>
120115
<map>
121116
<key>tooltip</key>
122-
<string>A 128 bit unique identifier (UUID).\nThe key is represented as hexidecimal characters (A-F and 0-9), grouped into sections (8,4,4,4,12 characters) and separated by hyphens (for a total of 36 characters). e.g. "A822FF2B-FF02-461D-B45D-DCD10A2DE0C2".</string>
117+
<string>Text data (UTF‑8).</string>
123118
</map>
124-
<key>list</key>
119+
<key>table</key>
125120
<map>
126121
<key>tooltip</key>
127-
<string>A collection of other data types.\nLists are signified by square brackets surrounding their elements; the elements inside are separated by commas. e.g. [0, 1, 2, 3, 4] or ["Yes", "No", "Perhaps"].</string>
122+
<string>Collection of key‑value pairs.</string>
128123
</map>
129-
<key>quaternion</key>
124+
<key>thread</key>
130125
<map>
131126
<key>tooltip</key>
132-
<string>The quaternion type is a left over from way back when LSL was created. It was later renamed to &lt;rotation&gt; to make it more user friendly, but it appears someone forgot to remove it ;-)</string>
127+
<string>Represents a coroutine.</string>
133128
</map>
134-
<key>rotation</key>
129+
<key>userdata</key>
135130
<map>
136131
<key>tooltip</key>
137-
<string>The rotation type is one of several ways to represent an orientation in 3D.\nIt is a mathematical object called a quaternion. You can think of a quaternion as four numbers (x, y, z, w), three of which represent the direction an object is facing and a fourth that represents the object's banking left or right around that direction.</string>
132+
<string>Opaque external data.</string>
138133
</map>
139-
<key>string</key>
134+
<key>vector</key>
140135
<map>
141136
<key>tooltip</key>
142-
<string>Text data.\nThe editor accepts UTF-8 encoded text.</string>
137+
<string>A vector is a data type that contains a set of three float values.\nVectors are used to represent colors (RGB), positions, and directions/velocities.</string>
143138
</map>
144-
<key>vector</key>
139+
<key>uuid</key>
145140
<map>
146141
<key>tooltip</key>
147-
<string>A vector is a data type that contains a set of three float values.\nVectors are used to represent colors (RGB), positions, and directions/velocities.</string>
142+
<string>A 128‑bit unique identifier formatted as 36 hexadecimal characters (8‑4‑4‑4‑12), e.g. "A822FF2B-FF02-461D-B45D-DCD10A2DE0C2".</string>
148143
</map>
149144
</map>
150145
<key>constants</key>

indra/newview/skins/default/xui/de/floater_about.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ mit Open-Source-Beiträgen von:</text>
2323
Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
2424
jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW).
2525
jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
26+
luau Copyright (c) 1994–2019 Lua.org, PUC-Rio / Copyright (c) 2019-2024 Roblox Corporation
27+
meshoptimizer Copyright (c) 2016-2021 Arseny Kapoulkine
2628
ogg/vorbis Copyright (C) 2002, Xiphophorus.
2729
OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
2830
PCRE Copyright (c) 1997-2012 University of Cambridge.

indra/newview/skins/default/xui/en/floater_about.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ Dummy Name replaced at run time
105105
Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
106106
jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW)
107107
jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
108+
luau Copyright (c) 1994–2019 Lua.org, PUC-Rio / Copyright (c) 2019-2024 Roblox Corporation
108109
meshoptimizer Copyright (c) 2016-2021 Arseny Kapoulkine
109110
ogg/vorbis Copyright (C) 2002, Xiphophorus
110111
OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.

indra/newview/skins/default/xui/es/floater_about.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ con contribuciones de código abierto de:</text>
2323
Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
2424
jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW)
2525
jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
26+
luau Copyright (c) 1994–2019 Lua.org, PUC-Rio / Copyright (c) 2019-2024 Roblox Corporation
27+
meshoptimizer Copyright (c) 2016-2021 Arseny Kapoulkine
2628
ogg/vorbis Copyright (C) 2002, Xiphophorus
2729
OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
2830
PCRE Copyright (c) 1997-2012 University of Cambridge

indra/newview/skins/default/xui/fr/floater_about.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ avec les contributions Open Source de :</text>
2323
Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
2424
jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW)
2525
jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
26+
luau Copyright (c) 1994–2019 Lua.org, PUC-Rio / Copyright (c) 2019-2024 Roblox Corporation
27+
meshoptimizer Copyright (c) 2016-2021 Arseny Kapoulkine
2628
ogg/vorbis Copyright (C) 2002, Xiphophorus
2729
OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
2830
PCRE Copyright (c) 1997-2012 University of Cambridge

indra/newview/skins/default/xui/it/floater_about.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ con contributi open source da:</text>
2323
Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
2424
jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW)
2525
jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
26+
luau Copyright (c) 1994–2019 Lua.org, PUC-Rio / Copyright (c) 2019-2024 Roblox Corporation
27+
meshoptimizer Copyright (c) 2016-2021 Arseny Kapoulkine
2628
ogg/vorbis Copyright (C) 2002, Xiphophorus
2729
OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
2830
PCRE Copyright (c) 1997-2012 University of Cambridge

0 commit comments

Comments
 (0)