sh_pas.pp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. {
  2. "SHEdit" - Text editor with syntax highlighting
  3. Copyright (C) 1999-2000 by Sebastian Guenther ([email protected])
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. }
  10. // Syntax highlighting class for Pascal sources
  11. {$MODE objfpc}
  12. {$H+}
  13. {$IFDEF Debug}
  14. {$ASSERTIONS On}
  15. {$ENDIF}
  16. unit sh_pas;
  17. interface
  18. uses doc_text, shedit;
  19. type
  20. TSHPasEdit = class(TSHTextEdit)
  21. protected
  22. procedure DoHighlighting(var flags: Byte; source, dest: PChar); override;
  23. procedure KeyReturn; override;
  24. public
  25. // Syntax highlighter style indices
  26. shInvalid, shSymbol, shKeyword, shComment, shDirective, shNumbers,
  27. shCharacters, shStrings, shAssembler: Integer;
  28. end;
  29. implementation
  30. uses Strings;
  31. const
  32. LF_SH_Comment1 = LF_SH_Multiline1; { Normal braced Comments}
  33. LF_SH_Comment2 = LF_SH_Multiline2; { (* *) Comments}
  34. LF_SH_Asm = LF_SH_Multiline3;
  35. MaxKeywordLength = 15;
  36. MaxKeyword = 60;
  37. KeywordTable: array[0..MaxKeyword] of PChar =
  38. ('AND', 'ARRAY', 'ASM', 'ASSEMBLER',
  39. 'BEGIN', 'BREAK',
  40. 'CASE', 'CONST', 'CONSTRUCTOR', 'CLASS',
  41. 'DEFAULT', 'DESTRUCTOR', 'DIV', 'DO', 'DOWNTO',
  42. 'ELSE', 'END', 'EXCEPT', 'EXIT',
  43. 'FINALIZATION', 'FINALLY', 'FOR', 'FUNCTION',
  44. 'GOTO',
  45. 'IF', 'IMPLEMENTATION', 'IN', 'INHERITED', 'INITIALIZATION', 'INTERFACE',
  46. 'NIL', 'NOT',
  47. 'OBJECT', 'OF', 'ON', 'OR', 'OVERRIDE',
  48. 'PACKED', 'PRIVATE', 'PROCEDURE', 'PROGRAM', 'PROPERTY', 'PROTECTED',
  49. 'PUBLIC', 'PUBLISHED',
  50. 'RAISE', 'RECORD', 'REPEAT', 'RESOURCESTRING',
  51. 'SET',
  52. 'THEN', 'TRY', 'TYPE',
  53. 'UNIT', 'UNTIL', 'USES',
  54. 'VAR', 'VIRTUAL',
  55. 'WHILE', 'WITH',
  56. 'XOR');
  57. KeywordAsmIndex = 2;
  58. procedure TSHPasEdit.KeyReturn;
  59. var
  60. s: String;
  61. i, count: Integer;
  62. begin
  63. // Get # of spaces in front of previous line
  64. s := FDoc.LineText[CursorY - 1];
  65. i := 1; count := 0;
  66. while (i <= Length(s)) and (s[i] = ' ') do begin
  67. Inc(i);
  68. Inc(count);
  69. end;
  70. FDoc.LineText[CursorY] := Copy(s, 1, count) + FDoc.LineText[CursorY];
  71. Inc(FCursorX, count);
  72. AddUndoInfo(TUndoEdit.Create(count), True);
  73. ChangeInLine(CursorY);
  74. end;
  75. procedure TSHPasEdit.DoHighlighting(var flags: Byte; source, dest: PChar);
  76. var
  77. dp: Integer; // Destination position - current offset in dest
  78. LastSHPos: Integer; // Position of last highlighting character, or 0
  79. procedure AddSH(sh: Byte);
  80. begin
  81. ASSERT(sh > 0);
  82. if (LastSHPos > 0) and (dp = LastSHPos + 1) then Dec(dp, 2);
  83. dest[dp] := LF_Escape; Inc(dp);
  84. LastSHPos := dp;
  85. dest[dp] := Chr(sh); Inc(dp);
  86. end;
  87. procedure PutChar;
  88. begin
  89. dest[dp] := source[0]; Inc(dp); Inc(source);
  90. end;
  91. procedure ProcessComment1;
  92. begin
  93. while source[0] <> #0 do begin
  94. if source[0] = '}' then begin
  95. PutChar;
  96. flags := flags and not LF_SH_Comment1;
  97. AddSH(shDefault);
  98. break;
  99. end;
  100. PutChar;
  101. end;
  102. end;
  103. procedure ProcessComment2;
  104. begin
  105. while source[0] <> #0 do begin
  106. if (source[0] = '*') and (source[1] = ')') then begin
  107. PutChar; PutChar;
  108. flags := flags and not LF_SH_Comment2;
  109. AddSH(shDefault);
  110. break;
  111. end;
  112. PutChar;
  113. end;
  114. end;
  115. { Checks if we are at the beginning of a comment (or directive) and processes
  116. all types of comments and directives, or returns False }
  117. function CheckForComment: Boolean;
  118. begin
  119. Result := True;
  120. if source[0] = '{' then begin
  121. if source[1] = '$' then
  122. AddSH(shDirective)
  123. else
  124. AddSH(shComment);
  125. PutChar;
  126. flags := flags or LF_SH_Comment1;
  127. ProcessComment1;
  128. end else if (source[0] = '(') and (source[1] = '*') then begin
  129. AddSH(shComment);
  130. PutChar; PutChar;
  131. flags := flags or LF_SH_Comment2;
  132. ProcessComment2;
  133. end else if (source[0] = '/') and (source[1] = '/') then begin
  134. AddSH(shComment);
  135. repeat PutChar until source[0] = #0;
  136. AddSH(shDefault);
  137. end else
  138. Result := False;
  139. end;
  140. procedure ProcessAsm;
  141. var
  142. LastChar: Char;
  143. begin
  144. LastChar := ' ';
  145. while source[0] <> #0 do begin
  146. if (LastChar in [' ', #9, #10, #13]) and
  147. (UpCase(source[0]) = 'E') and (UpCase(source[1]) = 'N') and
  148. (UpCase(source[2]) = 'D') then begin
  149. AddSH(shKeyword);
  150. PutChar; PutChar; PutChar;
  151. flags := flags and not LF_SH_Asm;
  152. AddSH(shDefault);
  153. break;
  154. end else
  155. if CheckForComment then LastChar := ' '
  156. else begin
  157. LastChar := source[0];
  158. PutChar;
  159. end;
  160. end;
  161. end;
  162. procedure ProcessSymbol;
  163. begin
  164. AddSH(shSymbol);
  165. if (source[0] = ':') and (source[1] = '=') then
  166. PutChar;
  167. PutChar;
  168. AddSH(shDefault);
  169. end;
  170. function CheckForKeyword: Boolean;
  171. var
  172. keyword, ukeyword: array[0..MaxKeywordLength] of Char;
  173. i, j: Integer;
  174. begin
  175. i := 0;
  176. while (source[i] <> #0) and (i < MaxKeywordLength) and
  177. (source[i] in ['0'..'9', 'A'..'Z', 'a'..'z']) do begin
  178. keyword[i] := source[i];
  179. ukeyword[i] := UpCase(source[i]);
  180. Inc(i);
  181. end;
  182. keyword[i] := #0; ukeyword[i] := #0;
  183. Result := False;
  184. if i < MaxKeywordLength then
  185. for j := 0 to MaxKeyword do
  186. if StrIComp(KeywordTable[j], ukeyword) = 0 then begin
  187. Result := True; break;
  188. end;
  189. if not Result then exit;
  190. Inc(source, i);
  191. AddSH(shKeyword);
  192. StrCopy(dest + dp, keyword);
  193. Inc(dp, i);
  194. if j <> KeywordAsmIndex then
  195. AddSH(shDefault)
  196. else begin
  197. AddSH(shAssembler);
  198. flags := flags or LF_SH_Asm;
  199. ProcessAsm;
  200. end;
  201. end;
  202. var
  203. StringLength: Integer;
  204. begin
  205. dp := 0;
  206. LastSHPos := 0;
  207. if (flags and LF_SH_Comment1) <> 0 then begin
  208. AddSH(shComment);
  209. ProcessComment1;
  210. end;
  211. if (flags and LF_SH_Comment2) <> 0 then begin
  212. AddSH(shComment);
  213. ProcessComment2;
  214. end;
  215. if (flags and LF_SH_Asm) <> 0 then begin
  216. AddSH(shAssembler);
  217. ProcessAsm;
  218. end;
  219. while source[0] <> #0 do begin
  220. if CheckForComment then continue;
  221. case source[0] of
  222. ',', ';', ':', '.', '(', ')', '[', ']', '<', '>', '=',
  223. '*', '/', '+', '-', '^', '&', '@': ProcessSymbol;
  224. '#': begin
  225. AddSH(shCharacters);
  226. PutChar;
  227. if source[0] = '$' then PutChar;
  228. while (source[0] >= '0') and (source[0] <= '9') do PutChar;
  229. AddSH(shDefault);
  230. end;
  231. '$': begin
  232. AddSH(shNumbers);
  233. PutChar;
  234. while source[0] in ['0'..'9', 'A'..'F', 'a'..'f'] do PutChar;
  235. AddSH(shDefault);
  236. end;
  237. '0'..'9': begin
  238. AddSH(shNumbers);
  239. PutChar;
  240. while (source[0] >= '0') and (source[0] <= '9') do PutChar;
  241. AddSH(shDefault);
  242. end;
  243. '''': begin
  244. AddSH(shStrings);
  245. PutChar;
  246. StringLength := 0;
  247. while source[0] <> #0 do begin
  248. if source[0] = '''' then
  249. if source[1] = '''' then PutChar
  250. else begin
  251. PutChar; break;
  252. end;
  253. Inc(StringLength);
  254. PutChar;
  255. end;
  256. if StringLength = 1 then
  257. dest[LastSHPos] := Chr(shCharacters);
  258. if (source[0] = #0) and (dest[dp - 1] <> '''') then
  259. dest[LastSHPos] := Chr(shInvalid);
  260. AddSH(shDefault);
  261. end;
  262. '_', 'A'..'Z', 'a'..'z': begin
  263. if not CheckForKeyword then
  264. repeat
  265. PutChar
  266. until not (source[0] in ['0'..'9', '_', 'A'..'Z', 'a'..'z']);
  267. end;
  268. ' ': PutChar;
  269. else begin
  270. AddSH(shInvalid);
  271. PutChar; // = found an invalid char!
  272. AddSH(shDefault);
  273. end;
  274. end;
  275. end;
  276. dest[dp] := #0;
  277. end;
  278. end.