ruby-mode.el 86 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276
  1. ;;; ruby-mode.el --- Major mode for editing Ruby files
  2. ;; Copyright (C) 1994-2015 Free Software Foundation, Inc.
  3. ;; Authors: Yukihiro Matsumoto
  4. ;; Nobuyoshi Nakada
  5. ;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode
  6. ;; Created: Fri Feb 4 14:49:13 JST 1994
  7. ;; Keywords: languages ruby
  8. ;; Version: 1.2
  9. ;; This file is part of GNU Emacs.
  10. ;; GNU Emacs is free software: you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation, either version 3 of the License, or
  13. ;; (at your option) any later version.
  14. ;; GNU Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  20. ;;; Commentary:
  21. ;; Provides font-locking, indentation support, and navigation for Ruby code.
  22. ;;
  23. ;; If you're installing manually, you should add this to your .emacs
  24. ;; file after putting it on your load path:
  25. ;;
  26. ;; (autoload 'ruby-mode "ruby-mode" "Major mode for ruby files" t)
  27. ;; (add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
  28. ;; (add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
  29. ;;
  30. ;; Still needs more docstrings; search below for TODO.
  31. ;;; Code:
  32. (defgroup ruby nil
  33. "Major mode for editing Ruby code."
  34. :prefix "ruby-"
  35. :group 'languages)
  36. (defconst ruby-block-beg-keywords
  37. '("class" "module" "def" "if" "unless" "case" "while" "until" "for" "begin" "do")
  38. "Keywords at the beginning of blocks.")
  39. (defconst ruby-block-beg-re
  40. (regexp-opt ruby-block-beg-keywords)
  41. "Regexp to match the beginning of blocks.")
  42. (defconst ruby-non-block-do-re
  43. (regexp-opt '("while" "until" "for" "rescue") 'symbols)
  44. "Regexp to match keywords that nest without blocks.")
  45. (defconst ruby-indent-beg-re
  46. (concat "^\\(\\s *" (regexp-opt '("class" "module" "def")) "\\|"
  47. (regexp-opt '("if" "unless" "case" "while" "until" "for" "begin"))
  48. "\\)\\_>")
  49. "Regexp to match where the indentation gets deeper.")
  50. (defconst ruby-modifier-beg-keywords
  51. '("if" "unless" "while" "until")
  52. "Modifiers that are the same as the beginning of blocks.")
  53. (defconst ruby-modifier-beg-re
  54. (regexp-opt ruby-modifier-beg-keywords)
  55. "Regexp to match modifiers same as the beginning of blocks.")
  56. (defconst ruby-modifier-re
  57. (regexp-opt (cons "rescue" ruby-modifier-beg-keywords))
  58. "Regexp to match modifiers.")
  59. (defconst ruby-block-mid-keywords
  60. '("then" "else" "elsif" "when" "rescue" "ensure")
  61. "Keywords where the indentation gets shallower in middle of block statements.")
  62. (defconst ruby-block-mid-re
  63. (regexp-opt ruby-block-mid-keywords)
  64. "Regexp to match where the indentation gets shallower in middle of block statements.")
  65. (defconst ruby-block-op-keywords
  66. '("and" "or" "not")
  67. "Regexp to match boolean keywords.")
  68. (defconst ruby-block-hanging-re
  69. (regexp-opt (append ruby-modifier-beg-keywords ruby-block-op-keywords))
  70. "Regexp to match hanging block modifiers.")
  71. (defconst ruby-block-end-re "\\_<end\\_>")
  72. (defconst ruby-defun-beg-re
  73. '"\\(def\\|class\\|module\\)"
  74. "Regexp to match the beginning of a defun, in the general sense.")
  75. (defconst ruby-singleton-class-re
  76. "class\\s *<<"
  77. "Regexp to match the beginning of a singleton class context.")
  78. (eval-and-compile
  79. (defconst ruby-here-doc-beg-re
  80. "\\(<\\)<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)"
  81. "Regexp to match the beginning of a heredoc.")
  82. (defconst ruby-expression-expansion-re
  83. "\\(?:[^\\]\\|\\=\\)\\(\\\\\\\\\\)*\\(#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\|\\$[^a-zA-Z \n]\\)\\)"))
  84. (defun ruby-here-doc-end-match ()
  85. "Return a regexp to find the end of a heredoc.
  86. This should only be called after matching against `ruby-here-doc-beg-re'."
  87. (concat "^"
  88. (if (match-string 2) "[ \t]*" nil)
  89. (regexp-quote
  90. (or (match-string 4)
  91. (match-string 5)
  92. (match-string 6)))))
  93. (defconst ruby-delimiter
  94. (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\_<\\("
  95. ruby-block-beg-re
  96. "\\)\\_>\\|" ruby-block-end-re
  97. "\\|^=begin\\|" ruby-here-doc-beg-re))
  98. (defconst ruby-negative
  99. (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|"
  100. ruby-block-end-re "\\|}\\|\\]\\)")
  101. "Regexp to match where the indentation gets shallower.")
  102. (defconst ruby-operator-re "[-,.+*/%&|^~=<>:]\\|\\\\$"
  103. "Regexp to match operators.")
  104. (defconst ruby-symbol-chars "a-zA-Z0-9_"
  105. "List of characters that symbol names may contain.")
  106. (defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]")
  107. "Regexp to match symbols.")
  108. (defvar ruby-use-smie t)
  109. (defvar ruby-mode-map
  110. (let ((map (make-sparse-keymap)))
  111. (unless ruby-use-smie
  112. (define-key map (kbd "M-C-b") 'ruby-backward-sexp)
  113. (define-key map (kbd "M-C-f") 'ruby-forward-sexp)
  114. (define-key map (kbd "M-C-q") 'ruby-indent-exp))
  115. (when ruby-use-smie
  116. (define-key map (kbd "M-C-d") 'smie-down-list))
  117. (define-key map (kbd "M-C-p") 'ruby-beginning-of-block)
  118. (define-key map (kbd "M-C-n") 'ruby-end-of-block)
  119. (define-key map (kbd "C-c {") 'ruby-toggle-block)
  120. (define-key map (kbd "C-c '") 'ruby-toggle-string-quotes)
  121. map)
  122. "Keymap used in Ruby mode.")
  123. (easy-menu-define
  124. ruby-mode-menu
  125. ruby-mode-map
  126. "Ruby Mode Menu"
  127. '("Ruby"
  128. ["Beginning of Block" ruby-beginning-of-block t]
  129. ["End of Block" ruby-end-of-block t]
  130. ["Toggle Block" ruby-toggle-block t]
  131. "--"
  132. ["Toggle String Quotes" ruby-toggle-string-quotes t]
  133. "--"
  134. ["Backward Sexp" ruby-backward-sexp
  135. :visible (not ruby-use-smie)]
  136. ["Backward Sexp" backward-sexp
  137. :visible ruby-use-smie]
  138. ["Forward Sexp" ruby-forward-sexp
  139. :visible (not ruby-use-smie)]
  140. ["Forward Sexp" forward-sexp
  141. :visible ruby-use-smie]
  142. ["Indent Sexp" ruby-indent-exp
  143. :visible (not ruby-use-smie)]
  144. ["Indent Sexp" prog-indent-sexp
  145. :visible ruby-use-smie]))
  146. (defvar ruby-mode-syntax-table
  147. (let ((table (make-syntax-table)))
  148. (modify-syntax-entry ?\' "\"" table)
  149. (modify-syntax-entry ?\" "\"" table)
  150. (modify-syntax-entry ?\` "\"" table)
  151. (modify-syntax-entry ?# "<" table)
  152. (modify-syntax-entry ?\n ">" table)
  153. (modify-syntax-entry ?\\ "\\" table)
  154. (modify-syntax-entry ?$ "." table)
  155. (modify-syntax-entry ?_ "_" table)
  156. (modify-syntax-entry ?: "_" table)
  157. (modify-syntax-entry ?< "." table)
  158. (modify-syntax-entry ?> "." table)
  159. (modify-syntax-entry ?& "." table)
  160. (modify-syntax-entry ?| "." table)
  161. (modify-syntax-entry ?% "." table)
  162. (modify-syntax-entry ?= "." table)
  163. (modify-syntax-entry ?/ "." table)
  164. (modify-syntax-entry ?+ "." table)
  165. (modify-syntax-entry ?* "." table)
  166. (modify-syntax-entry ?- "." table)
  167. (modify-syntax-entry ?\; "." table)
  168. (modify-syntax-entry ?\( "()" table)
  169. (modify-syntax-entry ?\) ")(" table)
  170. (modify-syntax-entry ?\{ "(}" table)
  171. (modify-syntax-entry ?\} "){" table)
  172. (modify-syntax-entry ?\[ "(]" table)
  173. (modify-syntax-entry ?\] ")[" table)
  174. table)
  175. "Syntax table to use in Ruby mode.")
  176. (defcustom ruby-indent-tabs-mode nil
  177. "Indentation can insert tabs in Ruby mode if this is non-nil."
  178. :type 'boolean
  179. :group 'ruby
  180. :safe 'booleanp)
  181. (defcustom ruby-indent-level 2
  182. "Indentation of Ruby statements."
  183. :type 'integer
  184. :group 'ruby
  185. :safe 'integerp)
  186. (defcustom ruby-comment-column (default-value 'comment-column)
  187. "Indentation column of comments."
  188. :type 'integer
  189. :group 'ruby
  190. :safe 'integerp)
  191. (defconst ruby-alignable-keywords '(if while unless until begin case for def)
  192. "Keywords that can be used in `ruby-align-to-stmt-keywords'.")
  193. (defcustom ruby-align-to-stmt-keywords '(def)
  194. "Keywords after which we align the expression body to statement.
  195. When nil, an expression that begins with one these keywords is
  196. indented to the column of the keyword. Example:
  197. tee = if foo
  198. bar
  199. else
  200. qux
  201. end
  202. If this value is t or contains a symbol with the name of given
  203. keyword, the expression is indented to align to the beginning of
  204. the statement:
  205. tee = if foo
  206. bar
  207. else
  208. qux
  209. end
  210. Only has effect when `ruby-use-smie' is t.
  211. "
  212. :type `(choice
  213. (const :tag "None" nil)
  214. (const :tag "All" t)
  215. (repeat :tag "User defined"
  216. (choice ,@(mapcar
  217. (lambda (kw) (list 'const kw))
  218. ruby-alignable-keywords))))
  219. :group 'ruby
  220. :safe 'listp
  221. :version "24.4")
  222. (defcustom ruby-align-chained-calls nil
  223. "If non-nil, align chained method calls.
  224. Each method call on a separate line will be aligned to the column
  225. of its parent.
  226. Only has effect when `ruby-use-smie' is t."
  227. :type 'boolean
  228. :group 'ruby
  229. :safe 'booleanp
  230. :version "24.4")
  231. (defcustom ruby-deep-arglist t
  232. "Deep indent lists in parenthesis when non-nil.
  233. Also ignores spaces after parenthesis when `space'.
  234. Only has effect when `ruby-use-smie' is nil."
  235. :type 'boolean
  236. :group 'ruby
  237. :safe 'booleanp)
  238. ;; FIXME Woefully under documented. What is the point of the last t?.
  239. (defcustom ruby-deep-indent-paren '(?\( ?\[ ?\] t)
  240. "Deep indent lists in parenthesis when non-nil.
  241. The value t means continuous line.
  242. Also ignores spaces after parenthesis when `space'.
  243. Only has effect when `ruby-use-smie' is nil."
  244. :type '(choice (const nil)
  245. character
  246. (repeat (choice character
  247. (cons character (choice (const nil)
  248. (const t)))
  249. (const t) ; why?
  250. )))
  251. :group 'ruby)
  252. (defcustom ruby-deep-indent-paren-style 'space
  253. "Default deep indent style.
  254. Only has effect when `ruby-use-smie' is nil."
  255. :type '(choice (const t) (const nil) (const space))
  256. :group 'ruby)
  257. (defcustom ruby-encoding-map
  258. '((us-ascii . nil) ;; Do not put coding: us-ascii
  259. (shift-jis . cp932) ;; Emacs charset name of Shift_JIS
  260. (shift_jis . cp932) ;; MIME charset name of Shift_JIS
  261. (japanese-cp932 . cp932)) ;; Emacs charset name of CP932
  262. "Alist to map encoding name from Emacs to Ruby.
  263. Associating an encoding name with nil means it needs not be
  264. explicitly declared in magic comment."
  265. :type '(repeat (cons (symbol :tag "From") (symbol :tag "To")))
  266. :group 'ruby)
  267. (defcustom ruby-insert-encoding-magic-comment t
  268. "Insert a magic Ruby encoding comment upon save if this is non-nil.
  269. The encoding will be auto-detected. The format of the encoding comment
  270. is customizable via `ruby-encoding-magic-comment-style'.
  271. When set to `always-utf8' an utf-8 comment will always be added,
  272. even if it's not required."
  273. :type 'boolean :group 'ruby)
  274. (defcustom ruby-encoding-magic-comment-style 'ruby
  275. "The style of the magic encoding comment to use."
  276. :type '(choice
  277. (const :tag "Emacs Style" emacs)
  278. (const :tag "Ruby Style" ruby)
  279. (const :tag "Custom Style" custom))
  280. :group 'ruby
  281. :version "24.4")
  282. (defcustom ruby-custom-encoding-magic-comment-template "# encoding: %s"
  283. "A custom encoding comment template.
  284. It is used when `ruby-encoding-magic-comment-style' is set to `custom'."
  285. :type 'string
  286. :group 'ruby
  287. :version "24.4")
  288. (defcustom ruby-use-encoding-map t
  289. "Use `ruby-encoding-map' to set encoding magic comment if this is non-nil."
  290. :type 'boolean :group 'ruby)
  291. ;;; SMIE support
  292. (require 'smie)
  293. ;; Here's a simplified BNF grammar, for reference:
  294. ;; http://www.cse.buffalo.edu/~regan/cse305/RubyBNF.pdf
  295. (defconst ruby-smie-grammar
  296. (smie-prec2->grammar
  297. (smie-merge-prec2s
  298. (smie-bnf->prec2
  299. '((id)
  300. (insts (inst) (insts ";" insts))
  301. (inst (exp) (inst "iuwu-mod" exp)
  302. ;; Somewhat incorrect (both can be used multiple times),
  303. ;; but avoids lots of conflicts:
  304. (exp "and" exp) (exp "or" exp))
  305. (exp (exp1) (exp "," exp) (exp "=" exp)
  306. (id " @ " exp))
  307. (exp1 (exp2) (exp2 "?" exp1 ":" exp1))
  308. (exp2 (exp3) (exp3 "." exp2))
  309. (exp3 ("def" insts "end")
  310. ("begin" insts-rescue-insts "end")
  311. ("do" insts "end")
  312. ("class" insts "end") ("module" insts "end")
  313. ("for" for-body "end")
  314. ("[" expseq "]")
  315. ("{" hashvals "}")
  316. ("{" insts "}")
  317. ("while" insts "end")
  318. ("until" insts "end")
  319. ("unless" insts "end")
  320. ("if" if-body "end")
  321. ("case" cases "end"))
  322. (formal-params ("opening-|" exp "closing-|"))
  323. (for-body (for-head ";" insts))
  324. (for-head (id "in" exp))
  325. (cases (exp "then" insts)
  326. (cases "when" cases) (insts "else" insts))
  327. (expseq (exp) );;(expseq "," expseq)
  328. (hashvals (id "=>" exp1) (hashvals "," hashvals))
  329. (insts-rescue-insts (insts)
  330. (insts-rescue-insts "rescue" insts-rescue-insts)
  331. (insts-rescue-insts "ensure" insts-rescue-insts))
  332. (itheni (insts) (exp "then" insts))
  333. (ielsei (itheni) (itheni "else" insts))
  334. (if-body (ielsei) (if-body "elsif" if-body)))
  335. '((nonassoc "in") (assoc ";") (right " @ ")
  336. (assoc ",") (right "="))
  337. '((assoc "when"))
  338. '((assoc "elsif"))
  339. '((assoc "rescue" "ensure"))
  340. '((assoc ",")))
  341. (smie-precs->prec2
  342. '((right "=")
  343. (right "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^="
  344. "<<=" ">>=" "&&=" "||=")
  345. (left ".." "...")
  346. (left "+" "-")
  347. (left "*" "/" "%" "**")
  348. (left "&&" "||")
  349. (left "^" "&" "|")
  350. (nonassoc "<=>")
  351. (nonassoc ">" ">=" "<" "<=")
  352. (nonassoc "==" "===" "!=")
  353. (nonassoc "=~" "!~")
  354. (left "<<" ">>")
  355. (right "."))))))
  356. (defun ruby-smie--bosp ()
  357. (save-excursion (skip-chars-backward " \t")
  358. (or (bolp) (memq (char-before) '(?\; ?=)))))
  359. (defun ruby-smie--implicit-semi-p ()
  360. (save-excursion
  361. (skip-chars-backward " \t")
  362. (not (or (bolp)
  363. (memq (char-before) '(?\[ ?\())
  364. (and (memq (char-before)
  365. '(?\; ?- ?+ ?* ?/ ?: ?. ?, ?\\ ?& ?> ?< ?% ?~ ?^))
  366. ;; Not a binary operator symbol.
  367. (not (eq (char-before (1- (point))) ?:))
  368. ;; Not the end of a regexp or a percent literal.
  369. (not (memq (car (syntax-after (1- (point)))) '(7 15))))
  370. (and (eq (char-before) ?\?)
  371. (equal (save-excursion (ruby-smie--backward-token)) "?"))
  372. (and (eq (char-before) ?=)
  373. ;; Not a symbol :==, :!=, or a foo= method.
  374. (string-match "\\`\\s." (save-excursion
  375. (ruby-smie--backward-token))))
  376. (and (eq (char-before) ?|)
  377. (member (save-excursion (ruby-smie--backward-token))
  378. '("|" "||")))
  379. (and (eq (car (syntax-after (1- (point)))) 2)
  380. (member (save-excursion (ruby-smie--backward-token))
  381. '("iuwu-mod" "and" "or")))
  382. (save-excursion
  383. (forward-comment 1)
  384. (eq (char-after) ?.))))))
  385. (defun ruby-smie--redundant-do-p (&optional skip)
  386. (save-excursion
  387. (if skip (backward-word 1))
  388. (member (nth 2 (smie-backward-sexp ";")) '("while" "until" "for"))))
  389. (defun ruby-smie--opening-pipe-p ()
  390. (save-excursion
  391. (if (eq ?| (char-before)) (forward-char -1))
  392. (skip-chars-backward " \t\n")
  393. (or (eq ?\{ (char-before))
  394. (looking-back "\\_<do" (- (point) 2)))))
  395. (defun ruby-smie--closing-pipe-p ()
  396. (save-excursion
  397. (if (eq ?| (char-before)) (forward-char -1))
  398. (and (re-search-backward "|" (line-beginning-position) t)
  399. (ruby-smie--opening-pipe-p))))
  400. (defun ruby-smie--args-separator-p (pos)
  401. (and
  402. (< pos (line-end-position))
  403. (or (eq (char-syntax (preceding-char)) '?w)
  404. ;; FIXME: Check that the preceding token is not a keyword.
  405. ;; This isn't very important most of the time, though.
  406. (and (memq (preceding-char) '(?! ??))
  407. (eq (char-syntax (char-before (1- (point)))) '?w)))
  408. (save-excursion
  409. (goto-char pos)
  410. (or (and (eq (char-syntax (char-after)) ?w)
  411. (not (looking-at (regexp-opt '("unless" "if" "while" "until" "or"
  412. "else" "elsif" "do" "end" "and")
  413. 'symbols))))
  414. (memq (car (syntax-after pos)) '(7 15))
  415. (looking-at "[([]\\|[-+!~]\\sw\\|:\\(?:\\sw\\|\\s.\\)")))))
  416. (defun ruby-smie--at-dot-call ()
  417. (and (eq ?w (char-syntax (following-char)))
  418. (eq (char-before) ?.)
  419. (not (eq (char-before (1- (point))) ?.))))
  420. (defun ruby-smie--forward-token ()
  421. (let ((pos (point)))
  422. (skip-chars-forward " \t")
  423. (cond
  424. ((and (looking-at "\n") (looking-at "\\s\"")) ;A heredoc.
  425. ;; Tokenize the whole heredoc as semicolon.
  426. (goto-char (scan-sexps (point) 1))
  427. ";")
  428. ((and (looking-at "[\n#]")
  429. (ruby-smie--implicit-semi-p)) ;Only add implicit ; when needed.
  430. (if (eolp) (forward-char 1) (forward-comment 1))
  431. ";")
  432. (t
  433. (forward-comment (point-max))
  434. (cond
  435. ((and (< pos (point))
  436. (save-excursion
  437. (ruby-smie--args-separator-p (prog1 (point) (goto-char pos)))))
  438. " @ ")
  439. ((looking-at ":\\s.+")
  440. (goto-char (match-end 0)) (match-string 0)) ;bug#15208.
  441. ((looking-at "\\s\"") "") ;A string.
  442. (t
  443. (let ((dot (ruby-smie--at-dot-call))
  444. (tok (smie-default-forward-token)))
  445. (when dot
  446. (setq tok (concat "." tok)))
  447. (cond
  448. ((member tok '("unless" "if" "while" "until"))
  449. (if (save-excursion (forward-word -1) (ruby-smie--bosp))
  450. tok "iuwu-mod"))
  451. ((string-match-p "\\`|[*&]?\\'" tok)
  452. (forward-char (- 1 (length tok)))
  453. (setq tok "|")
  454. (cond
  455. ((ruby-smie--opening-pipe-p) "opening-|")
  456. ((ruby-smie--closing-pipe-p) "closing-|")
  457. (t tok)))
  458. ((and (equal tok "") (looking-at "\\\\\n"))
  459. (goto-char (match-end 0)) (ruby-smie--forward-token))
  460. ((equal tok "do")
  461. (cond
  462. ((not (ruby-smie--redundant-do-p 'skip)) tok)
  463. ((> (save-excursion (forward-comment (point-max)) (point))
  464. (line-end-position))
  465. (ruby-smie--forward-token)) ;Fully redundant.
  466. (t ";")))
  467. (t tok)))))))))
  468. (defun ruby-smie--backward-token ()
  469. (let ((pos (point)))
  470. (forward-comment (- (point)))
  471. (cond
  472. ((and (> pos (line-end-position)) (ruby-smie--implicit-semi-p))
  473. (skip-chars-forward " \t") ";")
  474. ((and (bolp) (not (bobp))) ;Presumably a heredoc.
  475. ;; Tokenize the whole heredoc as semicolon.
  476. (goto-char (scan-sexps (point) -1))
  477. ";")
  478. ((and (> pos (point)) (not (bolp))
  479. (ruby-smie--args-separator-p pos))
  480. ;; We have "ID SPC ID", which is a method call, but it binds less tightly
  481. ;; than commas, since a method call can also be "ID ARG1, ARG2, ARG3".
  482. ;; In some textbooks, "e1 @ e2" is used to mean "call e1 with arg e2".
  483. " @ ")
  484. (t
  485. (let ((tok (smie-default-backward-token))
  486. (dot (ruby-smie--at-dot-call)))
  487. (when dot
  488. (setq tok (concat "." tok)))
  489. (when (and (eq ?: (char-before)) (string-match "\\`\\s." tok))
  490. (forward-char -1) (setq tok (concat ":" tok))) ;; bug#15208.
  491. (cond
  492. ((member tok '("unless" "if" "while" "until"))
  493. (if (ruby-smie--bosp)
  494. tok "iuwu-mod"))
  495. ((equal tok "|")
  496. (cond
  497. ((ruby-smie--opening-pipe-p) "opening-|")
  498. ((ruby-smie--closing-pipe-p) "closing-|")
  499. (t tok)))
  500. ((string-match-p "\\`|[*&]\\'" tok)
  501. (forward-char 1)
  502. (substring tok 1))
  503. ((and (equal tok "") (eq ?\\ (char-before)) (looking-at "\n"))
  504. (forward-char -1) (ruby-smie--backward-token))
  505. ((equal tok "do")
  506. (cond
  507. ((not (ruby-smie--redundant-do-p)) tok)
  508. ((> (save-excursion (forward-word 1)
  509. (forward-comment (point-max)) (point))
  510. (line-end-position))
  511. (ruby-smie--backward-token)) ;Fully redundant.
  512. (t ";")))
  513. (t tok)))))))
  514. (defun ruby-smie--indent-to-stmt ()
  515. (save-excursion
  516. (smie-backward-sexp ";")
  517. (cons 'column (smie-indent-virtual))))
  518. (defun ruby-smie--indent-to-stmt-p (keyword)
  519. (or (eq t ruby-align-to-stmt-keywords)
  520. (memq (intern keyword) ruby-align-to-stmt-keywords)))
  521. (defun ruby-smie-rules (kind token)
  522. (pcase (cons kind token)
  523. (`(:elem . basic) ruby-indent-level)
  524. ;; "foo" "bar" is the concatenation of the two strings, so the second
  525. ;; should be aligned with the first.
  526. (`(:elem . args) (if (looking-at "\\s\"") 0))
  527. ;; (`(:after . ",") (smie-rule-separator kind))
  528. (`(:before . ";")
  529. (cond
  530. ((smie-rule-parent-p "def" "begin" "do" "class" "module" "for"
  531. "while" "until" "unless"
  532. "if" "then" "elsif" "else" "when"
  533. "rescue" "ensure" "{")
  534. (smie-rule-parent ruby-indent-level))
  535. ;; For (invalid) code between switch and case.
  536. ;; (if (smie-parent-p "switch") 4)
  537. ))
  538. (`(:before . ,(or `"(" `"[" `"{"))
  539. (cond
  540. ((and (equal token "{")
  541. (not (smie-rule-prev-p "(" "{" "[" "," "=>" "=" "return" ";"))
  542. (save-excursion
  543. (forward-comment -1)
  544. (not (eq (preceding-char) ?:))))
  545. ;; Curly block opener.
  546. (ruby-smie--indent-to-stmt))
  547. ((smie-rule-hanging-p)
  548. ;; Treat purely syntactic block-constructs as being part of their parent,
  549. ;; when the opening token is hanging and the parent is not an
  550. ;; open-paren.
  551. (cond
  552. ((eq (car (smie-indent--parent)) t) nil)
  553. ;; When after `.', let's always de-indent,
  554. ;; because when `.' is inside the line, the
  555. ;; additional indentation from it looks out of place.
  556. ((smie-rule-parent-p ".")
  557. (let (smie--parent)
  558. (save-excursion
  559. ;; Traverse up the parents until the parent is "." at
  560. ;; indentation, or any other token.
  561. (while (and (let ((parent (smie-indent--parent)))
  562. (goto-char (cadr parent))
  563. (save-excursion
  564. (unless (integerp (car parent)) (forward-char -1))
  565. (not (ruby-smie--bosp))))
  566. (progn
  567. (setq smie--parent nil)
  568. (smie-rule-parent-p "."))))
  569. (smie-rule-parent))))
  570. (t (smie-rule-parent))))))
  571. (`(:after . ,(or `"(" "[" "{"))
  572. ;; FIXME: Shouldn't this be the default behavior of
  573. ;; `smie-indent-after-keyword'?
  574. (save-excursion
  575. (forward-char 1)
  576. (skip-chars-forward " \t")
  577. ;; `smie-rule-hanging-p' is not good enough here,
  578. ;; because we want to reject hanging tokens at bol, too.
  579. (unless (or (eolp) (forward-comment 1))
  580. (cons 'column (current-column)))))
  581. (`(:before . " @ ")
  582. (save-excursion
  583. (skip-chars-forward " \t")
  584. (cons 'column (current-column))))
  585. (`(:before . "do") (ruby-smie--indent-to-stmt))
  586. (`(:before . ".")
  587. (if (smie-rule-sibling-p)
  588. (and ruby-align-chained-calls 0)
  589. ruby-indent-level))
  590. (`(:before . ,(or `"else" `"then" `"elsif" `"rescue" `"ensure"))
  591. (smie-rule-parent))
  592. (`(:before . "when")
  593. ;; Align to the previous `when', but look up the virtual
  594. ;; indentation of `case'.
  595. (if (smie-rule-sibling-p) 0 (smie-rule-parent)))
  596. (`(:after . ,(or "=" "iuwu-mod" "+" "-" "*" "/" "&&" "||" "%" "**" "^" "&"
  597. "<=>" ">" "<" ">=" "<=" "==" "===" "!=" "<<" ">>"
  598. "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^=" "|"
  599. "<<=" ">>=" "&&=" "||=" "and" "or"))
  600. (and (smie-rule-parent-p ";" nil)
  601. (smie-indent--hanging-p)
  602. ruby-indent-level))
  603. (`(:after . ,(or "?" ":")) ruby-indent-level)
  604. (`(:before . ,(guard (memq (intern-soft token) ruby-alignable-keywords)))
  605. (when (not (ruby--at-indentation-p))
  606. (if (ruby-smie--indent-to-stmt-p token)
  607. (ruby-smie--indent-to-stmt)
  608. (cons 'column (current-column)))))
  609. ))
  610. (defun ruby--at-indentation-p (&optional point)
  611. (save-excursion
  612. (unless point (setq point (point)))
  613. (forward-line 0)
  614. (skip-chars-forward " \t")
  615. (eq (point) point)))
  616. (defun ruby-imenu-create-index-in-block (prefix beg end)
  617. "Create an imenu index of methods inside a block."
  618. (let ((index-alist '()) (case-fold-search nil)
  619. name next pos decl sing)
  620. (goto-char beg)
  621. (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^(<\n ]+\\)\\|\\(def\\|alias\\)\\s +\\([^(\n ]+\\)\\)" end t)
  622. (setq sing (match-beginning 3))
  623. (setq decl (match-string 5))
  624. (setq next (match-end 0))
  625. (setq name (or (match-string 4) (match-string 6)))
  626. (setq pos (match-beginning 0))
  627. (cond
  628. ((string= "alias" decl)
  629. (if prefix (setq name (concat prefix name)))
  630. (push (cons name pos) index-alist))
  631. ((string= "def" decl)
  632. (if prefix
  633. (setq name
  634. (cond
  635. ((string-match "^self\\." name)
  636. (concat (substring prefix 0 -1) (substring name 4)))
  637. (t (concat prefix name)))))
  638. (push (cons name pos) index-alist)
  639. (ruby-accurate-end-of-block end))
  640. (t
  641. (if (string= "self" name)
  642. (if prefix (setq name (substring prefix 0 -1)))
  643. (if prefix (setq name (concat (substring prefix 0 -1) "::" name)))
  644. (push (cons name pos) index-alist))
  645. (ruby-accurate-end-of-block end)
  646. (setq beg (point))
  647. (setq index-alist
  648. (nconc (ruby-imenu-create-index-in-block
  649. (concat name (if sing "." "#"))
  650. next beg) index-alist))
  651. (goto-char beg))))
  652. index-alist))
  653. (defun ruby-imenu-create-index ()
  654. "Create an imenu index of all methods in the buffer."
  655. (nreverse (ruby-imenu-create-index-in-block nil (point-min) nil)))
  656. (defun ruby-accurate-end-of-block (&optional end)
  657. "Jump to the end of the current block or END, whichever is closer."
  658. (let (state
  659. (end (or end (point-max))))
  660. (if ruby-use-smie
  661. (save-restriction
  662. (back-to-indentation)
  663. (narrow-to-region (point) end)
  664. (smie-forward-sexp))
  665. (while (and (setq state (apply 'ruby-parse-partial end state))
  666. (>= (nth 2 state) 0) (< (point) end))))))
  667. (defun ruby-mode-variables ()
  668. "Set up initial buffer-local variables for Ruby mode."
  669. (setq indent-tabs-mode ruby-indent-tabs-mode)
  670. (if ruby-use-smie
  671. (smie-setup ruby-smie-grammar #'ruby-smie-rules
  672. :forward-token #'ruby-smie--forward-token
  673. :backward-token #'ruby-smie--backward-token)
  674. (setq-local indent-line-function 'ruby-indent-line))
  675. (setq-local comment-start "# ")
  676. (setq-local comment-end "")
  677. (setq-local comment-column ruby-comment-column)
  678. (setq-local comment-start-skip "#+ *")
  679. (setq-local parse-sexp-ignore-comments t)
  680. (setq-local parse-sexp-lookup-properties t)
  681. (setq-local paragraph-start (concat "$\\|" page-delimiter))
  682. (setq-local paragraph-separate paragraph-start)
  683. (setq-local paragraph-ignore-fill-prefix t))
  684. (defun ruby--insert-coding-comment (encoding)
  685. "Insert a magic coding comment for ENCODING.
  686. The style of the comment is controlled by `ruby-encoding-magic-comment-style'."
  687. (let ((encoding-magic-comment-template
  688. (pcase ruby-encoding-magic-comment-style
  689. (`ruby "# coding: %s")
  690. (`emacs "# -*- coding: %s -*-")
  691. (`custom
  692. ruby-custom-encoding-magic-comment-template))))
  693. (insert
  694. (format encoding-magic-comment-template encoding)
  695. "\n")))
  696. (defun ruby--detect-encoding ()
  697. (if (eq ruby-insert-encoding-magic-comment 'always-utf8)
  698. "utf-8"
  699. (let ((coding-system
  700. (or save-buffer-coding-system
  701. buffer-file-coding-system)))
  702. (if coding-system
  703. (setq coding-system
  704. (or (coding-system-get coding-system 'mime-charset)
  705. (coding-system-change-eol-conversion coding-system nil))))
  706. (if coding-system
  707. (symbol-name
  708. (if ruby-use-encoding-map
  709. (let ((elt (assq coding-system ruby-encoding-map)))
  710. (if elt (cdr elt) coding-system))
  711. coding-system))
  712. "ascii-8bit"))))
  713. (defun ruby--encoding-comment-required-p ()
  714. (or (eq ruby-insert-encoding-magic-comment 'always-utf8)
  715. (re-search-forward "[^\0-\177]" nil t)))
  716. (defun ruby-mode-set-encoding ()
  717. "Insert a magic comment header with the proper encoding if necessary."
  718. (save-excursion
  719. (widen)
  720. (goto-char (point-min))
  721. (when (ruby--encoding-comment-required-p)
  722. (goto-char (point-min))
  723. (let ((coding-system (ruby--detect-encoding)))
  724. (when coding-system
  725. (if (looking-at "^#!") (beginning-of-line 2))
  726. (cond ((looking-at "\\s *#\\s *.*\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)")
  727. ;; update existing encoding comment if necessary
  728. (unless (string= (match-string 2) coding-system)
  729. (goto-char (match-beginning 2))
  730. (delete-region (point) (match-end 2))
  731. (insert coding-system)))
  732. ((looking-at "\\s *#.*coding\\s *[:=]"))
  733. (t (when ruby-insert-encoding-magic-comment
  734. (ruby--insert-coding-comment coding-system))))
  735. (when (buffer-modified-p)
  736. (basic-save-buffer-1)))))))
  737. (defvar ruby--electric-indent-chars '(?. ?\) ?} ?\]))
  738. (defun ruby--electric-indent-p (char)
  739. (cond
  740. ((memq char ruby--electric-indent-chars)
  741. ;; Reindent after typing a char affecting indentation.
  742. (ruby--at-indentation-p (1- (point))))
  743. ((memq (char-after) ruby--electric-indent-chars)
  744. ;; Reindent after inserting something in front of the above.
  745. (ruby--at-indentation-p (1- (point))))
  746. ((or (and (>= char ?a) (<= char ?z)) (memq char '(?_ ?? ?! ?:)))
  747. (let ((pt (point)))
  748. (save-excursion
  749. (skip-chars-backward "[:alpha:]:_?!")
  750. (and (ruby--at-indentation-p)
  751. (looking-at (regexp-opt (cons "end" ruby-block-mid-keywords)))
  752. ;; Outdent after typing a keyword.
  753. (or (eq (match-end 0) pt)
  754. ;; Reindent if it wasn't a keyword after all.
  755. (eq (match-end 0) (1- pt)))))))))
  756. ;; FIXME: Remove this? It's unused here, but some redefinitions of
  757. ;; `ruby-calculate-indent' in user init files still call it.
  758. (defun ruby-current-indentation ()
  759. "Return the indentation level of current line."
  760. (save-excursion
  761. (beginning-of-line)
  762. (back-to-indentation)
  763. (current-column)))
  764. (defun ruby-indent-line (&optional ignored)
  765. "Correct the indentation of the current Ruby line."
  766. (interactive)
  767. (ruby-indent-to (ruby-calculate-indent)))
  768. (defun ruby-indent-to (column)
  769. "Indent the current line to COLUMN."
  770. (when column
  771. (let (shift top beg)
  772. (and (< column 0) (error "Invalid nesting"))
  773. (setq shift (current-column))
  774. (beginning-of-line)
  775. (setq beg (point))
  776. (back-to-indentation)
  777. (setq top (current-column))
  778. (skip-chars-backward " \t")
  779. (if (>= shift top) (setq shift (- shift top))
  780. (setq shift 0))
  781. (if (and (bolp)
  782. (= column top))
  783. (move-to-column (+ column shift))
  784. (move-to-column top)
  785. (delete-region beg (point))
  786. (beginning-of-line)
  787. (indent-to column)
  788. (move-to-column (+ column shift))))))
  789. (defun ruby-special-char-p (&optional pos)
  790. "Return t if the character before POS is a special character.
  791. If omitted, POS defaults to the current point.
  792. Special characters are `?', `$', `:' when preceded by whitespace,
  793. and `\\' when preceded by `?'."
  794. (setq pos (or pos (point)))
  795. (let ((c (char-before pos)) (b (and (< (point-min) pos)
  796. (char-before (1- pos)))))
  797. (cond ((or (eq c ??) (eq c ?$)))
  798. ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? ))))
  799. ((eq c ?\\) (eq b ??)))))
  800. (defun ruby-singleton-class-p (&optional pos)
  801. (save-excursion
  802. (when pos (goto-char pos))
  803. (forward-word -1)
  804. (and (or (bolp) (not (eq (char-before (point)) ?_)))
  805. (looking-at ruby-singleton-class-re))))
  806. (defun ruby-expr-beg (&optional option)
  807. "Check if point is possibly at the beginning of an expression.
  808. OPTION specifies the type of the expression.
  809. Can be one of `heredoc', `modifier', `expr-qstr', `expr-re'."
  810. (save-excursion
  811. (store-match-data nil)
  812. (let ((space (skip-chars-backward " \t"))
  813. (start (point)))
  814. (cond
  815. ((bolp) t)
  816. ((progn
  817. (forward-char -1)
  818. (and (looking-at "\\?")
  819. (or (eq (char-syntax (char-before (point))) ?w)
  820. (ruby-special-char-p))))
  821. nil)
  822. ((looking-at ruby-operator-re))
  823. ((eq option 'heredoc)
  824. (and (< space 0) (not (ruby-singleton-class-p start))))
  825. ((or (looking-at "[\\[({,;]")
  826. (and (looking-at "[!?]")
  827. (or (not (eq option 'modifier))
  828. (bolp)
  829. (save-excursion (forward-char -1) (looking-at "\\Sw$"))))
  830. (and (looking-at ruby-symbol-re)
  831. (skip-chars-backward ruby-symbol-chars)
  832. (cond
  833. ((looking-at (regexp-opt
  834. (append ruby-block-beg-keywords
  835. ruby-block-op-keywords
  836. ruby-block-mid-keywords)
  837. 'words))
  838. (goto-char (match-end 0))
  839. (not (looking-at "\\s_")))
  840. ((eq option 'expr-qstr)
  841. (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]"))
  842. ((eq option 'expr-re)
  843. (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]"))
  844. (t nil)))))))))
  845. (defun ruby-forward-string (term &optional end no-error expand)
  846. "Move forward across one balanced pair of string delimiters.
  847. Skips escaped delimiters. If EXPAND is non-nil, also ignores
  848. delimiters in interpolated strings.
  849. TERM should be a string containing either a single, self-matching
  850. delimiter (e.g. \"/\"), or a pair of matching delimiters with the
  851. close delimiter first (e.g. \"][\").
  852. When non-nil, search is bounded by position END.
  853. Throws an error if a balanced match is not found, unless NO-ERROR
  854. is non-nil, in which case nil will be returned.
  855. This command assumes the character after point is an opening
  856. delimiter."
  857. (let ((n 1) (c (string-to-char term))
  858. (re (concat "[^\\]\\(\\\\\\\\\\)*\\("
  859. (if (string= term "^") ;[^] is not a valid regexp
  860. "\\^"
  861. (concat "[" term "]"))
  862. (when expand "\\|\\(#{\\)")
  863. "\\)")))
  864. (while (and (re-search-forward re end no-error)
  865. (if (match-beginning 3)
  866. (ruby-forward-string "}{" end no-error nil)
  867. (> (setq n (if (eq (char-before (point)) c)
  868. (1- n) (1+ n))) 0)))
  869. (forward-char -1))
  870. (cond ((zerop n))
  871. (no-error nil)
  872. ((error "Unterminated string")))))
  873. (defun ruby-deep-indent-paren-p (c)
  874. "TODO: document."
  875. (cond ((listp ruby-deep-indent-paren)
  876. (let ((deep (assoc c ruby-deep-indent-paren)))
  877. (cond (deep
  878. (or (cdr deep) ruby-deep-indent-paren-style))
  879. ((memq c ruby-deep-indent-paren)
  880. ruby-deep-indent-paren-style))))
  881. ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style)
  882. ((eq c ?\( ) ruby-deep-arglist)))
  883. (defun ruby-parse-partial (&optional end in-string nest depth pcol indent)
  884. "TODO: document throughout function body."
  885. (or depth (setq depth 0))
  886. (or indent (setq indent 0))
  887. (when (re-search-forward ruby-delimiter end 'move)
  888. (let ((pnt (point)) w re expand)
  889. (goto-char (match-beginning 0))
  890. (cond
  891. ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw"))
  892. (goto-char pnt))
  893. ((looking-at "[\"`]") ;skip string
  894. (cond
  895. ((and (not (eobp))
  896. (ruby-forward-string (buffer-substring (point) (1+ (point)))
  897. end t t))
  898. nil)
  899. (t
  900. (setq in-string (point))
  901. (goto-char end))))
  902. ((looking-at "'")
  903. (cond
  904. ((and (not (eobp))
  905. (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t))
  906. nil)
  907. (t
  908. (setq in-string (point))
  909. (goto-char end))))
  910. ((looking-at "/=")
  911. (goto-char pnt))
  912. ((looking-at "/")
  913. (cond
  914. ((and (not (eobp)) (ruby-expr-beg 'expr-re))
  915. (if (ruby-forward-string "/" end t t)
  916. nil
  917. (setq in-string (point))
  918. (goto-char end)))
  919. (t
  920. (goto-char pnt))))
  921. ((looking-at "%")
  922. (cond
  923. ((and (not (eobp))
  924. (ruby-expr-beg 'expr-qstr)
  925. (not (looking-at "%="))
  926. (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)"))
  927. (goto-char (match-beginning 1))
  928. (setq expand (not (memq (char-before) '(?q ?w))))
  929. (setq w (match-string 1))
  930. (cond
  931. ((string= w "[") (setq re "]["))
  932. ((string= w "{") (setq re "}{"))
  933. ((string= w "(") (setq re ")("))
  934. ((string= w "<") (setq re "><"))
  935. ((and expand (string= w "\\"))
  936. (setq w (concat "\\" w))))
  937. (unless (cond (re (ruby-forward-string re end t expand))
  938. (expand (ruby-forward-string w end t t))
  939. (t (re-search-forward
  940. (if (string= w "\\")
  941. "\\\\[^\\]*\\\\"
  942. (concat "[^\\]\\(\\\\\\\\\\)*" w))
  943. end t)))
  944. (setq in-string (point))
  945. (goto-char end)))
  946. (t
  947. (goto-char pnt))))
  948. ((looking-at "\\?") ;skip ?char
  949. (cond
  950. ((and (ruby-expr-beg)
  951. (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?."))
  952. (goto-char (match-end 0)))
  953. (t
  954. (goto-char pnt))))
  955. ((looking-at "\\$") ;skip $char
  956. (goto-char pnt)
  957. (forward-char 1))
  958. ((looking-at "#") ;skip comment
  959. (forward-line 1)
  960. (goto-char (point))
  961. )
  962. ((looking-at "[\\[{(]")
  963. (let ((deep (ruby-deep-indent-paren-p (char-after))))
  964. (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg)))
  965. (progn
  966. (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]")
  967. (setq pnt (1- (match-end 0))))
  968. (setq nest (cons (cons (char-after (point)) pnt) nest))
  969. (setq pcol (cons (cons pnt depth) pcol))
  970. (setq depth 0))
  971. (setq nest (cons (cons (char-after (point)) pnt) nest))
  972. (setq depth (1+ depth))))
  973. (goto-char pnt)
  974. )
  975. ((looking-at "[])}]")
  976. (if (ruby-deep-indent-paren-p (matching-paren (char-after)))
  977. (setq depth (cdr (car pcol)) pcol (cdr pcol))
  978. (setq depth (1- depth)))
  979. (setq nest (cdr nest))
  980. (goto-char pnt))
  981. ((looking-at ruby-block-end-re)
  982. (if (or (and (not (bolp))
  983. (progn
  984. (forward-char -1)
  985. (setq w (char-after (point)))
  986. (or (eq ?_ w)
  987. (eq ?. w))))
  988. (progn
  989. (goto-char pnt)
  990. (setq w (char-after (point)))
  991. (or (eq ?_ w)
  992. (eq ?! w)
  993. (eq ?? w))))
  994. nil
  995. (setq nest (cdr nest))
  996. (setq depth (1- depth)))
  997. (goto-char pnt))
  998. ((looking-at "def\\s +[^(\n;]*")
  999. (if (or (bolp)
  1000. (progn
  1001. (forward-char -1)
  1002. (not (eq ?_ (char-after (point))))))
  1003. (progn
  1004. (setq nest (cons (cons nil pnt) nest))
  1005. (setq depth (1+ depth))))
  1006. (goto-char (match-end 0)))
  1007. ((looking-at (concat "\\_<\\(" ruby-block-beg-re "\\)\\_>"))
  1008. (and
  1009. (save-match-data
  1010. (or (not (looking-at "do\\_>"))
  1011. (save-excursion
  1012. (back-to-indentation)
  1013. (not (looking-at ruby-non-block-do-re)))))
  1014. (or (bolp)
  1015. (progn
  1016. (forward-char -1)
  1017. (setq w (char-after (point)))
  1018. (not (or (eq ?_ w)
  1019. (eq ?. w)))))
  1020. (goto-char pnt)
  1021. (not (eq ?! (char-after (point))))
  1022. (skip-chars-forward " \t")
  1023. (goto-char (match-beginning 0))
  1024. (or (not (looking-at ruby-modifier-re))
  1025. (ruby-expr-beg 'modifier))
  1026. (goto-char pnt)
  1027. (setq nest (cons (cons nil pnt) nest))
  1028. (setq depth (1+ depth)))
  1029. (goto-char pnt))
  1030. ((looking-at ":\\(['\"]\\)")
  1031. (goto-char (match-beginning 1))
  1032. (ruby-forward-string (match-string 1) end t))
  1033. ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\|![~=]?\\)")
  1034. (goto-char (match-end 0)))
  1035. ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*[!?=]?\\)?")
  1036. (goto-char (match-end 0)))
  1037. ((or (looking-at "\\.\\.\\.?")
  1038. (looking-at "\\.[0-9]+")
  1039. (looking-at "\\.[a-zA-Z_0-9]+")
  1040. (looking-at "\\."))
  1041. (goto-char (match-end 0)))
  1042. ((looking-at "^=begin")
  1043. (if (re-search-forward "^=end" end t)
  1044. (forward-line 1)
  1045. (setq in-string (match-end 0))
  1046. (goto-char end)))
  1047. ((looking-at "<<")
  1048. (cond
  1049. ((and (ruby-expr-beg 'heredoc)
  1050. (looking-at "<<\\(-\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)"))
  1051. (setq re (regexp-quote (or (match-string 4) (match-string 2))))
  1052. (if (match-beginning 1) (setq re (concat "\\s *" re)))
  1053. (let* ((id-end (goto-char (match-end 0)))
  1054. (line-end-position (point-at-eol))
  1055. (state (list in-string nest depth pcol indent)))
  1056. ;; parse the rest of the line
  1057. (while (and (> line-end-position (point))
  1058. (setq state (apply 'ruby-parse-partial
  1059. line-end-position state))))
  1060. (setq in-string (car state)
  1061. nest (nth 1 state)
  1062. depth (nth 2 state)
  1063. pcol (nth 3 state)
  1064. indent (nth 4 state))
  1065. ;; skip heredoc section
  1066. (if (re-search-forward (concat "^" re "$") end 'move)
  1067. (forward-line 1)
  1068. (setq in-string id-end)
  1069. (goto-char end))))
  1070. (t
  1071. (goto-char pnt))))
  1072. ((looking-at "^__END__$")
  1073. (goto-char pnt))
  1074. ((and (looking-at ruby-here-doc-beg-re)
  1075. (boundp 'ruby-indent-point))
  1076. (if (re-search-forward (ruby-here-doc-end-match)
  1077. ruby-indent-point t)
  1078. (forward-line 1)
  1079. (setq in-string (match-end 0))
  1080. (goto-char ruby-indent-point)))
  1081. (t
  1082. (error "Bad string %s" (buffer-substring (point) pnt))))))
  1083. (list in-string nest depth pcol))
  1084. (defun ruby-parse-region (start end)
  1085. "TODO: document."
  1086. (let (state)
  1087. (save-excursion
  1088. (if start
  1089. (goto-char start)
  1090. (ruby-beginning-of-indent))
  1091. (save-restriction
  1092. (narrow-to-region (point) end)
  1093. (while (and (> end (point))
  1094. (setq state (apply 'ruby-parse-partial end state))))))
  1095. (list (nth 0 state) ; in-string
  1096. (car (nth 1 state)) ; nest
  1097. (nth 2 state) ; depth
  1098. (car (car (nth 3 state))) ; pcol
  1099. ;(car (nth 5 state)) ; indent
  1100. )))
  1101. (defun ruby-indent-size (pos nest)
  1102. "Return the indentation level in spaces NEST levels deeper than POS."
  1103. (+ pos (* (or nest 1) ruby-indent-level)))
  1104. (defun ruby-calculate-indent (&optional parse-start)
  1105. "Return the proper indentation level of the current line."
  1106. ;; TODO: Document body
  1107. (save-excursion
  1108. (beginning-of-line)
  1109. (let ((ruby-indent-point (point))
  1110. (case-fold-search nil)
  1111. state eol begin op-end
  1112. (paren (progn (skip-syntax-forward " ")
  1113. (and (char-after) (matching-paren (char-after)))))
  1114. (indent 0))
  1115. (if parse-start
  1116. (goto-char parse-start)
  1117. (ruby-beginning-of-indent)
  1118. (setq parse-start (point)))
  1119. (back-to-indentation)
  1120. (setq indent (current-column))
  1121. (setq state (ruby-parse-region parse-start ruby-indent-point))
  1122. (cond
  1123. ((nth 0 state) ; within string
  1124. (setq indent nil)) ; do nothing
  1125. ((car (nth 1 state)) ; in paren
  1126. (goto-char (setq begin (cdr (nth 1 state))))
  1127. (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state)))))
  1128. (if deep
  1129. (cond ((and (eq deep t) (eq (car (nth 1 state)) paren))
  1130. (skip-syntax-backward " ")
  1131. (setq indent (1- (current-column))))
  1132. ((let ((s (ruby-parse-region (point) ruby-indent-point)))
  1133. (and (nth 2 s) (> (nth 2 s) 0)
  1134. (or (goto-char (cdr (nth 1 s))) t)))
  1135. (forward-word -1)
  1136. (setq indent (ruby-indent-size (current-column)
  1137. (nth 2 state))))
  1138. (t
  1139. (setq indent (current-column))
  1140. (cond ((eq deep 'space))
  1141. (paren (setq indent (1- indent)))
  1142. (t (setq indent (ruby-indent-size (1- indent) 1))))))
  1143. (if (nth 3 state) (goto-char (nth 3 state))
  1144. (goto-char parse-start) (back-to-indentation))
  1145. (setq indent (ruby-indent-size (current-column) (nth 2 state))))
  1146. (and (eq (car (nth 1 state)) paren)
  1147. (ruby-deep-indent-paren-p (matching-paren paren))
  1148. (search-backward (char-to-string paren))
  1149. (setq indent (current-column)))))
  1150. ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest
  1151. (if (null (cdr (nth 1 state)))
  1152. (error "Invalid nesting"))
  1153. (goto-char (cdr (nth 1 state)))
  1154. (forward-word -1) ; skip back a keyword
  1155. (setq begin (point))
  1156. (cond
  1157. ((looking-at "do\\>[^_]") ; iter block is a special case
  1158. (if (nth 3 state) (goto-char (nth 3 state))
  1159. (goto-char parse-start) (back-to-indentation))
  1160. (setq indent (ruby-indent-size (current-column) (nth 2 state))))
  1161. (t
  1162. (setq indent (+ (current-column) ruby-indent-level)))))
  1163. ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
  1164. (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
  1165. (when indent
  1166. (goto-char ruby-indent-point)
  1167. (end-of-line)
  1168. (setq eol (point))
  1169. (beginning-of-line)
  1170. (cond
  1171. ((and (not (ruby-deep-indent-paren-p paren))
  1172. (re-search-forward ruby-negative eol t))
  1173. (and (not (eq ?_ (char-after (match-end 0))))
  1174. (setq indent (- indent ruby-indent-level))))
  1175. ((and
  1176. (save-excursion
  1177. (beginning-of-line)
  1178. (not (bobp)))
  1179. (or (ruby-deep-indent-paren-p t)
  1180. (null (car (nth 1 state)))))
  1181. ;; goto beginning of non-empty no-comment line
  1182. (let (end done)
  1183. (while (not done)
  1184. (skip-chars-backward " \t\n")
  1185. (setq end (point))
  1186. (beginning-of-line)
  1187. (if (re-search-forward "^\\s *#" end t)
  1188. (beginning-of-line)
  1189. (setq done t))))
  1190. (end-of-line)
  1191. ;; skip the comment at the end
  1192. (skip-chars-backward " \t")
  1193. (let (end (pos (point)))
  1194. (beginning-of-line)
  1195. (while (and (re-search-forward "#" pos t)
  1196. (setq end (1- (point)))
  1197. (or (ruby-special-char-p end)
  1198. (and (setq state (ruby-parse-region
  1199. parse-start end))
  1200. (nth 0 state))))
  1201. (setq end nil))
  1202. (goto-char (or end pos))
  1203. (skip-chars-backward " \t")
  1204. (setq begin (if (and end (nth 0 state)) pos (cdr (nth 1 state))))
  1205. (setq state (ruby-parse-region parse-start (point))))
  1206. (or (bobp) (forward-char -1))
  1207. (and
  1208. (or (and (looking-at ruby-symbol-re)
  1209. (skip-chars-backward ruby-symbol-chars)
  1210. (looking-at (concat "\\<\\(" ruby-block-hanging-re
  1211. "\\)\\>"))
  1212. (not (eq (point) (nth 3 state)))
  1213. (save-excursion
  1214. (goto-char (match-end 0))
  1215. (not (looking-at "[a-z_]"))))
  1216. (and (looking-at ruby-operator-re)
  1217. (not (ruby-special-char-p))
  1218. (save-excursion
  1219. (forward-char -1)
  1220. (or (not (looking-at ruby-operator-re))
  1221. (not (eq (char-before) ?:))))
  1222. ;; Operator at the end of line.
  1223. (let ((c (char-after (point))))
  1224. (and
  1225. ;; (or (null begin)
  1226. ;; (save-excursion
  1227. ;; (goto-char begin)
  1228. ;; (skip-chars-forward " \t")
  1229. ;; (not (or (eolp) (looking-at "#")
  1230. ;; (and (eq (car (nth 1 state)) ?{)
  1231. ;; (looking-at "|"))))))
  1232. ;; Not a regexp or percent literal.
  1233. (null (nth 0 (ruby-parse-region (or begin parse-start)
  1234. (point))))
  1235. (or (not (eq ?| (char-after (point))))
  1236. (save-excursion
  1237. (or (eolp) (forward-char -1))
  1238. (cond
  1239. ((search-backward "|" nil t)
  1240. (skip-chars-backward " \t\n")
  1241. (and (not (eolp))
  1242. (progn
  1243. (forward-char -1)
  1244. (not (looking-at "{")))
  1245. (progn
  1246. (forward-word -1)
  1247. (not (looking-at "do\\>[^_]")))))
  1248. (t t))))
  1249. (not (eq ?, c))
  1250. (setq op-end t)))))
  1251. (setq indent
  1252. (cond
  1253. ((and
  1254. (null op-end)
  1255. (not (looking-at (concat "\\<\\(" ruby-block-hanging-re
  1256. "\\)\\>")))
  1257. (eq (ruby-deep-indent-paren-p t) 'space)
  1258. (not (bobp)))
  1259. (widen)
  1260. (goto-char (or begin parse-start))
  1261. (skip-syntax-forward " ")
  1262. (current-column))
  1263. ((car (nth 1 state)) indent)
  1264. (t
  1265. (+ indent ruby-indent-level))))))))
  1266. (goto-char ruby-indent-point)
  1267. (beginning-of-line)
  1268. (skip-syntax-forward " ")
  1269. (if (looking-at "\\.[^.]")
  1270. (+ indent ruby-indent-level)
  1271. indent))))
  1272. (defun ruby-beginning-of-defun (&optional arg)
  1273. "Move backward to the beginning of the current defun.
  1274. With ARG, move backward multiple defuns. Negative ARG means
  1275. move forward."
  1276. (interactive "p")
  1277. (let (case-fold-search)
  1278. (and (re-search-backward (concat "^\\s *" ruby-defun-beg-re "\\_>")
  1279. nil t (or arg 1))
  1280. (beginning-of-line))))
  1281. (defun ruby-end-of-defun ()
  1282. "Move point to the end of the current defun.
  1283. The defun begins at or after the point. This function is called
  1284. by `end-of-defun'."
  1285. (interactive "p")
  1286. (ruby-forward-sexp)
  1287. (let (case-fold-search)
  1288. (when (looking-back (concat "^\\s *" ruby-block-end-re)
  1289. (line-beginning-position))
  1290. (forward-line 1))))
  1291. (defun ruby-beginning-of-indent ()
  1292. "Backtrack to a line which can be used as a reference for
  1293. calculating indentation on the lines after it."
  1294. (while (and (re-search-backward ruby-indent-beg-re nil 'move)
  1295. (if (ruby-in-ppss-context-p 'anything)
  1296. t
  1297. ;; We can stop, then.
  1298. (beginning-of-line)))))
  1299. (defun ruby-move-to-block (n)
  1300. "Move to the beginning (N < 0) or the end (N > 0) of the
  1301. current block, a sibling block, or an outer block. Do that (abs N) times."
  1302. (back-to-indentation)
  1303. (let ((signum (if (> n 0) 1 -1))
  1304. (backward (< n 0))
  1305. (depth (or (nth 2 (ruby-parse-region (point) (line-end-position))) 0))
  1306. case-fold-search
  1307. down done)
  1308. (when (looking-at ruby-block-mid-re)
  1309. (setq depth (+ depth signum)))
  1310. (when (< (* depth signum) 0)
  1311. ;; Moving end -> end or beginning -> beginning.
  1312. (setq depth 0))
  1313. (dotimes (_ (abs n))
  1314. (setq done nil)
  1315. (setq down (save-excursion
  1316. (back-to-indentation)
  1317. ;; There is a block start or block end keyword on this
  1318. ;; line, don't need to look for another block.
  1319. (and (re-search-forward
  1320. (if backward ruby-block-end-re
  1321. (concat "\\_<\\(" ruby-block-beg-re "\\)\\_>"))
  1322. (line-end-position) t)
  1323. (not (nth 8 (syntax-ppss))))))
  1324. (while (and (not done) (not (if backward (bobp) (eobp))))
  1325. (forward-line signum)
  1326. (cond
  1327. ;; Skip empty and commented out lines.
  1328. ((looking-at "^\\s *$"))
  1329. ((looking-at "^\\s *#"))
  1330. ;; Skip block comments;
  1331. ((and (not backward) (looking-at "^=begin\\>"))
  1332. (re-search-forward "^=end\\>"))
  1333. ((and backward (looking-at "^=end\\>"))
  1334. (re-search-backward "^=begin\\>"))
  1335. ;; Jump over a multiline literal.
  1336. ((ruby-in-ppss-context-p 'string)
  1337. (goto-char (nth 8 (syntax-ppss)))
  1338. (unless backward
  1339. (forward-sexp)
  1340. (when (bolp) (forward-char -1)))) ; After a heredoc.
  1341. (t
  1342. (let ((state (ruby-parse-region (point) (line-end-position))))
  1343. (unless (car state) ; Line ends with unfinished string.
  1344. (setq depth (+ (nth 2 state) depth))))
  1345. (cond
  1346. ;; Increased depth, we found a block.
  1347. ((> (* signum depth) 0)
  1348. (setq down t))
  1349. ;; We're at the same depth as when we started, and we've
  1350. ;; encountered a block before. Stop.
  1351. ((and down (zerop depth))
  1352. (setq done t))
  1353. ;; Lower depth, means outer block, can stop now.
  1354. ((< (* signum depth) 0)
  1355. (setq done t)))))))
  1356. (back-to-indentation)))
  1357. (defun ruby-beginning-of-block (&optional arg)
  1358. "Move backward to the beginning of the current block.
  1359. With ARG, move up multiple blocks."
  1360. (interactive "p")
  1361. (ruby-move-to-block (- (or arg 1))))
  1362. (defun ruby-end-of-block (&optional arg)
  1363. "Move forward to the end of the current block.
  1364. With ARG, move out of multiple blocks."
  1365. (interactive "p")
  1366. (ruby-move-to-block (or arg 1)))
  1367. (defun ruby-forward-sexp (&optional arg)
  1368. "Move forward across one balanced expression (sexp).
  1369. With ARG, do it many times. Negative ARG means move backward."
  1370. ;; TODO: Document body
  1371. (interactive "p")
  1372. (cond
  1373. (ruby-use-smie (forward-sexp arg))
  1374. ((and (numberp arg) (< arg 0)) (ruby-backward-sexp (- arg)))
  1375. (t
  1376. (let ((i (or arg 1)))
  1377. (condition-case nil
  1378. (while (> i 0)
  1379. (skip-syntax-forward " ")
  1380. (if (looking-at ",\\s *") (goto-char (match-end 0)))
  1381. (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ")
  1382. (goto-char (match-end 0)))
  1383. ((progn
  1384. (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*")
  1385. (looking-at "\\s("))
  1386. (goto-char (scan-sexps (point) 1)))
  1387. ((and (looking-at (concat "\\<\\(" ruby-block-beg-re
  1388. "\\)\\>"))
  1389. (not (eq (char-before (point)) ?.))
  1390. (not (eq (char-before (point)) ?:)))
  1391. (ruby-end-of-block)
  1392. (forward-word 1))
  1393. ((looking-at "\\(\\$\\|@@?\\)?\\sw")
  1394. (while (progn
  1395. (while (progn (forward-word 1) (looking-at "_")))
  1396. (cond ((looking-at "::") (forward-char 2) t)
  1397. ((> (skip-chars-forward ".") 0))
  1398. ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)")
  1399. (forward-char 1) nil)))))
  1400. ((let (state expr)
  1401. (while
  1402. (progn
  1403. (setq expr (or expr (ruby-expr-beg)
  1404. (looking-at "%\\sw?\\Sw\\|[\"'`/]")))
  1405. (nth 1 (setq state (apply #'ruby-parse-partial
  1406. nil state))))
  1407. (setq expr t)
  1408. (skip-chars-forward "<"))
  1409. (not expr))))
  1410. (setq i (1- i)))
  1411. ((error) (forward-word 1)))
  1412. i))))
  1413. (defun ruby-backward-sexp (&optional arg)
  1414. "Move backward across one balanced expression (sexp).
  1415. With ARG, do it many times. Negative ARG means move forward."
  1416. ;; TODO: Document body
  1417. (interactive "p")
  1418. (cond
  1419. (ruby-use-smie (backward-sexp arg))
  1420. ((and (numberp arg) (< arg 0)) (ruby-forward-sexp (- arg)))
  1421. (t
  1422. (let ((i (or arg 1)))
  1423. (condition-case nil
  1424. (while (> i 0)
  1425. (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*")
  1426. (forward-char -1)
  1427. (cond ((looking-at "\\s)")
  1428. (goto-char (scan-sexps (1+ (point)) -1))
  1429. (pcase (char-before)
  1430. (`?% (forward-char -1))
  1431. ((or `?q `?Q `?w `?W `?r `?x)
  1432. (if (eq (char-before (1- (point))) ?%)
  1433. (forward-char -2))))
  1434. nil)
  1435. ((looking-at "\\s\"\\|\\\\\\S_")
  1436. (let ((c (char-to-string (char-before (match-end 0)))))
  1437. (while (and (search-backward c)
  1438. (eq (logand (skip-chars-backward "\\") 1)
  1439. 1))))
  1440. nil)
  1441. ((looking-at "\\s.\\|\\s\\")
  1442. (if (ruby-special-char-p) (forward-char -1)))
  1443. ((looking-at "\\s(") nil)
  1444. (t
  1445. (forward-char 1)
  1446. (while (progn (forward-word -1)
  1447. (pcase (char-before)
  1448. (`?_ t)
  1449. (`?. (forward-char -1) t)
  1450. ((or `?$ `?@)
  1451. (forward-char -1)
  1452. (and (eq (char-before) (char-after))
  1453. (forward-char -1)))
  1454. (`?:
  1455. (forward-char -1)
  1456. (eq (char-before) :)))))
  1457. (if (looking-at ruby-block-end-re)
  1458. (ruby-beginning-of-block))
  1459. nil))
  1460. (setq i (1- i)))
  1461. ((error)))
  1462. i))))
  1463. (defun ruby-indent-exp (&optional ignored)
  1464. "Indent each line in the balanced expression following the point."
  1465. (interactive "*P")
  1466. (let ((here (point-marker)) start top column (nest t))
  1467. (set-marker-insertion-type here t)
  1468. (unwind-protect
  1469. (progn
  1470. (beginning-of-line)
  1471. (setq start (point) top (current-indentation))
  1472. (while (and (not (eobp))
  1473. (progn
  1474. (setq column (ruby-calculate-indent start))
  1475. (cond ((> column top)
  1476. (setq nest t))
  1477. ((and (= column top) nest)
  1478. (setq nest nil) t))))
  1479. (ruby-indent-to column)
  1480. (beginning-of-line 2)))
  1481. (goto-char here)
  1482. (set-marker here nil))))
  1483. (defun ruby-add-log-current-method ()
  1484. "Return the current method name as a string.
  1485. This string includes all namespaces.
  1486. For example:
  1487. #exit
  1488. String#gsub
  1489. Net::HTTP#active?
  1490. File.open
  1491. See `add-log-current-defun-function'."
  1492. (condition-case nil
  1493. (save-excursion
  1494. (let* ((indent 0) mname mlist
  1495. (start (point))
  1496. (make-definition-re
  1497. (lambda (re)
  1498. (concat "^[ \t]*" re "[ \t]+"
  1499. "\\("
  1500. ;; \\. and :: for class methods
  1501. "\\([A-Za-z_]" ruby-symbol-re "*\\|\\.\\|::" "\\)"
  1502. "+\\)")))
  1503. (definition-re (funcall make-definition-re ruby-defun-beg-re))
  1504. (module-re (funcall make-definition-re "\\(class\\|module\\)")))
  1505. ;; Get the current method definition (or class/module).
  1506. (when (re-search-backward definition-re nil t)
  1507. (goto-char (match-beginning 1))
  1508. (if (not (string-equal "def" (match-string 1)))
  1509. (setq mlist (list (match-string 2)))
  1510. ;; We're inside the method. For classes and modules,
  1511. ;; this check is skipped for performance.
  1512. (when (ruby-block-contains-point start)
  1513. (setq mname (match-string 2))))
  1514. (setq indent (current-column))
  1515. (beginning-of-line))
  1516. ;; Walk up the class/module nesting.
  1517. (while (and (> indent 0)
  1518. (re-search-backward module-re nil t))
  1519. (goto-char (match-beginning 1))
  1520. (when (< (current-column) indent)
  1521. (setq mlist (cons (match-string 2) mlist))
  1522. (setq indent (current-column))
  1523. (beginning-of-line)))
  1524. ;; Process the method name.
  1525. (when mname
  1526. (let ((mn (split-string mname "\\.\\|::")))
  1527. (if (cdr mn)
  1528. (progn
  1529. (unless (string-equal "self" (car mn)) ; def self.foo
  1530. ;; def C.foo
  1531. (let ((ml (nreverse mlist)))
  1532. ;; If the method name references one of the
  1533. ;; containing modules, drop the more nested ones.
  1534. (while ml
  1535. (if (string-equal (car ml) (car mn))
  1536. (setq mlist (nreverse (cdr ml)) ml nil))
  1537. (or (setq ml (cdr ml)) (nreverse mlist))))
  1538. (if mlist
  1539. (setcdr (last mlist) (butlast mn))
  1540. (setq mlist (butlast mn))))
  1541. (setq mname (concat "." (car (last mn)))))
  1542. ;; See if the method is in singleton class context.
  1543. (let ((in-singleton-class
  1544. (when (re-search-forward ruby-singleton-class-re start t)
  1545. (goto-char (match-beginning 0))
  1546. ;; FIXME: Optimize it out, too?
  1547. ;; This can be slow in a large file, but
  1548. ;; unlike class/module declaration
  1549. ;; indentations, method definitions can be
  1550. ;; intermixed with these, and may or may not
  1551. ;; be additionally indented after visibility
  1552. ;; keywords.
  1553. (ruby-block-contains-point start))))
  1554. (setq mname (concat
  1555. (if in-singleton-class "." "#")
  1556. mname))))))
  1557. ;; Generate the string.
  1558. (if (consp mlist)
  1559. (setq mlist (mapconcat (function identity) mlist "::")))
  1560. (if mname
  1561. (if mlist (concat mlist mname) mname)
  1562. mlist)))))
  1563. (defun ruby-block-contains-point (pt)
  1564. (save-excursion
  1565. (save-match-data
  1566. (ruby-forward-sexp)
  1567. (> (point) pt))))
  1568. (defun ruby-brace-to-do-end (orig end)
  1569. (let (beg-marker end-marker)
  1570. (goto-char end)
  1571. (when (eq (char-before) ?\})
  1572. (delete-char -1)
  1573. (when (save-excursion
  1574. (skip-chars-backward " \t")
  1575. (not (bolp)))
  1576. (insert "\n"))
  1577. (insert "end")
  1578. (setq end-marker (point-marker))
  1579. (when (and (not (eobp)) (eq (char-syntax (char-after)) ?w))
  1580. (insert " "))
  1581. (goto-char orig)
  1582. (delete-char 1)
  1583. (when (eq (char-syntax (char-before)) ?w)
  1584. (insert " "))
  1585. (insert "do")
  1586. (setq beg-marker (point-marker))
  1587. (when (looking-at "\\(\\s \\)*|")
  1588. (unless (match-beginning 1)
  1589. (insert " "))
  1590. (goto-char (1+ (match-end 0)))
  1591. (search-forward "|"))
  1592. (unless (looking-at "\\s *$")
  1593. (insert "\n"))
  1594. (indent-region beg-marker end-marker)
  1595. (goto-char beg-marker)
  1596. t)))
  1597. (defun ruby-do-end-to-brace (orig end)
  1598. (let (beg-marker end-marker beg-pos end-pos)
  1599. (goto-char (- end 3))
  1600. (when (looking-at ruby-block-end-re)
  1601. (delete-char 3)
  1602. (setq end-marker (point-marker))
  1603. (insert "}")
  1604. (goto-char orig)
  1605. (delete-char 2)
  1606. ;; Maybe this should be customizable, let's see if anyone asks.
  1607. (insert "{ ")
  1608. (setq beg-marker (point-marker))
  1609. (when (looking-at "\\s +|")
  1610. (delete-char (- (match-end 0) (match-beginning 0) 1))
  1611. (forward-char)
  1612. (re-search-forward "|" (line-end-position) t))
  1613. (save-excursion
  1614. (skip-chars-forward " \t\n\r")
  1615. (setq beg-pos (point))
  1616. (goto-char end-marker)
  1617. (skip-chars-backward " \t\n\r")
  1618. (setq end-pos (point)))
  1619. (when (or
  1620. (< end-pos beg-pos)
  1621. (and (= (line-number-at-pos beg-pos) (line-number-at-pos end-pos))
  1622. (< (+ (current-column) (- end-pos beg-pos) 2) fill-column)))
  1623. (just-one-space -1)
  1624. (goto-char end-marker)
  1625. (just-one-space -1))
  1626. (goto-char beg-marker)
  1627. t)))
  1628. (defun ruby-toggle-block ()
  1629. "Toggle block type from do-end to braces or back.
  1630. The block must begin on the current line or above it and end after the point.
  1631. If the result is do-end block, it will always be multiline."
  1632. (interactive)
  1633. (let ((start (point)) beg end)
  1634. (end-of-line)
  1635. (unless
  1636. (if (and (re-search-backward "\\(?:[^#]\\)\\({\\)\\|\\(\\_<do\\_>\\)")
  1637. (progn
  1638. (goto-char (or (match-beginning 1) (match-beginning 2)))
  1639. (setq beg (point))
  1640. (save-match-data (ruby-forward-sexp))
  1641. (setq end (point))
  1642. (> end start)))
  1643. (if (match-beginning 1)
  1644. (ruby-brace-to-do-end beg end)
  1645. (ruby-do-end-to-brace beg end)))
  1646. (goto-char start))))
  1647. (defun ruby--string-region ()
  1648. "Return region for string at point."
  1649. (let ((state (syntax-ppss)))
  1650. (when (memq (nth 3 state) '(?' ?\"))
  1651. (save-excursion
  1652. (goto-char (nth 8 state))
  1653. (forward-sexp)
  1654. (list (nth 8 state) (point))))))
  1655. (defun ruby-string-at-point-p ()
  1656. "Check if cursor is at a string or not."
  1657. (ruby--string-region))
  1658. (defun ruby--inverse-string-quote (string-quote)
  1659. "Get the inverse string quoting for STRING-QUOTE."
  1660. (if (equal string-quote "\"") "'" "\""))
  1661. (defun ruby-toggle-string-quotes ()
  1662. "Toggle string literal quoting between single and double."
  1663. (interactive)
  1664. (when (ruby-string-at-point-p)
  1665. (let* ((region (ruby--string-region))
  1666. (min (nth 0 region))
  1667. (max (nth 1 region))
  1668. (string-quote (ruby--inverse-string-quote (buffer-substring-no-properties min (1+ min))))
  1669. (content
  1670. (buffer-substring-no-properties (1+ min) (1- max))))
  1671. (setq content
  1672. (if (equal string-quote "\"")
  1673. (replace-regexp-in-string "\\\\\"" "\"" (replace-regexp-in-string "\\([^\\\\]\\)'" "\\1\\\\'" content))
  1674. (replace-regexp-in-string "\\\\'" "'" (replace-regexp-in-string "\\([^\\\\]\\)\"" "\\1\\\\\"" content))))
  1675. (let ((orig-point (point)))
  1676. (delete-region min max)
  1677. (insert
  1678. (format "%s%s%s" string-quote content string-quote))
  1679. (goto-char orig-point)))))
  1680. (eval-and-compile
  1681. (defconst ruby-percent-literal-beg-re
  1682. "\\(%\\)[qQrswWxIi]?\\([[:punct:]]\\)"
  1683. "Regexp to match the beginning of percent literal.")
  1684. (defconst ruby-syntax-methods-before-regexp
  1685. '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match"
  1686. "assert_match" "Given" "Then" "When")
  1687. "Methods that can take regexp as the first argument.
  1688. It will be properly highlighted even when the call omits parens.")
  1689. (defvar ruby-syntax-before-regexp-re
  1690. (concat
  1691. ;; Special tokens that can't be followed by a division operator.
  1692. "\\(^\\|[[{|=(,~;<>!]"
  1693. ;; Distinguish ternary operator tokens.
  1694. ;; FIXME: They don't really have to be separated with spaces.
  1695. "\\|[?:] "
  1696. ;; Control flow keywords and operators following bol or whitespace.
  1697. "\\|\\(?:^\\|\\s \\)"
  1698. (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and"
  1699. "or" "not" "&&" "||"))
  1700. ;; Method name from the list.
  1701. "\\|\\_<"
  1702. (regexp-opt ruby-syntax-methods-before-regexp)
  1703. "\\)\\s *")
  1704. "Regexp to match text that can be followed by a regular expression."))
  1705. (defun ruby-syntax-propertize-function (start end)
  1706. "Syntactic keywords for Ruby mode. See `syntax-propertize-function'."
  1707. (let (case-fold-search)
  1708. (goto-char start)
  1709. (remove-text-properties start end '(ruby-expansion-match-data))
  1710. (ruby-syntax-propertize-heredoc end)
  1711. (ruby-syntax-enclosing-percent-literal end)
  1712. (funcall
  1713. (syntax-propertize-rules
  1714. ;; $' $" $` .... are variables.
  1715. ;; ?' ?" ?` are character literals (one-char strings in 1.9+).
  1716. ("\\([?$]\\)[#\"'`]"
  1717. (1 (if (save-excursion
  1718. (nth 3 (syntax-ppss (match-beginning 0))))
  1719. ;; Within a string, skip.
  1720. (goto-char (match-end 1))
  1721. (string-to-syntax "\\"))))
  1722. ;; Part of symbol when at the end of a method name.
  1723. ("[!?]"
  1724. (0 (unless (save-excursion
  1725. (or (nth 8 (syntax-ppss (match-beginning 0)))
  1726. (eq (char-before) ?:)
  1727. (let (parse-sexp-lookup-properties)
  1728. (zerop (skip-syntax-backward "w_")))
  1729. (memq (preceding-char) '(?@ ?$))))
  1730. (string-to-syntax "_"))))
  1731. ;; Regular expressions. Start with matching unescaped slash.
  1732. ("\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(/\\)"
  1733. (1 (let ((state (save-excursion (syntax-ppss (match-beginning 1)))))
  1734. (when (or
  1735. ;; Beginning of a regexp.
  1736. (and (null (nth 8 state))
  1737. (save-excursion
  1738. (forward-char -1)
  1739. (looking-back ruby-syntax-before-regexp-re
  1740. (point-at-bol))))
  1741. ;; End of regexp. We don't match the whole
  1742. ;; regexp at once because it can have
  1743. ;; string interpolation inside, or span
  1744. ;; several lines.
  1745. (eq ?/ (nth 3 state)))
  1746. (string-to-syntax "\"/")))))
  1747. ;; Expression expansions in strings. We're handling them
  1748. ;; here, so that the regexp rule never matches inside them.
  1749. (ruby-expression-expansion-re
  1750. (0 (ignore (ruby-syntax-propertize-expansion))))
  1751. ("^=en\\(d\\)\\_>" (1 "!"))
  1752. ("^\\(=\\)begin\\_>" (1 "!"))
  1753. ;; Handle here documents.
  1754. ((concat ruby-here-doc-beg-re ".*\\(\n\\)")
  1755. (7 (unless (or (nth 8 (save-excursion
  1756. (syntax-ppss (match-beginning 0))))
  1757. (ruby-singleton-class-p (match-beginning 0)))
  1758. (put-text-property (match-beginning 7) (match-end 7)
  1759. 'syntax-table (string-to-syntax "\""))
  1760. (ruby-syntax-propertize-heredoc end))))
  1761. ;; Handle percent literals: %w(), %q{}, etc.
  1762. ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re)
  1763. (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end)))))
  1764. (point) end)))
  1765. (defun ruby-syntax-propertize-heredoc (limit)
  1766. (let ((ppss (syntax-ppss))
  1767. (res '()))
  1768. (when (eq ?\n (nth 3 ppss))
  1769. (save-excursion
  1770. (goto-char (nth 8 ppss))
  1771. (beginning-of-line)
  1772. (while (re-search-forward ruby-here-doc-beg-re
  1773. (line-end-position) t)
  1774. (unless (ruby-singleton-class-p (match-beginning 0))
  1775. (push (concat (ruby-here-doc-end-match) "\n") res))))
  1776. (save-excursion
  1777. ;; With multiple openers on the same line, we don't know in which
  1778. ;; part `start' is, so we have to go back to the beginning.
  1779. (when (cdr res)
  1780. (goto-char (nth 8 ppss))
  1781. (setq res (nreverse res)))
  1782. (while (and res (re-search-forward (pop res) limit 'move))
  1783. (if (null res)
  1784. (put-text-property (1- (point)) (point)
  1785. 'syntax-table (string-to-syntax "\""))))
  1786. ;; End up at bol following the heredoc openers.
  1787. ;; Propertize expression expansions from this point forward.
  1788. ))))
  1789. (defun ruby-syntax-enclosing-percent-literal (limit)
  1790. (let ((state (syntax-ppss))
  1791. (start (point)))
  1792. ;; When already inside percent literal, re-propertize it.
  1793. (when (eq t (nth 3 state))
  1794. (goto-char (nth 8 state))
  1795. (when (looking-at ruby-percent-literal-beg-re)
  1796. (ruby-syntax-propertize-percent-literal limit))
  1797. (when (< (point) start) (goto-char start)))))
  1798. (defun ruby-syntax-propertize-percent-literal (limit)
  1799. (goto-char (match-beginning 2))
  1800. ;; Not inside a simple string or comment.
  1801. (when (eq t (nth 3 (syntax-ppss)))
  1802. (let* ((op (char-after))
  1803. (ops (char-to-string op))
  1804. (cl (or (cdr (aref (syntax-table) op))
  1805. (cdr (assoc op '((?< . ?>))))))
  1806. parse-sexp-lookup-properties)
  1807. (save-excursion
  1808. (condition-case nil
  1809. (progn
  1810. (if cl ; Paired delimiters.
  1811. ;; Delimiter pairs of the same kind can be nested
  1812. ;; inside the literal, as long as they are balanced.
  1813. ;; Create syntax table that ignores other characters.
  1814. (with-syntax-table (make-char-table 'syntax-table nil)
  1815. (modify-syntax-entry op (concat "(" (char-to-string cl)))
  1816. (modify-syntax-entry cl (concat ")" ops))
  1817. (modify-syntax-entry ?\\ "\\")
  1818. (save-restriction
  1819. (narrow-to-region (point) limit)
  1820. (forward-list))) ; skip to the paired character
  1821. ;; Single character delimiter.
  1822. (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*"
  1823. (regexp-quote ops)) limit nil))
  1824. ;; Found the closing delimiter.
  1825. (put-text-property (1- (point)) (point) 'syntax-table
  1826. (string-to-syntax "|")))
  1827. ;; Unclosed literal, do nothing.
  1828. ((scan-error search-failed)))))))
  1829. (defun ruby-syntax-propertize-expansion ()
  1830. ;; Save the match data to a text property, for font-locking later.
  1831. ;; Set the syntax of all double quotes and backticks to punctuation.
  1832. (let* ((beg (match-beginning 2))
  1833. (end (match-end 2))
  1834. (state (and beg (save-excursion (syntax-ppss beg)))))
  1835. (when (ruby-syntax-expansion-allowed-p state)
  1836. (put-text-property beg (1+ beg) 'ruby-expansion-match-data
  1837. (match-data))
  1838. (goto-char beg)
  1839. (while (re-search-forward "[\"`]" end 'move)
  1840. (put-text-property (match-beginning 0) (match-end 0)
  1841. 'syntax-table (string-to-syntax "."))))))
  1842. (defun ruby-syntax-expansion-allowed-p (parse-state)
  1843. "Return non-nil if expression expansion is allowed."
  1844. (let ((term (nth 3 parse-state)))
  1845. (cond
  1846. ((memq term '(?\" ?` ?\n ?/)))
  1847. ((eq term t)
  1848. (save-match-data
  1849. (save-excursion
  1850. (goto-char (nth 8 parse-state))
  1851. (looking-at "%\\(?:[QWrxI]\\|\\W\\)")))))))
  1852. (defun ruby-syntax-propertize-expansions (start end)
  1853. (save-excursion
  1854. (goto-char start)
  1855. (while (re-search-forward ruby-expression-expansion-re end 'move)
  1856. (ruby-syntax-propertize-expansion))))
  1857. (defun ruby-in-ppss-context-p (context &optional ppss)
  1858. (let ((ppss (or ppss (syntax-ppss (point)))))
  1859. (if (cond
  1860. ((eq context 'anything)
  1861. (or (nth 3 ppss)
  1862. (nth 4 ppss)))
  1863. ((eq context 'string)
  1864. (nth 3 ppss))
  1865. ((eq context 'heredoc)
  1866. (eq ?\n (nth 3 ppss)))
  1867. ((eq context 'non-heredoc)
  1868. (and (ruby-in-ppss-context-p 'anything)
  1869. (not (ruby-in-ppss-context-p 'heredoc))))
  1870. ((eq context 'comment)
  1871. (nth 4 ppss))
  1872. (t
  1873. (error (concat
  1874. "Internal error on `ruby-in-ppss-context-p': "
  1875. "context name `%s' is unknown")
  1876. context)))
  1877. t)))
  1878. (defvar ruby-font-lock-syntax-table
  1879. (let ((tbl (copy-syntax-table ruby-mode-syntax-table)))
  1880. (modify-syntax-entry ?_ "w" tbl)
  1881. tbl)
  1882. "The syntax table to use for fontifying Ruby mode buffers.
  1883. See `font-lock-syntax-table'.")
  1884. (defconst ruby-font-lock-keyword-beg-re "\\(?:^\\|[^.@$]\\|\\.\\.\\)")
  1885. (defconst ruby-font-lock-keywords
  1886. `(;; Functions.
  1887. ("^\\s *def\\s +\\(?:[^( \t\n.]*\\.\\)?\\([^( \t\n]+\\)"
  1888. 1 font-lock-function-name-face)
  1889. ;; Keywords.
  1890. (,(concat
  1891. ruby-font-lock-keyword-beg-re
  1892. (regexp-opt
  1893. '("alias"
  1894. "and"
  1895. "begin"
  1896. "break"
  1897. "case"
  1898. "class"
  1899. "def"
  1900. "defined?"
  1901. "do"
  1902. "elsif"
  1903. "else"
  1904. "fail"
  1905. "ensure"
  1906. "for"
  1907. "end"
  1908. "if"
  1909. "in"
  1910. "module"
  1911. "next"
  1912. "not"
  1913. "or"
  1914. "redo"
  1915. "rescue"
  1916. "retry"
  1917. "return"
  1918. "self"
  1919. "super"
  1920. "then"
  1921. "unless"
  1922. "undef"
  1923. "until"
  1924. "when"
  1925. "while"
  1926. "yield")
  1927. 'symbols))
  1928. (1 font-lock-keyword-face))
  1929. ;; Core methods that have required arguments.
  1930. (,(concat
  1931. ruby-font-lock-keyword-beg-re
  1932. (regexp-opt
  1933. '( ;; built-in methods on Kernel
  1934. "at_exit"
  1935. "autoload"
  1936. "autoload?"
  1937. "callcc"
  1938. "catch"
  1939. "eval"
  1940. "exec"
  1941. "format"
  1942. "lambda"
  1943. "load"
  1944. "loop"
  1945. "open"
  1946. "p"
  1947. "print"
  1948. "printf"
  1949. "proc"
  1950. "putc"
  1951. "puts"
  1952. "require"
  1953. "require_relative"
  1954. "spawn"
  1955. "sprintf"
  1956. "syscall"
  1957. "system"
  1958. "throw"
  1959. "trace_var"
  1960. "trap"
  1961. "untrace_var"
  1962. "warn"
  1963. ;; keyword-like private methods on Module
  1964. "alias_method"
  1965. "attr"
  1966. "attr_accessor"
  1967. "attr_reader"
  1968. "attr_writer"
  1969. "define_method"
  1970. "extend"
  1971. "include"
  1972. "module_function"
  1973. "prepend"
  1974. "private_class_method"
  1975. "private_constant"
  1976. "public_class_method"
  1977. "public_constant"
  1978. "refine"
  1979. "using")
  1980. 'symbols))
  1981. (1 (unless (looking-at " *\\(?:[]|,.)}=]\\|$\\)")
  1982. font-lock-builtin-face)))
  1983. ;; Kernel methods that have no required arguments.
  1984. (,(concat
  1985. ruby-font-lock-keyword-beg-re
  1986. (regexp-opt
  1987. '("__callee__"
  1988. "__dir__"
  1989. "__method__"
  1990. "abort"
  1991. "binding"
  1992. "block_given?"
  1993. "caller"
  1994. "exit"
  1995. "exit!"
  1996. "fail"
  1997. "fork"
  1998. "global_variables"
  1999. "local_variables"
  2000. "private"
  2001. "protected"
  2002. "public"
  2003. "raise"
  2004. "rand"
  2005. "readline"
  2006. "readlines"
  2007. "sleep"
  2008. "srand")
  2009. 'symbols))
  2010. (1 font-lock-builtin-face))
  2011. ;; Here-doc beginnings.
  2012. (,ruby-here-doc-beg-re
  2013. (0 (unless (ruby-singleton-class-p (match-beginning 0))
  2014. 'font-lock-string-face)))
  2015. ;; Perl-ish keywords.
  2016. "\\_<\\(?:BEGIN\\|END\\)\\_>\\|^__END__$"
  2017. ;; Variables.
  2018. (,(concat ruby-font-lock-keyword-beg-re
  2019. "\\_<\\(nil\\|true\\|false\\)\\_>")
  2020. 1 font-lock-constant-face)
  2021. ;; Keywords that evaluate to certain values.
  2022. ("\\_<__\\(?:LINE\\|ENCODING\\|FILE\\)__\\_>"
  2023. (0 font-lock-builtin-face))
  2024. ;; Symbols with symbol characters.
  2025. ("\\(^\\|[^:]\\)\\(:@?\\(?:\\w\\|_\\)+\\)\\([!?=]\\)?"
  2026. (2 font-lock-constant-face)
  2027. (3 (unless (and (eq (char-before (match-end 3)) ?=)
  2028. (eq (char-after (match-end 3)) ?>))
  2029. ;; bug#18466
  2030. font-lock-constant-face)
  2031. nil t))
  2032. ;; Symbols with special characters.
  2033. ("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)"
  2034. 2 font-lock-constant-face)
  2035. ;; Special globals.
  2036. (,(concat "\\$\\(?:[:\"!@;,/\\._><\\$?~=*&`'+0-9]\\|-[0adFiIlpvw]\\|"
  2037. (regexp-opt '("LOAD_PATH" "LOADED_FEATURES" "PROGRAM_NAME"
  2038. "ERROR_INFO" "ERROR_POSITION"
  2039. "FS" "FIELD_SEPARATOR"
  2040. "OFS" "OUTPUT_FIELD_SEPARATOR"
  2041. "RS" "INPUT_RECORD_SEPARATOR"
  2042. "ORS" "OUTPUT_RECORD_SEPARATOR"
  2043. "NR" "INPUT_LINE_NUMBER"
  2044. "LAST_READ_LINE" "DEFAULT_OUTPUT" "DEFAULT_INPUT"
  2045. "PID" "PROCESS_ID" "CHILD_STATUS"
  2046. "LAST_MATCH_INFO" "IGNORECASE"
  2047. "ARGV" "MATCH" "PREMATCH" "POSTMATCH"
  2048. "LAST_PAREN_MATCH" "stdin" "stdout" "stderr"
  2049. "DEBUG" "FILENAME" "VERBOSE" "SAFE" "CLASSPATH"
  2050. "JRUBY_VERSION" "JRUBY_REVISION" "ENV_JAVA"))
  2051. "\\_>\\)")
  2052. 0 font-lock-builtin-face)
  2053. ("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
  2054. 0 font-lock-variable-name-face)
  2055. ;; Constants.
  2056. ("\\(?:\\_<\\|::\\)\\([A-Z]+\\(\\w\\|_\\)*\\)"
  2057. 1 (unless (eq ?\( (char-after)) font-lock-type-face))
  2058. ("\\(^\\s *\\|[[{(,]\\s *\\|\\sw\\s +\\)\\(\\(\\sw\\|_\\)+\\):[^:]"
  2059. (2 font-lock-constant-face))
  2060. ;; Conversion methods on Kernel.
  2061. (,(concat ruby-font-lock-keyword-beg-re
  2062. (regexp-opt '("Array" "Complex" "Float" "Hash"
  2063. "Integer" "Rational" "String") 'symbols))
  2064. (1 font-lock-builtin-face))
  2065. ;; Expression expansion.
  2066. (ruby-match-expression-expansion
  2067. 2 font-lock-variable-name-face t)
  2068. ;; Negation char.
  2069. ("\\(?:^\\|[^[:alnum:]_]\\)\\(!+\\)[^=~]"
  2070. 1 font-lock-negation-char-face)
  2071. ;; Character literals.
  2072. ;; FIXME: Support longer escape sequences.
  2073. ("\\_<\\?\\\\?\\S " 0 font-lock-string-face)
  2074. ;; Regexp options.
  2075. ("\\(?:\\s|\\|/\\)\\([imxo]+\\)"
  2076. 1 (when (save-excursion
  2077. (let ((state (syntax-ppss (match-beginning 0))))
  2078. (and (nth 3 state)
  2079. (or (eq (char-after) ?/)
  2080. (progn
  2081. (goto-char (nth 8 state))
  2082. (looking-at "%r"))))))
  2083. font-lock-preprocessor-face))
  2084. )
  2085. "Additional expressions to highlight in Ruby mode.")
  2086. (defun ruby-match-expression-expansion (limit)
  2087. (let* ((prop 'ruby-expansion-match-data)
  2088. (pos (next-single-char-property-change (point) prop nil limit))
  2089. value)
  2090. (when (and pos (> pos (point)))
  2091. (goto-char pos)
  2092. (or (and (setq value (get-text-property pos prop))
  2093. (progn (set-match-data value) t))
  2094. (ruby-match-expression-expansion limit)))))
  2095. ;;;###autoload
  2096. (define-derived-mode ruby-mode prog-mode "Ruby"
  2097. "Major mode for editing Ruby code.
  2098. \\{ruby-mode-map}"
  2099. (ruby-mode-variables)
  2100. (setq-local imenu-create-index-function 'ruby-imenu-create-index)
  2101. (setq-local add-log-current-defun-function 'ruby-add-log-current-method)
  2102. (setq-local beginning-of-defun-function 'ruby-beginning-of-defun)
  2103. (setq-local end-of-defun-function 'ruby-end-of-defun)
  2104. (add-hook 'after-save-hook 'ruby-mode-set-encoding nil 'local)
  2105. (add-hook 'electric-indent-functions 'ruby--electric-indent-p nil 'local)
  2106. (setq-local font-lock-defaults '((ruby-font-lock-keywords) nil nil))
  2107. (setq-local font-lock-keywords ruby-font-lock-keywords)
  2108. (setq-local font-lock-syntax-table ruby-font-lock-syntax-table)
  2109. (setq-local syntax-propertize-function #'ruby-syntax-propertize-function))
  2110. ;;; Invoke ruby-mode when appropriate
  2111. ;;;###autoload
  2112. (add-to-list 'auto-mode-alist
  2113. (cons (purecopy (concat "\\(?:\\."
  2114. "rb\\|ru\\|rake\\|thor"
  2115. "\\|jbuilder\\|rabl\\|gemspec\\|podspec"
  2116. "\\|/"
  2117. "\\(?:Gem\\|Rake\\|Cap\\|Thor"
  2118. "\\|Puppet\\|Berks"
  2119. "\\|Vagrant\\|Guard\\|Pod\\)file"
  2120. "\\)\\'")) 'ruby-mode))
  2121. ;;;###autoload
  2122. (dolist (name (list "ruby" "rbx" "jruby" "ruby1.9" "ruby1.8"))
  2123. (add-to-list 'interpreter-mode-alist (cons (purecopy name) 'ruby-mode)))
  2124. (provide 'ruby-mode)
  2125. ;;; ruby-mode.el ends here