LUAEditorSyntaxHighlighter.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "LUAEditorSyntaxHighlighter.hxx"
  9. #include <Source/LUA/moc_LUAEditorSyntaxHighlighter.cpp>
  10. #include "LUAEditorStyleMessages.h"
  11. #include "LUAEditorBlockState.h"
  12. namespace LUAEditor
  13. {
  14. namespace
  15. {
  16. template<typename Container>
  17. void CreateTypes([[maybe_unused]] Container& container)
  18. {
  19. }
  20. template<typename Container, typename Type, typename ... Types>
  21. void CreateTypes(Container& container)
  22. {
  23. container.push_back(azcreate(Type, ()));
  24. CreateTypes<Container, Types...>(container);
  25. }
  26. enum class ParserStates : int
  27. {
  28. Null,
  29. Name,
  30. ShortComment,
  31. LongComment,
  32. Number,
  33. NumberHex,
  34. StringLiteral,
  35. NumStates
  36. };
  37. class BaseParserState
  38. {
  39. public:
  40. virtual ~BaseParserState() {}
  41. virtual bool IsMultilineState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine) const { return false; }
  42. virtual void StartState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine) {}
  43. //note you only get 13 bits of usable space here. see QTBlockState m_syntaxHighlighterStateExtra
  44. virtual AZ::u16 GetSaveState() const { return 0; }
  45. virtual void SetSaveState([[maybe_unused]] AZ::u16 state) {}
  46. virtual void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) = 0;
  47. };
  48. class NullParserState
  49. : public BaseParserState
  50. {
  51. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  52. };
  53. class NameParserState
  54. : public BaseParserState
  55. {
  56. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  57. };
  58. class ShortCommentParserState
  59. : public BaseParserState
  60. {
  61. void StartState(LUASyntaxHighlighter::StateMachine& machine) override;
  62. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  63. bool m_mightBeLong;
  64. };
  65. class NumberParserState
  66. : public BaseParserState
  67. {
  68. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  69. };
  70. class NumberHexParserState
  71. : public BaseParserState
  72. {
  73. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  74. };
  75. class LongCommentParserState
  76. : public BaseParserState
  77. {
  78. bool IsMultilineState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine) const override { return true; }
  79. void StartState(LUASyntaxHighlighter::StateMachine& machine) override;
  80. AZ::u16 GetSaveState() const override { return m_bracketLevel; }
  81. void SetSaveState(AZ::u16 state) override;
  82. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  83. AZ::u16 m_bracketLevel;
  84. QString m_bracketEnd;
  85. bool m_endNextChar;
  86. };
  87. class StringLiteralParserState
  88. : public BaseParserState
  89. {
  90. bool IsMultilineState(LUASyntaxHighlighter::StateMachine& machine) const override;
  91. void StartState(LUASyntaxHighlighter::StateMachine& machine) override;
  92. AZ::u16 GetSaveState() const override { return m_bracketLevel; }
  93. void SetSaveState(AZ::u16 state) override;
  94. void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
  95. AZ::u16 m_bracketLevel; //if 0 string started with a ' if 1 started with a " if 2 or more started with an long braket of level m_BracketLevel-2
  96. bool m_endNextChar;
  97. QString m_bracketEnd;
  98. bool m_mightBeLong;
  99. };
  100. }
  101. // GCC triggers a subobject linkage warning if a symbol in an anonymous namespace in included in another file
  102. // When unity files are enabled in CMake, this file is included in a .cxx file which triggers this warning
  103. // The code is actually OK as this file is a translation unit and not a header file where this warning would normally be triggered
  104. AZ_PUSH_DISABLE_WARNING_GCC("-Wsubobject-linkage");
  105. class LUASyntaxHighlighter::StateMachine
  106. {
  107. AZ_POP_DISABLE_WARNING_GCC
  108. public:
  109. StateMachine();
  110. ~StateMachine();
  111. void Parse(const QString& text);
  112. //extraBack is if you want to include previous chars as part as the current string after the state change
  113. void SetState(ParserStates state, int extraBack = 0);
  114. void PassState(ParserStates state); //change state but keep data captured so far, use if we are in "wrong" state
  115. void Reset();
  116. int CurrentLength() const { return m_currentChar - m_start + 1; }
  117. QStringRef CurrentString() const { return QStringRef(m_currentString, m_start, CurrentLength()); }
  118. const QString* GetFullLine() const { return m_currentString; }
  119. QTBlockState GetSaveState() const;
  120. void SetSaveState(QTBlockState state);
  121. void IncFoldLevel()
  122. {
  123. ++m_foldLevel;
  124. if (m_onIncFoldLevel)
  125. {
  126. m_onIncFoldLevel(m_foldLevel);
  127. }
  128. }
  129. void DecFoldLevel()
  130. {
  131. if (m_foldLevel > 0)
  132. {
  133. --m_foldLevel;
  134. }
  135. if (m_onDecFoldLevel)
  136. {
  137. m_onDecFoldLevel(m_foldLevel);
  138. }
  139. }
  140. ParserStates GetCurrentParserState() const { return m_currentState; }
  141. bool IsJoiningNames() const { return m_joinNames; }
  142. void SetJoiningNames(bool joinNames) { m_joinNames = joinNames; }
  143. template<typename T>
  144. void SetOnIncFoldLevel(const T& callable) { m_onIncFoldLevel = callable; }
  145. template<typename T>
  146. void SetOnDecFoldLevel(const T& callable) { m_onDecFoldLevel = callable; }
  147. AZStd::function<void(ParserStates state, int position, int length)> CaptureToken;
  148. private:
  149. BaseParserState* GetCurrentState() const { return m_states[static_cast<int>(m_currentState)]; }
  150. using StatesListType = AZStd::fixed_vector<BaseParserState*, static_cast<int>(ParserStates::NumStates)>;
  151. StatesListType m_states;
  152. ParserStates m_currentState{ ParserStates::Null };
  153. int m_currentChar;
  154. int m_start;
  155. const QString* m_currentString;
  156. int m_foldLevel;
  157. bool m_joinNames{ true }; //consider names seperated by . and : as one for highlighting purposes
  158. AZStd::function<void(int)> m_onIncFoldLevel;
  159. AZStd::function<void(int)> m_onDecFoldLevel;
  160. };
  161. LUASyntaxHighlighter::StateMachine::StateMachine()
  162. {
  163. CreateTypes<StatesListType, NullParserState, NameParserState, ShortCommentParserState,
  164. LongCommentParserState, NumberParserState, NumberHexParserState, StringLiteralParserState>(m_states);
  165. Reset();
  166. }
  167. LUASyntaxHighlighter::StateMachine::~StateMachine()
  168. {
  169. AZStd::for_each(m_states.begin(), m_states.end(), [](BaseParserState* state) { azdestroy(state); });
  170. }
  171. void LUASyntaxHighlighter::StateMachine::Reset()
  172. {
  173. m_start = 0;
  174. m_currentChar = -1;
  175. m_currentState = ParserStates::Null;
  176. m_currentString = nullptr;
  177. m_foldLevel = 0;
  178. }
  179. void LUASyntaxHighlighter::StateMachine::Parse(const QString& text)
  180. {
  181. m_currentString = &text;
  182. for (m_currentChar = 0; m_currentChar != text.length(); ++m_currentChar)
  183. {
  184. GetCurrentState()->Parse(*this, text[m_currentChar]);
  185. }
  186. //we only highlight on line at most at a time. so if this is a multiline highlight, go ahead an highlight this line as part of that now.
  187. if (GetCurrentState()->IsMultilineState(*this))
  188. {
  189. if (CaptureToken && m_start != m_currentChar)
  190. {
  191. CaptureToken(m_currentState, m_start, m_currentChar - m_start);
  192. }
  193. }
  194. else
  195. {
  196. if (m_currentState != ParserStates::Null)
  197. {
  198. SetState(ParserStates::Null);
  199. }
  200. else
  201. {
  202. if (CaptureToken && m_start != m_currentChar)
  203. {
  204. CaptureToken(m_currentState, m_start, m_currentChar - m_start);
  205. }
  206. }
  207. }
  208. }
  209. void LUASyntaxHighlighter::StateMachine::SetState(ParserStates state, int extraBack)
  210. {
  211. if (m_currentState != state)
  212. {
  213. int currentChar = m_currentChar - extraBack;
  214. if (CaptureToken && m_start < currentChar)
  215. {
  216. CaptureToken(m_currentState, m_start, currentChar - m_start);
  217. }
  218. m_currentState = state;
  219. m_start = currentChar;
  220. GetCurrentState()->StartState(*this);
  221. //if going back to null state, see if this char might be the start of a new capture.
  222. if (m_currentState == ParserStates::Null && m_start < m_currentString->length())
  223. {
  224. GetCurrentState()->Parse(*this, m_currentString->at(m_start));
  225. }
  226. }
  227. }
  228. void LUASyntaxHighlighter::StateMachine::PassState(ParserStates state)
  229. {
  230. m_currentState = state;
  231. GetCurrentState()->StartState(*this);
  232. //if going back to null state, see if this char might be the start of a new capture.
  233. if (m_currentState == ParserStates::Null && m_start < m_currentString->length())
  234. {
  235. GetCurrentState()->Parse(*this, m_currentString->at(m_currentChar));
  236. }
  237. }
  238. ///1st bit to detect uninitialized blocks, next 14 bits store folding depth, next 3 bits store state machine state, final 14 bits store state specific user data.
  239. QTBlockState LUASyntaxHighlighter::StateMachine::GetSaveState() const
  240. {
  241. QTBlockState result;
  242. result.m_blockState.m_uninitialized = 0;
  243. result.m_blockState.m_folded = 0;
  244. result.m_blockState.m_foldLevel = m_foldLevel;
  245. result.m_blockState.m_syntaxHighlighterState = static_cast<int>(m_currentState);
  246. result.m_blockState.m_syntaxHighlighterStateExtra = GetCurrentState()->GetSaveState();
  247. return result;
  248. }
  249. void LUASyntaxHighlighter::StateMachine::SetSaveState(QTBlockState state)
  250. {
  251. static_assert(static_cast<int>(ParserStates::NumStates) <= 8, "We are only using 3 bits for state in lua parser currently");
  252. Reset();
  253. if (!state.m_blockState.m_uninitialized)
  254. {
  255. m_currentState = static_cast<ParserStates>(state.m_blockState.m_syntaxHighlighterState);
  256. GetCurrentState()->SetSaveState(state.m_blockState.m_syntaxHighlighterStateExtra);
  257. m_foldLevel = state.m_blockState.m_foldLevel;
  258. }
  259. }
  260. void NullParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
  261. {
  262. if (nextChar.isLetter() || nextChar == '_')
  263. {
  264. machine.SetState(ParserStates::Name);
  265. }
  266. else if (nextChar.isNumber() || nextChar == '-' || nextChar == '+')
  267. {
  268. machine.SetState(ParserStates::Number);
  269. }
  270. else if (nextChar == '\'' || nextChar == '"' || nextChar == '[')
  271. {
  272. machine.SetState(ParserStates::StringLiteral);
  273. }
  274. else if (nextChar == '{')
  275. {
  276. machine.IncFoldLevel();
  277. }
  278. else if (nextChar == '}')
  279. {
  280. machine.DecFoldLevel();
  281. }
  282. }
  283. void NameParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
  284. {
  285. if (!nextChar.isLetterOrNumber() && nextChar != '_' && (!machine.IsJoiningNames() || (nextChar != '.' && nextChar != ':')))
  286. {
  287. machine.SetState(ParserStates::Null);
  288. }
  289. }
  290. void ShortCommentParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
  291. {
  292. if (machine.CurrentLength() == 3 && nextChar != '[')
  293. {
  294. m_mightBeLong = false;
  295. }
  296. else if (machine.CurrentLength() >= 4 && machine.CurrentLength() < USHRT_MAX && m_mightBeLong) //we cant catch longer than 16k level comments.
  297. {
  298. if (nextChar != '=' && nextChar != '[')
  299. {
  300. m_mightBeLong = false;
  301. }
  302. else if (nextChar == '[')
  303. {
  304. machine.PassState(ParserStates::LongComment);
  305. }
  306. }
  307. }
  308. void ShortCommentParserState::StartState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine)
  309. {
  310. m_mightBeLong = true;
  311. }
  312. void LongCommentParserState::StartState(LUASyntaxHighlighter::StateMachine& machine)
  313. {
  314. auto token = machine.CurrentString();
  315. int start = token.indexOf('[');
  316. AZ_Assert(start != -1, "Shouldn't have been able to get to this state without opening long bracket");
  317. int finish = token.indexOf('[', start + 1);
  318. AZ_Assert(finish != -1 && finish != start, "Shouldn't have been able to get to this state without opening long bracket");
  319. m_bracketLevel = static_cast<AZ::u16>(finish - start - 1);
  320. m_bracketEnd = "]";
  321. for (int i = 0; i < m_bracketLevel; ++i)
  322. {
  323. m_bracketEnd.append('=');
  324. }
  325. m_bracketEnd.append(']');
  326. m_endNextChar = false;
  327. }
  328. void LongCommentParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, [[maybe_unused]] const QChar& nextChar)
  329. {
  330. if (m_endNextChar)
  331. {
  332. machine.SetState(ParserStates::Null);
  333. return;
  334. }
  335. QStringRef token = machine.CurrentString();
  336. if (token.endsWith(m_bracketEnd))
  337. {
  338. if (machine.GetFullLine()->size() >= token.size())
  339. {
  340. machine.SetState(ParserStates::Null, -1);
  341. }
  342. else
  343. {
  344. m_endNextChar = true;
  345. }
  346. }
  347. }
  348. void LongCommentParserState::SetSaveState(AZ::u16 state)
  349. {
  350. m_bracketLevel = state;
  351. m_bracketEnd = "]";
  352. for (int i = 0; i < m_bracketLevel; ++i)
  353. {
  354. m_bracketEnd.append('=');
  355. }
  356. m_bracketEnd.append(']');
  357. m_endNextChar = false;
  358. }
  359. void NumberParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
  360. {
  361. auto currentString = machine.CurrentString();
  362. if (currentString.endsWith("--"))
  363. {
  364. machine.SetState(ParserStates::ShortComment, 1);
  365. return;
  366. }
  367. auto lower = nextChar.toLower();
  368. if (lower == 'x')
  369. {
  370. machine.PassState(ParserStates::NumberHex);
  371. }
  372. else if (!(nextChar.isNumber() || nextChar == '.' || lower == 'e'))
  373. {
  374. if (currentString.length() == 2 && (currentString.at(0) == '+' || currentString.at(0) == '-'))
  375. {
  376. machine.PassState(ParserStates::Null);
  377. }
  378. else
  379. {
  380. machine.SetState(ParserStates::Null);
  381. }
  382. }
  383. }
  384. void NumberHexParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
  385. {
  386. auto lower = nextChar.toLower();
  387. if (!(nextChar.isNumber() || nextChar == '.' || lower == 'p'))
  388. {
  389. machine.SetState(ParserStates::Null);
  390. }
  391. }
  392. void StringLiteralParserState::StartState(LUASyntaxHighlighter::StateMachine& machine)
  393. {
  394. m_endNextChar = false;
  395. m_mightBeLong = false;
  396. auto currentString = machine.CurrentString();
  397. AZ_Assert(!currentString.isEmpty(), "Sting literal string shouldn't be empty")
  398. if (currentString.at(0) == '\'')
  399. {
  400. m_bracketLevel = 0;
  401. }
  402. if (currentString.at(0) == '"')
  403. {
  404. m_bracketLevel = 1;
  405. }
  406. if (currentString.at(0) == '[')
  407. {
  408. m_bracketLevel = 2;
  409. m_mightBeLong = true;
  410. m_bracketEnd = "]";
  411. }
  412. }
  413. void StringLiteralParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
  414. {
  415. if (m_endNextChar)
  416. {
  417. machine.SetState(ParserStates::Null);
  418. return;
  419. }
  420. if (m_mightBeLong)
  421. {
  422. if (nextChar == '[')
  423. {
  424. m_mightBeLong = false; //it actually is long, = length will already be in m_bracketLevel
  425. m_bracketEnd += ']';
  426. return;
  427. }
  428. else if (nextChar == '=')
  429. {
  430. ++m_bracketLevel;
  431. m_bracketEnd += '=';
  432. }
  433. else
  434. {
  435. machine.PassState(ParserStates::Null); //turns out we weren't actually a string literal
  436. return;
  437. }
  438. }
  439. if (m_bracketLevel == 0 && nextChar == '\'' && !machine.CurrentString().endsWith("\\'"))
  440. {
  441. m_endNextChar = true;
  442. }
  443. if (m_bracketLevel == 1 && nextChar == '"' && !machine.CurrentString().endsWith("\\\""))
  444. {
  445. m_endNextChar = true;
  446. }
  447. if (m_bracketLevel >= 2 && machine.CurrentString().endsWith(m_bracketEnd))
  448. {
  449. m_endNextChar = true;
  450. }
  451. }
  452. bool StringLiteralParserState::IsMultilineState(LUASyntaxHighlighter::StateMachine& machine) const
  453. {
  454. auto currentString = machine.GetFullLine();
  455. if ((m_bracketLevel > 1 && !m_endNextChar && !m_mightBeLong) || currentString->endsWith(QString("\\")) || currentString->endsWith(QString("\\z")))
  456. {
  457. return true;
  458. }
  459. else
  460. {
  461. return false;
  462. }
  463. }
  464. void StringLiteralParserState::SetSaveState(AZ::u16 state)
  465. {
  466. m_bracketLevel = state;
  467. if (m_bracketLevel >= 2)
  468. {
  469. m_bracketEnd = "]";
  470. for (int i = 0; i < m_bracketLevel - 2; ++i)
  471. {
  472. m_bracketEnd.append('=');
  473. }
  474. m_bracketEnd.append(']');
  475. }
  476. m_endNextChar = false;
  477. m_mightBeLong = false;
  478. }
  479. LUASyntaxHighlighter::LUASyntaxHighlighter(QWidget* parent)
  480. : QSyntaxHighlighter(parent)
  481. , m_machine(azcreate(StateMachine, ()))
  482. {
  483. AddBlockKeywords();
  484. }
  485. LUASyntaxHighlighter::LUASyntaxHighlighter(QTextDocument* parent)
  486. : QSyntaxHighlighter(parent)
  487. , m_machine(azcreate(StateMachine, ()))
  488. {
  489. AddBlockKeywords();
  490. }
  491. void LUASyntaxHighlighter::AddBlockKeywords()
  492. {
  493. //these don't catch tables. that is handled in the null machine state.
  494. m_LUAStartBlockKeywords.clear();
  495. AZStd::string startKeywords[] = {
  496. {"do"}, {"if"}, {"function"}, {"repeat"}
  497. };
  498. m_LUAStartBlockKeywords.insert(std::begin(startKeywords), std::end(startKeywords));
  499. m_LUAEndBlockKeywords.clear();
  500. AZStd::string endKeywords[] = {
  501. {"end"}, {"until"}
  502. };
  503. m_LUAEndBlockKeywords.insert(std::begin(endKeywords), std::end(endKeywords));
  504. }
  505. LUASyntaxHighlighter::~LUASyntaxHighlighter()
  506. {
  507. azdestroy(m_machine);
  508. }
  509. void LUASyntaxHighlighter::highlightBlock(const QString& text)
  510. {
  511. m_machine->SetJoiningNames(true);
  512. m_machine->SetOnIncFoldLevel([](int) {});
  513. m_machine->SetOnDecFoldLevel([](int) {});
  514. auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC("LUA Editor Text Settings", 0xb6e15565), AZ::UserSettings::CT_GLOBAL);
  515. const HighlightedWords::LUAKeywordsType* keywords = nullptr;
  516. HighlightedWords::Bus::BroadcastResult(keywords, &HighlightedWords::Bus::Events::GetLUAKeywords);
  517. const HighlightedWords::LUAKeywordsType* libraryFuncs = nullptr;
  518. HighlightedWords::Bus::BroadcastResult(libraryFuncs, &HighlightedWords::Bus::Events::GetLUALibraryFunctions);
  519. auto cBlock = currentBlock();
  520. QTBlockState currentState;
  521. currentState.m_qtBlockState = currentBlockState();
  522. QTextCharFormat textFormat;
  523. textFormat.setFont(m_font);
  524. textFormat.setForeground(colors->GetTextColor());
  525. setFormat(0, cBlock.length(), textFormat);
  526. QTextCharFormat spaceFormat = QTextCharFormat();
  527. spaceFormat.setForeground(colors->GetTextWhitespaceColor());
  528. QRegExp tabsAndSpaces("( |\t)+");
  529. int index = tabsAndSpaces.indexIn(text);
  530. while (index >= 0)
  531. {
  532. int length = tabsAndSpaces.matchedLength();
  533. setFormat(index, length, spaceFormat);
  534. index = tabsAndSpaces.indexIn(text, index + length);
  535. }
  536. //first take care of bracket highlighting. let later overwrite to handle case of brackets inside of a comment,ect
  537. if (m_openBracketPos >= 0 && m_closeBracketPos >= 0)
  538. {
  539. if (cBlock.contains(m_openBracketPos))
  540. {
  541. setFormat(m_openBracketPos - cBlock.position(), 1, colors->GetBracketColor());
  542. }
  543. if (cBlock.contains(m_closeBracketPos))
  544. {
  545. setFormat(m_closeBracketPos - cBlock.position(), 1, colors->GetBracketColor());
  546. }
  547. }
  548. else if (m_openBracketPos >= 0)
  549. {
  550. if (cBlock.contains(m_openBracketPos))
  551. {
  552. setFormat(m_openBracketPos - cBlock.position(), 1, colors->GetUnmatchedBracketColor());
  553. }
  554. if (cBlock.contains(m_closeBracketPos))
  555. {
  556. setFormat(m_closeBracketPos - cBlock.position(), 1, colors->GetUnmatchedBracketColor());
  557. }
  558. }
  559. QTBlockState prevState;
  560. prevState.m_qtBlockState = previousBlockState();
  561. m_machine->SetSaveState(prevState);
  562. auto startingState = m_machine->GetCurrentParserState();
  563. m_machine->CaptureToken = [&](ParserStates state, int position, int length)
  564. {
  565. if (state == ParserStates::Name)
  566. {
  567. AZStd::string dhText(text.mid(position, length).toUtf8().constData());
  568. if (keywords && keywords->find(dhText) != keywords->end())
  569. {
  570. textFormat.setForeground(colors->GetKeywordColor());
  571. setFormat(position, length, textFormat);
  572. }
  573. else if (libraryFuncs && libraryFuncs->find(dhText) != libraryFuncs->end())
  574. {
  575. textFormat.setForeground(colors->GetLibraryColor());
  576. setFormat(position, length, textFormat);
  577. }
  578. else
  579. {
  580. textFormat.setForeground(colors->GetTextColor());
  581. setFormat(position, length, textFormat);
  582. }
  583. if (m_LUAStartBlockKeywords.find(dhText) != m_LUAStartBlockKeywords.end())
  584. {
  585. m_machine->IncFoldLevel();
  586. }
  587. if (m_LUAEndBlockKeywords.find(dhText) != m_LUAEndBlockKeywords.end())
  588. {
  589. m_machine->DecFoldLevel();
  590. }
  591. }
  592. if (state == ParserStates::ShortComment || state == ParserStates::LongComment)
  593. {
  594. textFormat.setForeground(colors->GetCommentColor());
  595. setFormat(position, length, textFormat);
  596. }
  597. if (state == ParserStates::Number || state == ParserStates::NumberHex)
  598. {
  599. textFormat.setForeground(colors->GetNumberColor());
  600. setFormat(position, length, textFormat);
  601. }
  602. if (state == ParserStates::StringLiteral)
  603. {
  604. textFormat.setForeground(colors->GetStringLiteralColor());
  605. setFormat(position, length, textFormat);
  606. }
  607. };
  608. m_machine->Parse(text);
  609. auto endingState = m_machine->GetCurrentParserState();
  610. if (startingState != ParserStates::LongComment && endingState == ParserStates::LongComment)
  611. {
  612. m_machine->IncFoldLevel();
  613. }
  614. if (startingState != ParserStates::StringLiteral && endingState == ParserStates::StringLiteral) //should only be true if a long string literal
  615. {
  616. m_machine->IncFoldLevel();
  617. }
  618. if (startingState == ParserStates::LongComment && endingState != ParserStates::LongComment)
  619. {
  620. m_machine->DecFoldLevel();
  621. }
  622. if (startingState == ParserStates::StringLiteral && endingState != ParserStates::StringLiteral) //should only be true if a long string literal
  623. {
  624. m_machine->DecFoldLevel();
  625. }
  626. QTBlockState newState = m_machine->GetSaveState();
  627. newState.m_blockState.m_folded = currentState.m_blockState.m_uninitialized ? 0 : currentState.m_blockState.m_folded;
  628. setCurrentBlockState(newState.m_qtBlockState);
  629. }
  630. void LUASyntaxHighlighter::SetBracketHighlighting(int openBracketPos, int closeBracketPos)
  631. {
  632. auto oldOpenBracketPos = m_openBracketPos;
  633. auto oldcloseBracketPos = m_closeBracketPos;
  634. m_openBracketPos = openBracketPos;
  635. m_closeBracketPos = closeBracketPos;
  636. auto openBlock = document()->findBlock(m_openBracketPos);
  637. auto closeBlock = document()->findBlock(m_closeBracketPos);
  638. if (openBlock.isValid())
  639. {
  640. rehighlightBlock(openBlock);
  641. }
  642. if (closeBlock.isValid())
  643. {
  644. rehighlightBlock(closeBlock);
  645. }
  646. if (oldOpenBracketPos >= 0)
  647. {
  648. openBlock = document()->findBlock(oldOpenBracketPos);
  649. if (openBlock.isValid())
  650. {
  651. rehighlightBlock(openBlock);
  652. }
  653. }
  654. if (oldcloseBracketPos >= 0)
  655. {
  656. closeBlock = document()->findBlock(oldcloseBracketPos);
  657. if (closeBlock.isValid())
  658. {
  659. rehighlightBlock(closeBlock);
  660. }
  661. }
  662. }
  663. //This code is also getting the list of lua names that are currently in scope
  664. QList<QTextEdit::ExtraSelection> LUASyntaxHighlighter::HighlightMatchingNames(const QTextCursor& cursor, const QString&) const
  665. {
  666. m_machine->SetJoiningNames(false);
  667. m_machine->SetOnIncFoldLevel([](int) {});
  668. m_machine->SetOnDecFoldLevel([](int) {});
  669. const HighlightedWords::LUAKeywordsType* keywords = nullptr;
  670. HighlightedWords::Bus::BroadcastResult(keywords, &HighlightedWords::Bus::Events::GetLUAKeywords);
  671. const HighlightedWords::LUAKeywordsType* libraryFuncs = nullptr;
  672. HighlightedWords::Bus::BroadcastResult(libraryFuncs, &HighlightedWords::Bus::Events::GetLUALibraryFunctions);
  673. auto syntaxSettings = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC("LUA Editor Text Settings", 0xb6e15565), AZ::UserSettings::CT_GLOBAL);
  674. auto font = syntaxSettings->GetFont();
  675. QList<QTextEdit::ExtraSelection> list;
  676. QTextEdit::ExtraSelection selection;
  677. selection.cursor = cursor;
  678. QTextCharFormat textFormat;
  679. textFormat.setFont(font);
  680. textFormat.setBackground(syntaxSettings->GetCurrentIdentifierColor());
  681. selection.format = textFormat;
  682. QTBlockState mState;
  683. mState.m_qtBlockState = -1;
  684. m_machine->SetSaveState(mState);
  685. QString searchString;
  686. ParserStates matchState = ParserStates::Null;
  687. int currentScopeBlock = -1;
  688. QStringList luaNames;
  689. for (auto block = document()->begin(); block != document()->end(); block = block.next())
  690. {
  691. auto text = block.text();
  692. auto cursorPos = cursor.position() - block.position();
  693. int delayedFold = 0;
  694. m_machine->CaptureToken = [&](ParserStates state, int position, int length)
  695. {
  696. if (cursorPos >= position && cursorPos <= position + length)
  697. {
  698. if (state == ParserStates::Name)
  699. {
  700. QString match = text.mid(position, length);
  701. AZStd::string dhText(match.toUtf8().constData());
  702. if (!(keywords && keywords->find(dhText) != keywords->end()) &&
  703. !(libraryFuncs && libraryFuncs->find(dhText) != libraryFuncs->end()))
  704. {
  705. searchString = AZStd::move(match);
  706. matchState = state;
  707. currentScopeBlock = block.blockNumber();
  708. }
  709. }
  710. }
  711. if (state == ParserStates::Name)
  712. {
  713. AZStd::string dhText(text.mid(position, length).toUtf8().constData());
  714. if (m_LUAStartBlockKeywords.find(dhText) != m_LUAStartBlockKeywords.end())
  715. {
  716. m_machine->IncFoldLevel();
  717. }
  718. if (m_LUAEndBlockKeywords.find(dhText) != m_LUAEndBlockKeywords.end())
  719. {
  720. m_machine->DecFoldLevel();
  721. }
  722. if (length > 1)
  723. {
  724. luaNames.push_back(text.mid(position, length));
  725. }
  726. }
  727. };
  728. auto startingState = m_machine->GetCurrentParserState();
  729. m_machine->Parse(text);
  730. while (delayedFold > 0)
  731. {
  732. m_machine->IncFoldLevel();
  733. --delayedFold;
  734. }
  735. auto endingState = m_machine->GetCurrentParserState();
  736. if (startingState != ParserStates::LongComment && endingState == ParserStates::LongComment)
  737. {
  738. m_machine->IncFoldLevel();
  739. }
  740. if (startingState != ParserStates::StringLiteral && endingState == ParserStates::StringLiteral) //should only be true if a long string literal
  741. {
  742. m_machine->IncFoldLevel();
  743. }
  744. if (startingState == ParserStates::LongComment && endingState != ParserStates::LongComment)
  745. {
  746. m_machine->DecFoldLevel();
  747. }
  748. if (startingState == ParserStates::StringLiteral && endingState != ParserStates::StringLiteral) //should only be true if a long string literal
  749. {
  750. m_machine->DecFoldLevel();
  751. }
  752. }
  753. m_machine->SetOnIncFoldLevel([](int) {});
  754. m_machine->SetOnDecFoldLevel([](int) {});
  755. if (matchState != ParserStates::Null)
  756. {
  757. for (auto block = document()->begin(); block != document()->end(); block = block.next())
  758. {
  759. auto text = block.text();
  760. m_machine->CaptureToken = [&](ParserStates state, int position, int length)
  761. {
  762. QString token = text.mid(position, length);
  763. if (state == matchState)
  764. {
  765. if (token == searchString)
  766. {
  767. selection.cursor.setPosition(position + block.position());
  768. selection.cursor.setPosition(position + block.position() + length, QTextCursor::MoveMode::KeepAnchor);
  769. list.push_back(selection);
  770. }
  771. }
  772. };
  773. m_machine->Parse(text);
  774. }
  775. }
  776. if (currentScopeBlock != -1 && currentScopeBlock != m_currentScopeBlock)
  777. {
  778. LUANamesInScopeChanged(luaNames);
  779. m_currentScopeBlock = currentScopeBlock;
  780. }
  781. return list;
  782. }
  783. QString LUASyntaxHighlighter::GetLUAName(const QTextCursor& cursor)
  784. {
  785. auto block = document()->findBlock(cursor.position());
  786. if (!block.isValid())
  787. {
  788. return "";
  789. }
  790. auto prevBlock = block.previous();
  791. QTBlockState prevState;
  792. if (prevBlock.isValid())
  793. {
  794. prevState.m_qtBlockState = prevBlock.userState();
  795. }
  796. else
  797. {
  798. prevState.m_qtBlockState = -1;
  799. }
  800. m_machine->SetSaveState(prevState);
  801. m_machine->SetJoiningNames(true);
  802. m_machine->SetOnIncFoldLevel([](int) {});
  803. m_machine->SetOnDecFoldLevel([](int) {});
  804. auto cursorPos = cursor.position() - block.position();
  805. auto text = block.text();
  806. QString result;
  807. m_machine->CaptureToken = [&](ParserStates state, int position, int length)
  808. {
  809. if (cursorPos >= position && cursorPos <= position + length)
  810. {
  811. if (state == ParserStates::Name)
  812. {
  813. result = text.mid(position, length);
  814. }
  815. }
  816. };
  817. m_machine->Parse(block.text());
  818. return result;
  819. }
  820. }