LexRegistry.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. // Scintilla source code edit control
  2. /**
  3. * @file LexRegistry.cxx
  4. * @date July 26 2014
  5. * @brief Lexer for Windows registration files(.reg)
  6. * @author nkmathew
  7. *
  8. * The License.txt file describes the conditions under which this software may be
  9. * distributed.
  10. *
  11. */
  12. #include <cstdlib>
  13. #include <cassert>
  14. #include <cctype>
  15. #include <cstdio>
  16. #include <string>
  17. #include <vector>
  18. #include <map>
  19. #include "ILexer.h"
  20. #include "Scintilla.h"
  21. #include "SciLexer.h"
  22. #include "WordList.h"
  23. #include "LexAccessor.h"
  24. #include "StyleContext.h"
  25. #include "CharacterSet.h"
  26. #include "LexerModule.h"
  27. #include "OptionSet.h"
  28. #ifdef SCI_NAMESPACE
  29. using namespace Scintilla;
  30. #endif
  31. static const char *const RegistryWordListDesc[] = {
  32. 0
  33. };
  34. struct OptionsRegistry {
  35. bool foldCompact;
  36. bool fold;
  37. OptionsRegistry() {
  38. foldCompact = false;
  39. fold = false;
  40. }
  41. };
  42. struct OptionSetRegistry : public OptionSet<OptionsRegistry> {
  43. OptionSetRegistry() {
  44. DefineProperty("fold.compact", &OptionsRegistry::foldCompact);
  45. DefineProperty("fold", &OptionsRegistry::fold);
  46. DefineWordListSets(RegistryWordListDesc);
  47. }
  48. };
  49. class LexerRegistry : public ILexer {
  50. OptionsRegistry options;
  51. OptionSetRegistry optSetRegistry;
  52. static bool IsStringState(int state) {
  53. return (state == SCE_REG_VALUENAME || state == SCE_REG_STRING);
  54. }
  55. static bool IsKeyPathState(int state) {
  56. return (state == SCE_REG_ADDEDKEY || state == SCE_REG_DELETEDKEY);
  57. }
  58. static bool AtValueType(LexAccessor &styler, Sci_Position start) {
  59. Sci_Position i = 0;
  60. while (i < 10) {
  61. i++;
  62. char curr = styler.SafeGetCharAt(start+i, '\0');
  63. if (curr == ':') {
  64. return true;
  65. } else if (!curr) {
  66. return false;
  67. }
  68. }
  69. return false;
  70. }
  71. static bool IsNextNonWhitespace(LexAccessor &styler, Sci_Position start, char ch) {
  72. Sci_Position i = 0;
  73. while (i < 100) {
  74. i++;
  75. char curr = styler.SafeGetCharAt(start+i, '\0');
  76. char next = styler.SafeGetCharAt(start+i+1, '\0');
  77. bool atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
  78. if (curr == ch) {
  79. return true;
  80. } else if (!isspacechar(curr) || atEOL) {
  81. return false;
  82. }
  83. }
  84. return false;
  85. }
  86. // Looks for the equal sign at the end of the string
  87. static bool AtValueName(LexAccessor &styler, Sci_Position start) {
  88. bool atEOL = false;
  89. Sci_Position i = 0;
  90. bool escaped = false;
  91. while (!atEOL) {
  92. i++;
  93. char curr = styler.SafeGetCharAt(start+i, '\0');
  94. char next = styler.SafeGetCharAt(start+i+1, '\0');
  95. atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
  96. if (escaped) {
  97. escaped = false;
  98. continue;
  99. }
  100. escaped = curr == '\\';
  101. if (curr == '"') {
  102. return IsNextNonWhitespace(styler, start+i, '=');
  103. } else if (!curr) {
  104. return false;
  105. }
  106. }
  107. return false;
  108. }
  109. static bool AtKeyPathEnd(LexAccessor &styler, Sci_Position start) {
  110. bool atEOL = false;
  111. Sci_Position i = 0;
  112. while (!atEOL) {
  113. i++;
  114. char curr = styler.SafeGetCharAt(start+i, '\0');
  115. char next = styler.SafeGetCharAt(start+i+1, '\0');
  116. atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
  117. if (curr == ']' || !curr) {
  118. // There's still at least one or more square brackets ahead
  119. return false;
  120. }
  121. }
  122. return true;
  123. }
  124. static bool AtGUID(LexAccessor &styler, Sci_Position start) {
  125. int count = 8;
  126. int portion = 0;
  127. int offset = 1;
  128. char digit = '\0';
  129. while (portion < 5) {
  130. int i = 0;
  131. while (i < count) {
  132. digit = styler.SafeGetCharAt(start+offset);
  133. if (!(isxdigit(digit) || digit == '-')) {
  134. return false;
  135. }
  136. offset++;
  137. i++;
  138. }
  139. portion++;
  140. count = (portion == 4) ? 13 : 5;
  141. }
  142. digit = styler.SafeGetCharAt(start+offset);
  143. if (digit == '}') {
  144. return true;
  145. } else {
  146. return false;
  147. }
  148. }
  149. public:
  150. LexerRegistry() {}
  151. virtual ~LexerRegistry() {}
  152. virtual int SCI_METHOD Version() const {
  153. return lvOriginal;
  154. }
  155. virtual void SCI_METHOD Release() {
  156. delete this;
  157. }
  158. virtual const char *SCI_METHOD PropertyNames() {
  159. return optSetRegistry.PropertyNames();
  160. }
  161. virtual int SCI_METHOD PropertyType(const char *name) {
  162. return optSetRegistry.PropertyType(name);
  163. }
  164. virtual const char *SCI_METHOD DescribeProperty(const char *name) {
  165. return optSetRegistry.DescribeProperty(name);
  166. }
  167. virtual Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) {
  168. if (optSetRegistry.PropertySet(&options, key, val)) {
  169. return 0;
  170. }
  171. return -1;
  172. }
  173. virtual Sci_Position SCI_METHOD WordListSet(int, const char *) {
  174. return -1;
  175. }
  176. virtual void *SCI_METHOD PrivateCall(int, void *) {
  177. return 0;
  178. }
  179. static ILexer *LexerFactoryRegistry() {
  180. return new LexerRegistry;
  181. }
  182. virtual const char *SCI_METHOD DescribeWordListSets() {
  183. return optSetRegistry.DescribeWordListSets();
  184. }
  185. virtual void SCI_METHOD Lex(Sci_PositionU startPos,
  186. Sci_Position length,
  187. int initStyle,
  188. IDocument *pAccess);
  189. virtual void SCI_METHOD Fold(Sci_PositionU startPos,
  190. Sci_Position length,
  191. int initStyle,
  192. IDocument *pAccess);
  193. };
  194. void SCI_METHOD LexerRegistry::Lex(Sci_PositionU startPos,
  195. Sci_Position length,
  196. int initStyle,
  197. IDocument *pAccess) {
  198. int beforeGUID = SCE_REG_DEFAULT;
  199. int beforeEscape = SCE_REG_DEFAULT;
  200. CharacterSet setOperators = CharacterSet(CharacterSet::setNone, "-,.=:\\@()");
  201. LexAccessor styler(pAccess);
  202. StyleContext context(startPos, length, initStyle, styler);
  203. bool highlight = true;
  204. bool afterEqualSign = false;
  205. while (context.More()) {
  206. if (context.atLineStart) {
  207. Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
  208. bool continued = styler[currPos-3] == '\\';
  209. highlight = continued ? true : false;
  210. }
  211. switch (context.state) {
  212. case SCE_REG_COMMENT:
  213. if (context.atLineEnd) {
  214. context.SetState(SCE_REG_DEFAULT);
  215. }
  216. break;
  217. case SCE_REG_VALUENAME:
  218. case SCE_REG_STRING: {
  219. Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
  220. if (context.ch == '"') {
  221. context.ForwardSetState(SCE_REG_DEFAULT);
  222. } else if (context.ch == '\\') {
  223. beforeEscape = context.state;
  224. context.SetState(SCE_REG_ESCAPED);
  225. context.Forward();
  226. } else if (context.ch == '{') {
  227. if (AtGUID(styler, currPos)) {
  228. beforeGUID = context.state;
  229. context.SetState(SCE_REG_STRING_GUID);
  230. }
  231. }
  232. if (context.state == SCE_REG_STRING &&
  233. context.ch == '%' &&
  234. (isdigit(context.chNext) || context.chNext == '*')) {
  235. context.SetState(SCE_REG_PARAMETER);
  236. }
  237. }
  238. break;
  239. case SCE_REG_PARAMETER:
  240. context.ForwardSetState(SCE_REG_STRING);
  241. if (context.ch == '"') {
  242. context.ForwardSetState(SCE_REG_DEFAULT);
  243. }
  244. break;
  245. case SCE_REG_VALUETYPE:
  246. if (context.ch == ':') {
  247. context.SetState(SCE_REG_DEFAULT);
  248. afterEqualSign = false;
  249. }
  250. break;
  251. case SCE_REG_HEXDIGIT:
  252. case SCE_REG_OPERATOR:
  253. context.SetState(SCE_REG_DEFAULT);
  254. break;
  255. case SCE_REG_DELETEDKEY:
  256. case SCE_REG_ADDEDKEY: {
  257. Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
  258. if (context.ch == ']' && AtKeyPathEnd(styler, currPos)) {
  259. context.ForwardSetState(SCE_REG_DEFAULT);
  260. } else if (context.ch == '{') {
  261. if (AtGUID(styler, currPos)) {
  262. beforeGUID = context.state;
  263. context.SetState(SCE_REG_KEYPATH_GUID);
  264. }
  265. }
  266. }
  267. break;
  268. case SCE_REG_ESCAPED:
  269. if (context.ch == '"') {
  270. context.SetState(beforeEscape);
  271. context.ForwardSetState(SCE_REG_DEFAULT);
  272. } else if (context.ch == '\\') {
  273. context.Forward();
  274. } else {
  275. context.SetState(beforeEscape);
  276. beforeEscape = SCE_REG_DEFAULT;
  277. }
  278. break;
  279. case SCE_REG_STRING_GUID:
  280. case SCE_REG_KEYPATH_GUID: {
  281. if (context.ch == '}') {
  282. context.ForwardSetState(beforeGUID);
  283. beforeGUID = SCE_REG_DEFAULT;
  284. }
  285. Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
  286. if (context.ch == '"' && IsStringState(context.state)) {
  287. context.ForwardSetState(SCE_REG_DEFAULT);
  288. } else if (context.ch == ']' &&
  289. AtKeyPathEnd(styler, currPos) &&
  290. IsKeyPathState(context.state)) {
  291. context.ForwardSetState(SCE_REG_DEFAULT);
  292. } else if (context.ch == '\\' && IsStringState(context.state)) {
  293. beforeEscape = context.state;
  294. context.SetState(SCE_REG_ESCAPED);
  295. context.Forward();
  296. }
  297. }
  298. break;
  299. }
  300. // Determine if a new state should be entered.
  301. if (context.state == SCE_REG_DEFAULT) {
  302. Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
  303. if (context.ch == ';') {
  304. context.SetState(SCE_REG_COMMENT);
  305. } else if (context.ch == '"') {
  306. if (AtValueName(styler, currPos)) {
  307. context.SetState(SCE_REG_VALUENAME);
  308. } else {
  309. context.SetState(SCE_REG_STRING);
  310. }
  311. } else if (context.ch == '[') {
  312. if (IsNextNonWhitespace(styler, currPos, '-')) {
  313. context.SetState(SCE_REG_DELETEDKEY);
  314. } else {
  315. context.SetState(SCE_REG_ADDEDKEY);
  316. }
  317. } else if (context.ch == '=') {
  318. afterEqualSign = true;
  319. highlight = true;
  320. } else if (afterEqualSign) {
  321. bool wordStart = isalpha(context.ch) && !isalpha(context.chPrev);
  322. if (wordStart && AtValueType(styler, currPos)) {
  323. context.SetState(SCE_REG_VALUETYPE);
  324. }
  325. } else if (isxdigit(context.ch) && highlight) {
  326. context.SetState(SCE_REG_HEXDIGIT);
  327. }
  328. highlight = (context.ch == '@') ? true : highlight;
  329. if (setOperators.Contains(context.ch) && highlight) {
  330. context.SetState(SCE_REG_OPERATOR);
  331. }
  332. }
  333. context.Forward();
  334. }
  335. context.Complete();
  336. }
  337. // Folding similar to that of FoldPropsDoc in LexOthers
  338. void SCI_METHOD LexerRegistry::Fold(Sci_PositionU startPos,
  339. Sci_Position length,
  340. int,
  341. IDocument *pAccess) {
  342. if (!options.fold) {
  343. return;
  344. }
  345. LexAccessor styler(pAccess);
  346. Sci_Position currLine = styler.GetLine(startPos);
  347. int visibleChars = 0;
  348. Sci_PositionU endPos = startPos + length;
  349. bool atKeyPath = false;
  350. for (Sci_PositionU i = startPos; i < endPos; i++) {
  351. atKeyPath = IsKeyPathState(styler.StyleAt(i)) ? true : atKeyPath;
  352. char curr = styler.SafeGetCharAt(i);
  353. char next = styler.SafeGetCharAt(i+1);
  354. bool atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
  355. if (atEOL || i == (endPos-1)) {
  356. int level = SC_FOLDLEVELBASE;
  357. if (currLine > 0) {
  358. int prevLevel = styler.LevelAt(currLine-1);
  359. if (prevLevel & SC_FOLDLEVELHEADERFLAG) {
  360. level += 1;
  361. } else {
  362. level = prevLevel;
  363. }
  364. }
  365. if (!visibleChars && options.foldCompact) {
  366. level |= SC_FOLDLEVELWHITEFLAG;
  367. } else if (atKeyPath) {
  368. level = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
  369. }
  370. if (level != styler.LevelAt(currLine)) {
  371. styler.SetLevel(currLine, level);
  372. }
  373. currLine++;
  374. visibleChars = 0;
  375. atKeyPath = false;
  376. }
  377. if (!isspacechar(curr)) {
  378. visibleChars++;
  379. }
  380. }
  381. // Make the folding reach the last line in the file
  382. int level = SC_FOLDLEVELBASE;
  383. if (currLine > 0) {
  384. int prevLevel = styler.LevelAt(currLine-1);
  385. if (prevLevel & SC_FOLDLEVELHEADERFLAG) {
  386. level += 1;
  387. } else {
  388. level = prevLevel;
  389. }
  390. }
  391. styler.SetLevel(currLine, level);
  392. }
  393. LexerModule lmRegistry(SCLEX_REGISTRY,
  394. LexerRegistry::LexerFactoryRegistry,
  395. "registry",
  396. RegistryWordListDesc);