opascal.el 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787
  1. ;;; opascal.el --- major mode for editing Object Pascal source in Emacs -*- lexical-binding: t -*-
  2. ;; Copyright (C) 1998-1999, 2001-2015 Free Software Foundation, Inc.
  3. ;; Authors: Ray Blaak <blaak@infomatch.com>,
  4. ;; Simon South <ssouth@member.fsf.org>
  5. ;; Maintainer: Simon South <ssouth@member.fsf.org>
  6. ;; Keywords: languages
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; To enter OPascal mode when you find an Object Pascal source file, one must
  20. ;; override the auto-mode-alist to associate OPascal with .pas (and .dpr and
  21. ;; .dpk) files. Emacs, by default, will otherwise enter Pascal mode. E.g.
  22. ;;
  23. ;; (autoload 'opascal-mode "opascal")
  24. ;; (add-to-list 'auto-mode-alist
  25. ;; '("\\.\\(pas\\|dpr\\|dpk\\)\\'" . opascal-mode))
  26. ;; When you have entered OPascal mode, you may get more info by pressing
  27. ;; C-h m.
  28. ;; This OPascal mode implementation is fairly tolerant of syntax errors,
  29. ;; relying as much as possible on the indentation of the previous statement.
  30. ;; This also makes it faster and simpler, since there is less searching for
  31. ;; properly constructed beginnings.
  32. ;;; Code:
  33. (defgroup opascal nil
  34. "Major mode for editing OPascal source in Emacs."
  35. :version "24.4"
  36. :group 'languages)
  37. (defconst opascal-debug nil
  38. "True if in debug mode.")
  39. (define-obsolete-variable-alias
  40. 'delphi-search-path 'opascal-search-path "24.4")
  41. (defcustom opascal-search-path "."
  42. "Directories to search when finding external units.
  43. It is a list of directory strings. If only a single directory,
  44. it can be a single string instead of a list. If a directory
  45. ends in \"...\" then that directory is recursively searched."
  46. :type 'string)
  47. (define-obsolete-variable-alias
  48. 'delphi-indent-level 'opascal-indent-level "24.4")
  49. (defcustom opascal-indent-level 3
  50. "Indentation of OPascal statements with respect to containing block.
  51. E.g.
  52. begin
  53. // This is an indent of 3.
  54. end;"
  55. :type 'integer)
  56. (define-obsolete-variable-alias
  57. 'delphi-compound-block-indent 'opascal-compound-block-indent "24.4")
  58. (defcustom opascal-compound-block-indent 0
  59. "Extra indentation for blocks in compound statements. E.g.
  60. // block indent = 0 vs // block indent = 2
  61. if b then if b then
  62. begin begin
  63. end else begin end
  64. end; else
  65. begin
  66. end;"
  67. :type 'integer)
  68. (define-obsolete-variable-alias
  69. 'delphi-case-label-indent 'opascal-case-label-indent "24.4")
  70. (defcustom opascal-case-label-indent opascal-indent-level
  71. "Extra indentation for case statement labels. E.g.
  72. // case indent = 0 vs // case indent = 3
  73. case value of case value of
  74. v1: process_v1; v1: process_v1;
  75. v2: process_v2; v2: process_v2;
  76. else else
  77. process_else; process_else;
  78. end; end;"
  79. :type 'integer)
  80. (define-obsolete-variable-alias 'delphi-verbose 'opascal-verbose "24.4")
  81. (defcustom opascal-verbose t ; nil
  82. "If true then OPascal token processing progress is reported to the user."
  83. :type 'boolean)
  84. (define-obsolete-variable-alias
  85. 'delphi-tab-always-indents 'opascal-tab-always-indents "24.4")
  86. (defcustom opascal-tab-always-indents tab-always-indent
  87. "Non-nil means `opascal-tab' should always reindent the current line.
  88. That is, regardless of where in the line point is at the time."
  89. :type 'boolean)
  90. (make-obsolete-variable 'opascal-tab-always-indents
  91. "use `indent-for-tab-command' and `tab-always-indent'."
  92. "24.4")
  93. (defconst opascal-directives
  94. '(absolute abstract assembler automated cdecl default dispid dynamic
  95. export external far forward index inline message name near nodefault
  96. overload override pascal private protected public published read readonly
  97. register reintroduce resident resourcestring safecall stdcall stored
  98. virtual write writeonly)
  99. "OPascal4 directives.")
  100. (defconst opascal-keywords
  101. (append
  102. '(;; Keywords.
  103. and array as asm at begin case class const constructor contains
  104. destructor dispinterface div do downto else end except exports
  105. file finalization finally for function goto if implementation implements
  106. in inherited initialization interface is label library mod nil not
  107. of object on or out package packed procedure program property
  108. raise record repeat requires result self set shl shr then threadvar
  109. to try type unit uses until var while with xor
  110. ;; These routines should be keywords, if Borland had the balls.
  111. break exit)
  112. ;; We want directives to look like keywords.
  113. opascal-directives)
  114. "OPascal4 keywords.")
  115. (defconst opascal-previous-terminators `(semicolon comma)
  116. "Expression/statement terminators that denote a previous expression.")
  117. (defconst opascal-comments
  118. '(comment-single-line comment-multi-line-1 comment-multi-line-2)
  119. "Tokens that represent comments.")
  120. (defconst opascal-strings
  121. '(string double-quoted-string)
  122. "Tokens that represent string literals.")
  123. (defconst opascal-whitespace `(space newline ,@opascal-comments)
  124. "Tokens that are considered whitespace.")
  125. (defconst opascal-routine-statements
  126. '(procedure function constructor destructor property)
  127. "Marks the start of a routine, or routine-ish looking expression.")
  128. (defconst opascal-body-expr-statements '(if while for on)
  129. "Statements that have either a single statement or a block as a body and also
  130. are followed by an expression.")
  131. (defconst opascal-expr-statements `(case ,@opascal-body-expr-statements)
  132. "Expression statements contain expressions after their keyword.")
  133. (defconst opascal-body-statements `(else ,@opascal-body-expr-statements)
  134. "Statements that have either a single statement or a block as a body.")
  135. (defconst opascal-expr-delimiters '(then do of)
  136. "Expression delimiter tokens.")
  137. (defconst opascal-binary-ops
  138. '(plus minus equals not-equals times divides div mod and or xor)
  139. "OPascal binary operations.")
  140. (defconst opascal-visibilities '(public private protected published automated)
  141. "Class visibilities.")
  142. (defconst opascal-block-statements
  143. '(begin try case repeat initialization finalization asm)
  144. "Statements that contain multiple substatements.")
  145. (defconst opascal-mid-block-statements
  146. `(except finally ,@opascal-visibilities)
  147. "Statements that mark mid sections of the enclosing block.")
  148. (defconst opascal-end-block-statements `(end until)
  149. "Statements that end block sections.")
  150. (defconst opascal-match-block-statements
  151. `(,@opascal-end-block-statements ,@opascal-mid-block-statements)
  152. "Statements that match the indentation of the parent block.")
  153. (defconst opascal-decl-sections '(type const var label resourcestring)
  154. "Denotes the start of a declaration section.")
  155. (defconst opascal-interface-types '(dispinterface interface)
  156. "Interface types.")
  157. (defconst opascal-class-types '(class object)
  158. "Class types.")
  159. (defconst opascal-composite-types
  160. `(,@opascal-class-types ,@opascal-interface-types record)
  161. "Types that contain declarations within them.")
  162. (defconst opascal-unit-sections
  163. '(interface implementation program library package)
  164. "Unit sections within which the indent is 0.")
  165. (defconst opascal-use-clauses `(uses requires exports contains)
  166. "Statements that refer to foreign symbols.")
  167. (defconst opascal-unit-statements
  168. `(,@opascal-use-clauses ,@opascal-unit-sections initialization finalization)
  169. "Statements indented at level 0.")
  170. (defconst opascal-decl-delimiters
  171. `(,@opascal-decl-sections ,@opascal-unit-statements
  172. ,@opascal-routine-statements)
  173. "Statements that a declaration statement should align with.")
  174. (defconst opascal-decl-matchers
  175. `(begin ,@opascal-decl-sections)
  176. "Statements that should match to declaration statement indentation.")
  177. (defconst opascal-enclosing-statements
  178. `(,@opascal-block-statements ,@opascal-mid-block-statements
  179. ,@opascal-decl-sections ,@opascal-use-clauses ,@opascal-routine-statements)
  180. "Delimits an enclosing statement.")
  181. (defconst opascal-previous-statements
  182. `(,@opascal-unit-statements ,@opascal-routine-statements)
  183. "Delimits a previous statement.")
  184. (defconst opascal-previous-enclosing-statements
  185. `(,@opascal-block-statements ,@opascal-mid-block-statements
  186. ,@opascal-decl-sections)
  187. "Delimits a previous enclosing statement.")
  188. (defconst opascal-begin-enclosing-tokens
  189. `(,@opascal-block-statements ,@opascal-mid-block-statements)
  190. "Tokens that a begin token indents from.")
  191. (defconst opascal-begin-previous-tokens
  192. `(,@opascal-decl-sections ,@opascal-routine-statements)
  193. "Tokens that a begin token aligns with, but only if not part of a nested
  194. routine.")
  195. (defconst opascal-space-chars "\000-\011\013- ") ; all except \n
  196. (defconst opascal-non-space-chars (concat "^" opascal-space-chars))
  197. (defconst opascal-spaces-re (concat "[" opascal-space-chars "]*"))
  198. (defconst opascal-leading-spaces-re (concat "^" opascal-spaces-re))
  199. (defconst opascal-word-chars "a-zA-Z0-9_")
  200. (defvar opascal-mode-syntax-table
  201. (let ((st (make-syntax-table)))
  202. ;; Strings.
  203. (modify-syntax-entry ?\" "\"" st)
  204. (modify-syntax-entry ?\' "\"" st)
  205. ;; Comments.
  206. (modify-syntax-entry ?\{ "<" st)
  207. (modify-syntax-entry ?\} ">" st)
  208. (modify-syntax-entry ?\( "()1" st)
  209. (modify-syntax-entry ?\) ")(4" st)
  210. (modify-syntax-entry ?* ". 23b" st)
  211. (modify-syntax-entry ?/ ". 12c" st)
  212. (modify-syntax-entry ?\n "> c" st)
  213. st))
  214. (defmacro opascal-save-excursion (&rest forms)
  215. ;; Executes the forms such that any movements have no effect, including
  216. ;; searches.
  217. `(save-excursion
  218. (save-match-data
  219. (let ((inhibit-point-motion-hooks t)
  220. (deactivate-mark nil))
  221. (progn ,@forms)))))
  222. (defsubst opascal-is (element in-set)
  223. ;; If the element is in the set, the element cdr is returned, otherwise nil.
  224. (memq element in-set))
  225. (defun opascal-string-of (start end)
  226. ;; Returns the buffer string from start to end.
  227. (buffer-substring-no-properties start end))
  228. (defun opascal-looking-at-string (p s)
  229. ;; True if point p marks the start of string s. s is not a regular
  230. ;; expression.
  231. (let ((limit (+ p (length s))))
  232. (and (<= limit (point-max))
  233. (string= s (opascal-string-of p limit)))))
  234. (defun opascal-token-of (kind start end)
  235. ;; Constructs a token from a kind symbol and its start/end points.
  236. `[,kind ,start ,end])
  237. (defsubst opascal-token-kind (token)
  238. ;; Returns the kind symbol of the token.
  239. (if token (aref token 0) nil))
  240. (defun opascal-set-token-kind (token to-kind)
  241. ;; Sets the kind symbol of the token.
  242. (if token (aset token 0 to-kind)))
  243. (defsubst opascal-token-start (token)
  244. ;; Returns the start point of the token.
  245. (if token (aref token 1) (point-min)))
  246. (defsubst opascal-token-end (token)
  247. ;; Returns the end point of the token.
  248. (if token (aref token 2) (point-min)))
  249. (defun opascal-set-token-start (token start)
  250. ;; Sets the start point of the token.
  251. (if token (aset token 1 start)))
  252. (defun opascal-set-token-end (token end)
  253. ;; Sets the end point of the token.
  254. (if token (aset token 2 end)))
  255. (defun opascal-token-string (token)
  256. ;; Returns the string image of the token.
  257. (if token
  258. (opascal-string-of (opascal-token-start token) (opascal-token-end token))
  259. ""))
  260. (defun opascal-in-token (p token)
  261. ;; Returns true if the point p is within the token's start/end points.
  262. (and (<= (opascal-token-start token) p) (< p (opascal-token-end token))))
  263. (defun opascal-column-of (p)
  264. ;; Returns the column of the point p.
  265. (save-excursion (goto-char p) (current-column)))
  266. (defvar opascal-progress-last-reported-point nil
  267. "The last point at which progress was reported.")
  268. (defconst opascal-parsing-progress-step 16384
  269. "Number of chars to process before the next parsing progress report.")
  270. (defconst opascal-scanning-progress-step 2048
  271. "Number of chars to process before the next scanning progress report.")
  272. (defun opascal-progress-start ()
  273. ;; Initializes progress reporting.
  274. (setq opascal-progress-last-reported-point nil))
  275. (defun opascal-progress-done (&rest msgs)
  276. ;; Finalizes progress reporting.
  277. (setq opascal-progress-last-reported-point nil)
  278. (when opascal-verbose
  279. (if (null msgs)
  280. (message "")
  281. (apply #'message msgs))))
  282. (defun opascal-step-progress (p desc step-size)
  283. ;; If enough distance has elapsed since the last reported point, then report
  284. ;; the current progress to the user.
  285. (cond ((null opascal-progress-last-reported-point)
  286. ;; This is the first progress step.
  287. (setq opascal-progress-last-reported-point p))
  288. ((and opascal-verbose
  289. (>= (abs (- p opascal-progress-last-reported-point)) step-size))
  290. ;; Report the percentage complete.
  291. (setq opascal-progress-last-reported-point p)
  292. (message "%s %s ... %d%%"
  293. desc (buffer-name) (floor (* 100.0 p) (point-max))))))
  294. (defun opascal-next-line-start (&optional from-point)
  295. ;; Returns the first point of the next line.
  296. (let ((curr-point (point))
  297. (next nil))
  298. (if from-point (goto-char from-point))
  299. (end-of-line)
  300. (setq next (min (1+ (point)) (point-max)))
  301. (goto-char curr-point)
  302. next))
  303. (defconst opascal--literal-start-re (regexp-opt '("//" "{" "(*" "'" "\"")))
  304. (defun opascal-literal-kind (p)
  305. ;; Returns the literal kind the point p is in (or nil if not in a literal).
  306. (when (and (<= (point-min) p) (<= p (point-max)))
  307. (save-excursion
  308. (let ((ppss (syntax-ppss p)))
  309. ;; We want to return non-nil when right in front
  310. ;; of a comment/string.
  311. (if (null (nth 8 ppss))
  312. (when (looking-at opascal--literal-start-re)
  313. (pcase (char-after)
  314. (`?/ 'comment-single-line)
  315. (`?\{ 'comment-multi-line-1)
  316. (`?\( 'comment-multi-line-2)
  317. (`?\' 'string)
  318. (`?\" 'double-quoted-string)))
  319. (if (nth 3 ppss) ;String.
  320. (if (eq (nth 3 ppss) ?\")
  321. 'double-quoted-string 'string)
  322. (pcase (nth 7 ppss)
  323. (`2 'comment-single-line)
  324. (`1 'comment-multi-line-2)
  325. (_ 'comment-multi-line-1))))))))
  326. (defun opascal-literal-start-pattern (literal-kind)
  327. ;; Returns the start pattern of the literal kind.
  328. (cdr (assoc literal-kind
  329. '((comment-single-line . "//")
  330. (comment-multi-line-1 . "{")
  331. (comment-multi-line-2 . "(*")
  332. (string . "'")
  333. (double-quoted-string . "\"")))))
  334. (defun opascal-literal-end-pattern (literal-kind)
  335. ;; Returns the end pattern of the literal kind.
  336. (cdr (assoc literal-kind
  337. '((comment-single-line . "\n")
  338. (comment-multi-line-1 . "}")
  339. (comment-multi-line-2 . "*)")
  340. (string . "'")
  341. (double-quoted-string . "\"")))))
  342. (defun opascal-literal-stop-pattern (literal-kind)
  343. ;; Returns the pattern that delimits end of the search for the literal kind.
  344. ;; These are regular expressions.
  345. (cdr (assoc literal-kind
  346. '((comment-single-line . "\n")
  347. (comment-multi-line-1 . "}")
  348. (comment-multi-line-2 . "\\*)")
  349. ;; Strings cannot span lines.
  350. (string . "['\n]")
  351. (double-quoted-string . "[\"\n]")))))
  352. (defun opascal-is-literal-end (p)
  353. ;; True if the point p is at the end point of a (completed) literal.
  354. (save-excursion
  355. (and (null (nth 8 (syntax-ppss p)))
  356. (nth 8 (syntax-ppss (1- p))))))
  357. (defun opascal-literal-token-at (p)
  358. "Return the literal token surrounding the point P, or nil if none."
  359. (save-excursion
  360. (let ((ppss (syntax-ppss p)))
  361. (when (or (nth 8 ppss) (looking-at opascal--literal-start-re))
  362. (let* ((new-start (or (nth 8 ppss) p))
  363. (new-end (progn
  364. (goto-char new-start)
  365. (condition-case nil
  366. (if (memq (char-after) '(?\' ?\"))
  367. (forward-sexp 1)
  368. (forward-comment 1))
  369. (scan-error (goto-char (point-max))))
  370. (point))))
  371. (opascal-token-of (opascal-literal-kind p) new-start new-end))))))
  372. (defun opascal-point-token-at (p kind)
  373. ;; Returns the single character token at the point p.
  374. (opascal-token-of kind p (1+ p)))
  375. (defsubst opascal-char-token-at (p char kind)
  376. ;; Returns the token at the point p that describes the specified character.
  377. ;; If not actually over such a character, nil is returned.
  378. (when (eq char (char-after p))
  379. (opascal-token-of kind p (1+ p))))
  380. (defun opascal-charset-token-at (p charset kind)
  381. ;; Returns the token surrounding point p that contains only members of the
  382. ;; character set.
  383. (let ((currp (point))
  384. (end nil)
  385. (token nil))
  386. (goto-char p)
  387. (when (> (skip-chars-forward charset) 0)
  388. (setq end (point))
  389. (goto-char (1+ p))
  390. (skip-chars-backward charset)
  391. (setq token (opascal-token-of kind (point) end)))
  392. (goto-char currp)
  393. token))
  394. (defun opascal-space-token-at (p)
  395. ;; If point p is surrounded by space characters, then return the token of the
  396. ;; contiguous spaces.
  397. (opascal-charset-token-at p opascal-space-chars 'space))
  398. (defun opascal-word-token-at (p)
  399. ;; If point p is over a word (i.e. identifier characters), then return a word
  400. ;; token. If the word is actually a keyword, then return the keyword token.
  401. (let ((word (opascal-charset-token-at p opascal-word-chars 'word)))
  402. (when word
  403. (let* ((word-image (downcase (opascal-token-string word)))
  404. (keyword (intern-soft word-image)))
  405. (when (and (or keyword (string= "nil" word-image))
  406. (opascal-is keyword opascal-keywords))
  407. (opascal-set-token-kind word keyword))
  408. word))))
  409. (defun opascal-explicit-token-at (p token-string kind)
  410. ;; If point p is anywhere in the token string then returns the resulting
  411. ;; token.
  412. (let ((token (opascal-charset-token-at p token-string kind)))
  413. (when (and token (string= token-string (opascal-token-string token)))
  414. token)))
  415. (defun opascal-token-at (p)
  416. ;; Returns the token from parsing text at point p.
  417. (when (and (<= (point-min) p) (<= p (point-max)))
  418. (cond ((opascal-char-token-at p ?\n 'newline))
  419. ((opascal-literal-token-at p))
  420. ((opascal-space-token-at p))
  421. ((opascal-word-token-at p))
  422. ((opascal-char-token-at p ?\( 'open-group))
  423. ((opascal-char-token-at p ?\) 'close-group))
  424. ((opascal-char-token-at p ?\[ 'open-group))
  425. ((opascal-char-token-at p ?\] 'close-group))
  426. ((opascal-char-token-at p ?\; 'semicolon))
  427. ((opascal-char-token-at p ?. 'dot))
  428. ((opascal-char-token-at p ?, 'comma))
  429. ((opascal-char-token-at p ?= 'equals))
  430. ((opascal-char-token-at p ?+ 'plus))
  431. ((opascal-char-token-at p ?- 'minus))
  432. ((opascal-char-token-at p ?* 'times))
  433. ((opascal-char-token-at p ?/ 'divides))
  434. ((opascal-char-token-at p ?: 'colon))
  435. ((opascal-explicit-token-at p "<>" 'not-equals))
  436. ((opascal-point-token-at p 'punctuation)))))
  437. (defun opascal-current-token ()
  438. ;; Returns the opascal source token under the current point.
  439. (opascal-token-at (point)))
  440. (defun opascal-next-token (token)
  441. ;; Returns the token after the specified token.
  442. (when token
  443. (let ((next (opascal-token-at (opascal-token-end token))))
  444. (if next
  445. (opascal-step-progress (opascal-token-start next) "Scanning"
  446. opascal-scanning-progress-step))
  447. next)))
  448. (defun opascal-previous-token (token)
  449. ;; Returns the token before the specified token.
  450. (when token
  451. (let ((previous (opascal-token-at (1- (opascal-token-start token)))))
  452. (if previous
  453. (opascal-step-progress (opascal-token-start previous) "Scanning"
  454. opascal-scanning-progress-step))
  455. previous)))
  456. (defun opascal-next-visible-token (token)
  457. ;; Returns the first non-space token after the specified token.
  458. (let (next-token)
  459. (while (progn
  460. (setq next-token (opascal-next-token token))
  461. (opascal-is (opascal-token-kind next-token) '(space newline))))
  462. next-token))
  463. (defun opascal-group-start (from-token)
  464. ;; Returns the token that denotes the start of the ()/[] group.
  465. (let ((token (opascal-previous-token from-token))
  466. (token-kind nil))
  467. (catch 'done
  468. (while token
  469. (setq token-kind (opascal-token-kind token))
  470. (cond
  471. ;; Skip over nested groups.
  472. ((eq 'close-group token-kind) (setq token (opascal-group-start token)))
  473. ((eq 'open-group token-kind) (throw 'done token)))
  474. (setq token (opascal-previous-token token)))
  475. ;; Start not found.
  476. nil)))
  477. (defun opascal-group-end (from-token)
  478. ;; Returns the token that denotes the end of the ()/[] group.
  479. (let ((token (opascal-next-token from-token))
  480. (token-kind nil))
  481. (catch 'done
  482. (while token
  483. (setq token-kind (opascal-token-kind token))
  484. (cond
  485. ;; Skip over nested groups.
  486. ((eq 'open-group token-kind) (setq token (opascal-group-end token)))
  487. ((eq 'close-group token-kind) (throw 'done token)))
  488. (setq token (opascal-next-token token)))
  489. ;; end not found.
  490. nil)))
  491. (defun opascal-indent-of (token &optional offset)
  492. ;; Returns the start column of the token, plus any offset.
  493. (let ((indent (+ (opascal-column-of (opascal-token-start token))
  494. (if offset offset 0))))
  495. (when opascal-debug
  496. (opascal-debug-log
  497. (concat "\n Indent of: %S %S"
  498. "\n column: %d indent: %d offset: %d")
  499. token (opascal-token-string token)
  500. (opascal-column-of (opascal-token-start token))
  501. indent (if offset offset 0)))
  502. indent))
  503. (defun opascal-line-indent-of (from-token &optional offset &rest terminators)
  504. ;; Returns the column of first non-space character on the token's line, plus
  505. ;; any offset. We also stop if one of the terminators or an open ( or [ is
  506. ;; encountered.
  507. (let ((token (opascal-previous-token from-token))
  508. (last-token from-token)
  509. (kind nil))
  510. (catch 'done
  511. (while token
  512. (setq kind (opascal-token-kind token))
  513. (cond
  514. ;; Skip over ()/[] groups.
  515. ((eq 'close-group kind) (setq token (opascal-group-start token)))
  516. ;; Stop at the beginning of the line or an open group.
  517. ((opascal-is kind '(newline open-group)) (throw 'done nil))
  518. ;; Stop at one of the specified terminators.
  519. ((opascal-is kind terminators) (throw 'done nil)))
  520. (unless (opascal-is kind opascal-whitespace) (setq last-token token))
  521. (setq token (opascal-previous-token token))))
  522. (opascal-indent-of last-token offset)))
  523. (defun opascal-stmt-line-indent-of (from-token &optional offset)
  524. ;; Like `opascal-line-indent-of' except is also stops on a use clause, and
  525. ;; colons that precede statements (i.e. case labels).
  526. (let ((token (opascal-previous-token from-token))
  527. (last-token from-token)
  528. (kind nil))
  529. (catch 'done
  530. (while token
  531. (setq kind (opascal-token-kind token))
  532. (cond
  533. ((and (eq 'colon kind)
  534. (opascal-is (opascal-token-kind last-token)
  535. `(,@opascal-block-statements
  536. ,@opascal-expr-statements)))
  537. ;; We hit a label followed by a statement. Indent to the statement.
  538. (throw 'done nil))
  539. ;; Skip over ()/[] groups.
  540. ((eq 'close-group kind) (setq token (opascal-group-start token)))
  541. ((opascal-is kind `(newline open-group ,@opascal-use-clauses))
  542. ;; Stop at the beginning of the line, an open group, or a use clause
  543. (throw 'done nil)))
  544. (unless (opascal-is kind opascal-whitespace) (setq last-token token))
  545. (setq token (opascal-previous-token token))))
  546. (opascal-indent-of last-token offset)))
  547. (defun opascal-open-group-indent (token last-token &optional offset)
  548. ;; Returns the indent relative to an unmatched ( or [.
  549. (when (eq 'open-group (opascal-token-kind token))
  550. (if last-token
  551. (opascal-indent-of last-token offset)
  552. ;; There is nothing following the ( or [. Indent from its line.
  553. (opascal-stmt-line-indent-of token opascal-indent-level))))
  554. (defun opascal-composite-type-start (token last-token)
  555. ;; Returns true (actually the last-token) if the pair equals (= class), (=
  556. ;; dispinterface), (= interface), (= object), or (= record), and nil
  557. ;; otherwise.
  558. (if (and (eq 'equals (opascal-token-kind token))
  559. (opascal-is (opascal-token-kind last-token) opascal-composite-types))
  560. last-token))
  561. (defun opascal-is-simple-class-type (at-token limit-token)
  562. ;; True if at-token is the start of a simple class type. E.g.
  563. ;; class of TClass;
  564. ;; class (TBaseClass);
  565. ;; class;
  566. (when (opascal-is (opascal-token-kind at-token) opascal-class-types)
  567. (catch 'done
  568. ;; Scan until the semi colon.
  569. (let ((token (opascal-next-token at-token))
  570. (token-kind nil)
  571. (limit (opascal-token-start limit-token)))
  572. (while (and token (<= (opascal-token-start token) limit))
  573. (setq token-kind (opascal-token-kind token))
  574. (cond
  575. ;; A semicolon delimits the search.
  576. ((eq 'semicolon token-kind) (throw 'done token))
  577. ;; Skip over the inheritance list.
  578. ((eq 'open-group token-kind) (setq token (opascal-group-end token)))
  579. ;; Only allow "of" and whitespace, and an identifier
  580. ((opascal-is token-kind `(of word ,@opascal-whitespace)))
  581. ;; Otherwise we are not in a simple class declaration.
  582. ((throw 'done nil)))
  583. (setq token (opascal-next-token token)))))))
  584. (defun opascal-block-start (from-token &optional stop-on-class)
  585. ;; Returns the token that denotes the start of the block.
  586. (let ((token (opascal-previous-token from-token))
  587. (last-token nil)
  588. (token-kind nil))
  589. (catch 'done
  590. (while token
  591. (setq token-kind (opascal-token-kind token))
  592. (cond
  593. ;; Skip over nested blocks.
  594. ((opascal-is token-kind opascal-end-block-statements)
  595. (setq token (opascal-block-start token)))
  596. ;; Regular block start found.
  597. ((opascal-is token-kind opascal-block-statements)
  598. (throw 'done
  599. ;; As a special case, when a "case" block appears
  600. ;; within a record declaration (to denote a variant
  601. ;; part), the record declaration should be considered
  602. ;; the enclosing block.
  603. (if (eq 'case token-kind)
  604. (let ((enclosing-token
  605. (opascal-block-start token
  606. 'stop-on-class)))
  607. (if
  608. (eq 'record
  609. (opascal-token-kind enclosing-token))
  610. (if stop-on-class
  611. enclosing-token
  612. (opascal-previous-token enclosing-token))
  613. token))
  614. token)))
  615. ;; A class/record start also begins a block.
  616. ((opascal-composite-type-start token last-token)
  617. (throw 'done (if stop-on-class last-token token)))
  618. )
  619. (unless (opascal-is token-kind opascal-whitespace)
  620. (setq last-token token))
  621. (setq token (opascal-previous-token token)))
  622. ;; Start not found.
  623. nil)))
  624. (defun opascal-else-start (from-else)
  625. ;; Returns the token of the if or case statement.
  626. (let ((token (opascal-previous-token from-else))
  627. (token-kind nil)
  628. (semicolon-count 0))
  629. (catch 'done
  630. (while token
  631. (setq token-kind (opascal-token-kind token))
  632. (cond
  633. ;; Skip over nested groups.
  634. ((eq 'close-group token-kind) (setq token (opascal-group-start token)))
  635. ;; Skip over any nested blocks.
  636. ((opascal-is token-kind opascal-end-block-statements)
  637. (setq token (opascal-block-start token)))
  638. ((eq 'semicolon token-kind)
  639. ;; Semicolon means we are looking for an enclosing if, unless we
  640. ;; are in a case statement. Keep counts of the semicolons and decide
  641. ;; later.
  642. (setq semicolon-count (1+ semicolon-count)))
  643. ((and (eq 'if token-kind) (= semicolon-count 0))
  644. ;; We only can match an if when there have been no intervening
  645. ;; semicolons.
  646. (throw 'done token))
  647. ((eq 'case token-kind)
  648. ;; We have hit a case statement start.
  649. (throw 'done token)))
  650. (setq token (opascal-previous-token token)))
  651. ;; No if or case statement found.
  652. nil)))
  653. (defun opascal-comment-content-start (comment)
  654. ;; Returns the point of the first non-space character in the comment.
  655. (let ((kind (opascal-token-kind comment)))
  656. (when (opascal-is kind opascal-comments)
  657. (opascal-save-excursion
  658. (goto-char (+ (opascal-token-start comment)
  659. (length (opascal-literal-start-pattern kind))))
  660. (skip-chars-forward opascal-space-chars)
  661. (point)))))
  662. (defun opascal-comment-block-start (comment)
  663. ;; Returns the starting comment token of a contiguous // comment block. If
  664. ;; the comment is multiline (i.e. {...} or (*...*)), the original comment is
  665. ;; returned.
  666. (if (not (eq 'comment-single-line (opascal-token-kind comment)))
  667. comment
  668. ;; Scan until we run out of // comments.
  669. (let ((prev-comment comment)
  670. (start-comment comment))
  671. (while (let ((kind (opascal-token-kind prev-comment)))
  672. (cond ((eq kind 'space))
  673. ((eq kind 'comment-single-line)
  674. (setq start-comment prev-comment))
  675. (t nil)))
  676. (setq prev-comment (opascal-previous-token prev-comment)))
  677. start-comment)))
  678. (defun opascal-comment-block-end (comment)
  679. ;; Returns the end comment token of a contiguous // comment block. If the
  680. ;; comment is multiline (i.e. {...} or (*...*)), the original comment is
  681. ;; returned.
  682. (if (not (eq 'comment-single-line (opascal-token-kind comment)))
  683. comment
  684. ;; Scan until we run out of // comments.
  685. (let ((next-comment comment)
  686. (end-comment comment))
  687. (while (let ((kind (opascal-token-kind next-comment)))
  688. (cond ((eq kind 'space))
  689. ((eq kind 'comment-single-line)
  690. (setq end-comment next-comment))
  691. (t nil)))
  692. (setq next-comment (opascal-next-token next-comment)))
  693. end-comment)))
  694. (defun opascal-on-first-comment-line (comment)
  695. ;; Returns true if the current point is on the first line of the comment.
  696. (save-excursion
  697. (let ((comment-start (opascal-token-start comment))
  698. (current-point (point)))
  699. (goto-char comment-start)
  700. (end-of-line)
  701. (and (<= comment-start current-point) (<= current-point (point))))))
  702. (defun opascal-comment-indent-of (comment)
  703. ;; Returns the correct indentation for the comment.
  704. (let ((start-comment (opascal-comment-block-start comment)))
  705. (if (and (eq start-comment comment)
  706. (opascal-on-first-comment-line comment))
  707. ;; Indent as a statement.
  708. (opascal-enclosing-indent-of comment)
  709. (save-excursion
  710. (let ((kind (opascal-token-kind comment)))
  711. (beginning-of-line)
  712. (cond ((eq 'comment-single-line kind)
  713. ;; Indent to the first comment in the // block.
  714. (opascal-indent-of start-comment))
  715. ((looking-at (concat opascal-leading-spaces-re
  716. (opascal-literal-stop-pattern kind)))
  717. ;; Indent multi-line comment terminators to the comment start.
  718. (opascal-indent-of comment))
  719. ;; Indent according to the comment's content start.
  720. ((opascal-column-of (opascal-comment-content-start comment)))))))
  721. ))
  722. (defun opascal-is-use-clause-end (at-token last-token last-colon from-kind)
  723. ;; True if we are after the end of a uses type clause.
  724. (when (and last-token
  725. (not last-colon)
  726. (eq 'comma (opascal-token-kind at-token))
  727. (eq 'semicolon from-kind))
  728. ;; Scan for the uses statement, just to be sure.
  729. (let ((token (opascal-previous-token at-token))
  730. (token-kind nil))
  731. (catch 'done
  732. (while token
  733. (setq token-kind (opascal-token-kind token))
  734. (cond ((opascal-is token-kind opascal-use-clauses)
  735. (throw 'done t))
  736. ;; Whitespace, identifiers, strings, "in" keyword, and commas
  737. ;; are allowed in use clauses.
  738. ((or (opascal-is token-kind '(word comma in newline))
  739. (opascal-is token-kind opascal-whitespace)
  740. (opascal-is token-kind opascal-strings)))
  741. ;; Nothing else is.
  742. ((throw 'done nil)))
  743. (setq token (opascal-previous-token token)))
  744. nil))))
  745. (defun opascal-is-block-after-expr-statement (token)
  746. ;; Returns true if we have a block token trailing an expression delimiter (of
  747. ;; presumably an expression statement).
  748. (when (opascal-is (opascal-token-kind token) opascal-block-statements)
  749. (let ((previous (opascal-previous-token token))
  750. (previous-kind nil))
  751. (while (progn
  752. (setq previous-kind (opascal-token-kind previous))
  753. (eq previous-kind 'space))
  754. (setq previous (opascal-previous-token previous)))
  755. (or (opascal-is previous-kind opascal-expr-delimiters)
  756. (eq previous-kind 'else)))))
  757. (defun opascal-previous-indent-of (from-token)
  758. ;; Returns the indentation of the previous statement of the token.
  759. (let ((token (opascal-previous-token from-token))
  760. (token-kind nil)
  761. (from-kind (opascal-token-kind from-token))
  762. (last-colon nil)
  763. (last-of nil)
  764. (last-token nil))
  765. (catch 'done
  766. (while token
  767. (setq token-kind (opascal-token-kind token))
  768. (cond
  769. ;; An open ( or [ always is an indent point.
  770. ((eq 'open-group token-kind)
  771. (throw 'done (opascal-open-group-indent token last-token)))
  772. ;; Skip over any ()/[] groups.
  773. ((eq 'close-group token-kind) (setq token (opascal-group-start token)))
  774. ((opascal-is token-kind opascal-end-block-statements)
  775. (if (eq 'newline (opascal-token-kind (opascal-previous-token token)))
  776. ;; We can stop at an end token that is right up against the
  777. ;; margin.
  778. (throw 'done 0)
  779. ;; Otherwise, skip over any nested blocks.
  780. (setq token (opascal-block-start token))))
  781. ;; Special case: if we encounter a ", word;" then we assume that we
  782. ;; are in some kind of uses clause, and thus indent to column 0. This
  783. ;; works because no other constructs are known to have that form.
  784. ;; This fixes the irritating case of having indents after a uses
  785. ;; clause look like:
  786. ;; uses
  787. ;; someUnit,
  788. ;; someOtherUnit;
  789. ;; // this should be at column 0!
  790. ((opascal-is-use-clause-end token last-token last-colon from-kind)
  791. (throw 'done 0))
  792. ;; A previous terminator means we can stop. If we are on a directive,
  793. ;; however, then we are not actually encountering a new statement.
  794. ((and last-token
  795. (opascal-is token-kind opascal-previous-terminators)
  796. (not (opascal-is (opascal-token-kind last-token)
  797. opascal-directives)))
  798. (throw 'done (opascal-stmt-line-indent-of last-token 0)))
  799. ;; Ignore whitespace.
  800. ((opascal-is token-kind opascal-whitespace))
  801. ;; Remember any "of" we encounter, since that affects how we
  802. ;; indent to a case statement within a record declaration
  803. ;; (i.e. a variant part).
  804. ((eq 'of token-kind)
  805. (setq last-of token))
  806. ;; Remember any ':' we encounter (until we reach an "of"),
  807. ;; since that affects how we indent to case statements in
  808. ;; general.
  809. ((eq 'colon token-kind)
  810. (unless last-of (setq last-colon token)))
  811. ;; A case statement delimits a previous statement. We indent labels
  812. ;; specially.
  813. ((eq 'case token-kind)
  814. (throw 'done
  815. (if last-colon (opascal-line-indent-of last-colon)
  816. (opascal-line-indent-of token opascal-case-label-indent))))
  817. ;; If we are in a use clause then commas mark an enclosing rather than
  818. ;; a previous statement.
  819. ((opascal-is token-kind opascal-use-clauses)
  820. (throw 'done
  821. (if (eq 'comma from-kind)
  822. (if last-token
  823. ;; Indent to first unit in use clause.
  824. (opascal-indent-of last-token)
  825. ;; Indent from use clause keyword.
  826. (opascal-line-indent-of token opascal-indent-level))
  827. ;; Indent to use clause keyword.
  828. (opascal-line-indent-of token))))
  829. ;; Assembly sections always indent in from the asm keyword.
  830. ((eq token-kind 'asm)
  831. (throw 'done (opascal-stmt-line-indent-of token opascal-indent-level)))
  832. ;; An enclosing statement delimits a previous statement.
  833. ;; We try to use the existing indent of the previous statement,
  834. ;; otherwise we calculate from the enclosing statement.
  835. ((opascal-is token-kind opascal-previous-enclosing-statements)
  836. (throw 'done (if last-token
  837. ;; Otherwise indent to the last token
  838. (opascal-line-indent-of last-token)
  839. ;; Just indent from the enclosing keyword
  840. (opascal-line-indent-of token opascal-indent-level))))
  841. ;; A class or record declaration also delimits a previous statement.
  842. ((opascal-composite-type-start token last-token)
  843. (throw
  844. 'done
  845. (if (opascal-is-simple-class-type last-token from-token)
  846. ;; c = class; or c = class of T; are previous statements.
  847. (opascal-line-indent-of token)
  848. ;; Otherwise c = class ... or r = record ... are enclosing
  849. ;; statements.
  850. (opascal-line-indent-of last-token opascal-indent-level))))
  851. ;; We have a definite previous statement delimiter.
  852. ((opascal-is token-kind opascal-previous-statements)
  853. (throw 'done (opascal-stmt-line-indent-of token 0)))
  854. )
  855. (unless (opascal-is token-kind opascal-whitespace)
  856. (setq last-token token))
  857. (setq token (opascal-previous-token token)))
  858. ;; We ran out of tokens. Indent to column 0.
  859. 0)))
  860. (defun opascal-section-indent-of (section-token)
  861. ;; Returns the indentation appropriate for begin/var/const/type/label
  862. ;; tokens.
  863. (let* ((token (opascal-previous-token section-token))
  864. (token-kind nil)
  865. (last-token nil)
  866. (nested-block-count 0)
  867. (expr-delimited nil)
  868. (last-terminator nil))
  869. (catch 'done
  870. (while token
  871. (setq token-kind (opascal-token-kind token))
  872. (cond
  873. ;; Always stop at unmatched ( or [.
  874. ((eq token-kind 'open-group)
  875. (throw 'done (opascal-open-group-indent token last-token)))
  876. ;; Skip over any ()/[] groups.
  877. ((eq 'close-group token-kind) (setq token (opascal-group-start token)))
  878. ((opascal-is token-kind opascal-end-block-statements)
  879. (if (eq 'newline (opascal-token-kind (opascal-previous-token token)))
  880. ;; We can stop at an end token that is right up against the
  881. ;; margin.
  882. (throw 'done 0)
  883. ;; Otherwise, skip over any nested blocks.
  884. (setq token (opascal-block-start token)
  885. nested-block-count (1+ nested-block-count))))
  886. ;; Remember if we have encountered any forward routine declarations.
  887. ((eq 'forward token-kind)
  888. (setq nested-block-count (1+ nested-block-count)))
  889. ;; Mark the completion of a nested routine traversal.
  890. ((and (opascal-is token-kind opascal-routine-statements)
  891. (> nested-block-count 0))
  892. (setq nested-block-count (1- nested-block-count)))
  893. ;; Remember if we have encountered any statement terminators.
  894. ((eq 'semicolon token-kind) (setq last-terminator token))
  895. ;; Remember if we have encountered any expression delimiters.
  896. ((opascal-is token-kind opascal-expr-delimiters)
  897. (setq expr-delimited token))
  898. ;; Enclosing body statements are delimiting. We indent the compound
  899. ;; bodies specially.
  900. ((and (not last-terminator)
  901. (opascal-is token-kind opascal-body-statements))
  902. (throw 'done
  903. (opascal-stmt-line-indent-of token opascal-compound-block-indent)))
  904. ;; An enclosing ":" means a label.
  905. ((and (eq 'colon token-kind)
  906. (opascal-is (opascal-token-kind section-token)
  907. opascal-block-statements)
  908. (not last-terminator)
  909. (not expr-delimited)
  910. (not (eq 'equals (opascal-token-kind last-token))))
  911. (throw 'done
  912. (opascal-stmt-line-indent-of token opascal-indent-level)))
  913. ;; Block and mid block tokens are always enclosing
  914. ((opascal-is token-kind opascal-begin-enclosing-tokens)
  915. (throw 'done
  916. (opascal-stmt-line-indent-of token opascal-indent-level)))
  917. ;; Declaration sections and routines are delimiters, unless they
  918. ;; are part of a nested routine.
  919. ((and (opascal-is token-kind opascal-decl-delimiters)
  920. (= 0 nested-block-count))
  921. (throw 'done (opascal-line-indent-of token 0)))
  922. ;; Unit statements mean we indent right to the left.
  923. ((opascal-is token-kind opascal-unit-statements) (throw 'done 0))
  924. )
  925. (unless (opascal-is token-kind opascal-whitespace)
  926. (setq last-token token))
  927. (setq token (opascal-previous-token token)))
  928. ;; We ran out of tokens. Indent to column 0.
  929. 0)))
  930. (defun opascal-enclosing-indent-of (from-token)
  931. ;; Returns the indentation offset from the enclosing statement of the token.
  932. (let ((token (opascal-previous-token from-token))
  933. (from-kind (opascal-token-kind from-token))
  934. (token-kind nil)
  935. (stmt-start nil)
  936. (last-token nil)
  937. (equals-encountered nil)
  938. (before-equals nil)
  939. (expr-delimited nil))
  940. (catch 'done
  941. (while token
  942. (setq token-kind (opascal-token-kind token))
  943. (cond
  944. ;; An open ( or [ always is an indent point.
  945. ((eq 'open-group token-kind)
  946. (throw 'done
  947. (opascal-open-group-indent
  948. token last-token
  949. (if (opascal-is from-kind opascal-binary-ops)
  950. ;; Keep binary operations aligned with the open group.
  951. 0
  952. opascal-indent-level))))
  953. ;; Skip over any ()/[] groups.
  954. ((eq 'close-group token-kind) (setq token (opascal-group-start token)))
  955. ;; Skip over any nested blocks.
  956. ((opascal-is token-kind opascal-end-block-statements)
  957. (setq token (opascal-block-start token)))
  958. ;; An expression delimiter affects indentation depending on whether
  959. ;; the point is before or after it. Remember that we encountered one.
  960. ;; Also remember the last encountered token, since if it exists it
  961. ;; should be the actual indent point.
  962. ((opascal-is token-kind opascal-expr-delimiters)
  963. (setq expr-delimited token stmt-start last-token))
  964. ;; With a non-delimited expression statement we indent after the
  965. ;; statement's keyword, unless we are on the delimiter itself.
  966. ((and (not expr-delimited)
  967. (opascal-is token-kind opascal-expr-statements))
  968. (throw 'done
  969. (cond ((opascal-is from-kind opascal-expr-delimiters)
  970. ;; We are indenting a delimiter. Indent to the statement.
  971. (opascal-stmt-line-indent-of token 0))
  972. ((and last-token (opascal-is from-kind opascal-binary-ops))
  973. ;; Align binary ops with the expression.
  974. (opascal-indent-of last-token))
  975. (last-token
  976. ;; Indent in from the expression.
  977. (opascal-indent-of last-token opascal-indent-level))
  978. ;; Indent in from the statement's keyword.
  979. ((opascal-indent-of token opascal-indent-level)))))
  980. ;; A delimited case statement indents the label according to
  981. ;; a special rule.
  982. ((eq 'case token-kind)
  983. (throw 'done
  984. (if stmt-start
  985. ;; We are not actually indenting to the case statement,
  986. ;; but are within a label expression.
  987. (opascal-stmt-line-indent-of
  988. stmt-start opascal-indent-level)
  989. ;; Indent from the case keyword.
  990. (opascal-stmt-line-indent-of
  991. token opascal-case-label-indent))))
  992. ;; Body expression statements are enclosing. Indent from the
  993. ;; statement's keyword, unless we have a non-block statement following
  994. ;; it.
  995. ((opascal-is token-kind opascal-body-expr-statements)
  996. (throw 'done
  997. (opascal-stmt-line-indent-of
  998. (or stmt-start token) opascal-indent-level)))
  999. ;; An else statement is enclosing, but it doesn't have an expression.
  1000. ;; Thus we take into account last-token instead of stmt-start.
  1001. ((eq 'else token-kind)
  1002. (throw 'done (opascal-stmt-line-indent-of
  1003. (or last-token token) opascal-indent-level)))
  1004. ;; We indent relative to an enclosing declaration section.
  1005. ((opascal-is token-kind opascal-decl-sections)
  1006. (throw 'done (opascal-indent-of (if last-token last-token token)
  1007. opascal-indent-level)))
  1008. ;; In unit sections we indent right to the left.
  1009. ((opascal-is token-kind opascal-unit-sections)
  1010. (throw 'done
  1011. ;; Handle specially the case of "interface", which can be used
  1012. ;; to start either a unit section or an interface definition.
  1013. (if (opascal-is token-kind opascal-interface-types)
  1014. (progn
  1015. ;; Find the previous non-whitespace token.
  1016. (while (progn
  1017. (setq last-token token
  1018. token (opascal-previous-token token)
  1019. token-kind (opascal-token-kind token))
  1020. (and token
  1021. (opascal-is token-kind
  1022. opascal-whitespace))))
  1023. ;; If this token is an equals sign, "interface" is being
  1024. ;; used to start an interface definition and we should
  1025. ;; treat it as a composite type; otherwise, we should
  1026. ;; consider it the start of a unit section.
  1027. (if (and token (eq token-kind 'equals))
  1028. (opascal-line-indent-of last-token
  1029. opascal-indent-level)
  1030. 0))
  1031. 0)))
  1032. ;; A previous terminator means we can stop.
  1033. ((opascal-is token-kind opascal-previous-terminators)
  1034. (throw 'done
  1035. (cond ((and last-token
  1036. (eq 'comma token-kind)
  1037. (opascal-is from-kind opascal-binary-ops))
  1038. ;; Align binary ops with the expression.
  1039. (opascal-indent-of last-token))
  1040. (last-token
  1041. ;; Indent in from the expression.
  1042. (opascal-indent-of last-token opascal-indent-level))
  1043. ;; No enclosing expression; use the previous statement's
  1044. ;; indent.
  1045. ((opascal-previous-indent-of token)))))
  1046. ;; A block statement after an expression delimiter has its start
  1047. ;; column as the expression statement. E.g.
  1048. ;; if (a = b)
  1049. ;; and (a != c) then begin
  1050. ;; //...
  1051. ;; end;
  1052. ;; Remember it for when we encounter the expression statement start.
  1053. ((opascal-is-block-after-expr-statement token)
  1054. (throw 'done
  1055. (cond (last-token (opascal-indent-of last-token opascal-indent-level))
  1056. ((+ (opascal-section-indent-of token) opascal-indent-level)))))
  1057. ;; Assembly sections always indent in from the asm keyword.
  1058. ((eq token-kind 'asm)
  1059. (throw 'done (opascal-stmt-line-indent-of token opascal-indent-level)))
  1060. ;; Stop at an enclosing statement and indent from it.
  1061. ((opascal-is token-kind opascal-enclosing-statements)
  1062. (throw 'done (opascal-stmt-line-indent-of
  1063. (or last-token token) opascal-indent-level)))
  1064. ;; A class/record declaration is also enclosing.
  1065. ((opascal-composite-type-start token last-token)
  1066. (throw 'done
  1067. (opascal-line-indent-of last-token opascal-indent-level)))
  1068. ;; A ":" we indent relative to its line beginning. If we are in a
  1069. ;; parameter list, then stop also if we hit a ";".
  1070. ((and (eq token-kind 'colon)
  1071. (not expr-delimited)
  1072. (not (opascal-is from-kind opascal-expr-delimiters))
  1073. (not equals-encountered)
  1074. (not (eq from-kind 'equals)))
  1075. (throw 'done
  1076. (if last-token
  1077. (opascal-indent-of last-token opascal-indent-level)
  1078. (opascal-line-indent-of token opascal-indent-level 'semicolon))))
  1079. ;; If the ":" was not processed above and we have token after the "=",
  1080. ;; then indent from the "=". Ignore :=, however.
  1081. ((and (eq token-kind 'colon) equals-encountered before-equals)
  1082. (cond
  1083. ;; Ignore binary ops for now. It would do, for example:
  1084. ;; val := 1 + 2
  1085. ;; + 3;
  1086. ;; which is good, but also
  1087. ;; val := Foo
  1088. ;; (foo, args)
  1089. ;; + 2;
  1090. ;; which doesn't look right.
  1091. ;;;; Align binary ops with the before token.
  1092. ;;((opascal-is from-kind opascal-binary-ops)
  1093. ;;(throw 'done (opascal-indent-of before-equals 0)))
  1094. ;; Assignments (:=) we skip over to get a normal indent.
  1095. ((eq (opascal-token-kind last-token) 'equals))
  1096. ;; Otherwise indent in from the equals.
  1097. ((throw 'done
  1098. (opascal-indent-of before-equals opascal-indent-level)))))
  1099. ;; Remember any "=" we encounter if it has not already been processed.
  1100. ((eq token-kind 'equals)
  1101. (setq equals-encountered token
  1102. before-equals last-token))
  1103. )
  1104. (unless (opascal-is token-kind opascal-whitespace)
  1105. (setq last-token token))
  1106. (setq token (opascal-previous-token token)))
  1107. ;; We ran out of tokens. Indent to column 0.
  1108. 0)))
  1109. (defun opascal-corrected-indentation ()
  1110. ;; Returns the corrected indentation for the current line.
  1111. (opascal-save-excursion
  1112. (opascal-progress-start)
  1113. ;; Move to the first token on the line.
  1114. (beginning-of-line)
  1115. (skip-chars-forward opascal-space-chars)
  1116. (let* ((token (opascal-current-token))
  1117. (token-kind (opascal-token-kind token))
  1118. (indent
  1119. (cond ((eq 'close-group token-kind)
  1120. ;; Indent to the matching start ( or [.
  1121. (opascal-indent-of (opascal-group-start token)))
  1122. ((opascal-is token-kind opascal-unit-statements) 0)
  1123. ((opascal-is token-kind opascal-comments)
  1124. ;; In a comment.
  1125. (opascal-comment-indent-of token))
  1126. ((opascal-is token-kind opascal-decl-matchers)
  1127. ;; Use a previous section/routine's indent.
  1128. (opascal-section-indent-of token))
  1129. ((opascal-is token-kind opascal-match-block-statements)
  1130. ;; Use the block's indentation.
  1131. (let ((block-start
  1132. (opascal-block-start token 'stop-on-class)))
  1133. (cond
  1134. ;; When trailing a body statement, indent to
  1135. ;; the statement's keyword.
  1136. ((opascal-is-block-after-expr-statement block-start)
  1137. (opascal-section-indent-of block-start))
  1138. ;; Otherwise just indent to the block start.
  1139. ((opascal-stmt-line-indent-of block-start 0)))))
  1140. ((eq 'else token-kind)
  1141. ;; Find the start of the if or case statement.
  1142. (opascal-stmt-line-indent-of (opascal-else-start token) 0))
  1143. ;; Otherwise indent in from enclosing statement.
  1144. ((opascal-enclosing-indent-of
  1145. (if token token (opascal-token-at (1- (point)))))))))
  1146. (opascal-progress-done)
  1147. indent)))
  1148. (defun opascal-indent-line ()
  1149. "Indent the current line according to the current language construct.
  1150. If before the indent, the point is moved to the indent."
  1151. (interactive)
  1152. (save-match-data
  1153. (let ((marked-point (point-marker)) ; Maintain our position reliably.
  1154. (line-start nil)
  1155. (old-indent 0)
  1156. (new-indent 0))
  1157. (beginning-of-line)
  1158. (setq line-start (point))
  1159. (skip-chars-forward opascal-space-chars)
  1160. (setq old-indent (current-column))
  1161. (setq new-indent (opascal-corrected-indentation))
  1162. (if (< marked-point (point))
  1163. ;; If before the indent column, then move to it.
  1164. (set-marker marked-point (point)))
  1165. ;; Advance our marked point after inserted spaces.
  1166. (set-marker-insertion-type marked-point t)
  1167. (when (/= old-indent new-indent)
  1168. (delete-region line-start (point))
  1169. (insert (make-string new-indent ?\s)))
  1170. (goto-char marked-point)
  1171. (set-marker marked-point nil))))
  1172. (defvar opascal-mode-abbrev-table nil
  1173. "Abbrev table in use in OPascal mode buffers.")
  1174. (define-abbrev-table 'opascal-mode-abbrev-table ())
  1175. (defmacro opascal-ensure-buffer (buffer-var buffer-name)
  1176. ;; Ensures there exists a buffer of the specified name in the specified
  1177. ;; variable.
  1178. `(when (not (buffer-live-p ,buffer-var))
  1179. (setq ,buffer-var (get-buffer-create ,buffer-name))))
  1180. (defun opascal-log-msg (to-buffer the-msg)
  1181. ;; Writes a message to the end of the specified buffer.
  1182. (with-current-buffer to-buffer
  1183. (save-selected-window
  1184. (switch-to-buffer-other-window to-buffer)
  1185. (goto-char (point-max))
  1186. (set-window-point (get-buffer-window to-buffer) (point))
  1187. (insert the-msg))))
  1188. ;; Debugging helpers:
  1189. (defvar opascal-debug-buffer nil
  1190. "Buffer to write OPascal mode debug messages to. Created on demand.")
  1191. (defun opascal-debug-log (format-string &rest args)
  1192. ;; Writes a message to the log buffer.
  1193. (when opascal-debug
  1194. (opascal-ensure-buffer opascal-debug-buffer "*OPascal Debug Log*")
  1195. (opascal-log-msg opascal-debug-buffer
  1196. (concat (format-time-string "%H:%M:%S ")
  1197. (apply #'format (cons format-string args))
  1198. "\n"))))
  1199. (defun opascal-debug-token-string (token)
  1200. (let* ((image (opascal-token-string token))
  1201. (has-newline (string-match "^\\([^\n]*\\)\n\\(.+\\)?$" image)))
  1202. (when has-newline
  1203. (setq image (concat (match-string 1 image)
  1204. (if (match-beginning 2) "..."))))
  1205. image))
  1206. (defun opascal-debug-show-current-token ()
  1207. (interactive)
  1208. (let ((token (opascal-current-token)))
  1209. (opascal-debug-log "Token: %S %S" token (opascal-debug-token-string token))))
  1210. (defun opascal-debug-goto-point (p)
  1211. (interactive "NGoto char: ")
  1212. (goto-char p))
  1213. (defun opascal-debug-goto-next-token ()
  1214. (interactive)
  1215. (goto-char (opascal-token-start (opascal-next-token (opascal-current-token)))))
  1216. (defun opascal-debug-goto-previous-token ()
  1217. (interactive)
  1218. (goto-char
  1219. (opascal-token-start (opascal-previous-token (opascal-current-token)))))
  1220. (defun opascal-debug-show-current-string (from to)
  1221. (interactive "r")
  1222. (opascal-debug-log "String: %S" (buffer-substring from to)))
  1223. (defun opascal-debug-tokenize-region (from to)
  1224. (interactive)
  1225. (opascal-save-excursion
  1226. (opascal-progress-start)
  1227. (goto-char from)
  1228. (while (< (point) to)
  1229. (goto-char (opascal-token-end (opascal-current-token)))
  1230. (opascal-step-progress (point) "Tokenizing" opascal-scanning-progress-step))
  1231. (opascal-progress-done "Tokenizing done")))
  1232. (defun opascal-debug-tokenize-buffer ()
  1233. (interactive)
  1234. (opascal-debug-tokenize-region (point-min) (point-max)))
  1235. (defun opascal-debug-tokenize-window ()
  1236. (interactive)
  1237. (opascal-debug-tokenize-region (window-start) (window-end)))
  1238. (defun opascal-tab ()
  1239. "Indent the region, if Transient Mark mode is on and the region is active.
  1240. Otherwise, indent the current line or insert a TAB, depending on the
  1241. value of `opascal-tab-always-indents' and the current line position."
  1242. (interactive)
  1243. (cond ((use-region-p)
  1244. ;; If Transient Mark mode is enabled and the region is active, indent
  1245. ;; the entire region.
  1246. (indent-region (region-beginning) (region-end)))
  1247. ((or opascal-tab-always-indents
  1248. (save-excursion (skip-chars-backward opascal-space-chars) (bolp)))
  1249. ;; Otherwise, if we are configured always to indent (regardless of the
  1250. ;; point's position in the line) or we are before the first non-space
  1251. ;; character on the line, indent the line.
  1252. (opascal-indent-line))
  1253. (t
  1254. ;; Otherwise, insert a tab character.
  1255. (insert "\t"))))
  1256. (make-obsolete 'opascal-tab 'indent-for-tab-command "24.4")
  1257. (defun opascal-is-directory (path)
  1258. ;; True if the specified path is an existing directory.
  1259. (let ((attributes (file-attributes path)))
  1260. (and attributes (car attributes))))
  1261. (defun opascal-is-file (path)
  1262. ;; True if the specified file exists as a file.
  1263. (let ((attributes (file-attributes path)))
  1264. (and attributes (null (car attributes)))))
  1265. (defun opascal-search-directory (unit dir &optional recurse)
  1266. ;; Searches for the unit in the specified directory. If recurse is true, then
  1267. ;; the directory is recursively searched. File name comparison is done in a
  1268. ;; case insensitive manner.
  1269. (when (opascal-is-directory dir)
  1270. (let ((files (directory-files dir))
  1271. (unit-file (downcase unit)))
  1272. (catch 'done
  1273. ;; Search for the file.
  1274. (dolist (file files)
  1275. (let ((path (concat dir "/" file)))
  1276. (if (and (string= unit-file (downcase file))
  1277. (opascal-is-file path))
  1278. (throw 'done path))))
  1279. ;; Not found. Search subdirectories.
  1280. (when recurse
  1281. (dolist (subdir files)
  1282. (unless (member subdir '("." ".."))
  1283. (let ((path (opascal-search-directory
  1284. unit (concat dir "/" subdir) recurse)))
  1285. (if path (throw 'done path))))))
  1286. ;; Not found.
  1287. nil))))
  1288. (defun opascal-find-unit-in-directory (unit dir)
  1289. ;; Searches for the unit in the specified directory. If the directory ends
  1290. ;; in \"...\", then it is recursively searched.
  1291. (let ((dir-name dir)
  1292. (recurse nil))
  1293. ;; Check if we need to recursively search the directory.
  1294. (if (string-match "^\\(.+\\)\\.\\.\\.$" dir-name)
  1295. (setq dir-name (match-string 1 dir-name)
  1296. recurse t))
  1297. ;; Ensure the trailing slash is removed.
  1298. (if (string-match "^\\(.+\\)[\\\\/]$" dir-name)
  1299. (setq dir-name (match-string 1 dir-name)))
  1300. (opascal-search-directory unit dir-name recurse)))
  1301. (defun opascal-find-unit-file (unit)
  1302. ;; Finds the specified opascal source file according to `opascal-search-path'.
  1303. ;; If found, the full path is returned, otherwise nil is returned.
  1304. (catch 'done
  1305. (cond ((null opascal-search-path)
  1306. (opascal-find-unit-in-directory unit "."))
  1307. ((stringp opascal-search-path)
  1308. (opascal-find-unit-in-directory unit opascal-search-path))
  1309. ((dolist (dir opascal-search-path)
  1310. (let ((file (opascal-find-unit-in-directory unit dir)))
  1311. (if file (throw 'done file))))))
  1312. nil))
  1313. (defun opascal-find-unit (unit)
  1314. "Find the specified OPascal source file according to `opascal-search-path'.
  1315. If no extension is specified, .pas is assumed. Creates a buffer for the unit."
  1316. (interactive "sOPascal unit name: ")
  1317. (let* ((unit-file (if (string-match "^\\(.*\\)\\.[a-z]+$" unit)
  1318. unit
  1319. (concat unit ".pas")))
  1320. (file (opascal-find-unit-file unit-file)))
  1321. (if (null file)
  1322. (error "unit not found: %s" unit-file)
  1323. (find-file file)
  1324. (if (not (derived-mode-p 'opascal-mode))
  1325. (opascal-mode)))
  1326. file))
  1327. (defun opascal-find-current-def ()
  1328. "Find the definition of the identifier under the current point."
  1329. (interactive)
  1330. (error "opascal-find-current-def: not implemented yet"))
  1331. (defun opascal-find-current-xdef ()
  1332. "Find the definition of the identifier under the current point, searching
  1333. in external units if necessary (as listed in the current unit's use clause).
  1334. The set of directories to search for a unit is specified by the global variable
  1335. `opascal-search-path'."
  1336. (interactive)
  1337. (error "opascal-find-current-xdef: not implemented yet"))
  1338. (defun opascal-find-current-body ()
  1339. "Find the body of the identifier under the current point, assuming
  1340. it is a routine."
  1341. (interactive)
  1342. (error "opascal-find-current-body: not implemented yet"))
  1343. (defun opascal-fill-comment ()
  1344. "Fill the text of the current comment, according to `fill-column'.
  1345. An error is raised if not in a comment."
  1346. (interactive)
  1347. (save-excursion
  1348. (save-restriction
  1349. (let* ((comment (opascal-current-token))
  1350. (comment-kind (opascal-token-kind comment)))
  1351. (if (not (opascal-is comment-kind opascal-comments))
  1352. (error "Not in a comment")
  1353. (let* ((start-comment (opascal-comment-block-start comment))
  1354. (end-comment (opascal-comment-block-end comment))
  1355. ;; FIXME: Don't abuse global variables like `comment-end/start'.
  1356. (comment-start (opascal-token-start start-comment))
  1357. (comment-end (opascal-token-end end-comment))
  1358. (content-start (opascal-comment-content-start start-comment))
  1359. (content-indent (opascal-column-of content-start))
  1360. (content-prefix (make-string content-indent ?\s))
  1361. (content-prefix-re opascal-leading-spaces-re)
  1362. (p nil)
  1363. (marked-point (point-marker))) ; Maintain our position reliably.
  1364. (when (eq 'comment-single-line comment-kind)
  1365. ;; // style comments need more work.
  1366. (setq content-prefix
  1367. (let ((comment-indent (opascal-column-of comment-start)))
  1368. (concat (make-string comment-indent ?\s) "//"
  1369. (make-string (- content-indent comment-indent 2)
  1370. ?\s)))
  1371. content-prefix-re (concat opascal-leading-spaces-re
  1372. "//"
  1373. opascal-spaces-re)
  1374. comment-end (if (opascal-is-literal-end comment-end)
  1375. ;; Don't include the trailing newline.
  1376. (1- comment-end)
  1377. comment-end)))
  1378. ;; Advance our marked point after inserted spaces.
  1379. (set-marker-insertion-type marked-point t)
  1380. ;; Ensure we can modify the buffer
  1381. (goto-char content-start)
  1382. (insert " ")
  1383. (delete-char -1)
  1384. (narrow-to-region content-start comment-end)
  1385. ;; Strip off the comment prefixes
  1386. (setq p (point-min))
  1387. (while (when (< p (point-max))
  1388. (goto-char p)
  1389. (re-search-forward content-prefix-re nil t))
  1390. (replace-match "" nil nil)
  1391. (setq p (1+ (point))))
  1392. ;; add an extra line to prevent the fill from doing it for us.
  1393. (goto-char (point-max))
  1394. (insert "\n")
  1395. ;; Fill the comment contents.
  1396. (let ((fill-column (- fill-column content-indent)))
  1397. (fill-region (point-min) (point-max)))
  1398. (goto-char (point-max))
  1399. (delete-char -1)
  1400. ;; Restore comment prefixes.
  1401. (goto-char (point-min))
  1402. (end-of-line) ; Don't reset the first line.
  1403. (setq p (point))
  1404. (while (when (< p (point-max))
  1405. (goto-char p)
  1406. (re-search-forward "^" nil t))
  1407. (replace-match content-prefix nil nil)
  1408. (setq p (1+ (point))))
  1409. (setq comment-end (point-max))
  1410. (widen)
  1411. ;; Restore our position
  1412. (goto-char marked-point)
  1413. (set-marker marked-point nil)))))))
  1414. (defun opascal-new-comment-line ()
  1415. "If in a // comment, do a newline, indented such that one is still in the
  1416. comment block. If not in a // comment, just does a normal newline."
  1417. (interactive)
  1418. (let ((comment (opascal-current-token)))
  1419. (if (not (eq 'comment-single-line (opascal-token-kind comment)))
  1420. ;; Not in a // comment. Just do the normal newline.
  1421. (newline)
  1422. (let* ((start-comment (opascal-comment-block-start comment))
  1423. (comment-start (opascal-token-start start-comment))
  1424. (content-start (opascal-comment-content-start start-comment))
  1425. (prefix
  1426. (concat (make-string (opascal-column-of comment-start) ?\s) "//"
  1427. (make-string (- content-start comment-start 2) ?\s))))
  1428. (delete-horizontal-space)
  1429. (insert "\n" prefix)))))
  1430. (defun opascal-match-token (token limit)
  1431. ;; Sets the match region used by (match-string 0) and friends to the token's
  1432. ;; region. Sets the current point to the end of the token (or limit).
  1433. (set-match-data nil)
  1434. (if token
  1435. (let ((end (min (opascal-token-end token) limit)))
  1436. (set-match-data (list (opascal-token-start token) end))
  1437. (goto-char end)
  1438. token)))
  1439. (defconst opascal-font-lock-keywords
  1440. `(("\\_<\\(function\\|pro\\(cedure\\|gram\\)\\)[ \t]+\\([[:alpha:]][[:alnum:]_]*\\)"
  1441. (1 font-lock-keyword-face) (3 font-lock-function-name-face))
  1442. ,(concat "\\_<" (regexp-opt (mapcar #'symbol-name opascal-keywords))
  1443. "\\_>")))
  1444. (defconst opascal-font-lock-defaults
  1445. '(opascal-font-lock-keywords
  1446. nil ; Syntactic fontification does apply.
  1447. nil ; Don't care about case since we don't use regexps to find tokens.
  1448. nil ; Syntax alists don't apply.
  1449. nil ; Syntax begin movement doesn't apply.
  1450. )
  1451. "OPascal mode font-lock defaults. Syntactic fontification is ignored.")
  1452. (defconst opascal--syntax-propertize
  1453. (syntax-propertize-rules
  1454. ;; The syntax-table settings are too coarse and end up treating /* and (/
  1455. ;; as comment starters. Fix it here by removing the "2" from the syntax
  1456. ;; of the second char of such sequences.
  1457. ("/\\(\\*\\)" (1 ". 3b"))
  1458. ("(\\(\\/\\)" (1 (prog1 ". 1c" (forward-char -1) nil)))
  1459. ;; Pascal uses '' and "" rather than \' and \" to escape quotes.
  1460. ("''\\|\"\"" (0 (if (save-excursion
  1461. (nth 3 (syntax-ppss (match-beginning 0))))
  1462. (string-to-syntax ".")
  1463. ;; In case of 3 or more quotes in a row, only advance
  1464. ;; one quote at a time.
  1465. (forward-char -1)
  1466. nil)))))
  1467. (defvar opascal-debug-mode-map
  1468. (let ((kmap (make-sparse-keymap)))
  1469. (dolist (binding '(("n" opascal-debug-goto-next-token)
  1470. ("p" opascal-debug-goto-previous-token)
  1471. ("t" opascal-debug-show-current-token)
  1472. ("T" opascal-debug-tokenize-buffer)
  1473. ("W" opascal-debug-tokenize-window)
  1474. ("g" opascal-debug-goto-point)
  1475. ("s" opascal-debug-show-current-string)))
  1476. (define-key kmap (car binding) (cadr binding)))
  1477. kmap)
  1478. "Keystrokes for OPascal mode debug commands.")
  1479. (defvar opascal-mode-map
  1480. (let ((kmap (make-sparse-keymap)))
  1481. (dolist (binding
  1482. (list ;; '("\C-cd" opascal-find-current-def)
  1483. ;; '("\C-cx" opascal-find-current-xdef)
  1484. ;; '("\C-cb" opascal-find-current-body)
  1485. '("\C-cu" opascal-find-unit)
  1486. '("\M-q" opascal-fill-comment)
  1487. '("\M-j" opascal-new-comment-line)
  1488. ;; Debug bindings:
  1489. (list "\C-c\C-d" opascal-debug-mode-map)))
  1490. (define-key kmap (car binding) (cadr binding)))
  1491. kmap)
  1492. "Keymap used in OPascal mode.")
  1493. (define-obsolete-variable-alias 'delphi-mode-hook 'opascal-mode-hook "24.4")
  1494. ;;;###autoload
  1495. (define-obsolete-function-alias 'delphi-mode 'opascal-mode "24.4")
  1496. ;;;###autoload
  1497. (define-derived-mode opascal-mode prog-mode "OPascal"
  1498. "Major mode for editing OPascal code.\\<opascal-mode-map>
  1499. \\[opascal-find-unit]\t- Search for a OPascal source file.
  1500. \\[opascal-fill-comment]\t- Fill the current comment.
  1501. \\[opascal-new-comment-line]\t- If in a // comment, do a new comment line.
  1502. \\[indent-region] also works for indenting a whole region.
  1503. Customization:
  1504. `opascal-indent-level' (default 3)
  1505. Indentation of OPascal statements with respect to containing block.
  1506. `opascal-compound-block-indent' (default 0)
  1507. Extra indentation for blocks in compound statements.
  1508. `opascal-case-label-indent' (default 0)
  1509. Extra indentation for case statement labels.
  1510. `opascal-search-path' (default .)
  1511. Directories to search when finding external units.
  1512. `opascal-verbose' (default nil)
  1513. If true then OPascal token processing progress is reported to the user.
  1514. Coloring:
  1515. `opascal-keyword-face' (default `font-lock-keyword-face')
  1516. Face used to color OPascal keywords."
  1517. ;; Buffer locals:
  1518. (setq-local indent-line-function #'opascal-indent-line)
  1519. (setq-local comment-indent-function #'opascal-indent-line)
  1520. (setq-local case-fold-search t)
  1521. (setq-local opascal-progress-last-reported-point nil)
  1522. (setq-local font-lock-defaults opascal-font-lock-defaults)
  1523. (setq-local tab-always-indent opascal-tab-always-indents)
  1524. (setq-local syntax-propertize-function opascal--syntax-propertize)
  1525. (setq-local comment-start "// ")
  1526. (setq-local comment-start-skip "\\(?://\\|(\\*\\|{\\)[ \t]*")
  1527. (setq-local comment-end-skip "[ \t]*\\(?:\n\\|\\*)\\|}\\)"))
  1528. (provide 'opascal)
  1529. ;;; opascal.el ends here