summaryrefslogtreecommitdiff
path: root/indra/llui/llkeywords.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/llui/llkeywords.cpp
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (diff)
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts: # autobuild.xml # indra/cmake/CMakeLists.txt # indra/cmake/GoogleMock.cmake # indra/llaudio/llaudioengine_fmodstudio.cpp # indra/llaudio/llaudioengine_fmodstudio.h # indra/llaudio/lllistener_fmodstudio.cpp # indra/llaudio/lllistener_fmodstudio.h # indra/llaudio/llstreamingaudio_fmodstudio.cpp # indra/llaudio/llstreamingaudio_fmodstudio.h # indra/llcharacter/llmultigesture.cpp # indra/llcharacter/llmultigesture.h # indra/llimage/llimage.cpp # indra/llimage/llimagepng.cpp # indra/llimage/llimageworker.cpp # indra/llimage/tests/llimageworker_test.cpp # indra/llmessage/tests/llmockhttpclient.h # indra/llprimitive/llgltfmaterial.h # indra/llrender/llfontfreetype.cpp # indra/llui/llcombobox.cpp # indra/llui/llfolderview.cpp # indra/llui/llfolderviewmodel.h # indra/llui/lllineeditor.cpp # indra/llui/lllineeditor.h # indra/llui/lltextbase.cpp # indra/llui/lltextbase.h # indra/llui/lltexteditor.cpp # indra/llui/lltextvalidate.cpp # indra/llui/lltextvalidate.h # indra/llui/lluictrl.h # indra/llui/llview.cpp # indra/llwindow/llwindowmacosx.cpp # indra/newview/app_settings/settings.xml # indra/newview/llappearancemgr.cpp # indra/newview/llappearancemgr.h # indra/newview/llavatarpropertiesprocessor.cpp # indra/newview/llavatarpropertiesprocessor.h # indra/newview/llbreadcrumbview.cpp # indra/newview/llbreadcrumbview.h # indra/newview/llbreastmotion.cpp # indra/newview/llbreastmotion.h # indra/newview/llconversationmodel.h # indra/newview/lldensityctrl.cpp # indra/newview/lldensityctrl.h # indra/newview/llface.inl # indra/newview/llfloatereditsky.cpp # indra/newview/llfloatereditwater.cpp # indra/newview/llfloateremojipicker.h # indra/newview/llfloaterimsessiontab.cpp # indra/newview/llfloaterprofiletexture.cpp # indra/newview/llfloaterprofiletexture.h # indra/newview/llgesturemgr.cpp # indra/newview/llgesturemgr.h # indra/newview/llimpanel.cpp # indra/newview/llimpanel.h # indra/newview/llinventorybridge.cpp # indra/newview/llinventorybridge.h # indra/newview/llinventoryclipboard.cpp # indra/newview/llinventoryclipboard.h # indra/newview/llinventoryfunctions.cpp # indra/newview/llinventoryfunctions.h # indra/newview/llinventorygallery.cpp # indra/newview/lllistbrowser.cpp # indra/newview/lllistbrowser.h # indra/newview/llpanelobjectinventory.cpp # indra/newview/llpanelprofile.cpp # indra/newview/llpanelprofile.h # indra/newview/llpreviewgesture.cpp # indra/newview/llsavedsettingsglue.cpp # indra/newview/llsavedsettingsglue.h # indra/newview/lltooldraganddrop.cpp # indra/newview/llurllineeditorctrl.cpp # indra/newview/llvectorperfoptions.cpp # indra/newview/llvectorperfoptions.h # indra/newview/llviewerparceloverlay.cpp # indra/newview/llviewertexlayer.cpp # indra/newview/llviewertexturelist.cpp # indra/newview/macmain.h # indra/test/test.cpp
Diffstat (limited to 'indra/llui/llkeywords.cpp')
-rw-r--r--indra/llui/llkeywords.cpp1626
1 files changed, 813 insertions, 813 deletions
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
index fd8ad0721a..6eeb98fc53 100644
--- a/indra/llui/llkeywords.cpp
+++ b/indra/llui/llkeywords.cpp
@@ -1,813 +1,813 @@
-/**
- * @file llkeywords.cpp
- * @brief Keyword list for LSL
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include <iostream>
-#include <fstream>
-
-#include "llkeywords.h"
-#include "llsdserialize.h"
-#include "lltexteditor.h"
-#include "llstl.h"
-
-inline bool LLKeywordToken::isHead(const llwchar* s) const
-{
- // strncmp is much faster than string compare
- bool res = true;
- const llwchar* t = mToken.c_str();
- S32 len = mToken.size();
- for (S32 i=0; i<len; i++)
- {
- if (s[i] != t[i])
- {
- res = false;
- break;
- }
- }
- return res;
-}
-
-inline bool LLKeywordToken::isTail(const llwchar* s) const
-{
- bool res = true;
- const llwchar* t = mDelimiter.c_str();
- S32 len = mDelimiter.size();
- for (S32 i=0; i<len; i++)
- {
- if (s[i] != t[i])
- {
- res = false;
- break;
- }
- }
- return res;
-}
-
-LLKeywords::LLKeywords()
-: mLoaded(false)
-{
-}
-
-LLKeywords::~LLKeywords()
-{
- std::for_each(mWordTokenMap.begin(), mWordTokenMap.end(), DeletePairedPointer());
- mWordTokenMap.clear();
- std::for_each(mLineTokenList.begin(), mLineTokenList.end(), DeletePointer());
- mLineTokenList.clear();
- std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer());
- mDelimiterTokenList.clear();
-}
-
-// Add the token as described
-void LLKeywords::addToken(LLKeywordToken::ETokenType type,
- const std::string& key_in,
- const LLColor4& color,
- const std::string& tool_tip_in,
- const std::string& delimiter_in)
-{
- std::string tip_text = tool_tip_in;
- LLStringUtil::replaceString(tip_text, "\\n", "\n" );
- LLStringUtil::replaceString(tip_text, "\t", " " );
- if (tip_text.empty())
- {
- tip_text = "[no info]";
- }
- LLWString tool_tip = utf8str_to_wstring(tip_text);
-
- LLWString key = utf8str_to_wstring(key_in);
- LLWString delimiter = utf8str_to_wstring(delimiter_in);
- switch(type)
- {
- case LLKeywordToken::TT_CONSTANT:
- case LLKeywordToken::TT_CONTROL:
- case LLKeywordToken::TT_EVENT:
- case LLKeywordToken::TT_FUNCTION:
- case LLKeywordToken::TT_LABEL:
- case LLKeywordToken::TT_SECTION:
- case LLKeywordToken::TT_TYPE:
- case LLKeywordToken::TT_WORD:
- mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip, LLWStringUtil::null);
- break;
-
- case LLKeywordToken::TT_LINE:
- mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, LLWStringUtil::null));
- break;
-
- case LLKeywordToken::TT_TWO_SIDED_DELIMITER:
- case LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS:
- case LLKeywordToken::TT_ONE_SIDED_DELIMITER:
- mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, delimiter));
- break;
-
- default:
- llassert(0);
- }
-}
-
-std::string LLKeywords::getArguments(LLSD& arguments)
-{
- std::string argString = "";
-
- if (arguments.isArray())
- {
- U32 argsCount = arguments.size();
- LLSD::array_iterator arrayIt = arguments.beginArray();
- for ( ; arrayIt != arguments.endArray(); ++arrayIt)
- {
- LLSD& args = (*arrayIt);
- if (args.isMap())
- {
- LLSD::map_iterator argsIt = args.beginMap();
- for ( ; argsIt != args.endMap(); ++argsIt)
- {
- argString += argsIt->second.get("type").asString() + " " + argsIt->first;
- if (argsCount-- > 1)
- {
- argString += ", ";
- }
- }
- }
- else
- {
- LL_WARNS("SyntaxLSL") << "Argument array comtains a non-map element!" << LL_ENDL;
- }
- }
- }
- else if (!arguments.isUndefined())
- {
- LL_WARNS("SyntaxLSL") << "Not an array! Invalid arguments LLSD passed to function." << arguments << LL_ENDL;
- }
- return argString;
-}
-
-std::string LLKeywords::getAttribute(const std::string& key)
-{
- attribute_iterator_t it = mAttributes.find(key);
- return (it != mAttributes.end()) ? it->second : "";
-}
-
-LLColor4 LLKeywords::getColorGroup(const std::string& key_in)
-{
- std::string color_group = "ScriptText";
- if (key_in == "functions")
- {
- color_group = "SyntaxLslFunction";
- }
- else if (key_in == "controls")
- {
- color_group = "SyntaxLslControlFlow";
- }
- else if (key_in == "events")
- {
- color_group = "SyntaxLslEvent";
- }
- else if (key_in == "types")
- {
- color_group = "SyntaxLslDataType";
- }
- else if (key_in == "misc-flow-label")
- {
- color_group = "SyntaxLslControlFlow";
- }
- else if (key_in =="deprecated")
- {
- color_group = "SyntaxLslDeprecated";
- }
- else if (key_in =="god-mode")
- {
- color_group = "SyntaxLslGodMode";
- }
- else if (key_in == "constants"
- || key_in == "constants-integer"
- || key_in == "constants-float"
- || key_in == "constants-string"
- || key_in == "constants-key"
- || key_in == "constants-rotation"
- || key_in == "constants-vector")
- {
- color_group = "SyntaxLslConstant";
- }
- else
- {
- LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL;
- }
-
- return LLUIColorTable::instance().getColor(color_group);
-}
-
-void LLKeywords::initialize(LLSD SyntaxXML)
-{
- mSyntax = SyntaxXML;
- mLoaded = true;
-}
-
-void LLKeywords::processTokens()
-{
- if (!mLoaded)
- {
- return;
- }
-
- // 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", "\"" );
-
- LLSD::map_iterator itr = mSyntax.beginMap();
- for ( ; itr != mSyntax.endMap(); ++itr)
- {
- if (itr->first == "llsd-lsl-syntax-version")
- {
- // Skip over version key.
- }
- else
- {
- if (itr->second.isMap())
- {
- processTokensGroup(itr->second, itr->first);
- }
- else
- {
- LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL;
- }
- }
- }
- LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL;
-}
-
-void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group)
-{
- LLColor4 color;
- LLColor4 color_group;
- LLColor4 color_deprecated = getColorGroup("deprecated");
- LLColor4 color_god_mode = getColorGroup("god-mode");
-
- LLKeywordToken::ETokenType token_type = LLKeywordToken::TT_UNKNOWN;
- // If a new token type is added here, it must also be added to the 'addToken' method
- if (group == "constants")
- {
- token_type = LLKeywordToken::TT_CONSTANT;
- }
- else if (group == "controls")
- {
- token_type = LLKeywordToken::TT_CONTROL;
- }
- else if (group == "events")
- {
- token_type = LLKeywordToken::TT_EVENT;
- }
- else if (group == "functions")
- {
- token_type = LLKeywordToken::TT_FUNCTION;
- }
- else if (group == "label")
- {
- token_type = LLKeywordToken::TT_LABEL;
- }
- else if (group == "types")
- {
- token_type = LLKeywordToken::TT_TYPE;
- }
-
- color_group = getColorGroup(group);
- LL_DEBUGS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL;
-
- if (tokens.isMap())
- {
- LLSD::map_const_iterator outer_itr = tokens.beginMap();
- for ( ; outer_itr != tokens.endMap(); ++outer_itr )
- {
- if (outer_itr->second.isMap())
- {
- mAttributes.clear();
- LLSD arguments = LLSD();
- LLSD::map_const_iterator inner_itr = outer_itr->second.beginMap();
- for ( ; inner_itr != outer_itr->second.endMap(); ++inner_itr )
- {
- if (inner_itr->first == "arguments")
- {
- if (inner_itr->second.isArray())
- {
- arguments = inner_itr->second;
- }
- }
- else if (!inner_itr->second.isMap() && !inner_itr->second.isArray())
- {
- mAttributes[inner_itr->first] = inner_itr->second.asString();
- }
- else
- {
- LL_WARNS("SyntaxLSL") << "Not a valid attribute: " << inner_itr->first << LL_ENDL;
- }
- }
-
- std::string tooltip = "";
- switch (token_type)
- {
- case LLKeywordToken::TT_CONSTANT:
- if (getAttribute("type").length() > 0)
- {
- color_group = getColorGroup(group + "-" + getAttribute("type"));
- }
- else
- {
- color_group = getColorGroup(group);
- }
- tooltip = "Type: " + getAttribute("type") + ", Value: " + getAttribute("value");
- break;
- case LLKeywordToken::TT_EVENT:
- tooltip = outer_itr->first + "(" + getArguments(arguments) + ")";
- 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 (!getAttribute("sleep").empty())
- {
- tooltip += ", Sleep: " + getAttribute("sleep");
- }
- default:
- break;
- }
-
- if (!getAttribute("tooltip").empty())
- {
- if (!tooltip.empty())
- {
- tooltip.append("\n");
- }
- tooltip.append(getAttribute("tooltip"));
- }
-
- color = getAttribute("deprecated") == "true" ? color_deprecated : color_group;
-
- if (getAttribute("god-mode") == "true")
- {
- color = color_god_mode;
- }
-
- addToken(token_type, outer_itr->first, color, tooltip);
- }
- }
- }
- else if (tokens.isArray()) // Currently nothing should need this, but it's here for completeness
- {
- LL_INFOS("SyntaxLSL") << "Curious, shouldn't be an array here; adding all using color " << color << LL_ENDL;
- for (S32 count = 0; count < tokens.size(); ++count)
- {
- addToken(token_type, tokens[count], color, "");
- }
- }
- else
- {
- LL_WARNS("SyntaxLSL") << "Invalid map/array passed: '" << tokens << "'" << LL_ENDL;
- }
-}
-
-LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other)
-{
- if(other.mOwner)
- {
- copyData(other.mData, other.mLength);
- }
- else
- {
- mOwner = false;
- mLength = other.mLength;
- mData = other.mData;
- }
-}
-
-LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
-{
- copyData(str.data(), str.size());
-}
-
-LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length)
-: mData(start)
-, mLength(length)
-, mOwner(false)
-{
-}
-
-LLKeywords::WStringMapIndex::~WStringMapIndex()
-{
- if (mOwner)
- {
- delete[] mData;
- }
-}
-
-void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
-{
- llwchar *data = new llwchar[length];
- memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
-
- mOwner = true;
- mLength = length;
- mData = data;
-}
-
-bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
-{
- // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
- // The comparison only needs to strictly order all possible strings, and be stable.
-
- bool result = false;
- const llwchar* self_iter = mData;
- const llwchar* self_end = mData + mLength;
- const llwchar* other_iter = other.mData;
- const llwchar* other_end = other.mData + other.mLength;
-
- while(true)
- {
- if(other_iter >= other_end)
- {
- // We've hit the end of other.
- // This covers two cases: other being shorter than self, or the strings being equal.
- // In either case, we want to return false.
- result = false;
- break;
- }
- else if(self_iter >= self_end)
- {
- // self is shorter than other.
- result = true;
- break;
- }
- else if(*self_iter != *other_iter)
- {
- // The current character differs. The strings are not equal.
- result = *self_iter < *other_iter;
- break;
- }
-
- self_iter++;
- other_iter++;
- }
-
- return result;
-}
-
-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<LLTextSegmentPtr>* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style)
-{
- LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING);
- seg_list->clear();
-
- if( wtext.empty() )
- {
- return;
- }
-
- S32 text_len = wtext.size() + 1;
-
- seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) );
-
- 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, cur-base);
- text_segment->setToken( 0 );
- insertSegment( *seg_list, text_segment, text_len, style, editor);
- cur++;
- if( !*cur || *cur == '\n' )
- {
- continue;
- }
- }
-
- // Skip white space
- while( *cur && iswspace(*cur) && (*cur != '\n') )
- {
- cur++;
- }
- if( !*cur || *cur == '\n' )
- {
- continue;
- }
-
- // cur is now at the first non-whitespace character of a new line
-
- // Line start tokens
- {
- bool line_done = false;
- for (token_list_t::iterator iter = mLineTokenList.begin();
- iter != mLineTokenList.end(); ++iter)
- {
- LLKeywordToken* cur_token = *iter;
- if( cur_token->isHead( cur ) )
- {
- S32 seg_start = cur - base;
- while( *cur && *cur != '\n' )
- {
- // skip the rest of the line
- cur++;
- }
- S32 seg_end = cur - base;
-
- //create segments from seg_start to seg_end
- insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
- line_done = true; // to break out of second loop.
- break;
- }
- }
-
- if( line_done )
- {
- continue;
- }
- }
- }
-
- // Skip white space
- while( *cur && iswspace(*cur) && (*cur != '\n') )
- {
- cur++;
- }
-
- while( *cur && *cur != '\n' )
- {
- // Check against delimiters
- {
- S32 seg_start = 0;
- LLKeywordToken* cur_delimiter = NULL;
- for (token_list_t::iterator iter = mDelimiterTokenList.begin();
- iter != mDelimiterTokenList.end(); ++iter)
- {
- LLKeywordToken* delimiter = *iter;
- if( delimiter->isHead( cur ) )
- {
- cur_delimiter = delimiter;
- break;
- }
- }
-
- if( cur_delimiter )
- {
- S32 between_delimiters = 0;
- S32 seg_end = 0;
-
- seg_start = cur - base;
- cur += cur_delimiter->getLengthHead();
-
- LLKeywordToken::ETokenType type = cur_delimiter->getType();
- if( type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS )
- {
- while( *cur && !cur_delimiter->isTail(cur))
- {
- // Check for an escape sequence.
- if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\')
- {
- // Count the number of backslashes.
- S32 num_backslashes = 0;
- while (*cur == '\\')
- {
- num_backslashes++;
- between_delimiters++;
- cur++;
- }
- // If the next character is the end delimiter?
- if (cur_delimiter->isTail(cur))
- {
- // If there was an odd number of backslashes, then this delimiter
- // does not end the sequence.
- if (num_backslashes % 2 == 1)
- {
- between_delimiters++;
- cur++;
- }
- else
- {
- // This is an end delimiter.
- break;
- }
- }
- }
- else
- {
- between_delimiters++;
- cur++;
- }
- }
-
- if( *cur )
- {
- cur += cur_delimiter->getLengthHead();
- seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead() + cur_delimiter->getLengthTail();
- }
- else
- {
- // eof
- seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
- }
- }
- else
- {
- llassert( cur_delimiter->getType() == LLKeywordToken::TT_ONE_SIDED_DELIMITER );
- // Left side is the delimiter. Right side is eol or eof.
- while( *cur && ('\n' != *cur) )
- {
- between_delimiters++;
- cur++;
- }
- seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
- }
-
- insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor);
- /*
- LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
- text_segment->setToken( cur_delimiter );
- insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
- */
- // Note: we don't increment cur, since the end of one delimited seg may be immediately
- // followed by the start of another one.
- continue;
- }
- }
-
- // check against words
- llwchar prev = cur > base ? *(cur-1) : 0;
- if( !iswalnum( prev ) && (prev != '_') )
- {
- const llwchar* p = cur;
- while( iswalnum( *p ) || (*p == '_') )
- {
- p++;
- }
- S32 seg_len = p - cur;
- if( seg_len > 0 )
- {
- WStringMapIndex word( cur, seg_len );
- word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
- if( map_iter != mWordTokenMap.end() )
- {
- LLKeywordToken* cur_token = map_iter->second;
- S32 seg_start = cur - base;
- S32 seg_end = seg_start + seg_len;
-
- // LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL;
-
- insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
- }
- cur += seg_len;
- continue;
- }
- }
-
- if( *cur && *cur != '\n' )
- {
- cur++;
- }
- }
- }
-}
-
-void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor )
-{
- std::string::size_type pos = wtext.find('\n',seg_start);
-
- LLStyleConstSP cur_token_style = new LLStyle(LLStyle::Params().font(style->getFont()).color(cur_token->getColor()));
-
- while (pos!=-1 && pos < (std::string::size_type)seg_end)
- {
- if (pos!=seg_start)
- {
- LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, pos, editor);
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, style, editor);
- }
-
- LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos);
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, style, editor);
-
- seg_start = pos+1;
- pos = wtext.find('\n',seg_start);
- }
-
- LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, seg_end, editor);
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, style, editor);
-}
-
-void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
-{
- LLTextSegmentPtr last = seg_list.back();
- S32 new_seg_end = new_segment->getEnd();
-
- if( new_segment->getStart() == last->getStart() )
- {
- seg_list.pop_back();
- }
- else
- {
- last->setEnd( new_segment->getStart() );
- }
- seg_list.push_back( new_segment );
-
- if( new_seg_end < text_len )
- {
- seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
- }
-}
-
-void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, LLTextEditor& editor )
-{
- LLTextSegmentPtr last = seg_list.back();
- S32 new_seg_end = new_segment->getEnd();
-
- if( new_segment->getStart() == last->getStart() )
- {
- seg_list.pop_back();
- }
- else
- {
- last->setEnd( new_segment->getStart() );
- }
- seg_list.push_back( new_segment );
-
- if( new_seg_end < text_len )
- {
- seg_list.push_back( new LLNormalTextSegment( style, new_seg_end, text_len, editor ) );
- }
-}
-
-#ifdef _DEBUG
-void LLKeywords::dump()
-{
- LL_INFOS() << "LLKeywords" << LL_ENDL;
-
-
- LL_INFOS() << "LLKeywords::sWordTokenMap" << LL_ENDL;
- word_token_map_t::iterator word_token_iter = mWordTokenMap.begin();
- while( word_token_iter != mWordTokenMap.end() )
- {
- LLKeywordToken* word_token = word_token_iter->second;
- word_token->dump();
- ++word_token_iter;
- }
-
- LL_INFOS() << "LLKeywords::sLineTokenList" << LL_ENDL;
- for (token_list_t::iterator iter = mLineTokenList.begin();
- iter != mLineTokenList.end(); ++iter)
- {
- LLKeywordToken* line_token = *iter;
- line_token->dump();
- }
-
-
- LL_INFOS() << "LLKeywords::sDelimiterTokenList" << LL_ENDL;
- for (token_list_t::iterator iter = mDelimiterTokenList.begin();
- iter != mDelimiterTokenList.end(); ++iter)
- {
- LLKeywordToken* delimiter_token = *iter;
- delimiter_token->dump();
- }
-}
-
-void LLKeywordToken::dump()
-{
- LL_INFOS() << "[" <<
- mColor.mV[VX] << ", " <<
- mColor.mV[VY] << ", " <<
- mColor.mV[VZ] << "] [" <<
- wstring_to_utf8str(mToken) << "]" <<
- LL_ENDL;
-}
-
-#endif // DEBUG
+/**
+ * @file llkeywords.cpp
+ * @brief Keyword list for LSL
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <fstream>
+
+#include "llkeywords.h"
+#include "llsdserialize.h"
+#include "lltexteditor.h"
+#include "llstl.h"
+
+inline bool LLKeywordToken::isHead(const llwchar* s) const
+{
+ // strncmp is much faster than string compare
+ bool res = true;
+ const llwchar* t = mToken.c_str();
+ S32 len = mToken.size();
+ for (S32 i=0; i<len; i++)
+ {
+ if (s[i] != t[i])
+ {
+ res = false;
+ break;
+ }
+ }
+ return res;
+}
+
+inline bool LLKeywordToken::isTail(const llwchar* s) const
+{
+ bool res = true;
+ const llwchar* t = mDelimiter.c_str();
+ S32 len = mDelimiter.size();
+ for (S32 i=0; i<len; i++)
+ {
+ if (s[i] != t[i])
+ {
+ res = false;
+ break;
+ }
+ }
+ return res;
+}
+
+LLKeywords::LLKeywords()
+: mLoaded(false)
+{
+}
+
+LLKeywords::~LLKeywords()
+{
+ std::for_each(mWordTokenMap.begin(), mWordTokenMap.end(), DeletePairedPointer());
+ mWordTokenMap.clear();
+ std::for_each(mLineTokenList.begin(), mLineTokenList.end(), DeletePointer());
+ mLineTokenList.clear();
+ std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer());
+ mDelimiterTokenList.clear();
+}
+
+// Add the token as described
+void LLKeywords::addToken(LLKeywordToken::ETokenType type,
+ const std::string& key_in,
+ const LLColor4& color,
+ const std::string& tool_tip_in,
+ const std::string& delimiter_in)
+{
+ std::string tip_text = tool_tip_in;
+ LLStringUtil::replaceString(tip_text, "\\n", "\n" );
+ LLStringUtil::replaceString(tip_text, "\t", " " );
+ if (tip_text.empty())
+ {
+ tip_text = "[no info]";
+ }
+ LLWString tool_tip = utf8str_to_wstring(tip_text);
+
+ LLWString key = utf8str_to_wstring(key_in);
+ LLWString delimiter = utf8str_to_wstring(delimiter_in);
+ switch(type)
+ {
+ case LLKeywordToken::TT_CONSTANT:
+ case LLKeywordToken::TT_CONTROL:
+ case LLKeywordToken::TT_EVENT:
+ case LLKeywordToken::TT_FUNCTION:
+ case LLKeywordToken::TT_LABEL:
+ case LLKeywordToken::TT_SECTION:
+ case LLKeywordToken::TT_TYPE:
+ case LLKeywordToken::TT_WORD:
+ mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip, LLWStringUtil::null);
+ break;
+
+ case LLKeywordToken::TT_LINE:
+ mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, LLWStringUtil::null));
+ break;
+
+ case LLKeywordToken::TT_TWO_SIDED_DELIMITER:
+ case LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS:
+ case LLKeywordToken::TT_ONE_SIDED_DELIMITER:
+ mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, delimiter));
+ break;
+
+ default:
+ llassert(0);
+ }
+}
+
+std::string LLKeywords::getArguments(LLSD& arguments)
+{
+ std::string argString = "";
+
+ if (arguments.isArray())
+ {
+ U32 argsCount = arguments.size();
+ LLSD::array_iterator arrayIt = arguments.beginArray();
+ for ( ; arrayIt != arguments.endArray(); ++arrayIt)
+ {
+ LLSD& args = (*arrayIt);
+ if (args.isMap())
+ {
+ LLSD::map_iterator argsIt = args.beginMap();
+ for ( ; argsIt != args.endMap(); ++argsIt)
+ {
+ argString += argsIt->second.get("type").asString() + " " + argsIt->first;
+ if (argsCount-- > 1)
+ {
+ argString += ", ";
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Argument array comtains a non-map element!" << LL_ENDL;
+ }
+ }
+ }
+ else if (!arguments.isUndefined())
+ {
+ LL_WARNS("SyntaxLSL") << "Not an array! Invalid arguments LLSD passed to function." << arguments << LL_ENDL;
+ }
+ return argString;
+}
+
+std::string LLKeywords::getAttribute(const std::string& key)
+{
+ attribute_iterator_t it = mAttributes.find(key);
+ return (it != mAttributes.end()) ? it->second : "";
+}
+
+LLColor4 LLKeywords::getColorGroup(const std::string& key_in)
+{
+ std::string color_group = "ScriptText";
+ if (key_in == "functions")
+ {
+ color_group = "SyntaxLslFunction";
+ }
+ else if (key_in == "controls")
+ {
+ color_group = "SyntaxLslControlFlow";
+ }
+ else if (key_in == "events")
+ {
+ color_group = "SyntaxLslEvent";
+ }
+ else if (key_in == "types")
+ {
+ color_group = "SyntaxLslDataType";
+ }
+ else if (key_in == "misc-flow-label")
+ {
+ color_group = "SyntaxLslControlFlow";
+ }
+ else if (key_in =="deprecated")
+ {
+ color_group = "SyntaxLslDeprecated";
+ }
+ else if (key_in =="god-mode")
+ {
+ color_group = "SyntaxLslGodMode";
+ }
+ else if (key_in == "constants"
+ || key_in == "constants-integer"
+ || key_in == "constants-float"
+ || key_in == "constants-string"
+ || key_in == "constants-key"
+ || key_in == "constants-rotation"
+ || key_in == "constants-vector")
+ {
+ color_group = "SyntaxLslConstant";
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL;
+ }
+
+ return LLUIColorTable::instance().getColor(color_group);
+}
+
+void LLKeywords::initialize(LLSD SyntaxXML)
+{
+ mSyntax = SyntaxXML;
+ mLoaded = true;
+}
+
+void LLKeywords::processTokens()
+{
+ if (!mLoaded)
+ {
+ return;
+ }
+
+ // 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", "\"" );
+
+ LLSD::map_iterator itr = mSyntax.beginMap();
+ for ( ; itr != mSyntax.endMap(); ++itr)
+ {
+ if (itr->first == "llsd-lsl-syntax-version")
+ {
+ // Skip over version key.
+ }
+ else
+ {
+ if (itr->second.isMap())
+ {
+ processTokensGroup(itr->second, itr->first);
+ }
+ else
+ {
+ LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL;
+ }
+ }
+ }
+ LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL;
+}
+
+void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group)
+{
+ LLColor4 color;
+ LLColor4 color_group;
+ LLColor4 color_deprecated = getColorGroup("deprecated");
+ LLColor4 color_god_mode = getColorGroup("god-mode");
+
+ LLKeywordToken::ETokenType token_type = LLKeywordToken::TT_UNKNOWN;
+ // If a new token type is added here, it must also be added to the 'addToken' method
+ if (group == "constants")
+ {
+ token_type = LLKeywordToken::TT_CONSTANT;
+ }
+ else if (group == "controls")
+ {
+ token_type = LLKeywordToken::TT_CONTROL;
+ }
+ else if (group == "events")
+ {
+ token_type = LLKeywordToken::TT_EVENT;
+ }
+ else if (group == "functions")
+ {
+ token_type = LLKeywordToken::TT_FUNCTION;
+ }
+ else if (group == "label")
+ {
+ token_type = LLKeywordToken::TT_LABEL;
+ }
+ else if (group == "types")
+ {
+ token_type = LLKeywordToken::TT_TYPE;
+ }
+
+ color_group = getColorGroup(group);
+ LL_DEBUGS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL;
+
+ if (tokens.isMap())
+ {
+ LLSD::map_const_iterator outer_itr = tokens.beginMap();
+ for ( ; outer_itr != tokens.endMap(); ++outer_itr )
+ {
+ if (outer_itr->second.isMap())
+ {
+ mAttributes.clear();
+ LLSD arguments = LLSD();
+ LLSD::map_const_iterator inner_itr = outer_itr->second.beginMap();
+ for ( ; inner_itr != outer_itr->second.endMap(); ++inner_itr )
+ {
+ if (inner_itr->first == "arguments")
+ {
+ if (inner_itr->second.isArray())
+ {
+ arguments = inner_itr->second;
+ }
+ }
+ else if (!inner_itr->second.isMap() && !inner_itr->second.isArray())
+ {
+ mAttributes[inner_itr->first] = inner_itr->second.asString();
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Not a valid attribute: " << inner_itr->first << LL_ENDL;
+ }
+ }
+
+ std::string tooltip = "";
+ switch (token_type)
+ {
+ case LLKeywordToken::TT_CONSTANT:
+ if (getAttribute("type").length() > 0)
+ {
+ color_group = getColorGroup(group + "-" + getAttribute("type"));
+ }
+ else
+ {
+ color_group = getColorGroup(group);
+ }
+ tooltip = "Type: " + getAttribute("type") + ", Value: " + getAttribute("value");
+ break;
+ case LLKeywordToken::TT_EVENT:
+ tooltip = outer_itr->first + "(" + getArguments(arguments) + ")";
+ 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 (!getAttribute("sleep").empty())
+ {
+ tooltip += ", Sleep: " + getAttribute("sleep");
+ }
+ default:
+ break;
+ }
+
+ if (!getAttribute("tooltip").empty())
+ {
+ if (!tooltip.empty())
+ {
+ tooltip.append("\n");
+ }
+ tooltip.append(getAttribute("tooltip"));
+ }
+
+ color = getAttribute("deprecated") == "true" ? color_deprecated : color_group;
+
+ if (getAttribute("god-mode") == "true")
+ {
+ color = color_god_mode;
+ }
+
+ addToken(token_type, outer_itr->first, color, tooltip);
+ }
+ }
+ }
+ else if (tokens.isArray()) // Currently nothing should need this, but it's here for completeness
+ {
+ LL_INFOS("SyntaxLSL") << "Curious, shouldn't be an array here; adding all using color " << color << LL_ENDL;
+ for (S32 count = 0; count < tokens.size(); ++count)
+ {
+ addToken(token_type, tokens[count], color, "");
+ }
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Invalid map/array passed: '" << tokens << "'" << LL_ENDL;
+ }
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other)
+{
+ if(other.mOwner)
+ {
+ copyData(other.mData, other.mLength);
+ }
+ else
+ {
+ mOwner = false;
+ mLength = other.mLength;
+ mData = other.mData;
+ }
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
+{
+ copyData(str.data(), str.size());
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length)
+: mData(start)
+, mLength(length)
+, mOwner(false)
+{
+}
+
+LLKeywords::WStringMapIndex::~WStringMapIndex()
+{
+ if (mOwner)
+ {
+ delete[] mData;
+ }
+}
+
+void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
+{
+ llwchar *data = new llwchar[length];
+ memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
+
+ mOwner = true;
+ mLength = length;
+ mData = data;
+}
+
+bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
+{
+ // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
+ // The comparison only needs to strictly order all possible strings, and be stable.
+
+ bool result = false;
+ const llwchar* self_iter = mData;
+ const llwchar* self_end = mData + mLength;
+ const llwchar* other_iter = other.mData;
+ const llwchar* other_end = other.mData + other.mLength;
+
+ while(true)
+ {
+ if(other_iter >= other_end)
+ {
+ // We've hit the end of other.
+ // This covers two cases: other being shorter than self, or the strings being equal.
+ // In either case, we want to return false.
+ result = false;
+ break;
+ }
+ else if(self_iter >= self_end)
+ {
+ // self is shorter than other.
+ result = true;
+ break;
+ }
+ else if(*self_iter != *other_iter)
+ {
+ // The current character differs. The strings are not equal.
+ result = *self_iter < *other_iter;
+ break;
+ }
+
+ self_iter++;
+ other_iter++;
+ }
+
+ return result;
+}
+
+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<LLTextSegmentPtr>* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style)
+{
+ LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING);
+ seg_list->clear();
+
+ if( wtext.empty() )
+ {
+ return;
+ }
+
+ S32 text_len = wtext.size() + 1;
+
+ seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) );
+
+ 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, cur-base);
+ text_segment->setToken( 0 );
+ insertSegment( *seg_list, text_segment, text_len, style, editor);
+ cur++;
+ if( !*cur || *cur == '\n' )
+ {
+ continue;
+ }
+ }
+
+ // Skip white space
+ while( *cur && iswspace(*cur) && (*cur != '\n') )
+ {
+ cur++;
+ }
+ if( !*cur || *cur == '\n' )
+ {
+ continue;
+ }
+
+ // cur is now at the first non-whitespace character of a new line
+
+ // Line start tokens
+ {
+ bool line_done = false;
+ for (token_list_t::iterator iter = mLineTokenList.begin();
+ iter != mLineTokenList.end(); ++iter)
+ {
+ LLKeywordToken* cur_token = *iter;
+ if( cur_token->isHead( cur ) )
+ {
+ S32 seg_start = cur - base;
+ while( *cur && *cur != '\n' )
+ {
+ // skip the rest of the line
+ cur++;
+ }
+ S32 seg_end = cur - base;
+
+ //create segments from seg_start to seg_end
+ insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
+ line_done = true; // to break out of second loop.
+ break;
+ }
+ }
+
+ if( line_done )
+ {
+ continue;
+ }
+ }
+ }
+
+ // Skip white space
+ while( *cur && iswspace(*cur) && (*cur != '\n') )
+ {
+ cur++;
+ }
+
+ while( *cur && *cur != '\n' )
+ {
+ // Check against delimiters
+ {
+ S32 seg_start = 0;
+ LLKeywordToken* cur_delimiter = NULL;
+ for (token_list_t::iterator iter = mDelimiterTokenList.begin();
+ iter != mDelimiterTokenList.end(); ++iter)
+ {
+ LLKeywordToken* delimiter = *iter;
+ if( delimiter->isHead( cur ) )
+ {
+ cur_delimiter = delimiter;
+ break;
+ }
+ }
+
+ if( cur_delimiter )
+ {
+ S32 between_delimiters = 0;
+ S32 seg_end = 0;
+
+ seg_start = cur - base;
+ cur += cur_delimiter->getLengthHead();
+
+ LLKeywordToken::ETokenType type = cur_delimiter->getType();
+ if( type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS )
+ {
+ while( *cur && !cur_delimiter->isTail(cur))
+ {
+ // Check for an escape sequence.
+ if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\')
+ {
+ // Count the number of backslashes.
+ S32 num_backslashes = 0;
+ while (*cur == '\\')
+ {
+ num_backslashes++;
+ between_delimiters++;
+ cur++;
+ }
+ // If the next character is the end delimiter?
+ if (cur_delimiter->isTail(cur))
+ {
+ // If there was an odd number of backslashes, then this delimiter
+ // does not end the sequence.
+ if (num_backslashes % 2 == 1)
+ {
+ between_delimiters++;
+ cur++;
+ }
+ else
+ {
+ // This is an end delimiter.
+ break;
+ }
+ }
+ }
+ else
+ {
+ between_delimiters++;
+ cur++;
+ }
+ }
+
+ if( *cur )
+ {
+ cur += cur_delimiter->getLengthHead();
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead() + cur_delimiter->getLengthTail();
+ }
+ else
+ {
+ // eof
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
+ }
+ }
+ else
+ {
+ llassert( cur_delimiter->getType() == LLKeywordToken::TT_ONE_SIDED_DELIMITER );
+ // Left side is the delimiter. Right side is eol or eof.
+ while( *cur && ('\n' != *cur) )
+ {
+ between_delimiters++;
+ cur++;
+ }
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
+ }
+
+ insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor);
+ /*
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
+ text_segment->setToken( cur_delimiter );
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
+ */
+ // Note: we don't increment cur, since the end of one delimited seg may be immediately
+ // followed by the start of another one.
+ continue;
+ }
+ }
+
+ // check against words
+ llwchar prev = cur > base ? *(cur-1) : 0;
+ if( !iswalnum( prev ) && (prev != '_') )
+ {
+ const llwchar* p = cur;
+ while( iswalnum( *p ) || (*p == '_') )
+ {
+ p++;
+ }
+ S32 seg_len = p - cur;
+ if( seg_len > 0 )
+ {
+ WStringMapIndex word( cur, seg_len );
+ word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
+ if( map_iter != mWordTokenMap.end() )
+ {
+ LLKeywordToken* cur_token = map_iter->second;
+ S32 seg_start = cur - base;
+ S32 seg_end = seg_start + seg_len;
+
+ // LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL;
+
+ insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
+ }
+ cur += seg_len;
+ continue;
+ }
+ }
+
+ if( *cur && *cur != '\n' )
+ {
+ cur++;
+ }
+ }
+ }
+}
+
+void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor )
+{
+ std::string::size_type pos = wtext.find('\n',seg_start);
+
+ LLStyleConstSP cur_token_style = new LLStyle(LLStyle::Params().font(style->getFont()).color(cur_token->getColor()));
+
+ while (pos!=-1 && pos < (std::string::size_type)seg_end)
+ {
+ if (pos!=seg_start)
+ {
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, pos, editor);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, style, editor);
+ }
+
+ LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, style, editor);
+
+ seg_start = pos+1;
+ pos = wtext.find('\n',seg_start);
+ }
+
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, seg_end, editor);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, style, editor);
+}
+
+void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
+{
+ LLTextSegmentPtr last = seg_list.back();
+ S32 new_seg_end = new_segment->getEnd();
+
+ if( new_segment->getStart() == last->getStart() )
+ {
+ seg_list.pop_back();
+ }
+ else
+ {
+ last->setEnd( new_segment->getStart() );
+ }
+ seg_list.push_back( new_segment );
+
+ if( new_seg_end < text_len )
+ {
+ seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
+ }
+}
+
+void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, LLTextEditor& editor )
+{
+ LLTextSegmentPtr last = seg_list.back();
+ S32 new_seg_end = new_segment->getEnd();
+
+ if( new_segment->getStart() == last->getStart() )
+ {
+ seg_list.pop_back();
+ }
+ else
+ {
+ last->setEnd( new_segment->getStart() );
+ }
+ seg_list.push_back( new_segment );
+
+ if( new_seg_end < text_len )
+ {
+ seg_list.push_back( new LLNormalTextSegment( style, new_seg_end, text_len, editor ) );
+ }
+}
+
+#ifdef _DEBUG
+void LLKeywords::dump()
+{
+ LL_INFOS() << "LLKeywords" << LL_ENDL;
+
+
+ LL_INFOS() << "LLKeywords::sWordTokenMap" << LL_ENDL;
+ word_token_map_t::iterator word_token_iter = mWordTokenMap.begin();
+ while( word_token_iter != mWordTokenMap.end() )
+ {
+ LLKeywordToken* word_token = word_token_iter->second;
+ word_token->dump();
+ ++word_token_iter;
+ }
+
+ LL_INFOS() << "LLKeywords::sLineTokenList" << LL_ENDL;
+ for (token_list_t::iterator iter = mLineTokenList.begin();
+ iter != mLineTokenList.end(); ++iter)
+ {
+ LLKeywordToken* line_token = *iter;
+ line_token->dump();
+ }
+
+
+ LL_INFOS() << "LLKeywords::sDelimiterTokenList" << LL_ENDL;
+ for (token_list_t::iterator iter = mDelimiterTokenList.begin();
+ iter != mDelimiterTokenList.end(); ++iter)
+ {
+ LLKeywordToken* delimiter_token = *iter;
+ delimiter_token->dump();
+ }
+}
+
+void LLKeywordToken::dump()
+{
+ LL_INFOS() << "[" <<
+ mColor.mV[VX] << ", " <<
+ mColor.mV[VY] << ", " <<
+ mColor.mV[VZ] << "] [" <<
+ wstring_to_utf8str(mToken) << "]" <<
+ LL_ENDL;
+}
+
+#endif // DEBUG