diff options
Diffstat (limited to 'indra/llui/llkeywords.cpp')
-rw-r--r-- | indra/llui/llkeywords.cpp | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp new file mode 100644 index 0000000000..e8628c9374 --- /dev/null +++ b/indra/llui/llkeywords.cpp @@ -0,0 +1,502 @@ +/** + * @file llkeywords.cpp + * @brief Keyword list for LSL + * + * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include <iostream> +#include <fstream> + +#include "llkeywords.h" +#include "lltexteditor.h" +#include "llstl.h" +#include <boost/tokenizer.hpp> + +const U32 KEYWORD_FILE_CURRENT_VERSION = 2; + +inline BOOL LLKeywordToken::isHead(const llwchar* s) +{ + // 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; +} + +LLKeywords::LLKeywords() : mLoaded(FALSE) +{ +} + +LLKeywords::~LLKeywords() +{ + std::for_each(mWordTokenMap.begin(), mWordTokenMap.end(), DeletePairedPointer()); + std::for_each(mLineTokenList.begin(), mLineTokenList.end(), DeletePointer()); + std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer()); +} + +BOOL LLKeywords::loadFromFile( const LLString& filename ) +{ + mLoaded = FALSE; + + //////////////////////////////////////////////////////////// + // File header + + const S32 BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + + llifstream file; + file.open(filename.c_str()); + if( file.fail() ) + { + llinfos << "LLKeywords::loadFromFile() Unable to open file: " << filename << llendl; + return mLoaded; + } + + // Identifying string + file >> buffer; + if( strcmp( buffer, "llkeywords" ) ) + { + llinfos << filename << " does not appear to be a keyword file" << llendl; + return mLoaded; + } + + // Check file version + file >> buffer; + U32 version_num; + file >> version_num; + if( strcmp(buffer, "version") || version_num != (U32)KEYWORD_FILE_CURRENT_VERSION ) + { + llinfos << filename << " does not appear to be a version " << KEYWORD_FILE_CURRENT_VERSION << " keyword file" << llendl; + return mLoaded; + } + + // start of line (SOL) + const char SOL_COMMENT[] = "#"; + const char SOL_WORD[] = "[word "; + const char SOL_LINE[] = "[line "; + const char SOL_ONE_SIDED_DELIMITER[] = "[one_sided_delimiter "; + const char SOL_TWO_SIDED_DELIMITER[] = "[two_sided_delimiter "; + + LLColor3 cur_color( 1, 0, 0 ); + LLKeywordToken::TOKEN_TYPE cur_type = LLKeywordToken::WORD; + + while (!file.eof()) + { + file.getline( buffer, BUFFER_SIZE ); + if( !strncmp( buffer, SOL_COMMENT, strlen(SOL_COMMENT) ) ) + { + continue; + } + else + if( !strncmp( buffer, SOL_WORD, strlen(SOL_WORD) ) ) + { + cur_color = readColor( buffer + strlen(SOL_WORD) ); + cur_type = LLKeywordToken::WORD; + continue; + } + else + if( !strncmp( buffer, SOL_LINE, strlen(SOL_LINE) ) ) + { + cur_color = readColor( buffer + strlen(SOL_LINE) ); + cur_type = LLKeywordToken::LINE; + continue; + } + else + if( !strncmp( buffer, SOL_TWO_SIDED_DELIMITER, strlen(SOL_TWO_SIDED_DELIMITER) ) ) + { + cur_color = readColor( buffer + strlen(SOL_TWO_SIDED_DELIMITER) ); + cur_type = LLKeywordToken::TWO_SIDED_DELIMITER; + continue; + } + if( !strncmp( buffer, SOL_ONE_SIDED_DELIMITER, strlen(SOL_ONE_SIDED_DELIMITER) ) ) + { + cur_color = readColor( buffer + strlen(SOL_ONE_SIDED_DELIMITER) ); + cur_type = LLKeywordToken::ONE_SIDED_DELIMITER; + continue; + } + + LLString token_buffer( buffer ); + LLString::trim(token_buffer); + + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep_word("", " \t"); + tokenizer word_tokens(token_buffer, sep_word); + tokenizer::iterator token_word_iter = word_tokens.begin(); + + if( !token_buffer.empty() && token_word_iter != word_tokens.end() ) + { + // first word is keyword + LLString keyword = (*token_word_iter); + LLString::trim(keyword); + + // following words are tooltip + LLString tool_tip; + while (++token_word_iter != word_tokens.end()) + { + tool_tip += (*token_word_iter); + } + LLString::trim(tool_tip); + + if( !tool_tip.empty() ) + { + // Replace : with \n for multi-line tool tips. + LLString::replaceChar( tool_tip, ':', '\n' ); + addToken(cur_type, keyword, cur_color, tool_tip ); + } + else + { + addToken(cur_type, keyword, cur_color, NULL ); + } + } + } + + file.close(); + + mLoaded = TRUE; + return mLoaded; +} + +// Add the token as described +void LLKeywords::addToken(LLKeywordToken::TOKEN_TYPE type, + const LLString& key_in, + const LLColor3& color, + const LLString& tool_tip_in ) +{ + LLWString key = utf8str_to_wstring(key_in); + LLWString tool_tip = utf8str_to_wstring(tool_tip_in); + switch(type) + { + case LLKeywordToken::WORD: + mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip); + break; + + case LLKeywordToken::LINE: + mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip)); + break; + + case LLKeywordToken::TWO_SIDED_DELIMITER: + case LLKeywordToken::ONE_SIDED_DELIMITER: + mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip)); + break; + + default: + llassert(0); + } +} + +LLColor3 LLKeywords::readColor( const LLString& s ) +{ + F32 r, g, b; + r = g = b = 0.0f; + S32 read = sscanf(s.c_str(), "%f, %f, %f]", &r, &g, &b ); + if( read != 3 ) + { + llinfos << " poorly formed color in keyword file" << llendl; + } + return LLColor3( r, g, b ); +} + +// 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<LLTextSegment *>* seg_list, const LLWString& wtext) +{ + std::for_each(seg_list->begin(), seg_list->end(), DeletePointer()); + seg_list->clear(); + + if( wtext.empty() ) + { + return; + } + + S32 text_len = wtext.size(); + + seg_list->push_back( new LLTextSegment( LLColor3(0,0,0), 0, text_len ) ); + + const llwchar* base = wtext.c_str(); + const llwchar* cur = base; + const llwchar* line = NULL; + + while( *cur ) + { + if( *cur == '\n' || cur == base ) + { + if( *cur == '\n' ) + { + cur++; + if( !*cur || *cur == '\n' ) + { + continue; + } + } + + // Start of a new line + line = cur; + + // Skip white space + while( *cur && isspace(*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; + + //llinfos << "Seg: [" << (char*)LLString( base, seg_start, seg_end-seg_start) << "]" << llendl; + LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end ); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len); + line_done = TRUE; // to break out of second loop. + break; + } + } + + if( line_done ) + { + continue; + } + } + } + + // Skip white space + while( *cur && isspace(*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->getLength(); + + if( cur_delimiter->getType() == LLKeywordToken::TWO_SIDED_DELIMITER ) + { + while( *cur && !cur_delimiter->isHead(cur)) + { + // Check for an escape sequence. + if (*cur == '\\') + { + // Count the number of backslashes. + S32 num_backslashes = 0; + while (*cur == '\\') + { + num_backslashes++; + between_delimiters++; + cur++; + } + // Is the next character the end delimiter? + if (cur_delimiter->isHead(cur)) + { + // Is 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->getLength(); + seg_end = seg_start + between_delimiters + 2 * cur_delimiter->getLength(); + } + else + { + // eof + seg_end = seg_start + between_delimiters + cur_delimiter->getLength(); + } + } + else + { + llassert( cur_delimiter->getType() == LLKeywordToken::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->getLength(); + } + + + //llinfos << "Seg: [" << (char*)LLString( base, seg_start, seg_end-seg_start ) << "]" << llendl; + LLTextSegment* text_segment = new LLTextSegment( cur_delimiter->getColor(), seg_start, seg_end ); + text_segment->setToken( cur_delimiter ); + insertSegment( seg_list, text_segment, text_len); + + // 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( !isalnum( prev ) && (prev != '_') ) + { + const llwchar* p = cur; + while( isalnum( *p ) || (*p == '_') ) + { + p++; + } + S32 seg_len = p - cur; + if( seg_len > 0 ) + { + LLWString word( cur, 0, 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; + + // llinfos << "Seg: [" << word.c_str() << "]" << llendl; + + + LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end ); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len); + } + cur += seg_len; + continue; + } + } + + if( *cur && *cur != '\n' ) + { + cur++; + } + } + } +} + +void LLKeywords::insertSegment(std::vector<LLTextSegment*>* seg_list, LLTextSegment* new_segment, S32 text_len ) +{ + LLTextSegment* last = seg_list->back(); + S32 new_seg_end = new_segment->getEnd(); + + if( new_segment->getStart() == last->getStart() ) + { + *last = *new_segment; + delete new_segment; + } + else + { + last->setEnd( new_segment->getStart() ); + seg_list->push_back( new_segment ); + } + + if( new_seg_end < text_len ) + { + seg_list->push_back( new LLTextSegment( LLColor3(0,0,0), new_seg_end, text_len ) ); + } +} + +#ifdef _DEBUG +void LLKeywords::dump() +{ + llinfos << "LLKeywords" << llendl; + + + llinfos << "LLKeywords::sWordTokenMap" << llendl; + 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; + } + + llinfos << "LLKeywords::sLineTokenList" << llendl; + for (token_list_t::iterator iter = mLineTokenList.begin(); + iter != mLineTokenList.end(); ++iter) + { + LLKeywordToken* line_token = *iter; + line_token->dump(); + } + + + llinfos << "LLKeywords::sDelimiterTokenList" << llendl; + for (token_list_t::iterator iter = mDelimiterTokenList.begin(); + iter != mDelimiterTokenList.end(); ++iter) + { + LLKeywordToken* delimiter_token = *iter; + delimiter_token->dump(); + } +} + +void LLKeywordToken::dump() +{ + llinfos << "[" << + mColor.mV[VX] << ", " << + mColor.mV[VY] << ", " << + mColor.mV[VZ] << "] [" << + mToken.c_str() << "]" << + llendl; +} + +#endif // DEBUG |