LexPowerShell.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Scintilla source code edit control
  2. /** @file LexPowerShell.cxx
  3. ** Lexer for PowerShell scripts.
  4. **/
  5. // Copyright 2008 by Tim Gerundt <tim@gerundt.de>
  6. // The License.txt file describes the conditions under which this software may be distributed.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <stdarg.h>
  11. #include <assert.h>
  12. #include <ctype.h>
  13. #include "ILexer.h"
  14. #include "Scintilla.h"
  15. #include "SciLexer.h"
  16. #include "WordList.h"
  17. #include "LexAccessor.h"
  18. #include "Accessor.h"
  19. #include "StyleContext.h"
  20. #include "CharacterSet.h"
  21. #include "LexerModule.h"
  22. #ifdef SCI_NAMESPACE
  23. using namespace Scintilla;
  24. #endif
  25. // Extended to accept accented characters
  26. static inline bool IsAWordChar(int ch) {
  27. return ch >= 0x80 || isalnum(ch) || ch == '-' || ch == '_';
  28. }
  29. static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
  30. WordList *keywordlists[], Accessor &styler) {
  31. WordList &keywords = *keywordlists[0];
  32. WordList &keywords2 = *keywordlists[1];
  33. WordList &keywords3 = *keywordlists[2];
  34. WordList &keywords4 = *keywordlists[3];
  35. WordList &keywords5 = *keywordlists[4];
  36. WordList &keywords6 = *keywordlists[5];
  37. styler.StartAt(startPos);
  38. StyleContext sc(startPos, length, initStyle, styler);
  39. for (; sc.More(); sc.Forward()) {
  40. if (sc.state == SCE_POWERSHELL_COMMENT) {
  41. if (sc.atLineEnd) {
  42. sc.SetState(SCE_POWERSHELL_DEFAULT);
  43. }
  44. } else if (sc.state == SCE_POWERSHELL_COMMENTSTREAM) {
  45. if(sc.atLineStart) {
  46. while(IsASpaceOrTab(sc.ch)) {
  47. sc.Forward();
  48. }
  49. if (sc.ch == '.' && IsAWordChar(sc.chNext)) {
  50. sc.SetState(SCE_POWERSHELL_COMMENTDOCKEYWORD);
  51. }
  52. }
  53. if (sc.ch == '>' && sc.chPrev == '#') {
  54. sc.ForwardSetState(SCE_POWERSHELL_DEFAULT);
  55. }
  56. } else if (sc.state == SCE_POWERSHELL_COMMENTDOCKEYWORD) {
  57. if(!IsAWordChar(sc.ch)) {
  58. char s[100];
  59. sc.GetCurrentLowered(s, sizeof(s));
  60. if (!keywords6.InList(s + 1)) {
  61. sc.ChangeState(SCE_POWERSHELL_COMMENTSTREAM);
  62. }
  63. sc.SetState(SCE_POWERSHELL_COMMENTSTREAM);
  64. }
  65. } else if (sc.state == SCE_POWERSHELL_STRING) {
  66. // This is a doubles quotes string
  67. if (sc.ch == '\"') {
  68. sc.ForwardSetState(SCE_POWERSHELL_DEFAULT);
  69. }
  70. } else if (sc.state == SCE_POWERSHELL_CHARACTER) {
  71. // This is a single quote string
  72. if (sc.ch == '\'') {
  73. sc.ForwardSetState(SCE_POWERSHELL_DEFAULT);
  74. }
  75. } else if (sc.state == SCE_POWERSHELL_HERE_STRING) {
  76. // This is a doubles quotes here-string
  77. if (sc.atLineStart && sc.ch == '\"' && sc.chNext == '@') {
  78. sc.Forward(2);
  79. sc.SetState(SCE_POWERSHELL_DEFAULT);
  80. }
  81. } else if (sc.state == SCE_POWERSHELL_HERE_CHARACTER) {
  82. // This is a single quote here-string
  83. if (sc.atLineStart && sc.ch == '\'' && sc.chNext == '@') {
  84. sc.Forward(2);
  85. sc.SetState(SCE_POWERSHELL_DEFAULT);
  86. }
  87. } else if (sc.state == SCE_POWERSHELL_NUMBER) {
  88. if (!IsADigit(sc.ch)) {
  89. sc.SetState(SCE_POWERSHELL_DEFAULT);
  90. }
  91. } else if (sc.state == SCE_POWERSHELL_VARIABLE) {
  92. if (!IsAWordChar(sc.ch)) {
  93. sc.SetState(SCE_POWERSHELL_DEFAULT);
  94. }
  95. } else if (sc.state == SCE_POWERSHELL_OPERATOR) {
  96. if (!isoperator(static_cast<char>(sc.ch))) {
  97. sc.SetState(SCE_POWERSHELL_DEFAULT);
  98. }
  99. } else if (sc.state == SCE_POWERSHELL_IDENTIFIER) {
  100. if (!IsAWordChar(sc.ch)) {
  101. char s[100];
  102. sc.GetCurrentLowered(s, sizeof(s));
  103. if (keywords.InList(s)) {
  104. sc.ChangeState(SCE_POWERSHELL_KEYWORD);
  105. } else if (keywords2.InList(s)) {
  106. sc.ChangeState(SCE_POWERSHELL_CMDLET);
  107. } else if (keywords3.InList(s)) {
  108. sc.ChangeState(SCE_POWERSHELL_ALIAS);
  109. } else if (keywords4.InList(s)) {
  110. sc.ChangeState(SCE_POWERSHELL_FUNCTION);
  111. } else if (keywords5.InList(s)) {
  112. sc.ChangeState(SCE_POWERSHELL_USER1);
  113. }
  114. sc.SetState(SCE_POWERSHELL_DEFAULT);
  115. }
  116. }
  117. // Determine if a new state should be entered.
  118. if (sc.state == SCE_POWERSHELL_DEFAULT) {
  119. if (sc.ch == '#') {
  120. sc.SetState(SCE_POWERSHELL_COMMENT);
  121. } else if (sc.ch == '<' && sc.chNext == '#') {
  122. sc.SetState(SCE_POWERSHELL_COMMENTSTREAM);
  123. } else if (sc.ch == '\"') {
  124. sc.SetState(SCE_POWERSHELL_STRING);
  125. } else if (sc.ch == '\'') {
  126. sc.SetState(SCE_POWERSHELL_CHARACTER);
  127. } else if (sc.ch == '@' && sc.chNext == '\"') {
  128. sc.SetState(SCE_POWERSHELL_HERE_STRING);
  129. } else if (sc.ch == '@' && sc.chNext == '\'') {
  130. sc.SetState(SCE_POWERSHELL_HERE_CHARACTER);
  131. } else if (sc.ch == '$') {
  132. sc.SetState(SCE_POWERSHELL_VARIABLE);
  133. } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
  134. sc.SetState(SCE_POWERSHELL_NUMBER);
  135. } else if (isoperator(static_cast<char>(sc.ch))) {
  136. sc.SetState(SCE_POWERSHELL_OPERATOR);
  137. } else if (IsAWordChar(sc.ch)) {
  138. sc.SetState(SCE_POWERSHELL_IDENTIFIER);
  139. } else if (sc.ch == '`') {
  140. sc.Forward(); // skip next escaped character
  141. }
  142. }
  143. }
  144. sc.Complete();
  145. }
  146. // Store both the current line's fold level and the next lines in the
  147. // level store to make it easy to pick up with each increment
  148. // and to make it possible to fiddle the current level for "} else {".
  149. static void FoldPowerShellDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
  150. WordList *[], Accessor &styler) {
  151. bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
  152. bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
  153. bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
  154. Sci_PositionU endPos = startPos + length;
  155. int visibleChars = 0;
  156. Sci_Position lineCurrent = styler.GetLine(startPos);
  157. int levelCurrent = SC_FOLDLEVELBASE;
  158. if (lineCurrent > 0)
  159. levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
  160. int levelMinCurrent = levelCurrent;
  161. int levelNext = levelCurrent;
  162. char chNext = styler[startPos];
  163. int styleNext = styler.StyleAt(startPos);
  164. int style = initStyle;
  165. for (Sci_PositionU i = startPos; i < endPos; i++) {
  166. char ch = chNext;
  167. chNext = styler.SafeGetCharAt(i + 1);
  168. int stylePrev = style;
  169. style = styleNext;
  170. styleNext = styler.StyleAt(i + 1);
  171. bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
  172. if (style == SCE_POWERSHELL_OPERATOR) {
  173. if (ch == '{') {
  174. // Measure the minimum before a '{' to allow
  175. // folding on "} else {"
  176. if (levelMinCurrent > levelNext) {
  177. levelMinCurrent = levelNext;
  178. }
  179. levelNext++;
  180. } else if (ch == '}') {
  181. levelNext--;
  182. }
  183. } else if (foldComment && style == SCE_POWERSHELL_COMMENTSTREAM) {
  184. if (stylePrev != SCE_POWERSHELL_COMMENTSTREAM && stylePrev != SCE_POWERSHELL_COMMENTDOCKEYWORD) {
  185. levelNext++;
  186. } else if (styleNext != SCE_POWERSHELL_COMMENTSTREAM && styleNext != SCE_POWERSHELL_COMMENTDOCKEYWORD) {
  187. levelNext--;
  188. }
  189. } else if (foldComment && style == SCE_POWERSHELL_COMMENT) {
  190. if (ch == '#') {
  191. Sci_PositionU j = i + 1;
  192. while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
  193. j++;
  194. }
  195. if (styler.Match(j, "region")) {
  196. levelNext++;
  197. } else if (styler.Match(j, "endregion")) {
  198. levelNext--;
  199. }
  200. }
  201. }
  202. if (!IsASpace(ch))
  203. visibleChars++;
  204. if (atEOL || (i == endPos-1)) {
  205. int levelUse = levelCurrent;
  206. if (foldAtElse) {
  207. levelUse = levelMinCurrent;
  208. }
  209. int lev = levelUse | levelNext << 16;
  210. if (visibleChars == 0 && foldCompact)
  211. lev |= SC_FOLDLEVELWHITEFLAG;
  212. if (levelUse < levelNext)
  213. lev |= SC_FOLDLEVELHEADERFLAG;
  214. if (lev != styler.LevelAt(lineCurrent)) {
  215. styler.SetLevel(lineCurrent, lev);
  216. }
  217. lineCurrent++;
  218. levelCurrent = levelNext;
  219. levelMinCurrent = levelCurrent;
  220. visibleChars = 0;
  221. }
  222. }
  223. }
  224. static const char * const powershellWordLists[] = {
  225. "Commands",
  226. "Cmdlets",
  227. "Aliases",
  228. "Functions",
  229. "User1",
  230. "DocComment",
  231. 0
  232. };
  233. LexerModule lmPowerShell(SCLEX_POWERSHELL, ColourisePowerShellDoc, "powershell", FoldPowerShellDoc, powershellWordLists);