├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Eldev ├── README.md ├── elixir-format.el ├── elixir-mode.el ├── elixir-smie.el └── tests ├── elixir-format-test.el ├── elixir-mode-font-test.el ├── elixir-mode-helper-test.el ├── elixir-mode-indentation-test.el ├── elixir-mode-moving-test.el └── test-helper.el /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | pull_request: 8 | paths-ignore: 9 | - '**.md' 10 | 11 | jobs: 12 | test: 13 | name: test (Emacs ${{matrix.emacs_version}} | Elixir ${{matrix.elixir}} | Erlang/OTP ${{matrix.otp}}) 14 | runs-on: ubuntu-20.04 15 | strategy: 16 | matrix: 17 | emacs_version: ['27.2', '26.3', '25.3', 'snapshot'] 18 | otp: ['23.3'] 19 | elixir: ['1.11.4', '1.12.3', '1.13.4', '1.14.3'] 20 | 21 | steps: 22 | - name: Setup Emacs 23 | uses: purcell/setup-emacs@master 24 | with: 25 | version: ${{matrix.emacs_version}} 26 | 27 | - name: Setup OTP + Elixir 28 | uses: erlef/setup-beam@v1 29 | with: 30 | version-type: strict 31 | otp-version: ${{matrix.otp}} 32 | elixir-version: ${{matrix.elixir}} 33 | 34 | - name: Install Eldev 35 | run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh 36 | 37 | - name: Check out the source code 38 | uses: actions/checkout@v2 39 | 40 | - name: Test the project 41 | run: | 42 | eldev -p -dtT test 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *\~ 2 | ._* 3 | /.cask/ 4 | /dist/ 5 | *-emacs-elixir-format.ex 6 | elixir-mode-autoloads.el 7 | 8 | # Added automatically by ‘eldev init’. 9 | /.eldev 10 | /Eldev-local 11 | 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v2.6.0 - Unreleased 2 | 3 | ## v2.5.0 - 2023-06-26 4 | 5 | * [#505](https://github.com/elixir-editors/emacs-elixir/pull/505) - Update markdown, add Tips & Tricks 6 | * [#504](https://github.com/elixir-editors/emacs-elixir/pull/504) - Add guards to easymenu 7 | * [#502](https://github.com/elixir-editors/emacs-elixir/pull/502) - add H to list of sigil characters 8 | * [#499](https://github.com/elixir-editors/emacs-elixir/pull/499) - Fix CI 9 | * [#498](https://github.com/elixir-editors/emacs-elixir/pull/498) - Delete -emacs-elixir-format files when elixir-format is called uninteractively 10 | * [#496](https://github.com/elixir-editors/emacs-elixir/pull/496) - Add mention of Tree-Sitter WIP work 11 | * [#493](https://github.com/elixir-editors/emacs-elixir/pull/493) - Support ~H heex sigil 12 | * [#490](https://github.com/elixir-editors/emacs-elixir/pull/490) - chore(cleanup): remove Emacs 24 hack not supported anymore 13 | * [#489](https://github.com/elixir-editors/emacs-elixir/pull/489) - fix(tests): remove duplicated tests 14 | * [#487](https://github.com/elixir-editors/emacs-elixir/pull/487) - Bump otp and elixir versions for ci testing 15 | 16 | ## v2.4.0 - 2021-10-05 17 | * [#485](https://github.com/elixir-editors/emacs-elixir/pull/485) - Add require for cl-lib 18 | * [#482](https://github.com/elixir-editors/emacs-elixir/pull/482) - Remove pkg-info dependency 19 | * [#481](https://github.com/elixir-editors/emacs-elixir/pull/481) - Enable elixir-mode by default when opening mix.lock file. 20 | * [#475](https://github.com/elixir-editors/emacs-elixir/pull/475) - Backport ppss accessors, use setq-local & more 21 | * [#472](https://github.com/elixir-editors/emacs-elixir/pull/472) - chore: update guides with new build system 22 | * [#471](https://github.com/elixir-editors/emacs-elixir/pull/471) - feat: switch CI and build system 23 | * [#470](https://github.com/elixir-editors/emacs-elixir/pull/470) - Highlight atom map keys that end with a newline 24 | * [#468](https://github.com/elixir-editors/emacs-elixir/pull/468) - Sigil heredoc support 25 | * [#459](https://github.com/elixir-editors/emacs-elixir/pull/459) - Customizable face for numbers 26 | 27 | ## v2.3.2 - 2020-11-16 28 | * [#460](https://github.com/elixir-editors/emacs-elixir/pull/460) - @typedoc recognized as heredoc 29 | * [#454](https://github.com/elixir-editors/emacs-elixir/pull/454) - Add ~L, ~E, and ~e sigils for EEx and LiveView 30 | * [#449](https://github.com/elixir-editors/emacs-elixir/pull/449) - Disable smie-blink-matching-inners-locally 31 | * [#448](https://github.com/elixir-editors/emacs-elixir/pull/448) - make usable again in Emacs 27 32 | * [#438](https://github.com/elixir-editors/emacs-elixir/pull/438) - Run mix directly 33 | * [#442](https://github.com/elixir-editors/emacs-elixir/pull/442) - Add support for ~U sigil 34 | * [#441](https://github.com/elixir-editors/emacs-elixir/pull/441) - Inherit faces from appropriate built-in ones 35 | * [#433](https://github.com/elixir-editors/emacs-elixir/pull/433) - Special case for indentation of fat arrow in map literals 36 | * [#420](https://github.com/elixir-editors/emacs-elixir/pull/420) - change to mix.exs directory before format 37 | * [#425](https://github.com/elixir-editors/emacs-elixir/pull/425) - fontify defguard and defguardp 38 | * [#418](https://github.com/elixir-editors/emacs-elixir/pull/418) - locate closest formatter.exs on save 39 | * [#406](https://github.com/elixir-editors/emacs-elixir/pull/406) - add elixir-format function 40 | * [#381](https://github.com/elixir-editors/emacs-elixir/pull/381) - Fontify ~N and ~T sigils 41 | * [#383](https://github.com/elixir-editors/emacs-elixir/pull/383) - Fix @dox highlight 42 | * [#386](https://github.com/elixir-editors/emacs-elixir/pull/387) - Fix a broken command name 43 | * [#374](https://github.com/elixir-editors/emacs-elixir/pull/374) - Fix highlighting ignored variable in pattern match 44 | * [#373](https://github.com/elixir-editors/emacs-elixir/pull/373) - Fontify ~D sigil 45 | * [#368](https://github.com/elixir-editors/emacs-elixir/pull/368) - Implement moving defun command 46 | * [#364](https://github.com/elixir-editors/emacs-elixir/pull/364) - Fix issue that emacs hangs after def? statement 47 | * [#307](https://github.com/elixir-editors/emacs-elixir/pull/307) - Include - in punctuation class 48 | * [#357](https://github.com/elixir-editors/emacs-elixir/pull/357) - remove elixir-negation-face in favor of font-lock 49 | * [#358](https://github.com/elixir-editors/emacs-elixir/pull/358) - remove ignore-var-face in favor of comment-face 50 | * [#350](https://github.com/elixir-editors/emacs-elixir/pull/350) - Correct implementation for with keyword 51 | * [#345](https://github.com/elixir-editors/emacs-elixir/pull/345) - Correct default indent after match 52 | * [#343](https://github.com/elixir-editors/emacs-elixir/pull/343) - Fix for do oneline blocks 53 | * [#342](https://github.com/elixir-editors/emacs-elixir/pull/342) - Complex comment indentation 54 | * [#340](https://github.com/elixir-editors/emacs-elixir/pull/340) - Fix invalid highlighting question quote 55 | * [#339](https://github.com/elixir-editors/emacs-elixir/pull/339) - Fix heredoc indentation on first line inside block 56 | * [#338](https://github.com/elixir-editors/emacs-elixir/pull/338) - Fix "variable binding depth exceeds max-specpdl-size" error 57 | 58 | ## v2.3.1 - 2016/04/19 59 | * [#337](https://github.com/elixir-editors/emacs-elixir/pull/337) - Fix indentation issue after COMMA token 60 | * [#333](https://github.com/elixir-editors/emacs-elixir/pull/333) - Fix indentation of second element inside list of tuples 61 | * [#332](https://github.com/elixir-editors/emacs-elixir/pull/332) - Correct indent after using 'for' as function name 62 | * [#329](https://github.com/elixir-editors/emacs-elixir/pull/329) - Indent by one level if current line belongs to function call 63 | 64 | ## v2.3.0 - 2016/04/13 65 | * [#327](https://github.com/elixir-editors/emacs-elixir/pull/327) - Correct indentation of maps inside lists 66 | * [#326](https://github.com/elixir-editors/emacs-elixir/pull/326) - Correct anonymous fun indent inside block 67 | * [#325](https://github.com/elixir-editors/emacs-elixir/pull/325) - Fix indentation of statement keywords on dot call 68 | * [#324](https://github.com/elixir-editors/emacs-elixir/pull/324) - added failing tests for named functions in if and case 69 | * [#322](https://github.com/elixir-editors/emacs-elixir/pull/322) - added a failing indentation test for cond within with 70 | * [#321](https://github.com/elixir-editors/emacs-elixir/pull/321) - Fix invalid highlighting '::' in binaries 71 | * [#318](https://github.com/elixir-editors/emacs-elixir/pull/318) - Fix indent of pipes inside blocks of 'def' for example 72 | 73 | ## v2.2.9 - 2016/04/03 74 | * [#317](https://github.com/elixir-editors/emacs-elixir/pull/317) - Correct pipeline indentation 75 | * [#316](https://github.com/elixir-editors/emacs-elixir/pull/316) - Fix indentation of if within an else block 76 | * [#315](https://github.com/elixir-editors/emacs-elixir/pull/315) - Correct indentation with for-comprehensions within blocks 77 | * [#314](https://github.com/elixir-editors/emacs-elixir/pull/314) - Fix highlighting triple single quote(heredoc) 78 | * [#313](https://github.com/elixir-editors/emacs-elixir/pull/313) - Correct indentation after a one line 'fn' definition 79 | * [#305](https://github.com/elixir-editors/emacs-elixir/pull/305) - Added test case for for-comprehensions within case 80 | * [#303](https://github.com/elixir-editors/emacs-elixir/pull/303) - Fix escaped delimiter in sigil issue 81 | * [#295](https://github.com/elixir-editors/emacs-elixir/pull/295) - Demonstrate defstruct indention in a test case 82 | * [#261](https://github.com/elixir-editors/emacs-elixir/pull/261) - Test for multi-line function calls without parenthesis 83 | * [#299](https://github.com/elixir-editors/emacs-elixir/pull/299) - Added `with/1` to the highlighted keywords 84 | * [#298](https://github.com/elixir-editors/emacs-elixir/pull/298) - Added a test for alignment of the last key in multiline maps in cases 85 | * [#296](https://github.com/elixir-editors/emacs-elixir/pull/296) - Gray out ignored variables 86 | * [#291](https://github.com/elixir-editors/emacs-elixir/pull/291) - Added a test for indenting non-finished one-line if-else 87 | * [#289](https://github.com/elixir-editors/emacs-elixir/pull/289) - Added a test case for if within an else 88 | * [#287](https://github.com/elixir-editors/emacs-elixir/pull/287) - Fix sigil triple quotes 89 | * [#284](https://github.com/elixir-editors/emacs-elixir/pull/284) - Added a test for highlighting end after comment 90 | * [#285](https://github.com/elixir-editors/emacs-elixir/pull/285) - Don't capture '(or line-start (not (any ".")))' 91 | * [#282](https://github.com/elixir-editors/emacs-elixir/pull/282) - Indent multiple macro calls with do colon correct 92 | * [#280](https://github.com/elixir-editors/emacs-elixir/pull/280) - Fix one line definitions with equal char inside guard 93 | * [#279](https://github.com/elixir-editors/emacs-elixir/pull/279) - Fix indentation of single line fun declarations after single line fun declarations with when clauses 94 | * [#277](https://github.com/elixir-editors/emacs-elixir/pull/277) - Fix syntax highlighting sigils in string 95 | * [#273](https://github.com/elixir-editors/emacs-elixir/pull/273) - Removed send_after from highlighted keywords 96 | * [#272](https://github.com/elixir-editors/emacs-elixir/pull/272) - Added `send` and `send_after` to font lock 97 | * [#271](https://github.com/elixir-editors/emacs-elixir/pull/271) - Highlight module if preceded by a pipe 98 | 99 | ## v2.2.8 - 2015/10/19 100 | * [#270](https://github.com/elixir-editors/emacs-elixir/pull/270) - Fix highlighting hashmark in sigil 101 | * [#269](https://github.com/elixir-editors/emacs-elixir/pull/269) - Fix string interpolation 102 | * [#268](https://github.com/elixir-editors/emacs-elixir/pull/268) - added font-lock to defoverridable 103 | * [#262](https://github.com/elixir-editors/emacs-elixir/pull/262) - Add indentation tests for comprehensions 104 | * [#267](https://github.com/elixir-editors/emacs-elixir/pull/267) - ~s is sigil, not attribute 105 | * [#266](https://github.com/elixir-editors/emacs-elixir/pull/266) - Fix quotes in sigils 106 | * [#264](https://github.com/elixir-editors/emacs-elixir/pull/264) - Fix string interpolation 107 | 108 | ## v2.2.7 - 2015/09/17 109 | * [#260](https://github.com/elixir-editors/emacs-elixir/pull/260) - Correct indentation after "for" comprehension 110 | * [#259](https://github.com/elixir-editors/emacs-elixir/pull/259) - Indent receive/after matches correct 111 | * [#258](https://github.com/elixir-editors/emacs-elixir/pull/258) - Emacs hangs if `elixir-smie-forward-token` returns an empty string 112 | * [#253](https://github.com/elixir-editors/emacs-elixir/pull/253) - Fix Highlight atom issue(atom contains '!', '?', '@') 113 | * [#252](https://github.com/elixir-editors/emacs-elixir/pull/252) - Fix after dot highlighting 114 | * [#249](https://github.com/elixir-editors/emacs-elixir/pull/249) - Add correct indent for "if" inside a "->" block 115 | * [#246](https://github.com/elixir-editors/emacs-elixir/pull/246) - Fix highlighting true, false, nil 116 | * [#244](https://github.com/elixir-editors/emacs-elixir/pull/244) - True,false, nil are highlighted as atoms 117 | * [#241](https://github.com/elixir-editors/emacs-elixir/pull/241) - correct indent for oneline `do:` when moved to next line 118 | * [#240](https://github.com/elixir-editors/emacs-elixir/pull/240) - Correct indent in case expression when returning a tuple 119 | * [#236](https://github.com/elixir-editors/emacs-elixir/pull/236) - fontify special macros with prefix like '%' and '&' 120 | * [#235](https://github.com/elixir-editors/emacs-elixir/pull/235) - correct indentation for identifiers which contains built in words after a dot 121 | 122 | ## v2.2.6 - 2015/08/05 123 | * [#234](https://github.com/elixir-editors/emacs-elixir/pull/234) - don't highlights LHS as a variable in `==` case fixes #225 124 | * [#233](https://github.com/elixir-editors/emacs-elixir/pull/233) - module syntax highlighting also works correctly with & 125 | * [#232](https://github.com/elixir-editors/emacs-elixir/pull/232) - correct indentation for closing Map curly bracket fixes #223 126 | * [#231](https://github.com/elixir-editors/emacs-elixir/pull/231) - correct indentation for block with multiple matches 127 | * [#230](https://github.com/elixir-editors/emacs-elixir/pull/230) - update travis setup 128 | * [#228](https://github.com/elixir-editors/emacs-elixir/pull/228) - clear elixir-mode from deprecated functions 129 | 130 | ## v2.2.5 - 2015/06/18 131 | * [#222](https://github.com/elixir-editors/emacs-elixir/pull/222) - correct indentation inside heredoc strings 132 | * [#221](https://github.com/elixir-editors/emacs-elixir/pull/221) - highlight atoms correctly in a pattern match 133 | * [#220](https://github.com/elixir-editors/emacs-elixir/pull/220) - Correct indentation of parenthesized expression inside blocks 134 | * [#214](https://github.com/elixir-editors/emacs-elixir/pull/214) - correct indentation after one line definition with if keyword 135 | * [#213](https://github.com/elixir-editors/emacs-elixir/pull/213) - remove do keyword to handle also oneline definitions 136 | 137 | ## v2.2.4 - 2015/05/26 138 | * [#203](https://github.com/elixir-editors/emacs-elixir/pull/203) - Implement triple-quoted strings. 139 | 140 | ## v2.2.3 - 2015/05/26 141 | * [#202](https://github.com/elixir-editors/emacs-elixir/pull/202) - correct indentation after oneline def/if definition 142 | * [#200](https://github.com/elixir-editors/emacs-elixir/pull/200) - correct elements indentation for structs, maps and tuples 143 | * [#198](https://github.com/elixir-editors/emacs-elixir/pull/198) - indenting elements of a list correctly 144 | * [#196](https://github.com/elixir-editors/emacs-elixir/pull/196) - correct indentation outside of blocks 145 | 146 | ## v2.2.2 - 2015/05/22 147 | * [#193](https://github.com/elixir-editors/emacs-elixir/pull/193) - fix wrong indentation on empty line between existing code 148 | * [#195](https://github.com/elixir-editors/emacs-elixir/pull/195) - highlighting of capitalized modules when used as structs 149 | * [#192](https://github.com/elixir-editors/emacs-elixir/pull/192) - Fix (error "Lisp nesting exceeds `max-lisp-eval-depth'") which crashes correct indentation 150 | * [#190](https://github.com/elixir-editors/emacs-elixir/pull/190) - correct indentation for multiclause anonymous functions 151 | * [#189](https://github.com/elixir-editors/emacs-elixir/pull/189) - Modify indentation rules for one-line functions ending with bitstrings. 152 | * [#179](https://github.com/elixir-editors/emacs-elixir/pull/179) - Modify syntax highlighting so there is no differentiating between built-in and user-defined modules. 153 | * [#188](https://github.com/elixir-editors/emacs-elixir/pull/188) - Fix changelog. 154 | * [#187](https://github.com/elixir-editors/emacs-elixir/pull/187) - Add unresolved test case. 155 | * [#178](https://github.com/elixir-editors/emacs-elixir/pull/178) - Factor out `cask install` as its own task in Rakefile 156 | 157 | ## v2.2.1 - 2015/05/19 158 | * [#186](https://github.com/elixir-editors/emacs-elixir/pull/186) - Remove undocumented failing tests. 159 | * [#182](https://github.com/elixir-editors/emacs-elixir/pull/182) - Remove redundant local-pair tip for smartparens users 160 | * [#183](https://github.com/elixir-editors/emacs-elixir/pull/183) - Fix typos in README. 161 | * [#176](https://github.com/elixir-editors/emacs-elixir/pull/176) - Highlight digits in atoms 162 | * [#173](https://github.com/elixir-editors/emacs-elixir/pull/173) - Add function to apply `fill-region` in doc strings 163 | * [#175](https://github.com/elixir-editors/emacs-elixir/pull/175) - Add tips for smartparens users 164 | * [#177](https://github.com/elixir-editors/emacs-elixir/pull/177) - Prefer Emacs over emacs. 165 | * [#141](https://github.com/elixir-editors/emacs-elixir/pull/141) - Add documentation line explaining how to edit Elixir templates. 166 | * [Syntax Highlighting] Modules don't start with underscore. This fix corrects the fontification of private function. 167 | 168 | ## v2.2.0 2014/12/31 169 | 170 | ### Enhancements 171 | 172 | * [Indentation] Indent listing inside square brackets. (#160) 173 | * [Indentation] Indent of binary sequence inside match block 174 | * [Indentation] Indent correct after a binary sequence `<<1,2,3,4>>`. 175 | * [Indentation] Indent correct after oneline `def ... do:` function 176 | * [Indentation] Correct behavior after last line in buffer. (#145) 177 | 178 | ## v2.1.1 - 2014/12/24 179 | 180 | ### Enhancements 181 | 182 | * [Indentation] Indent block inside a fn match. 183 | 184 | ### Bugfixes 185 | 186 | * [Indentation] #152 Fix indentation inside parens wrapped around def 187 | 188 | ## v2.1.0 - 2014/12/23 189 | 190 | ### Enhancements 191 | 192 | * [Indentation] Continue of indentation in multiple line assignment. 193 | * [Indentation] Always indent with only 2 spaces except when function arguments span multiple lines. 194 | * [Indentation] Fix the indentation for mixed matchings. 195 | * [Indentation] Pipe |> indentation works correctly. 196 | * [Syntax Highlighting] Fontify continuation lines assignment. 197 | 198 | ### Changes 199 | 200 | * [Deprecated] Add deprecated message for eval and quoted functions. 201 | 202 | ## v2.0.2 - 2014/10/29 203 | * [#136](https://github.com/elixir-editors/emacs-elixir/pull/136) - Expand def of block operator regex to include non-newlines. 204 | * [#137](https://github.com/elixir-editors/emacs-elixir/pull/137) - update rake tasks 205 | * [#135](https://github.com/elixir-editors/emacs-elixir/pull/135) - Update README so the MELPA badge points to stable build. 206 | * [#133](https://github.com/elixir-editors/emacs-elixir/pull/133) - refine readme 207 | * [#134](https://github.com/elixir-editors/emacs-elixir/pull/134) - refine the ability to show the current elixir-mode version 208 | * [#132](https://github.com/elixir-editors/emacs-elixir/pull/132) - Added: test coverage with undercover.el 209 | * [#131](https://github.com/elixir-editors/emacs-elixir/pull/131) - Fix documentation url 210 | * [#127](https://github.com/elixir-editors/emacs-elixir/pull/127) - Add failing test for comment in cond expression (fixed) 211 | * [#128](https://github.com/elixir-editors/emacs-elixir/pull/128) - Added: collection of failing tests 212 | * [#124](https://github.com/elixir-editors/emacs-elixir/pull/124) - Fixed: Build Status badge 213 | * [#123](https://github.com/elixir-editors/emacs-elixir/pull/123) - TravisCI: add-apt-repository and apt-get install fix 214 | * [#121](https://github.com/elixir-editors/emacs-elixir/pull/121) - Add TravisCI support 215 | * [#122](https://github.com/elixir-editors/emacs-elixir/pull/122) - Remove unused code 216 | 217 | ## v2.0.1 - 2014/09/11 218 | * [#119](https://github.com/elixir-editors/emacs-elixir/pull/119) - Fixed: indent-inside-parens 219 | * [#117](https://github.com/elixir-editors/emacs-elixir/pull/117) - Fixed: emacs 24.3 tests 220 | * [#116](https://github.com/elixir-editors/emacs-elixir/pull/116) - Add "How to run tests" section to CONTRIBUTING.md 221 | * [#118](https://github.com/elixir-editors/emacs-elixir/pull/118) - Added: try/rescue rule 222 | * [#114](https://github.com/elixir-editors/emacs-elixir/pull/114) - Fixed: run tests interectively for terminal 223 | 224 | ## v2.0.0 - 2014/09/08 225 | * [#113](https://github.com/elixir-editors/emacs-elixir/pull/113) - Cask and ert-runner support 226 | * [#110](https://github.com/elixir-editors/emacs-elixir/pull/110) - Added: ability to run tests via EVM 227 | * [#111](https://github.com/elixir-editors/emacs-elixir/pull/111) - Fixed: elixir-quoted-minor-mode tests for ert-run-tests-interactively 228 | * [#108](https://github.com/elixir-editors/emacs-elixir/pull/108) - Fix various issues caused by code followed by inline comments 229 | 230 | ## v1.5.0 - 2014/08/27 231 | * [#103](https://github.com/elixir-editors/emacs-elixir/pull/103) - Add elixir-quoted-minor-mode. 232 | 233 | ## v1.4.10 - 2014/08/26 234 | * [#102](https://github.com/elixir-editors/emacs-elixir/pull/102) - Add support for syntax highlighting for variable interpolation. Fixes #93 235 | * [#101](https://github.com/elixir-editors/emacs-elixir/pull/101) - Fix indentation after inline comment. Fixes #95 236 | 237 | ## v1.4.9 - 2014/08/25 238 | * [#100](https://github.com/elixir-editors/emacs-elixir/pull/100) - Fix indentation in multi-line match expressions. Fixes #98 239 | * [#99](https://github.com/elixir-editors/emacs-elixir/pull/99) - Tokenize trailing whitespace properly. Fixes #97 240 | * [#96](https://github.com/elixir-editors/emacs-elixir/pull/96) - Remove syntax highlighting for operators. 241 | 242 | ## v1.4.8 - 2014/08/19 243 | * [#92](https://github.com/elixir-editors/emacs-elixir/pull/92) - Update Rakefile to also run the release.py script. Refs #88. 244 | * [#91](https://github.com/elixir-editors/emacs-elixir/pull/91) - Updates to regexes for various lexemes 245 | * [#90](https://github.com/elixir-editors/emacs-elixir/pull/90) - Fix bug in atom highlighting. 246 | 247 | ## v1.4.7 - 2014/08/18 248 | * [#87](https://github.com/elixir-editors/emacs-elixir/pull/87) - Add syntax highlighting for heredocs & failing tests for indentation. 249 | * [#85](https://github.com/elixir-editors/emacs-elixir/pull/85) - Improve regex for defmodule highlighting. 250 | * [#84](https://github.com/elixir-editors/emacs-elixir/pull/84) - Improve fontification for identifiers. 251 | 252 | ## v1.4.6 - 2014/08/18 253 | * [#82](https://github.com/elixir-editors/emacs-elixir/pull/82) - Remove broken SMIE rule. 254 | 255 | ## v1.4.5 - 2014/08/18 256 | * [#81](https://github.com/elixir-editors/emacs-elixir/pull/81) - Rewrite token emitting functions 257 | 258 | ## v1.4.4 - 2014/08/18 259 | * [#79](https://github.com/elixir-editors/emacs-elixir/pull/79) - Remove erroneous defrecord syntax. 260 | 261 | ## v1.4.3 - 2014/08/16 262 | * [#75](https://github.com/elixir-editors/emacs-elixir/pull/75) - Clean up several minor bugbears in elixir-smie. 263 | * [#74](https://github.com/elixir-editors/emacs-elixir/pull/74) - Remove special indentation rules for operators, except booleans. 264 | 265 | ## v1.4.2 - 2014/08/15 266 | * [#73](https://github.com/elixir-editors/emacs-elixir/pull/73) - Fix buggy syntax highlighting behavior involving "?" 267 | * [#71](https://github.com/elixir-editors/emacs-elixir/pull/71) - Need two backslashes in regex string. 268 | * [#70](https://github.com/elixir-editors/emacs-elixir/pull/70) - Use define-derived-mode to define elixir-mode 269 | * [#69](https://github.com/elixir-editors/emacs-elixir/pull/69) - Remove unused variable 270 | 271 | ## v1.4.1 - 2014/08/11 272 | * [#66](https://github.com/elixir-editors/emacs-elixir/pull/66) - Indent correctly after one-liner if/do: statements. Fixes #65 273 | * [#64](https://github.com/elixir-editors/emacs-elixir/pull/64) - wrong indentation if space between if and statement 274 | * [#63](https://github.com/elixir-editors/emacs-elixir/pull/63) - Correctly indent one-line anon fns AND block fns. Fixes #59 275 | 276 | ## v1.4.0 - 2014/07/09 277 | * [#62](https://github.com/elixir-editors/emacs-elixir/pull/62) - Remove grammar entry causing erroneous alignment to ".". Fixes #49 278 | * [#61](https://github.com/elixir-editors/emacs-elixir/pull/61) - Remove "=" & left-assoc opers from "OP" regex. Fixes #18. 279 | * [#60](https://github.com/elixir-editors/emacs-elixir/pull/60) - Refactor font face defaults. 280 | * [#58](https://github.com/elixir-editors/emacs-elixir/pull/58) - Use string syntax highlighting for regex patterns. 281 | * [#57](https://github.com/elixir-editors/emacs-elixir/pull/57) - Add beginnings of font-face testing. 282 | 283 | ## v1.3.1 - 2014/07/05 284 | * [#52](https://github.com/elixir-editors/emacs-elixir/pull/52) - Add CLI for running elixir-mode tests. 285 | * [#48](https://github.com/elixir-editors/emacs-elixir/pull/48) - Add a SMIE rule function for "def". Fixes #38 and #41 286 | * [#50](https://github.com/elixir-editors/emacs-elixir/pull/50) - Remove grammar clause for "fn" terminal. Fixes #7 287 | * [#44](https://github.com/elixir-editors/emacs-elixir/pull/44) - sigil % to ~ 288 | * [#46](https://github.com/elixir-editors/emacs-elixir/pull/46) - Added highlight for unless and Task module 289 | * [#45](https://github.com/elixir-editors/emacs-elixir/pull/45) - Highlight defstruct and Actor, Base modules 290 | * [#40](https://github.com/elixir-editors/emacs-elixir/pull/40) - IMenu: Show ExUnit tests under heading "Tests". 291 | * [#37](https://github.com/elixir-editors/emacs-elixir/pull/37) - Remove needless statement 292 | * [#35](https://github.com/elixir-editors/emacs-elixir/pull/35) - Added simple imenu support. 293 | * [#32](https://github.com/elixir-editors/emacs-elixir/pull/32) - Properly make variables local 294 | * [#31](https://github.com/elixir-editors/emacs-elixir/pull/31) - needed for effective code-navigation via syntax-ppss 295 | * [#28](https://github.com/elixir-editors/emacs-elixir/pull/28) - Recognize ? char syntax 296 | * [#24](https://github.com/elixir-editors/emacs-elixir/pull/24) - elixir-mode-eval-on-current-buffer binding comment incorrect 297 | * [#22](https://github.com/elixir-editors/emacs-elixir/pull/22) - Enhance `elixir-mode-iex` to accept additional arguments 298 | 299 | ## 1.3.0 (June 24, 2013) 300 | * Add `elixir-mode-eval-on-region` to evalute Elixir code on the 301 | marked region. 302 | * Add `elixir-mode-eval-on-current-buffer` to evalute Elixir code in the current buffer. 303 | * Add `elixir-mode-eval-on-current-line` to evalute Elixir code on the current line. 304 | * Add `elixir-mode-string-to-quoted-on-region` to get the representation of the expression on the marked region. 305 | * Add `elixir-mode-string-to-quoted-on-current-line` to get the 306 | representation of the expression on the current line. 307 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Guidelines For Reporting An Issue/Feature 2 | 3 | So you've found a bug or have a great idea for a feature. Here's the steps you 4 | should take to help get it added/fixed in emacs-elixir 5 | 6 | * First, check to see if there's an existing issue/pull request for the 7 | bug/feature. All issues are at https://github.com/elixir-editors/emacs-elixir/issues 8 | and pull reqs are at https://github.com/elixir-editors/emacs-elixir/pulls. 9 | * If there isn't one there, please file an issue. The ideal report includes: 10 | 11 | * A description of the problem/suggestion. 12 | * How to recreate the bug. 13 | * Versions of your: 14 | 15 | * operating system 16 | * elixir-mode 17 | * emacs 18 | 19 | * Ideally, creating a pull request with a test case demonstrating what's wrong. 20 | This makes it easy for us to review, reproduce & fix the problem. 21 | 22 | You might also hop into the IRC channel (``#elixir-lang`` on ``irc.freenode.net``) 23 | & raise your question there, as there may be someone who can help you with a 24 | work-around. 25 | 26 | 27 | ## Guidelines For Contributing Code 28 | 29 | If you're ready to take the plunge & contribute back some code, the 30 | process should look like: 31 | 32 | * Fork the project on GitHub into your own account. 33 | * Clone your copy of emacs-elixir. 34 | * Make a new branch in git & commit your changes there. 35 | * Push your new branch up to GitHub. 36 | * Again, ensure there isn't already an issue or pull request out there on it. 37 | If there is & you feel you have a better fix, please take note of the issue 38 | number & mention it in your pull request. 39 | * Create a new pull request (based on your branch), including what the 40 | problem/feature is, versions of your software & referencing any related 41 | issues/pull requests. 42 | 43 | In order to be merged into emacs-elixir, contributions must have the following: 44 | 45 | * A solid patch that: 46 | 47 | * is clear. 48 | * works across all supported versions of Emacs (25+). 49 | * follows the existing style of the code base. 50 | * comments included as needed. 51 | 52 | * A test case that demonstrates the previous flaw that now passes 53 | with the included patch. 54 | 55 | If your contribution lacks any of these things, they will have to be added 56 | by a core contributor before being merged into emacs-elixir proper, which may take 57 | substantial time for the all-volunteer team to get to. 58 | 59 | ## How to run tests 60 | 61 | We use [Eldev](https://github.com/doublep/eldev) as the project management tool and its built-in ERT integration. Our build matrix can be seen in the workflow file in `.github/workflows/ci.yml`. This ensure we run tests in all of our build matrix. 62 | 63 | Read more about Eldev in its home page. 64 | 65 | #### Examples of usage 66 | 67 | * Run all tests: 68 | 69 | ```bash 70 | $ eldev test 71 | ``` 72 | 73 | * Check dependencies 74 | 75 | ```bash 76 | $ eldev deps 77 | ``` 78 | 79 | ```bash 80 | $ eldev deps test 81 | ``` 82 | 83 | * Run linters 84 | 85 | ```bash 86 | $ eldev lint 87 | ``` 88 | 89 | * Compile project (byte compilation) 90 | 91 | ```bash 92 | $ eldev compile 93 | ``` 94 | 95 | * Clean-up, for example, after compilation 96 | 97 | ```bash 98 | $ eldev clean 99 | ``` 100 | -------------------------------------------------------------------------------- /Eldev: -------------------------------------------------------------------------------- 1 | ; -*- mode: emacs-lisp; lexical-binding: t; no-byte-compile: t -*- 2 | 3 | ;; Autodetermined by `eldev init'. 4 | (eldev-use-package-archive 'melpa) 5 | 6 | (eldev-use-plugin 'autoloads) 7 | (eldev-use-plugin 'undercover) 8 | 9 | (eldev-add-loading-roots 'test "tests") 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt) 2 | [![Build Status](https://github.com/elixir-editors/emacs-elixir/actions/workflows/ci.yml/badge.svg)](https://github.com/elixir-editors/emacs-elixir/actions) 3 | [![NonGNU ELPA](https://elpa.nongnu.org/nongnu/elixir-mode.svg)](https://elpa.nongnu.org/nongnu/elixir-mode.html) 4 | [![MELPA Stable](http://stable.melpa.org/packages/elixir-mode-badge.svg)](http://stable.melpa.org/#/elixir-mode) 5 | [![MELPA](http://melpa.org/packages/elixir-mode-badge.svg)](http://melpa.org/#/elixir-mode) 6 | 7 | > **WARNING** 8 | > There is a built-in Elixir mode with tree-sitter support from Emacs 30+ that can also be used with Emacs 29 9 | > This repository is for an older elixir-mode built with SMIE (Simple Minded Indentation Engine) which is not 10 | > as advanced as tree-sitter for a language like Elixir. 11 | 12 | # Elixir Mode 13 | 14 | Provides font-locking, indentation and navigation support for the 15 | [Elixir programming language.](http://elixir-lang.org/) 16 | 17 | ## Installation 18 | 19 | `elixir-mode` is available on [NON-GNU ELPA](https://elpa.nongnu.org/), 20 | [MELPA STABLE](https://stable.melpa.org/) and [MELPA](https://melpa.org/). 21 | 22 | ### Via package.el 23 | 24 | `package.el` is the built-in package manager in Emacs. 25 | 26 | You can install `elixir-mode` with the following command: 27 | 28 | M-x package-install [RET] elixir-mode [RET] 29 | 30 | or by adding this bit of Emacs Lisp code to your Emacs initialization file 31 | (`.emacs` or `init.el`): 32 | 33 | ```el 34 | (unless (package-installed-p 'elixir-mode) 35 | (package-install 'elixir-mode)) 36 | ``` 37 | 38 | If the installation doesn't work try refreshing the package list: 39 | 40 | M-x package-refresh-contents [RET] 41 | 42 | Keep in mind that MELPA packages are built automatically from 43 | the `master` branch, meaning bugs might creep in there from time to 44 | time. Never-the-less, installing from MELPA is the recommended way of 45 | obtaining `Elixir-Mode`. 46 | 47 | MELPA Stable contains packages released from our tags. 48 | 49 | ### Via use-package 50 | 51 | Since Emacs 29, `use-package` is a built-in feature. For versions prior to 29 52 | one can also install it from MELPA (and MELPA Stable). 53 | 54 | To install elixir-mode using `use-package` one can: 55 | 56 | ``` elisp 57 | (use-package elixir-mode 58 | :ensure t) 59 | ``` 60 | 61 | ## Usage 62 | 63 | ### Interactive Commands 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
Command (For the M-x prompt.)Description
elixir-modeSwitches to elixir-mode.
elixir-mode-open-githubOpen the GitHub page for Elixir.
elixir-mode-open-elixir-homeGo to Elixir README in the browser.
elixir-mode-open-docs-masterOpen the Elixir documentation for the master.
elixir-mode-open-docs-stableOpen the Elixir documentation for the latest stable release.
elixir-mode-show-versionPrint version info for elixir-mode.
96 | 97 | ### Configuration 98 | 99 | Any file that matches the glob `*.ex[s]` or `*.elixir` is 100 | automatically opened in elixir-mode, but you can change this 101 | functionality easily. 102 | 103 | ```lisp 104 | ;; Highlights *.elixir2 as well 105 | (add-to-list 'auto-mode-alist '("\\.elixir2\\'" . elixir-mode)) 106 | ``` 107 | 108 | ### Keymapping 109 | 110 | Keymaps can be added to the `elixir-mode-map` variable. 111 | 112 | ### Pairing 113 | 114 | [Smartparens](https://github.com/Fuco1/smartparens) has direct support for Elixir. 115 | 116 | Alternatively, if you want to use `ruby-end-mode`, you can add the following to your `elixir-mode-hook`: 117 | 118 | ```lisp 119 | (add-to-list 'elixir-mode-hook 120 | (defun auto-activate-ruby-end-mode-for-elixir-mode () 121 | (set (make-variable-buffer-local 'ruby-end-expand-keywords-before-re) 122 | "\\(?:^\\|\\s-+\\)\\(?:do\\)") 123 | (set (make-variable-buffer-local 'ruby-end-check-statement-modifiers) nil) 124 | (ruby-end-mode +1))) 125 | ``` 126 | 127 | ## Notes 128 | 129 | This package is tested only with a single version of OTP and 3 versions of Elixir. Please, always report versions 130 | (Emacs, Elixir and Erlang/OTP) when raising issues. 131 | 132 | ## Elixir Tooling Integration 133 | 134 | You can use [web-mode.el](http://web-mode.org) to edit elixir templates (eex files). 135 | 136 | [mix.el](https://github.com/ayrat555/mix.el) provides a minor mode for integration with Mix, a build tool that ships with Elixir. 137 | 138 | [exunit.el](https://github.com/ananthakumaran/exunit.el) provides `ExUnit` integration. 139 | 140 | ## Elixir Format 141 | 142 | This mode can call mix for formatting code. When inside an elixir buffer, just type `M-x elixir-format`. 143 | 144 | To automate that, you can add this command to the `before-save` hook. 145 | 146 | ``` elisp 147 | ;; Create a buffer-local hook to run elixir-format on save, only when we enable elixir-mode. 148 | (add-hook 'elixir-mode-hook 149 | (lambda () (add-hook 'before-save-hook 'elixir-format nil t))) 150 | ``` 151 | 152 | To use a `.formatter.exs` you can either set `elixir-format-arguments` globally to a path like this: 153 | 154 | ``` elisp 155 | (setq elixir-format-arguments (list "--dot-formatter" "/path/to/.formatter.exs")) 156 | ``` 157 | 158 | or you set `elixir-format-arguments` in a hook like this: 159 | 160 | ``` elisp 161 | (add-hook 'elixir-format-hook (lambda () 162 | (if (projectile-project-p) 163 | (setq elixir-format-arguments 164 | (list "--dot-formatter" 165 | (concat (locate-dominating-file buffer-file-name ".formatter.exs") ".formatter.exs"))) 166 | (setq elixir-format-arguments nil)))) 167 | ``` 168 | 169 | In this example we use [Projectile](https://github.com/bbatsov/projectile) to determine if we are in a project and then set `elixir-format-arguments` accordingly. 170 | 171 | Please note that this code snippet may cause unhappiness if there is no `.formatter.exs` file available. 172 | 173 | ## Tips & Tricks 174 | 175 | ### Prettify symbols 176 | 177 | Emacs supports [font ligatures](https://en.wikipedia.org/wiki/Ligature_(writing)). For enabling it for Elixir 178 | you can add it to your configuration: 179 | 180 | ``` elisp 181 | (add-hook 182 | 'elixir-mode-hook 183 | (lambda () 184 | (push '(">=" . ?\u2265) prettify-symbols-alist) 185 | (push '("<=" . ?\u2264) prettify-symbols-alist) 186 | (push '("!=" . ?\u2260) prettify-symbols-alist) 187 | (push '("==" . ?\u2A75) prettify-symbols-alist) 188 | (push '("=~" . ?\u2245) prettify-symbols-alist) 189 | (push '("<-" . ?\u2190) prettify-symbols-alist) 190 | (push '("->" . ?\u2192) prettify-symbols-alist) 191 | (push '("<-" . ?\u2190) prettify-symbols-alist) 192 | (push '("|>" . ?\u25B7) prettify-symbols-alist))) 193 | 194 | ;; Or if you use use-packge 195 | 196 | (use-package elixir-mode 197 | :hook (elixir-mode . (lambda () 198 | (push '(">=" . ?\u2265) prettify-symbols-alist) 199 | (push '("<=" . ?\u2264) prettify-symbols-alist) 200 | (push '("!=" . ?\u2260) prettify-symbols-alist) 201 | (push '("==" . ?\u2A75) prettify-symbols-alist) 202 | (push '("=~" . ?\u2245) prettify-symbols-alist) 203 | (push '("<-" . ?\u2190) prettify-symbols-alist) 204 | (push '("->" . ?\u2192) prettify-symbols-alist) 205 | (push '("<-" . ?\u2190) prettify-symbols-alist) 206 | (push '("|>" . ?\u25B7) prettify-symbols-alist)))) 207 | ``` 208 | 209 | ### Formatting 210 | 211 | If you have issues with the formatter provided by this package, you can try 212 | using the format function of language servers. Elixir-ls supports this. 213 | 214 | One way to configure this with `eglot` and `use-package` would be: 215 | 216 | ``` elisp 217 | (use-package elixir-mode 218 | :hook (elixir-mode . eglot-ensure) 219 | (before-save . eglot-format)) 220 | ``` 221 | 222 | ### Syntax highlighting for LiveView (and similar techniques for other syntaxes) 223 | 224 | When you need different major modes in the SAME buffer, Emacs do not provide a standard way. 225 | There is an external package that can help here by adding more than one major mode called `poly-mode`. 226 | 227 | A possible configuration would be: 228 | 229 | ``` elisp 230 | ;; Assumes web-mode and elixir-mode are already set up 231 | ;; 232 | (use-package polymode 233 | :mode ("\.ex$" . poly-elixir-web-mode) 234 | :config 235 | (define-hostmode poly-elixir-hostmode :mode 'elixir-mode) 236 | (define-innermode poly-liveview-expr-elixir-innermode 237 | :mode 'web-mode 238 | :head-matcher (rx line-start (* space) "~H" (= 3 (char "\"'")) line-end) 239 | :tail-matcher (rx line-start (* space) (= 3 (char "\"'")) line-end) 240 | :head-mode 'host 241 | :tail-mode 'host 242 | :allow-nested nil 243 | :keep-in-mode 'host 244 | :fallback-mode 'host) 245 | (define-polymode poly-elixir-web-mode 246 | :hostmode 'poly-elixir-hostmode 247 | :innermodes '(poly-liveview-expr-elixir-innermode)) 248 | ) 249 | (setq web-mode-engines-alist '(("elixir" . "\\.ex\\'"))) 250 | ``` 251 | 252 | ## History 253 | 254 | This mode is based on the [Emacs mode by secondplanet](https://github.com/secondplanet/elixir-mode). 255 | 256 | ## Contributing 257 | 258 | Please read [CONTRIBUTING.md](https://github.com/elixir-editors/emacs-elixir/blob/master/CONTRIBUTING.md) for guidelines on how to contribute to this project. 259 | 260 | ## License 261 | 262 | Copyright © 2011-2017 Samuel Tonini, Matt DeBoard, Andreas Fuchs, secondplanet and [contributors](https://github.com/elixir-editors/emacs-elixir/contributors). 263 | 264 | Distributed under the GNU General Public License, version 3 265 | 266 | [badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg 267 | -------------------------------------------------------------------------------- /elixir-format.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-format.el --- Emacs plugin to mix format Elixir files 2 | 3 | ;; Copyright 2017-2018 Anil Wadghule, Christian Kruse 4 | 5 | ;; This file is NOT part of GNU Emacs. 6 | 7 | ;; This program is free software; you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation; either version 2, or (at your option) 10 | ;; any later version. 11 | 12 | ;; This program 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 | 17 | ;;; Commentary: 18 | 19 | ;; The elixir-format function formats the elixir files with Elixir's `mix format` 20 | ;; command 21 | 22 | ;; e.g. 23 | ;; M-x elixir-format 24 | ;; 25 | 26 | (require 'ansi-color) 27 | (require 'cl-lib) 28 | 29 | (defcustom elixir-format-arguments nil 30 | "Additional arguments to 'mix format'." 31 | :type '(repeat string) 32 | :group 'elixir 33 | :group 'elixir-format) 34 | 35 | (defcustom elixir-format-hook nil 36 | "Hook called by `elixir-format'." 37 | :type 'hook 38 | :group 'elixir 39 | :group 'elixir-format) 40 | 41 | 42 | ;;; Code: 43 | 44 | (defun elixir-format--errbuff () 45 | (get-buffer-create "*elixir-format-errors*")) 46 | 47 | (defun elixir-format--outbuff () 48 | (get-buffer-create "*elixir-format-output*")) 49 | 50 | (defun elixir-format--elixir-executable () 51 | (executable-find "elixir")) 52 | 53 | (defun elixir-format--mix-executable () 54 | (executable-find "mix")) 55 | 56 | ;;;###autoload 57 | (defun elixir-format (&optional called-interactively-p) 58 | (interactive "p") 59 | (if (not (elixir-format--elixir-and-mix-path-set-p)) 60 | (elixir-format--display-missing-executables-error called-interactively-p) 61 | (unwind-protect 62 | (save-restriction 63 | (elixir-format--clean-output-buffers) 64 | (elixir-format--run-format called-interactively-p))))) 65 | 66 | (defun elixir-format--elixir-and-mix-path-set-p () 67 | (and (elixir-format--elixir-executable) 68 | (elixir-format--mix-executable))) 69 | 70 | (defun elixir-format--display-missing-executables-error (called-interactively-p) 71 | (with-current-buffer (elixir-format--errbuff) 72 | (setq buffer-read-only nil) 73 | (erase-buffer) 74 | (insert "Emacs is unable to find the executables for elixir and/or mix. Either they are not installed on your system or emacs' PATH is not as wide as it needs to be. The latter is most likely to happen on OSX, in which case the simplest answer may be to add the exec-path-from-shell package to your configuration.") 75 | (setq buffer-read-only t) 76 | (ansi-color-apply-on-region (point-min) (point-max)) 77 | (special-mode) 78 | (if called-interactively-p 79 | (display-buffer (elixir-format--errbuff)) 80 | (error "Elixir Format error see %s" (elixir-format--errbuff))))) 81 | 82 | (defun elixir-format--clean-output-buffers () 83 | (with-current-buffer (elixir-format--outbuff) 84 | (erase-buffer)) 85 | 86 | (with-current-buffer (elixir-format--errbuff) 87 | (setq buffer-read-only nil) 88 | (erase-buffer))) 89 | 90 | (defun elixir-format--target-file-name () 91 | "Returns the file name of current visited file. 92 | 93 | If the buffer is not visiting any file (like during tests) then 94 | it returns a file name based on the name of the buffer." 95 | (or buffer-file-name (concat (secure-hash 'md5 (buffer-name)) ".ex"))) 96 | 97 | (defun elixir-format--temp-file-path () 98 | "Make a temp file in the current directory, because mix format 99 | applies rules based on path patterns and looks for .formatter.exs 100 | files in subdirectories." 101 | (let ((target-file-name (elixir-format--target-file-name))) 102 | (concat (file-name-sans-extension target-file-name) 103 | "-emacs-elixir-format." 104 | (file-name-extension target-file-name)))) 105 | 106 | (defun elixir-format--run-format (called-interactively-p) 107 | (let ((tmpfile (elixir-format--temp-file-path)) 108 | (our-elixir-format-arguments (list "format"))) 109 | 110 | (write-region nil nil tmpfile) 111 | (run-hooks 'elixir-format-hook) 112 | 113 | (when elixir-format-arguments 114 | (setq our-elixir-format-arguments (append our-elixir-format-arguments elixir-format-arguments))) 115 | (setq our-elixir-format-arguments (append our-elixir-format-arguments (list tmpfile))) 116 | 117 | (unwind-protect 118 | (if (zerop (elixir-format--from-mix-root (elixir-format--mix-executable) (elixir-format--errbuff) our-elixir-format-arguments)) 119 | (elixir-format--call-format-command tmpfile) 120 | (elixir-format--failed-to-format called-interactively-p)) 121 | (delete-file tmpfile) 122 | (kill-buffer (elixir-format--outbuff))))) 123 | 124 | (defun elixir-format--call-format-command (tmpfile) 125 | (if (zerop (call-process-region (point-min) (point-max) "diff" nil (elixir-format--outbuff) nil "-n" "-" tmpfile)) 126 | (message "File is already formatted") 127 | (elixir-format--apply-rcs-patch (elixir-format--outbuff)) 128 | (message "elixir-format format applied")) 129 | (kill-buffer (elixir-format--errbuff))) 130 | 131 | (defun elixir-format--failed-to-format (called-interactively-p) 132 | (with-current-buffer (elixir-format--errbuff) 133 | (setq buffer-read-only t) 134 | (ansi-color-apply-on-region (point-min) (point-max)) 135 | (special-mode)) 136 | 137 | (if called-interactively-p 138 | (display-buffer (elixir-format--errbuff)) 139 | (error "elixir-format failed: see %s" (buffer-name (elixir-format--errbuff))))) 140 | 141 | (defun elixir-format--apply-rcs-patch (patch-buffer) 142 | "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer. 143 | Shamelessly stolen from go-mode (https://github.com/dominikh/go-mode.el)" 144 | 145 | (let ((target-buffer (current-buffer)) 146 | ;; Relative offset between buffer line numbers and line numbers 147 | ;; in patch. 148 | ;; 149 | ;; Line numbers in the patch are based on the source file, so 150 | ;; we have to keep an offset when making changes to the 151 | ;; buffer. 152 | ;; 153 | ;; Appending lines decrements the offset (possibly making it 154 | ;; negative), deleting lines increments it. This order 155 | ;; simplifies the forward-line invocations. 156 | (line-offset 0)) 157 | (save-excursion 158 | (with-current-buffer patch-buffer 159 | (goto-char (point-min)) 160 | (while (not (eobp)) 161 | (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") 162 | (error "Invalid rcs patch or internal error in elixir-format--apply-rcs-patch")) 163 | (forward-line) 164 | (let ((action (match-string 1)) 165 | (from (string-to-number (match-string 2))) 166 | (len (string-to-number (match-string 3)))) 167 | (cond 168 | ((equal action "a") 169 | (let ((start (point))) 170 | (forward-line len) 171 | (let ((text (buffer-substring start (point)))) 172 | (with-current-buffer target-buffer 173 | (cl-decf line-offset len) 174 | (goto-char (point-min)) 175 | (forward-line (- from len line-offset)) 176 | (insert text))))) 177 | ((equal action "d") 178 | (with-current-buffer target-buffer 179 | (elixir-format--goto-line (- from line-offset)) 180 | (cl-incf line-offset len) 181 | (elixir-format--delete-whole-line len))) 182 | (t 183 | (error "Invalid rcs patch or internal error in elixir-format--apply-rcs-patch"))))))))) 184 | 185 | (defun elixir-format--goto-line (line) 186 | (goto-char (point-min)) 187 | (forward-line (1- line))) 188 | 189 | (defun elixir-format--delete-whole-line (&optional arg) 190 | "Delete the current line without putting it in the `kill-ring'. 191 | Derived from function `kill-whole-line'. ARG is defined as for that 192 | function. 193 | 194 | Shamelessly stolen from go-mode (https://github.com/dominikh/go-mode.el)" 195 | (setq arg (or arg 1)) 196 | (if (and (> arg 0) 197 | (eobp) 198 | (save-excursion (forward-visible-line 0) (eobp))) 199 | (signal 'end-of-buffer nil)) 200 | (if (and (< arg 0) 201 | (bobp) 202 | (save-excursion (end-of-visible-line) (bobp))) 203 | (signal 'beginning-of-buffer nil)) 204 | (cond ((zerop arg) 205 | (delete-region (progn (forward-visible-line 0) (point)) 206 | (progn (end-of-visible-line) (point)))) 207 | ((< arg 0) 208 | (delete-region (progn (end-of-visible-line) (point)) 209 | (progn (forward-visible-line (1+ arg)) 210 | (unless (bobp) 211 | (backward-char)) 212 | (point)))) 213 | (t 214 | (delete-region (progn (forward-visible-line 0) (point)) 215 | (progn (forward-visible-line arg) (point)))))) 216 | 217 | 218 | (defun elixir-format--from-mix-root (mix-path errbuff format-arguments) 219 | "Run mix format where `mix.exs' is located, because mix is 220 | meant to be run from the project root. Otherwise, run in the 221 | current directory." 222 | (let ((original-default-directory default-directory) 223 | (mix-dir (locate-dominating-file (elixir-format--target-file-name) "mix.exs"))) 224 | 225 | (when mix-dir 226 | (setq default-directory (expand-file-name mix-dir))) 227 | 228 | (message (concat "Run " 229 | (abbreviate-file-name default-directory) ": " 230 | (mapconcat 'identity format-arguments " "))) 231 | 232 | (let ((result (apply #'call-process 233 | mix-path nil errbuff nil format-arguments))) 234 | (setq default-directory original-default-directory) 235 | result))) 236 | 237 | (provide 'elixir-format) 238 | 239 | ;;; elixir-format.el ends here 240 | -------------------------------------------------------------------------------- /elixir-mode.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-mode.el --- Major mode for editing Elixir files -*- lexical-binding: t -*- 2 | 3 | ;; Copyright 2011-2015 secondplanet 4 | ;; 2013-2015 Samuel Tonini, Matt DeBoard, Andreas Fuchs 5 | ;; Authors: Humza Yaqoob, 6 | ;; Andreas Fuchs , 7 | ;; Matt DeBoard 8 | ;; Samuel Tonini 9 | 10 | ;; URL: https://github.com/elixir-editors/emacs-elixir 11 | ;; Created: Mon Nov 7 2011 12 | ;; Keywords: languages elixir 13 | ;; Version: 2.5.0 14 | ;; Package-Requires: ((emacs "25")) 15 | 16 | ;; This file is not a part of GNU Emacs. 17 | 18 | ;; This program is free software; you can redistribute it and/or modify 19 | ;; it under the terms of the GNU General Public License as published by 20 | ;; the Free Software Foundation; either version 2, or (at your option) 21 | ;; any later version. 22 | 23 | ;; This program is distributed in the hope that it will be useful, 24 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | ;; GNU General Public License for more details. 27 | 28 | ;; You should have received a copy of the GNU General Public License 29 | ;; along with this program; if not, write to the Free Software 30 | ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 31 | 32 | ;;; Commentary: 33 | 34 | ;; Provides font-locking, indentation and navigation support 35 | ;; for the Elixir programming language. 36 | 37 | ;;; Code: 38 | 39 | (require 'easymenu) ; Elixir Mode menu definition 40 | (require 'elixir-smie) ; Syntax and indentation support 41 | (require 'elixir-format) ; Elixir Format functions 42 | 43 | (defgroup elixir nil 44 | "Major mode for editing Elixir code." 45 | :prefix "elixir-" 46 | :group 'languages 47 | :link '(url-link :tag "Github" "https://github.com/elixir-editors/emacs-elixir") 48 | :link '(emacs-commentary-link :tag "Commentary" "elixir-mode")) 49 | 50 | (defcustom elixir-mode-website-url "http://elixir-lang.org" 51 | "Official url of Elixir programming website." 52 | :type 'string) 53 | 54 | (defcustom elixir-mode-doc-url "https://hexdocs.pm/elixir" 55 | "Official documentation for the Elixir programming language." 56 | :type 'string) 57 | 58 | (defcustom elixir-mode-hook nil 59 | "Hook that runs when switching to major mode" 60 | :type 'hook) 61 | 62 | (defvar elixir-mode-map 63 | (let ((map (make-sparse-keymap))) 64 | map) 65 | "Keymap used in `elixir-mode'.") 66 | 67 | (defvar elixir-imenu-generic-expression 68 | '(("Modules" "^\\s-*defmodule[ \n\t]+\\([A-Z][A-Za-z0-9._]+\\)\\s-+.*$" 1) 69 | ("Public Functions" "^\\s-*def[ \n\t]+\\([a-z0-9_!\\?]+\\)\\(([^)]*)\\)*.*" 1) 70 | ("Private Functions" "^\\s-*defp[ \n\t]+\\([a-z0-9_!\\?]+\\)\\(([^)]*)\\)*.*" 1) 71 | ("Public Macros" "^\\s-*defmacro[ \n\t]+\\([a-z0-9_!\\?]+\\)\\(([^)]*)\\)*.*" 1) 72 | ("Private Macros" "^\\s-*defmacrop[ \n\t]+\\([a-z0-9_!\\?]+\\)\\(([^)]*)\\)*.*" 1) 73 | ("Public Guards" "^\\s-*defguard[ \n\t]+\\([a-z0-9_!\\?]+\\)\\(([^)]*)\\)*.*" 1) 74 | ("Private Guards" "^\\s-*defguardp[ \n\t]+\\([a-z0-9_!\\?]+\\)\\(([^)]*)\\)*.*" 1) 75 | ("Delegates" "^\\s-*defdelegate[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*.*" 1) 76 | ("Overridables" "^\\s-*defoverridable[ \n\t]+\\([a-z0-9_]+\\)\\(([^)]*)\\)*.*" 1) 77 | ("Tests" "^\\s-*test[ \t\n]+\"?\\(:?[a-z0-9_@+() \t-]+\\)\"?[ \t\n]+.*" 1)) 78 | "Imenu pattern for `elixir-mode'.") 79 | 80 | (defcustom elixir-basic-offset 2 81 | "Basic offset." 82 | :type 'integer) 83 | (defcustom elixir-key-label-offset 0 84 | "Offset used for key label." 85 | :type 'integer) 86 | (defcustom elixir-match-label-offset 2 87 | "Offset for a match label." 88 | :type 'integer) 89 | 90 | (defgroup elixir-faces nil 91 | "Font-lock faces for `elixir'." 92 | :group 'elixir 93 | :group 'faces) 94 | 95 | (defvar elixir-attribute-face 'elixir-attribute-face) 96 | (defface elixir-attribute-face 97 | '((t (:inherit font-lock-preprocessor-face))) 98 | "For use with module attribute tokens.") 99 | 100 | (defvar elixir-atom-face 'elixir-atom-face) 101 | (defface elixir-atom-face 102 | '((t (:inherit font-lock-builtin-face))) 103 | "For use with atoms & map keys.") 104 | 105 | (defvar elixir-number-face 'elixir-number-face) 106 | (defface elixir-number-face 107 | '((t (:inherit default))) 108 | "For use with numbers.") 109 | 110 | 111 | (eval-when-compile 112 | (defconst elixir-rx-constituents 113 | `( 114 | (string-delimiter . ,(rx (and 115 | ;; Match even number of backslashes. 116 | (or (not (any ?\\ ?\' ?\")) point 117 | ;; Quotes might be preceded by escaped quote 118 | (and (or (not (any ?\\)) point) ?\\ 119 | (* ?\\ ?\\) (any ?\' ?\"))) 120 | (* ?\\ ?\\) 121 | ;; Match single or triple quotes of any kind. 122 | (group (or "\"" "\"\"\"" "'" "'''"))))) 123 | (atoms . ,(rx ":" 124 | (or 125 | (and 126 | (any "a-z" "A-Z" "_" "\"" "'") 127 | (zero-or-more (any "a-z" "A-Z" "0-9" "_" "\"" "'" "!" "@" "?"))) 128 | (and "\"" (one-or-more (not (any "\""))) "\"") 129 | (and "'" (one-or-more (not (any "'"))) "'")))) 130 | (numbers . ,(rx (and symbol-start 131 | (? "-") 132 | (+ digit) 133 | (0+ (and "_" (= 3 digit))) 134 | symbol-end))) 135 | (builtin . ,(rx symbol-start 136 | (or "case" "cond" "for" "if" "quote" "raise" "receive" "send" 137 | "super" "throw" "try" "unless" "unquote" "unquote_splicing" 138 | "with") 139 | symbol-end)) 140 | (builtin-declaration . ,(rx symbol-start 141 | (or "def" "defp" "defmodule" "defprotocol" 142 | "defmacro" "defmacrop" "defdelegate" 143 | "defexception" "defstruct" "defimpl" 144 | "defguard" "defguardp" "defcallback" 145 | "defoverridable") 146 | symbol-end)) 147 | (builtin-namespace . ,(rx symbol-start 148 | (or "import" "require" "use" "alias") 149 | symbol-end)) 150 | ;; Set aside code point syntax for negation face. 151 | (code-point . ,(rx symbol-start 152 | "?" 153 | anything 154 | symbol-end)) 155 | (function-declaration . ,(rx (or line-start (not (any "."))) 156 | symbol-start 157 | (or "def" "defp") 158 | symbol-end)) 159 | ;; The first character of an identifier must be a letter or an underscore. 160 | ;; After that, they may contain any alphanumeric character + underscore. 161 | ;; Additionally, the final character may be either `?' or `!'. 162 | (identifiers . ,(rx (any "A-Z" "a-z" "_") 163 | (zero-or-more (any "A-Z" "a-z" "0-9" "_")) 164 | (optional (or "?" "!")))) 165 | (keyword . ,(rx symbol-start 166 | (or "fn" "do" "end" "after" "else" "rescue" "catch") 167 | symbol-end)) 168 | (keyword-operator . ,(rx symbol-start 169 | (or "not" "and" "or" "when" "in") 170 | symbol-end)) 171 | ;; Module and submodule names start with upper case letter. This 172 | ;; can then be followed by any combination of alphanumeric chars. 173 | ;; In turn, this can be followed by a `.' which begins the notation of 174 | ;; a submodule, which follows the same naming pattern of the module. 175 | ;; Finally, like other identifiers, it can be terminated with either `?' 176 | ;; or `!'. 177 | (module-names . ,(rx symbol-start 178 | (optional (or "%" "&")) 179 | (any "A-Z") 180 | (zero-or-more (any "A-Z" "a-z" "_" "0-9")) 181 | (zero-or-more 182 | (and "." 183 | (any "A-Z" "_") 184 | (zero-or-more (any "A-Z" "a-z" "_" "0-9")))) 185 | (optional (or "!" "?")) 186 | symbol-end)) 187 | (arrows . ,(rx (or "->" "<-" "=>" "|>"))) 188 | (pseudo-var . ,(rx symbol-start 189 | (optional (or "%" "&")) 190 | (or "_" "__MODULE__" "__DIR__" "__ENV__" "__CALLER__" 191 | "__block__" "__aliases__") 192 | symbol-end)) 193 | (sigils . ,(rx "~" (or "B" "C" "D" "E" "H" "L" "N" "R" "S" "T" "U" "b" "c" "e" "r" "s" "w"))))) 194 | 195 | (defmacro elixir-rx (&rest sexps) 196 | (let ((rx-constituents (append elixir-rx-constituents rx-constituents))) 197 | (cond ((null sexps) 198 | (error "No regexp")) 199 | ((cdr sexps) 200 | (rx-to-string `(and ,@sexps) t)) 201 | (t 202 | (rx-to-string (car sexps) t)))))) 203 | 204 | (defsubst elixir-syntax-in-string-or-comment-p () 205 | (elixir-ppss-comment-or-string-start (syntax-ppss))) 206 | 207 | (defsubst elixir-syntax-count-quotes (quote-char &optional point limit) 208 | "Count number of quotes around point (max is 3). 209 | QUOTE-CHAR is the quote char to count. Optional argument POINT is 210 | the point where scan starts (defaults to current point), and LIMIT 211 | is used to limit the scan." 212 | (let ((i 0)) 213 | (while (and (< i 3) 214 | (or (not limit) (< (+ point i) limit)) 215 | (eq (char-after (+ point i)) quote-char)) 216 | (setq i (1+ i))) 217 | i)) 218 | 219 | (defun elixir-syntax-stringify () 220 | "Put `syntax-table' property correctly on single/triple quotes." 221 | (let* ((num-quotes (length (match-string-no-properties 1))) 222 | (quote-starting-pos (- (point) num-quotes)) 223 | (quote-ending-pos (point)) 224 | (ppss (save-excursion 225 | (syntax-ppss quote-starting-pos))) 226 | (string-start (and (not (elixir-ppss-comment-depth ppss)) 227 | (elixir-ppss-comment-or-string-start ppss))) 228 | (num-closing-quotes 229 | (and string-start 230 | (elixir-syntax-count-quotes 231 | (char-before) string-start quote-starting-pos)))) 232 | (cond ((and string-start (= num-closing-quotes 0)) 233 | ;; This set of quotes doesn't match the string starting 234 | ;; kind. Do nothing. 235 | nil) 236 | ((not string-start) 237 | ;; This set of quotes delimit the start of a string. 238 | (put-text-property quote-starting-pos (1+ quote-starting-pos) 239 | 'syntax-table (string-to-syntax "|"))) 240 | ((= num-quotes num-closing-quotes) 241 | ;; This set of quotes delimit the end of a string. 242 | (put-text-property (1- quote-ending-pos) quote-ending-pos 243 | 'syntax-table (string-to-syntax "|"))) 244 | ((> num-quotes num-closing-quotes) 245 | ;; This may only happen whenever a triple quote is closing 246 | ;; a single quoted string. Add string delimiter syntax to 247 | ;; all three quotes. 248 | (put-text-property quote-starting-pos quote-ending-pos 249 | 'syntax-table (string-to-syntax "|")))))) 250 | 251 | 252 | (defun elixir-syntax-propertize-interpolation () 253 | (let* ((beg (match-beginning 0)) 254 | (context (save-excursion (save-match-data (syntax-ppss beg))))) 255 | (put-text-property beg (1+ beg) 'syntax-table (string-to-syntax "w")) 256 | (put-text-property beg (1+ beg) 'elixir-interpolation 257 | (cons (elixir-ppss-string-terminator context) 258 | (match-data))))) 259 | 260 | (defconst elixir-sigil-delimiter-pair 261 | '((?\( . ")") 262 | (?\{ . "}") 263 | (?\< . ">") 264 | (?\[ . "]"))) 265 | 266 | (defun elixir-syntax-replace-property-in-sigil () 267 | (unless (elixir-syntax-in-string-or-comment-p) 268 | (let ((heredoc-p (save-excursion 269 | (goto-char (match-beginning 0)) 270 | (looking-at-p "~[BCDEHLNRSTUbcersw]\\(?:'''\\|\"\"\"\\)")))) 271 | (unless heredoc-p 272 | (forward-char 1) 273 | (let* ((start-delim (char-after (1- (point)))) 274 | (end-delim (or (assoc-default start-delim elixir-sigil-delimiter-pair) 275 | (char-to-string start-delim))) 276 | (end (save-excursion 277 | (let (finish) 278 | (while (not finish) 279 | (skip-chars-forward (concat "^" end-delim)) 280 | (if (or (not (eq (char-before) ?\\)) 281 | (eq (char-before (1- (point))) ?\\) 282 | (eobp)) 283 | (setq finish t) 284 | (forward-char 1))) 285 | (point)))) 286 | (word-syntax (string-to-syntax "w"))) 287 | (when (memq start-delim '(?' ?\")) 288 | (setq end (1+ end)) 289 | (forward-char -1)) 290 | (while (re-search-forward "[\"'#]" end 'move) 291 | (put-text-property (1- (point)) (point) 'syntax-table word-syntax))))))) 292 | 293 | (defun elixir-syntax-propertize-function (start end) 294 | (let ((case-fold-search nil)) 295 | (goto-char start) 296 | (funcall 297 | (syntax-propertize-rules 298 | ("\\(\\?\\)[\"']" 299 | (1 (if (save-excursion 300 | (elixir-ppss-string-terminator 301 | (syntax-ppss (match-beginning 0)))) 302 | ;; Within a string, skip. 303 | (ignore 304 | (goto-char (match-end 1))) 305 | (put-text-property (match-end 1) (match-end 0) 306 | 'syntax-table (string-to-syntax "_")) 307 | (string-to-syntax "'")))) 308 | ((elixir-rx string-delimiter) 309 | (0 (ignore (elixir-syntax-stringify)))) 310 | ((elixir-rx sigils) 311 | (0 (ignore (elixir-syntax-replace-property-in-sigil)))) 312 | ((rx (group "#{" (0+ (not (any "}"))) "}")) 313 | (0 (ignore (elixir-syntax-propertize-interpolation))))) 314 | start end))) 315 | 316 | (defun elixir-match-interpolation (limit) 317 | (let ((pos (next-single-char-property-change (point) 'elixir-interpolation 318 | nil limit))) 319 | (when (and pos (> pos (point))) 320 | (goto-char pos) 321 | (let ((value (get-text-property pos 'elixir-interpolation))) 322 | (if (car value) 323 | (progn 324 | (set-match-data (cdr value)) 325 | t) 326 | (elixir-match-interpolation limit)))))) 327 | 328 | 329 | (defconst elixir-font-lock-keywords 330 | `( 331 | ;; String interpolation 332 | (elixir-match-interpolation 0 font-lock-variable-name-face t) 333 | 334 | ;; Module attributes 335 | (,(elixir-rx (and "@" identifiers)) 336 | 0 elixir-attribute-face) 337 | 338 | ;; Keywords 339 | (,(elixir-rx (and (or line-start (not (any "."))) 340 | (group (or builtin builtin-declaration builtin-namespace 341 | keyword keyword-operator)))) 342 | 1 font-lock-keyword-face) 343 | 344 | ;; Function names, i.e. `def foo do'. 345 | (,(elixir-rx (group function-declaration) 346 | space 347 | (group identifiers)) 348 | 2 font-lock-function-name-face) 349 | 350 | ;; Sigil patterns. Elixir has support for eight different sigil delimiters. 351 | ;; This isn't a very DRY approach here but it gets the job done. 352 | (,(elixir-rx (group sigils) 353 | (and "/" 354 | (group (zero-or-more (or (and "\\" "/") (not (any "/" "\n" "\r"))))) 355 | "/")) 356 | (1 font-lock-builtin-face) 357 | (2 font-lock-string-face)) 358 | (,(elixir-rx (group sigils) 359 | (and "[" 360 | (group (zero-or-more (or (and "\\" "]") (not (any "]" "\n" "\r"))))) 361 | "]")) 362 | (1 font-lock-builtin-face) 363 | (2 font-lock-string-face)) 364 | (,(elixir-rx (group sigils) 365 | (and "{" 366 | (group (zero-or-more (or (and "\\" "}") (not (any "}" "\n" "\r"))))) 367 | "}")) 368 | (1 font-lock-builtin-face) 369 | (2 font-lock-string-face)) 370 | (,(elixir-rx (group sigils) 371 | (and "(" 372 | (group (zero-or-more (or (and "\\" ")") (not (any ")" "\n" "\r"))))) 373 | ")")) 374 | (1 font-lock-builtin-face) 375 | (2 font-lock-string-face)) 376 | (,(elixir-rx (group sigils) 377 | (and "|" 378 | (group (zero-or-more (or (and "\\" "|") (not (any "|" "\n" "\r"))))) 379 | "|")) 380 | (1 font-lock-builtin-face) 381 | (2 font-lock-string-face)) 382 | (,(elixir-rx (group sigils) 383 | (and "\"" 384 | (group (zero-or-more (or (and "\\" "\"") (not (any "\"" "\n" "\r"))))) 385 | "\"")) 386 | (1 font-lock-builtin-face) 387 | (2 font-lock-string-face)) 388 | (,(elixir-rx (group sigils) 389 | (and "'" 390 | (group (zero-or-more (or (and "\\" "'") (not (any "'" "\n" "\r"))))) 391 | "'")) 392 | (1 font-lock-builtin-face) 393 | (2 font-lock-string-face)) 394 | (,(elixir-rx (group sigils) 395 | (and "<" 396 | (group (zero-or-more (or (and "\\" ">") (not (any ">" "\n" "\r"))))) 397 | ">")) 398 | (1 font-lock-builtin-face) 399 | (2 font-lock-string-face)) 400 | 401 | ;; Modules 402 | (,(elixir-rx (group module-names)) 403 | 1 font-lock-type-face) 404 | 405 | ;; Atoms and singleton-like words like true/false/nil. 406 | (,(elixir-rx symbol-start 407 | (group (or atoms "true" "false" "nil")) 408 | symbol-end 409 | (zero-or-more space) 410 | (optional "=")) 411 | 1 elixir-atom-face) 412 | 413 | ;; Numbers 414 | (,(elixir-rx (group numbers)) 415 | 1 elixir-number-face) 416 | 417 | ;; Gray out variables starting with "_" 418 | (,(elixir-rx symbol-start 419 | (group (and "_" 420 | (any "A-Z" "a-z" "0-9")) 421 | (zero-or-more (any "A-Z" "a-z" "0-9" "_")) 422 | (optional (or "?" "!")))) 423 | 1 font-lock-comment-face) 424 | 425 | ;; Variable definitions 426 | (,(elixir-rx (group identifiers) 427 | (zero-or-more space) 428 | (repeat 1 "=") 429 | (or (or sigils identifiers space) 430 | (one-or-more "\n"))) 431 | 1 font-lock-variable-name-face) 432 | 433 | ;; Map keys 434 | (,(elixir-rx (group (and identifiers ":")) (or space "\n")) 435 | 1 elixir-atom-face) 436 | 437 | ;; Pseudovariables 438 | (,(elixir-rx (group pseudo-var)) 439 | 1 font-lock-constant-face) 440 | 441 | ;; Arrows 442 | (,(elixir-rx (group arrows)) 443 | 1 font-lock-keyword-face) 444 | 445 | ;; Code points 446 | (,(elixir-rx (group code-point)) 447 | 1 font-lock-negation-char-face))) 448 | 449 | ;;;###autoload 450 | (defun elixir-mode-open-github () 451 | "Elixir mode open GitHub page." 452 | (interactive) 453 | (browse-url "https://github.com/elixir-editors/emacs-elixir")) 454 | 455 | ;;;###autoload 456 | (defun elixir-mode-open-elixir-home () 457 | "Elixir mode go to language home." 458 | (interactive) 459 | (browse-url elixir-mode-website-url)) 460 | 461 | ;;;###autoload 462 | (defun elixir-mode-open-docs-master () 463 | "Elixir mode go to master documentation." 464 | (interactive) 465 | (browse-url (concat elixir-mode-doc-url "/master"))) 466 | 467 | ;;;###autoload 468 | (defun elixir-mode-open-docs-stable () 469 | "Elixir mode go to stable documentation." 470 | (interactive) 471 | (browse-url elixir-mode-doc-url)) 472 | 473 | (defconst elixir--version 474 | (eval-when-compile 475 | (require 'lisp-mnt) 476 | (let ((file (or byte-compile-current-file 477 | load-file-name 478 | (buffer-file-name)))) 479 | (if file 480 | (with-temp-buffer 481 | (insert-file-contents file) 482 | (lm-version)) 483 | "Unknown"))) 484 | "The current version of `elixir-mode'.") 485 | 486 | ;;;###autoload 487 | (defun elixir-mode-version (&optional show-version) 488 | "Get the Elixir-Mode version as string. 489 | 490 | If called interactively or if SHOW-VERSION is non-nil, show the 491 | version in the echo area and the messages buffer. 492 | 493 | The returned string includes both, the version from package.el 494 | and the library version, if both a present and different. 495 | 496 | If the version number could not be determined, signal an error, 497 | if called interactively, or if SHOW-VERSION is non-nil, otherwise 498 | just return nil." 499 | (interactive (list t)) 500 | (when show-version 501 | (message "Elixir-Mode version: %s" elixir--version)) 502 | elixir--version) 503 | 504 | (defun elixir-mode-fill-doc-string () 505 | (interactive) 506 | (save-excursion 507 | (re-search-backward (rx "@" (or "moduledoc" "typedoc" "doc") space "\"\"\"") nil t) 508 | (re-search-forward "\"\"\"" nil t) 509 | (set-mark (point)) 510 | (re-search-forward "\"\"\"" nil t) 511 | (re-search-backward "^ *\"\"\"" nil t) 512 | (backward-char) 513 | (fill-region (point) (mark)))) 514 | 515 | (defun elixir-beginning-of-defun (&optional arg) 516 | (interactive "p") 517 | (let ((regexp (concat "^\\s-*" (elixir-rx builtin-declaration))) 518 | case-fold-search) 519 | (while (and (re-search-backward regexp nil t (or arg 1)) 520 | (elixir-syntax-in-string-or-comment-p))) 521 | (goto-char (line-beginning-position)))) 522 | 523 | (defun elixir-end-of-defun () 524 | (interactive) 525 | (goto-char (line-beginning-position)) 526 | (if (re-search-forward "\\_" nil t)) 534 | (when (and (not (elixir-syntax-in-string-or-comment-p)) 535 | (= (current-indentation) level)) 536 | (setq finish t))) 537 | (when (looking-back "^\\s-*\\_ "." table) 85 | (modify-syntax-entry ?_ "_" table) 86 | (modify-syntax-entry ?? "w" table) 87 | (modify-syntax-entry ?~ "w" table) 88 | (modify-syntax-entry ?! "_" table) 89 | (modify-syntax-entry ?' "\"'" table) 90 | (modify-syntax-entry ?\" "\"\"" table) 91 | (modify-syntax-entry ?# "<" table) 92 | (modify-syntax-entry ?\n ">" table) 93 | (modify-syntax-entry ?\( "()" table) 94 | (modify-syntax-entry ?\) ")(" table) 95 | (modify-syntax-entry ?\{ "(}" table) 96 | (modify-syntax-entry ?\} "){" table) 97 | (modify-syntax-entry ?\[ "(]" table) 98 | (modify-syntax-entry ?\] ")[" table) 99 | (modify-syntax-entry ?: "_" table) 100 | (modify-syntax-entry ?@ "_" table) 101 | table) 102 | "Elixir mode syntax table.") 103 | 104 | (defconst elixir-smie-grammar 105 | (smie-prec2->grammar 106 | (smie-merge-prec2s 107 | (smie-bnf->prec2 108 | '((id) 109 | (statements (statement) 110 | (statement ";" statements)) 111 | (statement (non-block-expr "fn" match-statements "end") 112 | (non-block-expr "do" statements "end") 113 | ("if" non-block-expr "do" statements "else" statements "end") 114 | ("if" non-block-expr "do" statements "end") 115 | ("if" non-block-expr "COMMA" "do:" non-block-expr) 116 | ("if" non-block-expr "COMMA" 117 | "do:" non-block-expr "COMMA" 118 | "else:" non-block-expr) 119 | ("try" "do" statements "after" statements "end") 120 | ("try" "do" statements "catch" match-statements "end") 121 | ("try" "do" statements "rescue" match-statements "end") 122 | ("try" "do" statements "end") 123 | ("case" non-block-expr "do" match-statements "end") 124 | ("for" non-block-expr "COMMA" "do:" non-block-expr) 125 | ("for" non-block-expr "COMMA" "into:" non-block-expr "COMMA" "do" statements "end") 126 | ("for" non-block-expr "COMMA" "into:" non-block-expr "COMMA" "do:" non-block-expr) 127 | ("with" non-block-expr "do" statements "else" statements "end") 128 | ("with" non-block-expr "do:" non-block-expr "COMMA" "else:" non-block-expr)) 129 | (non-block-expr (non-block-expr "OP" non-block-expr) 130 | ("(" non-block-expr ")") 131 | ("{" non-block-expr "}") 132 | ("[" non-block-expr "]") 133 | ("STRING")) 134 | (match-statements (match-statement "MATCH-STATEMENT-DELIMITER" 135 | match-statements) 136 | (match-statement)) 137 | (match-statement (non-block-expr "->" statements))) 138 | '((assoc "if" "do:" "else:") 139 | (assoc "COMMA") 140 | (left "OP"))) 141 | 142 | (smie-precs->prec2 143 | '((left "||") 144 | (left "&&") 145 | (nonassoc "=~" "===" "!==" "==" "!=" "<=" ">=" "<" ">") 146 | (left "+" "-" "<<<" ">>>" "^^^" "~~~" "&&&" "|||") 147 | (left "*" "/")))))) 148 | 149 | (defvar elixir-smie--operator-regexp 150 | (rx (or "<<<" ">>>" "^^^" "~~~" "&&&" "|||" "===" "!==" "==" "!=" "<=" 151 | "=" ">=" "<" ">" "&&" "||" "<>" "++" "--" "//" "/>" "=~" "|>"))) 152 | 153 | (defvar elixir-smie--binary-sequence-regexp 154 | (rx (or "<<" ">>"))) 155 | 156 | (defvar elixir-smie--block-operator-regexp 157 | (rx "->" (0+ nonl))) 158 | 159 | (defvar elixir-smie--oneline-def-operator-regexp 160 | (rx "do:" (0+ nonl))) 161 | 162 | (defvar elixir-smie--spaces-til-eol-regexp 163 | (rx (and (1+ space) eol)) 164 | "Regex representing one or more whitespace characters concluding with eol.") 165 | 166 | (defvar elixir-smie--comment-regexp 167 | (rx (and (0+ space) "#" (0+ not-newline))) 168 | "Regex matching comments.") 169 | 170 | (defvar elixir-smie-indent-basic 2) 171 | 172 | (defmacro elixir-smie-debug (message &rest format-args) 173 | `(progn 174 | (when elixir-smie-verbose-p 175 | (message (format ,message ,@format-args))) 176 | nil)) 177 | 178 | (defun elixir-smie--implicit-semi-p () 179 | (not (or (memq (char-before) '(?\{ ?\[)) 180 | (looking-back elixir-smie--operator-regexp (- (point) 3) t)))) 181 | 182 | (defun elixir-smie-current-line-contains-built-in-keyword-p () 183 | "Return non-nil if the current line contains built in keywords with a \".\"." 184 | (save-excursion 185 | (beginning-of-line) 186 | (looking-at ".+\\.\\(case\\|try\\|if\\|rescue\\)"))) 187 | 188 | (defun elixir-smie-last-line-end-with-block-operator-p () 189 | "Return non-nil if the previous line ends with a `->' operator." 190 | (save-excursion 191 | (forward-line -1) 192 | (move-end-of-line 1) 193 | (looking-back elixir-smie--block-operator-regexp (- (point) 3) t))) 194 | 195 | (defun elixir-smie-last-line-start-with-block-operator-p () 196 | (save-excursion 197 | (forward-line -1) 198 | (beginning-of-line) 199 | (looking-at "^\s+->.+$"))) 200 | 201 | (defun elixir-smie-current-line-start-with-pipe-operator-p () 202 | (save-excursion 203 | (beginning-of-line) 204 | (looking-at "^\s*|>.+$"))) 205 | 206 | (defun elixir-smie-last-line-is-assignment-p () 207 | (save-excursion 208 | (forward-line -1) 209 | (beginning-of-line) 210 | (looking-at "^.+=.+$"))) 211 | 212 | (defun elixir-smie-last-line-start-with-pipe-operator-p () 213 | (save-excursion 214 | (forward-line -1) 215 | (beginning-of-line) 216 | (looking-at "^\s*|>.+$"))) 217 | 218 | (defun elixir-smie-line-starts-with-do-colon-p () 219 | (save-excursion 220 | (beginning-of-line) 221 | (looking-at "^\s+do:"))) 222 | 223 | (defun elixir-smie--semi-ends-match () 224 | "Return non-nil if the current line concludes a match block." 225 | (when (not (eobp)) 226 | (save-excursion 227 | ;; Warning: Recursion. 228 | ;; This is easy though. 229 | 230 | ;; 1. If we're at a blank line, move forward a character. This takes us to 231 | ;; the next line. 232 | ;; 2. If we're not at the end of the buffer, call this function again. 233 | ;; (Otherwise, return nil.) 234 | 235 | ;; The point here is that we want to treat blank lines as a single semi- 236 | ;; colon when it comes to detecting the end of match statements. This could 237 | ;; also be handled by a `while' expression or some other looping mechanism. 238 | (cl-flet ((self-call () 239 | (if (< (point) (point-max)) 240 | (elixir-smie--semi-ends-match) 241 | nil))) 242 | (cond 243 | ((and (eolp) (bolp)) 244 | (forward-char) 245 | (self-call)) 246 | ((looking-at elixir-smie--spaces-til-eol-regexp) 247 | (forward-char) 248 | (self-call)) 249 | ;; And if we're NOT on a blank line, move to the end of the line, and see 250 | ;; if we're looking back at a block operator. 251 | (t (move-end-of-line 1) 252 | (and (looking-back elixir-smie--block-operator-regexp) 253 | (not (looking-back ".+fn.+"))))))))) 254 | 255 | (defun elixir-smie--same-line-as-parent (parent-pos child-pos) 256 | "Return non-nil if CHILD-POS is on same line as PARENT-POS." 257 | (= (line-number-at-pos parent-pos) (line-number-at-pos child-pos))) 258 | 259 | (defun elixir-smie-forward-token () 260 | (cond 261 | ;; If there is nothing but whitespace between the last token and eol, emit 262 | ;; a semicolon. 263 | ((looking-at elixir-smie--spaces-til-eol-regexp) 264 | (goto-char (match-end 0)) 265 | ";") 266 | ((and (or (looking-at elixir-smie--comment-regexp) 267 | (looking-at "[\n#]")) 268 | (elixir-smie--implicit-semi-p)) 269 | (if (eolp) (forward-char 1) (forward-comment 1)) 270 | ;; Note: `elixir-smie--semi-ends-match' will be called when the point is at 271 | ;; the beginning of a new line. Keep that in mind. 272 | (if (elixir-smie--semi-ends-match) 273 | "MATCH-STATEMENT-DELIMITER" 274 | (if (and (looking-at ".+,$") 275 | (not (> (elixir-ppss-depth (syntax-ppss)) 0))) 276 | "COMMA" 277 | ";"))) 278 | ((looking-at elixir-smie--block-operator-regexp) 279 | (goto-char (match-end 0)) 280 | "->") 281 | ((looking-at elixir-smie--operator-regexp) 282 | (goto-char (match-end 0)) 283 | "OP") 284 | (t 285 | (let ((token (smie-default-forward-token))) 286 | (unless (or (elixir-smie-empty-string-p token) 287 | (elixir-smie--at-dot-call)) 288 | token))))) 289 | 290 | (defun elixir-smie--at-dot-call () 291 | (and (eq ?w (char-syntax (following-char))) 292 | (eq (char-before) ?.) 293 | (not (eq (char-before (1- (point))) ?.)))) 294 | 295 | (defun elixir-smie-backward-token () 296 | (let ((pos (point))) 297 | (forward-comment (- (point))) 298 | (cond 299 | ((and (> pos (line-end-position)) 300 | (elixir-smie--implicit-semi-p)) 301 | (if (elixir-smie--semi-ends-match) 302 | "MATCH-STATEMENT-DELIMITER" 303 | (if (and (looking-back ",$" (- (point) 3) t) 304 | (not (> (elixir-ppss-depth (syntax-ppss)) 0))) 305 | "COMMA" 306 | ";"))) 307 | ((looking-back elixir-smie--block-operator-regexp (- (point) 3) t) 308 | (goto-char (match-beginning 0)) 309 | "->") 310 | ((looking-back elixir-smie--binary-sequence-regexp (- (point) 3) t) 311 | (goto-char (match-beginning 0)) 312 | "OP") 313 | ((looking-back elixir-smie--operator-regexp (- (point) 3) t) 314 | (goto-char (match-beginning 0)) 315 | "OP") 316 | (t (let ((token (smie-default-backward-token))) 317 | (unless (or (elixir-smie-empty-string-p token) 318 | (elixir-smie--at-dot-call)) 319 | token)))))) 320 | 321 | (defun verbose-elixir-smie-rules (kind token) 322 | (let ((value (elixir-smie-rules kind token))) 323 | (elixir-smie-debug "%s '%s'; sibling-p:%s parent:%s prev-is-OP:%s hanging:%s == %s" kind token 324 | (ignore-errors (smie-rule-sibling-p)) 325 | (ignore-errors smie--parent) 326 | (ignore-errors (smie-rule-prev-p "OP")) 327 | (ignore-errors (smie-rule-hanging-p)) 328 | value) 329 | value)) 330 | 331 | (defun elixir-smie-rules (kind token) 332 | (pcase (cons kind token) 333 | (`(:list-intro . ";") 334 | -4) 335 | (`(:list-intro . nil) 336 | -4) 337 | (`(:elem . args) 338 | -4) 339 | (`(:before . "COMMA") 340 | (cond 341 | ((and (smie-rule-parent-p "with") 342 | (smie-rule-hanging-p)) 343 | (smie-rule-parent 5)) 344 | ((and (smie-rule-parent-p ";") 345 | (smie-rule-hanging-p)) 346 | (smie-rule-parent)) 347 | ((and (smie-rule-parent-p "COMMA") 348 | (smie-rule-hanging-p)) 349 | (if (save-excursion 350 | (forward-line 1) 351 | (move-beginning-of-line 1) 352 | (looking-at "^.+do:.+$")) 353 | (smie-rule-parent -5) 354 | (smie-rule-parent))) 355 | ((and (smie-rule-parent-p "COMMA") 356 | (not (smie-rule-hanging-p))) 357 | (smie-rule-parent elixir-smie-indent-basic)) 358 | ((smie-rule-parent-p "(") 359 | (smie-rule-parent)) 360 | ((smie-rule-parent-p "if") 361 | (smie-rule-parent)) 362 | ((smie-rule-parent-p "->") 363 | (smie-rule-parent)))) 364 | (`(:after . "COMMA") 365 | (cond 366 | ((and (smie-rule-parent-p ";") 367 | (smie-rule-hanging-p)) 368 | (smie-rule-parent elixir-smie-indent-basic)) 369 | ((and (smie-rule-parent-p "with") 370 | (smie-rule-hanging-p)) 371 | (smie-rule-parent 5)) 372 | ((and (smie-rule-parent-p "{") 373 | (smie-rule-hanging-p)) 374 | (smie-rule-parent elixir-smie-indent-basic)) 375 | ((and (smie-rule-parent-p "[") 376 | (smie-rule-hanging-p)) 377 | 0) 378 | ((smie-rule-parent-p "COMMA") 379 | (smie-rule-parent elixir-smie-indent-basic)) 380 | ((smie-rule-parent-p "->") 381 | (smie-rule-parent elixir-smie-indent-basic)) 382 | (t (smie-rule-parent elixir-smie-indent-basic)))) 383 | (`(:before . "OP") 384 | (cond 385 | ((smie-rule-parent-p "for") 386 | (smie-rule-parent)) 387 | ((and (not (smie-rule-hanging-p)) 388 | (elixir-smie-current-line-start-with-pipe-operator-p) 389 | (elixir-smie-last-line-is-assignment-p)) 390 | (smie-rule-parent)) 391 | ((and (not (smie-rule-hanging-p)) 392 | (elixir-smie-current-line-start-with-pipe-operator-p)) 393 | (cons 'column (elixir-smie--previous-line-indentation))) 394 | ((and (not (smie-rule-hanging-p)) 395 | (smie-rule-prev-p "OP")) 396 | (- elixir-smie-indent-basic)) 397 | ((smie-rule-parent-p "def" "defp" "defmacro" "defmacrop") 398 | (smie-rule-parent)) 399 | (t (smie-rule-parent)))) 400 | (`(:after . "OP") 401 | (cond 402 | ((smie-rule-sibling-p) nil) 403 | ((smie-rule-hanging-p) 404 | (smie-rule-parent elixir-smie-indent-basic)) 405 | ((and (not (smie-rule-sibling-p)) 406 | (not (smie-rule-hanging-p)) 407 | (smie-rule-parent-p "do:")) 408 | (smie-rule-parent)) 409 | ((smie-rule-parent-p ";") 410 | (smie-rule-parent)) 411 | ((smie-rule-parent-p "{") 412 | (smie-rule-parent elixir-smie-indent-basic)) 413 | (t (smie-rule-parent (- elixir-smie-indent-basic))))) 414 | (`(:before . "MATCH-STATEMENT-DELIMITER") 415 | (cond 416 | ((and (smie-rule-parent-p "do") 417 | (smie-rule-hanging-p)) 418 | (smie-rule-parent)) 419 | ((and (smie-rule-parent-p "do") 420 | (not (smie-rule-hanging-p))) 421 | (smie-rule-parent elixir-smie-indent-basic)) 422 | ((and (smie-rule-parent-p "fn")) 423 | (smie-rule-parent elixir-smie-indent-basic)) 424 | ;; There is a case when between two line inside a def block 425 | ;; when jumping to the next line and indent, where the cursor 426 | ;; jumps too much in front. 427 | ;; 428 | ;; Example: 429 | ;; def generate_pkg(path, opts) do 430 | ;; name = Path.basename(Path.expand(path)) 431 | ;; 432 | ;; File.mkdir_p!(path) 433 | ;; <- 434 | ;; File.cd! path, fn -> 435 | ;; _generate_pkg(name, opts) 436 | ;; end 437 | ;; end 438 | ((and (smie-rule-parent-p "do") 439 | (not (smie-rule-hanging-p))) 440 | 0) 441 | ((and (not (smie-rule-sibling-p)) 442 | (elixir-ppss-last-complete-sexp-start smie--parent) 443 | (smie-rule-hanging-p)) 444 | (smie-rule-parent elixir-smie-indent-basic)) 445 | ((and (not (smie-rule-sibling-p)) 446 | (not (elixir-ppss-last-complete-sexp-start smie--parent)) 447 | (smie-rule-hanging-p)) 448 | (smie-rule-parent)))) 449 | (`(:after . "MATCH-STATEMENT-DELIMITER") 450 | (cond 451 | ((and (smie-rule-parent-p "MATCH-STATEMENT-DELIMITER") 452 | (smie-rule-hanging-p) 453 | (smie-rule-sibling-p)) 454 | (smie-rule-parent)) 455 | ((and (smie-rule-parent-p "after") 456 | (smie-rule-hanging-p)) 457 | (smie-rule-parent elixir-smie-indent-basic)) 458 | ;; Correct indentation after a one-line fn definition 459 | ;; Example: 460 | ;; 461 | ;; sum = Enum.reduce(dbms, fn(x, sum) -> x + sum end) 462 | ;; average_dbm = sum / length(addresses) 463 | ((smie-rule-parent-p "fn") 464 | (smie-rule-parent elixir-smie-indent-basic)) 465 | (t 466 | (smie-rule-parent)))) 467 | (`(:before . "fn") 468 | (cond 469 | ((smie-rule-parent-p "(") 470 | (smie-rule-parent)) 471 | (t (smie-rule-parent)))) 472 | (`(:before . "for") 473 | (cond 474 | ((elixir-smie-last-line-end-with-block-operator-p) 475 | (smie-rule-parent elixir-smie-indent-basic)) 476 | ((and (smie-rule-parent-p ";") 477 | (smie-rule-prev-p "OP")) 478 | (smie-rule-parent)) 479 | ((smie-rule-prev-p "OP" "def") 480 | (smie-rule-parent -2)))) 481 | (`(:before . "into:") 482 | (cond 483 | ((smie-rule-parent-p "COMMA") 484 | (smie-rule-parent elixir-smie-indent-basic)))) 485 | (`(:before . "do:") 486 | (cond 487 | ((smie-rule-parent-p "def" "defp" "defmacro" "defmacrop") 488 | (if (save-excursion 489 | (move-beginning-of-line 1) 490 | (looking-at "^\s*do:.+$")) 491 | (smie-rule-parent) 492 | (smie-rule-parent))) 493 | ;; Example 494 | ;; 495 | ;; hi = for i <- list, do: i 496 | ;; # weird spacing now <- Indent 497 | ;; 498 | ;; for i <- list, do: i 499 | ;; IO.puts 'WORKED' <- Indent 500 | ((and (smie-rule-parent-p "for") 501 | (not (smie-rule-hanging-p))) 502 | (smie-rule-parent)) 503 | ((and (smie-rule-parent-p ";") 504 | (not (smie-rule-hanging-p)) 505 | (save-excursion 506 | (move-beginning-of-line 1) 507 | (looking-at "^\s*do:.+$"))) 508 | (if (> (elixir-ppss-depth (syntax-ppss)) 0) 509 | (smie-rule-parent (- 3)) 510 | (smie-rule-parent elixir-smie-indent-basic))) 511 | ((and (smie-rule-parent-p ";") 512 | (not (smie-rule-hanging-p))) 513 | (if (> (elixir-ppss-depth (syntax-ppss)) 0) 514 | (smie-rule-parent (- elixir-smie-indent-basic)) 515 | (smie-rule-parent))) 516 | ((and (smie-rule-parent-p "OP") 517 | (not (smie-rule-hanging-p))) 518 | (smie-rule-parent elixir-smie-indent-basic)) 519 | ((and (smie-rule-parent-p "COMMA") 520 | (not (smie-rule-hanging-p))) 521 | (smie-rule-parent elixir-smie-indent-basic)))) 522 | (`(:before . "do") 523 | (cond 524 | ((and (smie-rule-parent-p "for") 525 | (smie-rule-hanging-p)) 526 | (if (save-excursion 527 | (move-beginning-of-line 1) 528 | (looking-at "^.+\sfor\s.+\sdo\s*")) 529 | (smie-rule-parent elixir-smie-indent-basic) 530 | (smie-rule-parent (+ elixir-smie-indent-basic 531 | elixir-smie-indent-basic)))) 532 | ((and (smie-rule-parent-p "case") 533 | (smie-rule-hanging-p)) 534 | (smie-rule-parent elixir-smie-indent-basic)) 535 | ;; There is a case when between two line inside a def block 536 | ;; when jumping to the next line and indent, where the cursor 537 | ;; jumps too much in front. 538 | ;; 539 | ;; Example: 540 | ;; def generate_pkg(path, opts) do 541 | ;; name = Path.basename(Path.expand(path)) 542 | ;; 543 | ;; File.mkdir_p!(path) 544 | ;; <- 545 | ;; File.cd! path, fn -> 546 | ;; _generate_pkg(name, opts) 547 | ;; end 548 | ;; end 549 | ((and (smie-rule-parent-p "def") 550 | (smie-rule-hanging-p)) 551 | (smie-rule-parent elixir-smie-indent-basic)) 552 | (t elixir-smie-indent-basic))) 553 | (`(:before . "end") 554 | (cond 555 | ((smie-rule-parent-p "for") 556 | (smie-rule-parent)) 557 | ((smie-rule-parent-p "(") 558 | (smie-rule-parent)) 559 | (t (smie-rule-parent)))) 560 | (`(:before . "else:") 561 | (cond 562 | ((smie-rule-parent-p ";") 563 | (if (> (elixir-ppss-depth (syntax-ppss)) 0) 564 | (smie-rule-parent elixir-smie-indent-basic) 565 | (smie-rule-parent))) 566 | ((smie-rule-parent-p "if") 567 | (smie-rule-parent elixir-smie-indent-basic)) 568 | (t (smie-rule-parent)))) 569 | ;; Closing paren on the other line 570 | (`(:before . "(") 571 | (cond 572 | ((smie-rule-parent-p "fn") 573 | (smie-rule-parent elixir-smie-indent-basic)) 574 | ;; Indent parenthesis correctly inside a block 575 | ;; 576 | ;; Example: 577 | ;; 578 | ;; def bar do 579 | ;; () 580 | ;; ..... 581 | ((smie-rule-parent-p "do") 582 | (smie-rule-parent)) 583 | ((smie-rule-parent-p "OP") 584 | (smie-rule-parent)) 585 | ((and (smie-rule-parent-p "with") 586 | (smie-rule-hanging-p)) 587 | (smie-rule-parent)) 588 | ((and (smie-rule-parent-p "with") 589 | (not (smie-rule-hanging-p))) 590 | (smie-rule-parent 3)) 591 | ((smie-rule-parent-p ";") 592 | (smie-rule-parent)) 593 | (t (smie-rule-parent)))) 594 | (`(:before . "[") 595 | (cond 596 | ((smie-rule-hanging-p) 597 | (smie-rule-parent)))) 598 | (`(:before . "{") 599 | (cond 600 | ((smie-rule-parent-p "COMMA") 601 | (smie-rule-parent)) 602 | ;; Example 603 | ;; 604 | ;; case parse do 605 | ;; { [ help: true ], _, _ } 606 | ;; -> :help 607 | ;; { _, [ user, project, count ], _ } 608 | ((and (not (smie-rule-hanging-p)) 609 | (smie-rule-parent-p "do")) 610 | ;; If the last line ends with a block operator `->' 611 | ;; indent two spaces more 612 | ;; 613 | ;; Example 614 | ;; 615 | ;; case File.read("/usr/share/dict/words") do 616 | ;; {:ok, contents} -> 617 | ;; {:something, contents} <- Indent here two spaces 618 | ;; ... 619 | (if (elixir-smie-last-line-end-with-block-operator-p) 620 | (smie-rule-parent elixir-smie-indent-basic))) 621 | ((and (smie-rule-parent-p "MATCH-STATEMENT-DELIMITER") 622 | (not (smie-rule-hanging-p))) 623 | (if (elixir-smie-last-line-end-with-block-operator-p) 624 | (smie-rule-parent elixir-smie-indent-basic) 625 | (if (elixir-smie-last-line-start-with-block-operator-p) 626 | (smie-rule-parent (- elixir-smie-indent-basic)) 627 | (smie-rule-parent)))) 628 | ((and (smie-rule-parent-p "OP") 629 | (smie-rule-hanging-p)) 630 | (smie-rule-parent)) 631 | ((smie-rule-parent-p ";") 632 | (if (save-excursion 633 | (move-end-of-line 1) 634 | (looking-back elixir-smie--block-operator-regexp (- (point) 3) t)) 635 | (smie-rule-parent (- elixir-smie-indent-basic)) 636 | (if (save-excursion 637 | (move-beginning-of-line 1) 638 | (looking-at "^.+->.+$")) 639 | (smie-rule-parent (- elixir-smie-indent-basic)) 640 | (smie-rule-parent)))))) 641 | (`(:after . "{") 642 | (cond 643 | ((smie-rule-hanging-p) 644 | (smie-rule-parent elixir-smie-indent-basic)) 645 | (t elixir-smie-indent-basic))) 646 | (`(:after . "[") 647 | (cond 648 | ((smie-rule-hanging-p) 649 | (smie-rule-parent elixir-smie-indent-basic)) 650 | (t elixir-smie-indent-basic))) 651 | (`(:before . "if") 652 | (cond 653 | ;; Indent when if is inside a `->' block 654 | ;; 655 | ;; Example: 656 | ;; 657 | ;; whatever -> 658 | ;; if true do <- 659 | ;; :foo 660 | ;; end 661 | ;; .... 662 | ((elixir-smie-last-line-end-with-block-operator-p) 663 | (smie-rule-parent elixir-smie-indent-basic)) 664 | ;; Indent if inside else 665 | ;; 666 | ;; Example: 667 | ;; 668 | ;; else 669 | ;; if condition, do: :bar <- 670 | ;; end 671 | ((smie-rule-parent-p "else") 672 | (smie-rule-parent elixir-smie-indent-basic)) 673 | (t (smie-rule-parent)))) 674 | (`(:before . "->") 675 | (cond 676 | ;; Example 677 | ;; 678 | ;; receive do 679 | ;; after 680 | ;; 2000 -> 681 | ;; IO.puts 'hello' 682 | ;; IO.puts 'status 2000 ends' <- Indent second line 683 | ;; { :ok } -> 684 | ;; .... 685 | ((and (smie-rule-parent-p "after") 686 | (not (smie-rule-sibling-p))) 687 | (smie-rule-parent (+ elixir-smie-indent-basic 688 | elixir-smie-indent-basic))) 689 | ;; Example 690 | ;; 691 | ;; case parse do 692 | ;; { [ help: true ], _, _ } 693 | ;; -> :help 694 | ;; ... 695 | ((and (not (smie-rule-hanging-p)) 696 | (smie-rule-parent-p "do")) 697 | elixir-smie-indent-basic) 698 | ((and (not (smie-rule-hanging-p)) 699 | (smie-rule-parent-p "MATCH-STATEMENT-DELIMITER")) 700 | (smie-rule-parent)) 701 | (t (smie-rule-parent elixir-smie-indent-basic)))) 702 | (`(:after . "->") 703 | (cond 704 | ;; This first condition is kind of complicated so I'll try to make this 705 | ;; comment as clear as possible. 706 | 707 | ;; "If `->' is the last thing on the line, and its parent token 708 | ;; is `fn' ..." 709 | ((and (smie-rule-hanging-p) 710 | (smie-rule-parent-p "fn")) 711 | ;; "... and if: 712 | 713 | ;; 1. `smie--parent' is non-nil 714 | ;; 2. the `->' token in question is on the same line as its parent (if 715 | ;; the logic has gotten this far, its parent will be `fn') 716 | 717 | ;; ... then indent the line after the `->' aligned with the 718 | ;; parent, offset by `elixir-smie-indent-basic'." 719 | (if (and smie--parent (elixir-smie--same-line-as-parent 720 | (elixir-ppss-innermost-start smie--parent) 721 | (point))) 722 | (smie-rule-parent elixir-smie-indent-basic) 723 | elixir-smie-indent-basic)) 724 | ;; Otherwise, if just indent by two. 725 | ((smie-rule-hanging-p) 726 | (cond 727 | ((smie-rule-parent-p "catch" "rescue" "else") 728 | (smie-rule-parent (+ elixir-smie-indent-basic 729 | elixir-smie-indent-basic))) 730 | ((smie-rule-parent-p "do" "try") 731 | (smie-rule-parent elixir-smie-indent-basic)) 732 | ;; Example 733 | ;; 734 | ;; receive do 735 | ;; after 736 | ;; 2000 -> 737 | ;; IO.puts 'hello' <- Indent two spaces 738 | ((and (smie-rule-parent-p "after") 739 | (smie-rule-hanging-p) 740 | (not (smie-rule-sibling-p))) 741 | (smie-rule-parent (+ elixir-smie-indent-basic 742 | elixir-smie-indent-basic))) 743 | (t (smie-rule-parent elixir-smie-indent-basic)))))) 744 | (`(:before . ";") 745 | (cond 746 | ;; Handle cases where built in keywords are used 747 | ;; as function names. 748 | ;; 749 | ;; Example: 750 | ;; 751 | ;; def foo(test) do 752 | ;; test_case = test.case 753 | ;; run(test_case) 754 | ;; end 755 | ((and (smie-rule-parent-p "case" "try" "rescue") 756 | (smie-rule-hanging-p) 757 | (elixir-smie-current-line-contains-built-in-keyword-p)) 758 | (+ (- (cdr (smie-rule-parent))) (+ elixir-smie-indent-basic 759 | elixir-smie-indent-basic))) 760 | ;; There is a case after an one line definition of functions/macros 761 | ;; when an `if' keyword token is involved, where the next block `end' 762 | ;; token will have a `if' as parent and it's hanging. 763 | ;; 764 | ;; Example: 765 | ;; 766 | ;; defmacro my_if(expr, do: if_block), do: if(expr, do: if_block, else: nil) 767 | ;; defmacro my_if(expr, do: if_block, else: else_block) do 768 | ;; ... 769 | ;; end <- parent is `if` 770 | ((and (smie-rule-parent-p "if") 771 | (smie-rule-hanging-p)) 772 | (smie-rule-parent)) 773 | ((and (smie-rule-parent-p "else") 774 | (smie-rule-hanging-p)) 775 | (smie-rule-parent elixir-smie-indent-basic)) 776 | ((smie-rule-parent-p "catch" "def" "defmodule" "defp" "do" "else" 777 | "fn" "if" "rescue" "try" "unless" "defmacro" "defmacrop") 778 | (smie-rule-parent)) 779 | ((smie-rule-parent-p "after") 780 | (smie-rule-parent elixir-smie-indent-basic)) 781 | ;; Example 782 | ;; 783 | ;; case parse do 784 | ;; { [ help: true ], _, _ } 785 | ;; -> :help 786 | ;; { _, [ user, project, count ], _ } 787 | ;; -> { user, project, count } 788 | ;; ... 789 | ((and (smie-rule-parent-p "->") 790 | (smie-rule-hanging-p)) 791 | (smie-rule-parent)) 792 | ((and (smie-rule-parent-p ";") 793 | (smie-rule-hanging-p) 794 | (save-excursion 795 | (move-beginning-of-line 1) 796 | (looking-at "^\s+else:.+$")) 797 | (not (save-excursion 798 | (move-beginning-of-line 1) 799 | (looking-at "^\s+else:.+)$")))) 800 | (smie-rule-parent (- elixir-smie-indent-basic))) 801 | ((and (smie-rule-parent-p ";") 802 | (save-excursion 803 | (move-beginning-of-line 1) 804 | (looking-at "^.+,$"))) 805 | (smie-rule-parent)) 806 | ((and (smie-rule-parent-p ";") 807 | (smie-rule-hanging-p) 808 | (save-excursion 809 | (move-beginning-of-line 1) 810 | (looking-at "^\s+do:.+$")) 811 | (not (save-excursion 812 | (move-beginning-of-line 1) 813 | (looking-at "^\s+do:.+)$")))) 814 | (smie-rule-parent)) 815 | ((elixir-smie-current-line-start-with-pipe-operator-p) 816 | (smie-rule-parent)) 817 | ((smie-rule-parent-p "(") 818 | (smie-rule-parent elixir-smie-indent-basic)))) 819 | (`(:after . ";") 820 | (cond 821 | ((smie-rule-parent-p "def") 822 | (smie-rule-parent)) 823 | ((and (smie-rule-parent-p "if") 824 | (elixir-smie-current-line-contains-built-in-keyword-p)) 825 | (+ (- (cdr (smie-rule-parent))) (+ elixir-smie-indent-basic 826 | elixir-smie-indent-basic))) 827 | ((smie-rule-parent-p "if") 828 | (smie-rule-parent)) 829 | ((smie-rule-parent-p "after") 830 | (smie-rule-parent elixir-smie-indent-basic)) 831 | ((and (smie-rule-parent-p "(") 832 | (boundp 'smie--parent) 833 | (save-excursion 834 | (goto-char (cadr smie--parent)) 835 | (smie-rule-hanging-p))) 836 | (smie-rule-parent elixir-smie-indent-basic)))))) 837 | 838 | (defun elixir-smie--heredoc-at-current-point-p () 839 | "Return non-nil if cursor is at a string." 840 | (save-excursion 841 | (or (save-excursion 842 | (let ((parse-data (parse-partial-sexp 1 (point)))) 843 | (and (elixir-ppss-string-terminator parse-data) 844 | (elixir-ppss-comment-or-string-start parse-data)))) 845 | (and (looking-at "\"\"\"") 846 | (match-beginning 0))))) 847 | 848 | (defun elixir-smie--previous-line-empty-p () 849 | "Return non-nil if the previous line is blank." 850 | (save-excursion 851 | (forward-line -1) 852 | (move-beginning-of-line 1) 853 | (looking-at "[[:space:]]*$"))) 854 | 855 | (defun elixir-smie--previous-line-indentation () 856 | "Return the indentation of the previous line." 857 | (save-excursion 858 | (forward-line -1) 859 | (current-indentation))) 860 | 861 | ;; Add the custom function to handle indentation inside heredoc to the 862 | ;; smie-indent-functions list. The indentation function will only be 863 | ;; process inside an elixir-mode. 864 | (defun elixir-smie--indent-inside-heredoc () 865 | "Handle indentation inside Elixir heredocs. 866 | 867 | Rules: 868 | 1. If the previous line is empty, indent as the basic indentation 869 | at the beginning of the heredoc. 870 | 2. If the previous line is not empty, indent as the previous line." 871 | (if (eq major-mode 'elixir-mode) 872 | (if (elixir-smie--heredoc-at-current-point-p) 873 | (let ((indent 874 | (save-excursion 875 | (when (re-search-backward "^\\(\s+\\)\\(@doc\\|@moduledoc\\|.*\\)\"\"\"" nil t) 876 | (string-width (match-string 1)))))) 877 | (cond 878 | ((elixir-smie--previous-line-empty-p) 879 | (goto-char indent)) 880 | ((and (not (save-excursion (looking-at "\"\"\""))) 881 | (not (elixir-smie--previous-line-empty-p))) 882 | (goto-char (elixir-smie--previous-line-indentation))) 883 | (indent 884 | (goto-char indent))))))) 885 | 886 | (defun elixir-smie-empty-string-p (string) 887 | "Return non-nil if STRING is null, blank or whitespace only." 888 | (or (null string) 889 | (string= string "") 890 | (if (string-match-p "^\s+$" string) t))) 891 | 892 | (add-to-list 'smie-indent-functions 'elixir-smie--indent-inside-heredoc) 893 | 894 | (provide 'elixir-smie) 895 | 896 | ;;; elixir-smie.el ends here 897 | -------------------------------------------------------------------------------- /tests/elixir-format-test.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-format-test.el --- Basic tests for elixir-format 2 | 3 | ;;; Code: 4 | (require 'test-helper) 5 | 6 | (ert-deftest elixir-format-indents-a-buffer () 7 | (when elixir-formatter-supported 8 | (ert-with-test-buffer (:name "(Expected)indents-a-buffer") 9 | (insert elixir-format-test-example) 10 | (elixir-format) 11 | (should (equal (buffer-string) elixir-format-formatted-test-example))))) 12 | 13 | (ert-deftest elixir-format-indents-a-buffer-and-undoes-changes () 14 | (when elixir-formatter-supported 15 | (ert-with-test-buffer () 16 | (buffer-enable-undo) 17 | (setq buffer-undo-list nil) 18 | 19 | (insert elixir-format-test-example) 20 | 21 | (undo-boundary) 22 | (elixir-format) 23 | 24 | (should (equal (buffer-string) elixir-format-formatted-test-example)) 25 | (undo 0) 26 | (should (equal (buffer-string) elixir-format-test-example))))) 27 | 28 | (ert-deftest elixir-format-should-run-hook-before-formatting () 29 | (when elixir-formatter-supported 30 | (ert-with-test-buffer () 31 | (let ((has-been-run nil)) 32 | (insert elixir-format-test-example) 33 | (add-hook 'elixir-format-hook (lambda () (setq has-been-run t))) 34 | (elixir-format) 35 | (should (equal has-been-run t)))))) 36 | 37 | (ert-deftest elixir-format-should-message-on-error () 38 | (when elixir-formatter-supported 39 | (ert-with-test-buffer () 40 | (insert elixir-format-wrong-test-example) 41 | (should-error 42 | (elixir-format))))) 43 | 44 | (provide 'elixir-format-test) 45 | 46 | ;;; elixir-format-test.el ends here. 47 | -------------------------------------------------------------------------------- /tests/elixir-mode-font-test.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-mode-font-test.el --- Font highlighting testsuite 2 | 3 | ;;; Commentary: 4 | ;; 5 | ;; `elixir-test-with-temp-buffer' and `elixir-test-face-at' are both slightly 6 | ;; modified versions of the original at 7 | ;; https://github.com/lunaryorn/puppet-mode/blob/master/test/puppet-mode-test.el 8 | 9 | ;;; Code: 10 | (require 'test-helper) 11 | 12 | (defun elixir-test-face-at (pos &optional content) 13 | "Get the face at POS in CONTENT. 14 | 15 | If CONTENT is not given, return the face at POS in the current 16 | buffer." 17 | (if content 18 | (elixir-test-with-temp-buffer content 19 | (get-text-property pos 'face)) 20 | (get-text-property pos 'face))) 21 | 22 | (ert-deftest elixir-mode-syntax-table/fontify-regex () 23 | :tags '(fontification syntax-table) 24 | (elixir-test-with-temp-buffer 25 | "match = ~r/foo/ 26 | match=~r/foo/" 27 | (should (eq (elixir-test-face-at 1) 'font-lock-variable-name-face)) 28 | (should (eq (elixir-test-face-at 9) 'font-lock-builtin-face)) 29 | (should (eq (elixir-test-face-at 12) 'font-lock-string-face)) 30 | (should (eq (elixir-test-face-at 18) 'font-lock-variable-name-face)) 31 | ;; no face for regex delimiters 32 | (should (eq (elixir-test-face-at 15) nil)))) 33 | 34 | (ert-deftest elixir-mode-syntax-table/sigils () 35 | :tags '(fontification syntax-table) 36 | (elixir-test-with-temp-buffer 37 | "asdfg = ~s{Capitalized noncapitalized}" 38 | (should (eq (elixir-test-face-at 1) 'font-lock-variable-name-face)) 39 | (should (eq (elixir-test-face-at 9) 'font-lock-builtin-face)) 40 | (should (eq (elixir-test-face-at 12) 'font-lock-string-face)) 41 | (should (eq (elixir-test-face-at 26) 'font-lock-string-face)) 42 | ;; no face for regex delimiters 43 | (should (eq (elixir-test-face-at 38) nil)))) 44 | 45 | (ert-deftest elixir-mode-syntax-table/fontify-special-macros () 46 | :tags '(fontification syntax-table) 47 | (elixir-test-with-temp-buffer 48 | "__MODULE__ 49 | __DIR__ 50 | __aliases__ 51 | %__MODULE__ 52 | &__MODULE__ 53 | &abc__DIR__" 54 | (should (eq (elixir-test-face-at 4) 'font-lock-constant-face)) 55 | (should (eq (elixir-test-face-at 14) 'font-lock-constant-face)) 56 | (should (eq (elixir-test-face-at 24) 'font-lock-constant-face)) 57 | (should (eq (elixir-test-face-at 34) 'font-lock-constant-face)) 58 | (should (eq (elixir-test-face-at 44) 'font-lock-constant-face)) 59 | (should-not (eq (elixir-test-face-at 60) 'font-lock-constant-face)))) 60 | 61 | (ert-deftest elixir-mode-syntax-table/fontify-modules-and-types () 62 | :tags '(fontification syntax-table) 63 | (elixir-test-with-temp-buffer 64 | "defmodule Application.Behavior do 65 | use Application.Behaviour 66 | Stand.Alone.call 67 | %RuntimeError{message: msg} 68 | &Enum" 69 | (should (eq (elixir-test-face-at 1) 'font-lock-keyword-face)) 70 | (should (eq (elixir-test-face-at 11) 'font-lock-type-face)) 71 | (should (eq (elixir-test-face-at 22) 'font-lock-type-face)) 72 | (should (eq (elixir-test-face-at 23) 'font-lock-type-face)) 73 | (should (eq (elixir-test-face-at 32) 'font-lock-keyword-face)) 74 | (should (eq (elixir-test-face-at 37) 'font-lock-keyword-face)) 75 | (should (eq (elixir-test-face-at 41) 'font-lock-type-face)) 76 | (should (eq (elixir-test-face-at 52) 'font-lock-type-face)) 77 | (should (eq (elixir-test-face-at 53) 'font-lock-type-face)) 78 | (should (eq (elixir-test-face-at 68) 'font-lock-type-face)) 79 | (should (eq (elixir-test-face-at 72) 'font-lock-type-face)) 80 | (should (eq (elixir-test-face-at 114) 'font-lock-type-face)) 81 | (should (eq (elixir-test-face-at 117) 'font-lock-type-face)) 82 | ;; no face for function call 83 | (should (eq (elixir-test-face-at 79) nil)) 84 | (should (eq (elixir-test-face-at 84) 'font-lock-type-face)) 85 | ;; no face for curly braces 86 | (should (eq (elixir-test-face-at 97) nil)))) 87 | 88 | (ert-deftest elixir-mode-syntax-table/fontify-regex-with-quote () 89 | "https://github.com/elixir-editors/emacs-elixir/issues/23" 90 | :tags '(fontification syntax-table) 91 | :expected-result :failed 92 | (elixir-test-with-temp-buffer 93 | "~r/\"/ 94 | x = 15" 95 | (should (eq (elixir-test-face-at 8) 'font-lock-variable-name-face)))) 96 | 97 | (ert-deftest elixir-mode-syntax-table/fontify-regex-with-question/1 () 98 | "https://github.com/elixir-editors/emacs-elixir/issues/36" 99 | :tags '(fontification syntax-table) 100 | (elixir-test-with-temp-buffer 101 | "~r/^matt: (?\d+)$/mg 102 | x = 15" 103 | (should (eq (elixir-test-face-at 4) 'font-lock-string-face)) 104 | (should (eq (elixir-test-face-at 25) 'font-lock-variable-name-face)))) 105 | 106 | (ert-deftest elixir-mode-syntax-table/fontify-regex-with-question/2 () 107 | "https://github.com/elixir-editors/emacs-elixir/issues/29" 108 | :tags '(fontification syntax-table) 109 | (elixir-test-with-temp-buffer 110 | "a = \"\" <> \"?\" 111 | x = 15" 112 | (should (eq (elixir-test-face-at 15) 'font-lock-variable-name-face)))) 113 | 114 | (ert-deftest elixir-mode-syntax-table/fontify-defguard () 115 | :tags '(fontification syntax-table) 116 | (elixir-test-with-temp-buffer 117 | "defmodule Foo do 118 | defguard is_foo(arg) when arg == true 119 | end" 120 | (should (eq (elixir-test-face-at 18) 'font-lock-keyword-face)))) 121 | 122 | (ert-deftest elixir-mode-syntax-table/fontify-defguardp () 123 | :tags '(fontification syntax-table) 124 | (elixir-test-with-temp-buffer 125 | "defmodule Foo do 126 | defguardp is_foo(arg) when arg == true 127 | end" 128 | (should (eq (elixir-test-face-at 18) 'font-lock-keyword-face)))) 129 | 130 | (ert-deftest elixir-mode-syntax-table/fontify-function-name/1 () 131 | :tags '(fontification syntax-table) 132 | (elixir-test-with-temp-buffer 133 | "def fooBar do 134 | :foo 135 | end" 136 | (should (eq (elixir-test-face-at 5) 'font-lock-function-name-face)) 137 | (should (eq (elixir-test-face-at 8) 'font-lock-function-name-face)))) 138 | 139 | (ert-deftest elixir-mode-syntax-table/fontify-function-name/2 () 140 | :tags '(fontification syntax-table) 141 | (elixir-test-with-temp-buffer 142 | "def foo? do 143 | :foo 144 | end" 145 | (should (eq (elixir-test-face-at 5) 'font-lock-function-name-face)) 146 | (should (eq (elixir-test-face-at 8) 'font-lock-function-name-face)))) 147 | 148 | (ert-deftest elixir-mode-syntax-table/fontify-function-name/3 () 149 | :tags '(fontification syntax-table) 150 | (elixir-test-with-temp-buffer 151 | "def foo! do 152 | :foo 153 | end" 154 | (should (eq (elixir-test-face-at 5) 'font-lock-function-name-face)) 155 | (should (eq (elixir-test-face-at 8) 'font-lock-function-name-face)))) 156 | 157 | (ert-deftest elixir-mode-syntax-table/fontify-defoverridable/1 () 158 | :tags '(fontification syntax-table) 159 | (elixir-test-with-temp-buffer 160 | "defmodule Foo do 161 | defmacro __using__(_opts) do 162 | quote do 163 | def bar, do: :ok 164 | defoverridable [bar: 0] 165 | end 166 | end 167 | end" 168 | (should (eq (elixir-test-face-at 91) 'font-lock-keyword-face)))) 169 | 170 | (ert-deftest elixir-mode-syntax-table/fontify-end-if-the-last-line-in-a-module-is-a-comment () 171 | "https://github.com/elixir-editors/emacs-elixir/issues/283" 172 | :tags '(fontification syntax-table) 173 | (elixir-test-with-temp-buffer 174 | "defmodule Foo do 175 | # foo 176 | end" 177 | (should (eq (elixir-test-face-at 26) 'font-lock-keyword-face)))) 178 | 179 | (ert-deftest elixir-mode-syntax-table/fontify-heredoc/1 () 180 | :tags '(fontification heredoc syntax-table) 181 | (elixir-test-with-temp-buffer 182 | "@doc \"\"\"" 183 | (should (eq (elixir-test-face-at 1) 'elixir-attribute-face)) 184 | (should (eq (elixir-test-face-at 2) 'elixir-attribute-face)) 185 | (should (eq (elixir-test-face-at 6) 'font-lock-doc-face)))) 186 | 187 | (ert-deftest elixir-mode-syntax-table/fontify-heredoc/2 () 188 | :tags '(fontification heredoc syntax-table) 189 | (elixir-test-with-temp-buffer 190 | "@moduledoc \"\"\"" 191 | (should (eq (elixir-test-face-at 1) 'elixir-attribute-face)) 192 | (should (eq (elixir-test-face-at 2) 'elixir-attribute-face)) 193 | (should (eq (elixir-test-face-at 12) 'font-lock-doc-face)))) 194 | 195 | (ert-deftest elixir-mode-syntax-table/fontify-heredoc/3 () 196 | :tags '(fontification heredoc syntax-table) 197 | (elixir-test-with-temp-buffer 198 | "~s\"\"\"" 199 | (should (eq (elixir-test-face-at 1) 'font-lock-builtin-face)) 200 | (should (eq (elixir-test-face-at 2) 'font-lock-builtin-face)) 201 | (should (eq (elixir-test-face-at 3) 'font-lock-string-face)))) 202 | 203 | (ert-deftest elixir-mode-syntax-table/fontify-heredoc/4 () 204 | :tags '(fontification heredoc syntax-table) 205 | (elixir-test-with-temp-buffer 206 | "@typedoc \"\"\"" 207 | (should (eq (elixir-test-face-at 1) 'elixir-attribute-face)) 208 | (should (eq (elixir-test-face-at 2) 'elixir-attribute-face)) 209 | (should (eq (elixir-test-face-at 10) 'font-lock-doc-face)))) 210 | 211 | (ert-deftest elixir-mode-syntax-table/fontify-atoms () 212 | :tags '(fontification atom syntax-table) 213 | (elixir-test-with-temp-buffer 214 | ":oriole 215 | :andale 216 | :ms2pid 217 | :CapitalizedAtom 218 | true 219 | false 220 | nil 221 | true_false_nil 222 | :insert! 223 | :insert@ 224 | :insert? 225 | " 226 | (should (eq (elixir-test-face-at 3) 'elixir-atom-face)) 227 | (should (eq (elixir-test-face-at 5) 'elixir-atom-face)) 228 | (should (eq (elixir-test-face-at 10) 'elixir-atom-face)) 229 | (should (eq (elixir-test-face-at 13) 'elixir-atom-face)) 230 | (should (eq (elixir-test-face-at 18) 'elixir-atom-face)) 231 | (should (eq (elixir-test-face-at 23) 'elixir-atom-face)) 232 | (should (eq (elixir-test-face-at 26) 'elixir-atom-face)) 233 | (should (eq (elixir-test-face-at 43) 'elixir-atom-face)) 234 | (should (eq (elixir-test-face-at 48) 'elixir-atom-face)) 235 | (should (eq (elixir-test-face-at 54) 'elixir-atom-face)) 236 | (should-not (eq (elixir-test-face-at 57) 'elixir-atom-face)) 237 | (should (eq (elixir-test-face-at 74) 'elixir-atom-face)) 238 | (should (eq (elixir-test-face-at 82) 'elixir-atom-face)) 239 | (should (eq (elixir-test-face-at 97) 'elixir-atom-face)))) 240 | 241 | (ert-deftest elixir-mode-syntax-table/fontify-map-keys () 242 | :tags '(fontification map syntax-table) 243 | (elixir-test-with-temp-buffer 244 | "%{a: 1, b: 2}" 245 | (should (eq (elixir-test-face-at 3) 'elixir-atom-face)) 246 | (should (eq (elixir-test-face-at 4) 'elixir-atom-face)) 247 | (should (eq (elixir-test-face-at 9) 'elixir-atom-face)) 248 | (should (eq (elixir-test-face-at 10) 'elixir-atom-face))) 249 | 250 | ;; https://github.com/elixir-editors/emacs-elixir/issues/320 251 | (elixir-test-with-temp-buffer 252 | "<>" 253 | (should-not (eq (elixir-test-face-at 3) 'elixir-atom-face))) 254 | 255 | (elixir-test-with-temp-buffer 256 | "%{key: 257 | a_very_long_value_that_needed_to_wrap}" 258 | (should (eq (elixir-test-face-at 3) 'elixir-atom-face)))) 259 | 260 | (ert-deftest elixir-mode-syntax-table/fontify-interpolation () 261 | :tags '(fontification interpolation syntax-table) 262 | (elixir-test-with-temp-buffer 263 | "\"#{1 + 2} is 3.\"" 264 | (should (eq (elixir-test-face-at 1) 'font-lock-string-face)) 265 | (should (eq (elixir-test-face-at 11) 'font-lock-string-face)))) 266 | 267 | (ert-deftest elixir-mode-syntax-table/fontify-continuation-lines-assignment () 268 | :tags '(fontification syntax-table) 269 | (elixir-test-with-temp-buffer 270 | "some_var = 271 | some_expr" 272 | (should (eq (elixir-test-face-at 1) 'font-lock-variable-name-face)))) 273 | 274 | (ert-deftest elixir-mode-syntax-table/dont-fontify-equal-match () 275 | :tags '(fontification syntax-table) 276 | (elixir-test-with-temp-buffer 277 | "this == that" 278 | (should-not (eq (elixir-test-face-at 2) 'font-lock-variable-name-face)))) 279 | 280 | (ert-deftest elixir-mode-syntax-table/fontify-triple-quoted-string () 281 | :tags '(fontification syntax-table) 282 | (elixir-test-with-temp-buffer 283 | "\"\"\"foo\"bar\"baz #{1 + 2} is 3.\"\"\"" 284 | (should (eq (elixir-test-face-at 1) 'font-lock-string-face)) 285 | (should (eq (elixir-test-face-at 5) 'font-lock-string-face)) 286 | (should (eq (elixir-test-face-at 19) 'font-lock-variable-name-face)) 287 | (should (eq (elixir-test-face-at 31) 'font-lock-string-face)))) 288 | 289 | (ert-deftest elixir-mode-syntax-table/fontify-atom-in-pattern-match () 290 | :tags '(fontification atom syntax-table) 291 | (elixir-test-with-temp-buffer 292 | ":any = into_to_type(type) 293 | :another=into_to_type(type)" 294 | (should (eq (elixir-test-face-at 3) 'elixir-atom-face)))) 295 | 296 | (ert-deftest elixir-mode-syntax-table/fontify-assignment-with-pattern/1 () 297 | :expected-result :failed 298 | :tags '(fontification syntax-table) 299 | (elixir-test-with-temp-buffer 300 | "{x, y} = some_expr" 301 | (should (eq (elixir-test-face-at 2) 'font-lock-variable-name-face)) 302 | (should (eq (elixir-test-face-at 5) 'font-lock-variable-name-face)))) 303 | 304 | (ert-deftest elixir-mode-syntax-table/fontify-assignment-with-pattern/2 () 305 | :expected-result :failed 306 | :tags '(fontification syntax-table) 307 | (elixir-test-with-temp-buffer 308 | "[h|t] = some_expr" 309 | (should (eq (elixir-test-face-at 2) 'font-lock-variable-name-face)) 310 | (should (eq (elixir-test-face-at 4) 'font-lock-variable-name-face)))) 311 | 312 | (ert-deftest elixir-mode-syntax-table/fontify-assignment-with-singleton () 313 | "https://github.com/elixir-editors/emacs-elixir/issues/245" 314 | :tags '(fontification syntax-table) 315 | (elixir-test-with-temp-buffer 316 | "true_false_nil = 1" 317 | (should (eq (elixir-test-face-at 1) 'font-lock-variable-name-face)) 318 | (should (eq (elixir-test-face-at 6) 'font-lock-variable-name-face)) 319 | (should (eq (elixir-test-face-at 12) 'font-lock-variable-name-face)))) 320 | 321 | (ert-deftest elixir-mode-syntax-table/fontify-keyword-after-dot () 322 | "https://github.com/elixir-editors/emacs-elixir/issues/250" 323 | :tags '(fontification syntax-table) 324 | (elixir-test-with-temp-buffer 325 | "Mix.raise 326 | raise 327 | Mix.def foo 328 | Mix.import 329 | import 330 | Mix.after 331 | after 332 | Mix.when 333 | when" 334 | (should-not (eq (elixir-test-face-at 5) 'font-lock-keyword-face)) 335 | (should (eq (elixir-test-face-at 11) 'font-lock-keyword-face)) 336 | (should-not (eq (elixir-test-face-at 21) 'font-lock-keyword-face)) 337 | (should-not (eq (elixir-test-face-at 25) 'font-lock-function-name-face)) 338 | (should-not (eq (elixir-test-face-at 33) 'font-lock-keyword-face)) 339 | (should (eq (elixir-test-face-at 40) 'font-lock-keyword-face)) 340 | 341 | (should-not (eq (elixir-test-face-at 51) 'font-lock-keyword-face)) 342 | (should (eq (elixir-test-face-at 57) 'font-lock-keyword-face)) 343 | (should-not (eq (elixir-test-face-at 67) 'font-lock-keyword-face)) 344 | (should (eq (elixir-test-face-at 72) 'font-lock-keyword-face)))) 345 | 346 | (ert-deftest elixir-mode-syntax-table/highlight-send () 347 | "Highlight send as a keyword" 348 | :tags '(fontification syntax-table) 349 | (elixir-test-with-temp-buffer 350 | "defmodule Foo do 351 | def bar(pid) do 352 | send pid, :baz 353 | end 354 | end" 355 | (should (eq (elixir-test-face-at 40) 'font-lock-keyword-face)))) 356 | 357 | (ert-deftest elixir-mode-syntax-table/highlight-with () 358 | "Highlight with as a keyword" 359 | :tags '(fontification syntax-table) 360 | (elixir-test-with-temp-buffer 361 | "defmodule Foo do 362 | def bar(opts) do 363 | with( 364 | {:ok, width} <- Map.fetch(opts, :width), 365 | {:ok, height} <- Map.fetch(opts, :height), 366 | do: {:ok, width * height} 367 | ) 368 | end 369 | end" 370 | (should (eq (elixir-test-face-at 41) 'font-lock-keyword-face)))) 371 | 372 | (ert-deftest elixir-mode-syntax-table/string-interpolation-in-words-list () 373 | "https://github.com/elixir-editors/emacs-elixir/issues/263" 374 | :tags '(fontification syntax-table) 375 | (elixir-test-with-temp-buffer 376 | "~w(SADD users #{user_id})" 377 | (should (eq (elixir-test-face-at 4) 'font-lock-string-face)) 378 | 379 | (should-not (eq (elixir-test-face-at 15) 'font-lock-comment-face)) 380 | (should-not (eq (elixir-test-face-at 17) 'font-lock-comment-face)) 381 | (should-not (eq (elixir-test-face-at 25) 'font-lock-comment-face)))) 382 | 383 | (ert-deftest elixir-mode-syntax-table/quotes-in-sigils () 384 | "https://github.com/elixir-editors/emacs-elixir/issues/265" 385 | :tags '(fontification syntax-table) 386 | (elixir-test-with-temp-buffer 387 | "~s/\"/ 388 | ~r|'| 389 | ~c\"'\" 390 | ~w'\"' 391 | ~s(\") 392 | ~r[\"] 393 | ~c{\"} 394 | ~w<\"> 395 | ~s\"\"\" 396 | foo 397 | \"\"\" 398 | ~D(\") 399 | ~N(\") 400 | ~T(\")" 401 | (should-not (eq (elixir-test-face-at 5) 'font-lock-string-face)) ; ~s// 402 | 403 | (should-not (eq (elixir-test-face-at 7) 'font-lock-string-face)) ; ~r|| 404 | (should (eq (elixir-test-face-at 7) 'font-lock-builtin-face)) 405 | (should-not (eq (elixir-test-face-at 11) 'font-lock-string-face)) 406 | 407 | (should-not (eq (elixir-test-face-at 13) 'font-lock-string-face)) ; ~c"" 408 | (should (eq (elixir-test-face-at 13) 'font-lock-builtin-face)) 409 | (should-not (eq (elixir-test-face-at 17) 'font-lock-string-face)) 410 | 411 | (should-not (eq (elixir-test-face-at 19) 'font-lock-string-face)) ; ~w'' 412 | (should (eq (elixir-test-face-at 19) 'font-lock-builtin-face)) 413 | (should-not (eq (elixir-test-face-at 23) 'font-lock-string-face)) 414 | 415 | (should-not (eq (elixir-test-face-at 25) 'font-lock-string-face)) ; ~s() 416 | (should (eq (elixir-test-face-at 25) 'font-lock-builtin-face)) ; ~s() 417 | (should-not (eq (elixir-test-face-at 29) 'font-lock-string-face)) 418 | 419 | (should-not (eq (elixir-test-face-at 31) 'font-lock-string-face)) ; ~r[] 420 | (should (eq (elixir-test-face-at 31) 'font-lock-builtin-face)) 421 | (should-not (eq (elixir-test-face-at 35) 'font-lock-string-face)) 422 | 423 | (should-not (eq (elixir-test-face-at 37) 'font-lock-string-face)) ; ~c{} 424 | (should (eq (elixir-test-face-at 37) 'font-lock-builtin-face)) 425 | (should-not (eq (elixir-test-face-at 41) 'font-lock-string-face)) 426 | 427 | (should-not (eq (elixir-test-face-at 43) 'font-lock-string-face)) ; ~w<> 428 | (should (eq (elixir-test-face-at 43) 'font-lock-builtin-face)) 429 | (should-not (eq (elixir-test-face-at 47) 'font-lock-string-face)) 430 | 431 | (should (eq (elixir-test-face-at 51) 'font-lock-string-face)) ; ~s""" """ 432 | (should (eq (elixir-test-face-at 52) 'font-lock-string-face)) 433 | (should (eq (elixir-test-face-at 53) 'font-lock-string-face)) 434 | (should (eq (elixir-test-face-at 55) 'font-lock-string-face)) 435 | 436 | (should (eq (elixir-test-face-at 66) 'font-lock-string-face)) ; ~D() 437 | 438 | (should (eq (elixir-test-face-at 72) 'font-lock-string-face)) ; ~N() 439 | 440 | (should (eq (elixir-test-face-at 78) 'font-lock-string-face)))) ; ~T() 441 | 442 | (ert-deftest elixir-mode-syntax-table/hashmark-in-sigils () 443 | "Don't treat hashmark in sigils as comment" 444 | :tags '(fontification syntax-table) 445 | (elixir-test-with-temp-buffer 446 | "~s(# foo)" 447 | (should-not (eq (elixir-test-face-at 4) 'font-lock-comment-face)) 448 | (should (eq (elixir-test-face-at 6) 'font-lock-string-face)))) 449 | 450 | (ert-deftest elixir-mode-syntax-table/highlight-modules-after-pipe () 451 | "Module names must be hightligthed if preceded by a pipe character." 452 | (elixir-test-with-temp-buffer 453 | "|List" 454 | (should-not (eq (elixir-test-face-at 1) 'font-lock-type-face)) 455 | (should (eq (elixir-test-face-at 3) 'font-lock-type-face))) 456 | (elixir-test-with-temp-buffer 457 | "[req_code(op_name)|Enum.reverse(data)]" 458 | (should-not (eq (elixir-test-face-at 19) 'font-lock-type-face)) 459 | (should (eq (elixir-test-face-at 21) 'font-lock-type-face)))) 460 | 461 | (ert-deftest elixir-mode-syntax-table/sigils-in-string () 462 | "https://github.com/elixir-editors/emacs-elixir/issues/275" 463 | :tags '(fontification syntax-table) 464 | (elixir-test-with-temp-buffer 465 | "@one 1 466 | @two \"~s\" 467 | @three :tre 468 | " 469 | (should-not (eq (elixir-test-face-at 18) 'font-lock-string-face)) 470 | (should (eq (elixir-test-face-at 19) 'elixir-attribute-face)) 471 | (should-not (eq (elixir-test-face-at 25) 'font-lock-string-face)) 472 | (should (eq (elixir-test-face-at 26) 'elixir-atom-face)))) 473 | 474 | (ert-deftest elixir-mode-syntax-table/sigil-triple-quote () 475 | "https://github.com/elixir-editors/emacs-elixir/issues/286" 476 | :tags '(fontification syntax-table) 477 | (elixir-test-with-temp-buffer 478 | "defmodule IEx do 479 | @moduledoc ~S\"\"\" 480 | Elixir's interactive shell. 481 | \"\"\" 482 | end 483 | " 484 | (should (eq (elixir-test-face-at 33) 'font-lock-string-face)))) 485 | 486 | (ert-deftest elixir-mode-syntax-table/single-triple-single-quote () 487 | "https://github.com/elixir-editors/emacs-elixir/issues/309" 488 | :tags '(fontification syntax-table) 489 | (elixir-test-with-temp-buffer 490 | "defmodule Module do 491 | @moduledoc ~S''' 492 | foo's \"bar\" 493 | '''" 494 | (should (eq (elixir-test-face-at 34) 'font-lock-builtin-face)) ;; ~S 495 | (should (eq (elixir-test-face-at 35) 'font-lock-builtin-face)) 496 | (should (eq (elixir-test-face-at 36) 'font-lock-string-face)) ;; ''' 497 | (should (eq (elixir-test-face-at 37) 'font-lock-string-face)) 498 | (should (eq (elixir-test-face-at 38) 'font-lock-string-face)) 499 | (should (eq (elixir-test-face-at 54) 'font-lock-string-face)) ;; ''' 500 | (should (eq (elixir-test-face-at 55) 'font-lock-string-face)) 501 | (should (eq (elixir-test-face-at 56) 'font-lock-string-face)))) 502 | 503 | (ert-deftest elixir-mode-syntax-table/comment-out-ignored-var () 504 | "https://github.com/elixir-editors/emacs-elixir/issues/292" 505 | :tags '(fontification syntax-table) 506 | (elixir-test-with-temp-buffer 507 | "variable 508 | _var 509 | _x 510 | _x! 511 | _x? 512 | _ 513 | __MODULE__ 514 | " 515 | (should (eq (elixir-test-face-at 4) nil)) 516 | (should (eq (elixir-test-face-at 14) 'font-lock-comment-face)) 517 | (should (eq (elixir-test-face-at 15) 'font-lock-comment-face)) 518 | (should (eq (elixir-test-face-at 23) 'font-lock-comment-face)) 519 | (should (eq (elixir-test-face-at 24) 'font-lock-comment-face)) 520 | (should (eq (elixir-test-face-at 30) 'font-lock-comment-face)) 521 | (should (eq (elixir-test-face-at 32) 'font-lock-comment-face)) 522 | (should (eq (elixir-test-face-at 38) 'font-lock-comment-face)) 523 | (should (eq (elixir-test-face-at 40) 'font-lock-comment-face)) 524 | (should (eq (elixir-test-face-at 46) 'font-lock-constant-face)) 525 | (should (eq (elixir-test-face-at 46) 'font-lock-constant-face)) 526 | (should (eq (elixir-test-face-at 52) 'font-lock-constant-face)) 527 | (should (eq (elixir-test-face-at 53) 'font-lock-constant-face)) 528 | (should (eq (elixir-test-face-at 55) 'font-lock-constant-face)))) 529 | 530 | (ert-deftest elixir-mode-syntax-table/escaped-sigil-delimiter () 531 | "https://github.com/elixir-editors/emacs-elixir/issues/302" 532 | :tags '(fontification syntax-table) 533 | (elixir-test-with-temp-buffer 534 | "def player_id(video) do 535 | ~r/^.*(?:youtu.be\\/|v\\/|e\\/|u\\/\\w+\\/|embed\\/|v=)(?[^#\\&\\?]*).*/ 536 | |> Regex.named_captures(video.url) 537 | |> get_in([\"id\"]) 538 | end 539 | " 540 | (should (eq (elixir-test-face-at 45) 'font-lock-string-face)) ;; escaped '/' 541 | (should (eq (elixir-test-face-at 84) 'font-lock-string-face)) ;; comment mark '#' 542 | 543 | (elixir-test-with-temp-buffer 544 | "~B/\\// 545 | ~C[\\]] 546 | ~R{\\}} 547 | ~C(\\)) 548 | ~b|\\|| 549 | ~c\"\\\"\" 550 | ~r'\\'' 551 | ~s<\\>>" 552 | (should (eq (elixir-test-face-at 5) 'font-lock-string-face)) ;; '/' 553 | (should (eq (elixir-test-face-at 12) 'font-lock-string-face)) ;; '[]' 554 | (should (eq (elixir-test-face-at 19) 'font-lock-string-face)) ;; '{}' 555 | (should (eq (elixir-test-face-at 26) 'font-lock-string-face)) ;; '()' 556 | (should (eq (elixir-test-face-at 33) 'font-lock-string-face)) ;; '|' 557 | (should (eq (elixir-test-face-at 40) 'font-lock-string-face)) ;; '"' 558 | (should (eq (elixir-test-face-at 47) 'font-lock-string-face)) ;; '\'' 559 | (should (eq (elixir-test-face-at 53) 'font-lock-string-face)) ;; '<>' 560 | ))) 561 | 562 | (ert-deftest elixir-mode-syntax-table/question-quote () 563 | "https://github.com/elixir-editors/emacs-elixir/issues/185" 564 | :tags '(fontification syntax-table) 565 | (elixir-test-with-temp-buffer 566 | "\"\\\"foo\\\"\" |> String.strip(?\")" 567 | (should-not (eq (elixir-test-face-at 28) 'font-lock-string-face))) 568 | 569 | (elixir-test-with-temp-buffer 570 | "\"\\\"foo\\\"\" |> String.strip(?')" 571 | (should-not (eq (elixir-test-face-at 28) 'font-lock-string-face)))) 572 | 573 | (ert-deftest elixir-mode-syntax-table/ignored-variables-in-pattern-match () 574 | "https://github.com/elixir-editors/emacs-elixir/issues/361" 575 | :tags '(fontification syntax-table) 576 | (elixir-test-with-temp-buffer 577 | "(_1_day = 86_400) 578 | _1_day" 579 | (should (eq (elixir-test-face-at 2) 'font-lock-comment-face)) 580 | (should (eq (elixir-test-face-at 19) 'font-lock-comment-face)))) 581 | 582 | (ert-deftest elixir-mode-syntax-table/arrows () 583 | :tags '(fontification syntax-table) 584 | 585 | (elixir-test-with-temp-buffer 586 | "with {:ok, _} <- SomeModule.call(), 587 | :ok <- OtherModule.call() do 588 | :ok 589 | end" 590 | (should (eq (elixir-test-face-at 15) 'font-lock-keyword-face)) 591 | (should (eq (elixir-test-face-at 50) 'font-lock-keyword-face))) 592 | 593 | (elixir-test-with-temp-buffer 594 | "%{ 595 | \"\"foo\"\" => \"bar\" 596 | }" 597 | (should (eq (elixir-test-face-at 18) 'font-lock-keyword-face))) 598 | 599 | (elixir-test-with-temp-buffer 600 | "[] |> IO.inspect()" 601 | (should (eq (elixir-test-face-at 4) 'font-lock-keyword-face))) 602 | 603 | (elixir-test-with-temp-buffer 604 | "a = fn x -> x end" 605 | (should (eq (elixir-test-face-at 10) 'font-lock-keyword-face)))) 606 | 607 | (ert-deftest elixir-mode-in-docstring () 608 | "https://github.com/elixir-editors/emacs-elixir/issues/355" 609 | :tags 'fontification 610 | (elixir-test-with-temp-buffer 611 | "# https://github.com/elixir-editors/emacs-elixir/issues/355 612 | 613 | @moduledoc \"\"\" 614 | Everything in here should be gray, including the @moduledoc and triple-quotes 615 | \"\"\" 616 | 617 | @doc \"\"\" 618 | Everything in here should be gray, including the @doc and triple-quotes 619 | \"\"\"" 620 | ;; (switch-to-buffer (current-buffer)) 621 | (search-forward "Everything") 622 | (should (elixir--docstring-p)) 623 | (search-forward "Everything") 624 | (should (elixir--docstring-p)))) 625 | 626 | (ert-deftest elixir-mode-docstring-face () 627 | "https://github.com/elixir-editors/emacs-elixir/issues/355" 628 | :tags 'fontification 629 | (elixir-test-with-temp-buffer 630 | "# https://github.com/elixir-editors/emacs-elixir/issues/355 631 | 632 | @moduledoc \"\"\" 633 | Everything in here should be gray, including the @moduledoc and triple-quotes 634 | \"\"\" 635 | 636 | @doc \"\"\" 637 | Everything in here should be gray, including the @doc and triple-quotes 638 | \"\"\"" 639 | (switch-to-buffer (current-buffer)) 640 | (search-forward "Everything") 641 | (should (eq 'font-lock-doc-face (get-char-property (point) 'face))) 642 | (search-forward "Everything") 643 | (should (eq 'font-lock-doc-face (get-char-property (point) 'face))))) 644 | 645 | (provide 'elixir-mode-font-test) 646 | 647 | ;;; elixir-mode-font-test.el ends here 648 | -------------------------------------------------------------------------------- /tests/elixir-mode-helper-test.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-mode-helper-test.el --- Tests for helper functions 2 | 3 | ;;; Code: 4 | (require 'test-helper) 5 | 6 | (ert-deftest check-if-currently-inside-heredoc () 7 | (should (with-temp-buffer 8 | (elixir-mode) 9 | (insert " 10 | defmodule Module.Name do 11 | 12 | @moduledoc \"\"\" 13 | ## Examples 14 | 15 | .... 16 | \"\"\" 17 | 18 | end") 19 | (goto-line 7) 20 | (elixir-smie--heredoc-at-current-point-p) 21 | )) 22 | (should (not (with-temp-buffer 23 | (elixir-mode) 24 | (insert " 25 | defmodule Module.Name do 26 | 27 | @moduledoc \"\"\" 28 | ## Examples 29 | 30 | .... 31 | \"\"\" 32 | 33 | end") 34 | (goto-line 3) 35 | (elixir-smie--heredoc-at-current-point-p))))) 36 | 37 | (ert-deftest get-previous-line-indentation () 38 | (should (equal 2 39 | (with-temp-buffer 40 | (elixir-mode) 41 | (insert " 42 | defmodule Module.Name do 43 | def what do 44 | 1 + 1 45 | end 46 | end") 47 | (goto-line 4) 48 | (elixir-smie--previous-line-indentation))))) 49 | 50 | 51 | (ert-deftest check-if-previous-line-blank () 52 | (should (not (with-temp-buffer 53 | (elixir-mode) 54 | (insert " 55 | defmodule Module.Name do 56 | 57 | def what do 58 | 1 + 1 59 | end 60 | end") 61 | (goto-line 3) 62 | (elixir-smie--previous-line-empty-p)))) 63 | (should (with-temp-buffer 64 | (elixir-mode) 65 | (insert " 66 | defmodule Module.Name do 67 | 68 | 69 | def what do 70 | 1 + 1 71 | end 72 | end") 73 | (goto-line 4) 74 | (elixir-smie--previous-line-empty-p)))) 75 | 76 | (ert-deftest test-current-line-contains-built-in-keyword () 77 | (should (not (with-temp-buffer 78 | (elixir-mode) 79 | (insert " 80 | defmodule Module.Name do 81 | 82 | def hey(test) do 83 | test.case 84 | end 85 | end") 86 | (goto-line 4) 87 | (elixir-smie-current-line-contains-built-in-keyword-p)))) 88 | (should (not (with-temp-buffer 89 | (elixir-mode) 90 | (insert " 91 | defmodule Module.Name do 92 | 93 | def hey(test) do 94 | case do 95 | end 96 | end 97 | end") 98 | (goto-line 4) 99 | (elixir-smie-current-line-contains-built-in-keyword-p))))) 100 | 101 | (ert-deftest test-if-string-is-empty () 102 | (should (equal (elixir-smie-empty-string-p nil) 103 | t)) 104 | (should (equal (elixir-smie-empty-string-p "") 105 | t)) 106 | (should (equal (elixir-smie-empty-string-p " ") 107 | t)) 108 | (should (equal (elixir-smie-empty-string-p "story") 109 | nil)) 110 | (should (equal (elixir-smie-empty-string-p " ") 111 | t))) 112 | 113 | (provide 'elixir-mode-helper-test) 114 | 115 | ;;; elixir-mode-helper-test.el ends here 116 | -------------------------------------------------------------------------------- /tests/elixir-mode-indentation-test.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-mode-indentation-test.el --- Indentation testsuite 2 | 3 | ;;; Commentary: 4 | ;; 5 | ;; Expected test failures indicates that the code tested by that test case is 6 | ;; indeed broken. My intention is that while working on a specific problem, 7 | ;; the failure expectation will be removed so that we know when the test case 8 | ;; passes. 9 | 10 | ;;; Code: 11 | (require 'test-helper) 12 | 13 | (elixir-def-indentation-test indent-use-dot-module-newline 14 | (:tags '(indentation)) 15 | " 16 | defmodule Foo do 17 | use GenServer.Behaviour 18 | 19 | def foobar do 20 | if true, do: IO.puts \"yay\" 21 | end 22 | end 23 | " 24 | 25 | " 26 | defmodule Foo do 27 | use GenServer.Behaviour 28 | 29 | def foobar do 30 | if true, do: IO.puts \"yay\" 31 | end 32 | end 33 | ") 34 | 35 | (elixir-def-indentation-test indent-use-dot-module 36 | (:tags '(indentation)) 37 | " 38 | defmodule Foo do 39 | use GenServer.Behaviour 40 | def foobar do 41 | if true, do: IO.puts \"yay\" 42 | end 43 | end 44 | " 45 | 46 | " 47 | defmodule Foo do 48 | use GenServer.Behaviour 49 | def foobar do 50 | if true, do: IO.puts \"yay\" 51 | end 52 | end 53 | ") 54 | 55 | (elixir-def-indentation-test indent-do-blocks 56 | (:tags '(indentation)) 57 | " 58 | defmodule Foo do 59 | def foobar do 60 | if true, do: IO.puts \"yay\" 61 | 20 62 | end 63 | end 64 | " 65 | 66 | " 67 | defmodule Foo do 68 | def foobar do 69 | if true, do: IO.puts \"yay\" 70 | 20 71 | end 72 | end 73 | ") 74 | 75 | (elixir-def-indentation-test indent-do-blocks-after-linebreak-two 76 | (:tags '(indentation)) 77 | " 78 | defmodule FooBar do 79 | def foo do 80 | if true, do: IO.puts \"yay\" 81 | 20 82 | end 83 | 84 | def bar do 85 | if true, do: IO.puts \"yay\" 86 | 20 87 | end 88 | end 89 | " 90 | 91 | " 92 | defmodule FooBar do 93 | def foo do 94 | if true, do: IO.puts \"yay\" 95 | 20 96 | end 97 | 98 | def bar do 99 | if true, do: IO.puts \"yay\" 100 | 20 101 | end 102 | end 103 | ") 104 | 105 | (elixir-def-indentation-test indent-do-blocks-after-linebreak-three 106 | (:tags '(indentation)) 107 | " 108 | defmodule FooBar do 109 | def foo do 110 | if true, do: IO.puts \"yay\" 111 | 20 112 | end 113 | 114 | def bar do 115 | if true, do: IO.puts \"yay\" 116 | 20 117 | end 118 | 119 | def baz do 120 | if true, do: IO.puts \"yay\" 121 | 20 122 | end 123 | end 124 | " 125 | 126 | " 127 | defmodule FooBar do 128 | def foo do 129 | if true, do: IO.puts \"yay\" 130 | 20 131 | end 132 | 133 | def bar do 134 | if true, do: IO.puts \"yay\" 135 | 20 136 | end 137 | 138 | def baz do 139 | if true, do: IO.puts \"yay\" 140 | 20 141 | end 142 | end 143 | ") 144 | 145 | (elixir-def-indentation-test indent-do-blocks-with-space-after-inline 146 | (:tags '(indentation)) 147 | " 148 | defmodule Foo do 149 | def foobar do 150 | if true, do: IO.puts \"yay\" 151 | 152 | 20 153 | end 154 | end 155 | " 156 | 157 | " 158 | defmodule Foo do 159 | def foobar do 160 | if true, do: IO.puts \"yay\" 161 | 162 | 20 163 | end 164 | end 165 | ") 166 | 167 | (elixir-def-indentation-test indent-after-empty-line 168 | (:tags '(indentation)) 169 | " 170 | def foo do 171 | a = 2 172 | 173 | b = a + 3 174 | 175 | c = a * b 176 | end 177 | " 178 | 179 | " 180 | def foo do 181 | a = 2 182 | 183 | b = a + 3 184 | 185 | c = a * b 186 | end 187 | ") 188 | 189 | (elixir-def-indentation-test indent-function-calls-without-parens 190 | (:tags '(indentation)) 191 | " 192 | test \"foo\" do 193 | assert true, \"should be true\" 194 | assert !false, \"should still be true\" 195 | end 196 | " 197 | 198 | " 199 | test \"foo\" do 200 | assert true, \"should be true\" 201 | assert !false, \"should still be true\" 202 | end 203 | ") 204 | 205 | (elixir-def-indentation-test indent-records-correctly 206 | (:tags '(indentation)) 207 | " 208 | defmodule MyModule do 209 | require Record 210 | Record.defrecord :money, [:currency_unit, :amount] 211 | 212 | Record.defrecord :animal, [:species, :name] 213 | end 214 | " 215 | 216 | " 217 | defmodule MyModule do 218 | require Record 219 | Record.defrecord :money, [:currency_unit, :amount] 220 | 221 | Record.defrecord :animal, [:species, :name] 222 | end 223 | ") 224 | 225 | (elixir-def-indentation-test indent-continuation-lines 226 | (:tags '(indentation)) 227 | " 228 | def foo do 229 | has_something(x) && 230 | has_something(y) || 231 | has_something(z) 232 | end 233 | " 234 | 235 | " 236 | def foo do 237 | has_something(x) && 238 | has_something(y) || 239 | has_something(z) 240 | end 241 | ") 242 | 243 | (elixir-def-indentation-test indent-continuation-lines-with-comments/1 244 | (:tags '(indentation)) 245 | " 246 | has_something(x) && # foo 247 | has_something(y) || 248 | has_something(z) 249 | " 250 | 251 | " 252 | has_something(x) && # foo 253 | has_something(y) || 254 | has_something(z) 255 | ") 256 | 257 | (elixir-def-indentation-test indent-continuation-lines-with-comments/2 258 | (:tags '(indentation)) 259 | " 260 | has_something(x) && 261 | has_something(y) || # foo 262 | has_something(z) 263 | " 264 | 265 | " 266 | has_something(x) && 267 | has_something(y) || # foo 268 | has_something(z) 269 | ") 270 | 271 | (elixir-def-indentation-test indent-continuation-lines-with-comments/3 272 | (:tags '(indentation)) 273 | " 274 | def str(s, sub, start_pos, end_pos) when is_binary(s) and is_binary(sub) do # and start_pos <= end_pos do 275 | len = end_pos-start_pos 276 | end 277 | " 278 | 279 | " 280 | def str(s, sub, start_pos, end_pos) when is_binary(s) and is_binary(sub) do # and start_pos <= end_pos do 281 | len = end_pos-start_pos 282 | end 283 | ") 284 | 285 | (elixir-def-indentation-test indent-continuation-lines-assignment 286 | (:tags '(indentation)) 287 | " 288 | some_var = 289 | some_expr 290 | " 291 | 292 | " 293 | some_var = 294 | some_expr 295 | ") 296 | 297 | (elixir-def-indentation-test indent-continuation-lines-assignment/2 298 | (:tags '(indentation)) 299 | " 300 | next_fun = 301 | case raw do 302 | true -> &IO.each_binstream(&1, line_or_bytes) 303 | false -> &IO.each_stream(&1, line_or_bytes) 304 | end 305 | " 306 | 307 | " 308 | next_fun = 309 | case raw do 310 | true -> &IO.each_binstream(&1, line_or_bytes) 311 | false -> &IO.each_stream(&1, line_or_bytes) 312 | end 313 | ") 314 | 315 | (elixir-def-indentation-test indent-continuation-lines-assignment/3 316 | (:expected-result :failed :tags '(indentation)) 317 | " 318 | start_fun = 319 | fn -> 320 | case :file.open(path, modes) do 321 | {:ok, device} -> device 322 | {:error, reason} -> 323 | raise File.Error, reason: reason, action: \"stream\", path: path 324 | end 325 | end 326 | " 327 | 328 | " 329 | start_fun = 330 | fn -> 331 | case :file.open(path, modes) do 332 | {:ok, device} -> device 333 | {:error, reason} -> 334 | raise File.Error, reason: reason, action: \"stream\", path: path 335 | end 336 | end 337 | ") 338 | 339 | 340 | (elixir-def-indentation-test indent-last-commented-line 341 | (:tags '(indentation)) 342 | " 343 | defmodule Foo do 344 | def bar do 345 | 2 346 | end 347 | 348 | # last line 349 | end 350 | " 351 | 352 | " 353 | defmodule Foo do 354 | def bar do 355 | 2 356 | end 357 | 358 | # last line 359 | end 360 | ") 361 | 362 | (elixir-def-indentation-test indent-if 363 | (:tags '(indentation)) 364 | " 365 | if condition do 366 | yes 367 | end 368 | " 369 | 370 | " 371 | if condition do 372 | yes 373 | end 374 | ") 375 | 376 | (elixir-def-indentation-test indent-if-else 377 | (:tags '(indentation)) 378 | " 379 | if condition do 380 | yes 381 | else 382 | no 383 | end 384 | " 385 | 386 | " 387 | if condition do 388 | yes 389 | else 390 | no 391 | end 392 | ") 393 | 394 | (elixir-def-indentation-test indent-if-else/2 395 | (:tags '(indentation)) 396 | " 397 | if condition do 398 | :foo 399 | else 400 | if condition, do: :bar 401 | end 402 | " 403 | 404 | " 405 | if condition do 406 | :foo 407 | else 408 | if condition, do: :bar 409 | end 410 | ") 411 | 412 | (elixir-def-indentation-test indent-tuple-after-if-else 413 | (:tags '(indentation)) 414 | " 415 | if foo do 416 | :ok 417 | else 418 | {:tuple} 419 | end 420 | " 421 | 422 | " 423 | if foo do 424 | :ok 425 | else 426 | {:tuple} 427 | end 428 | ") 429 | 430 | (elixir-def-indentation-test indent-non-finished-one-line-if-else 431 | (:tags '(indentation)) 432 | " 433 | if condition, 434 | do: :foo, 435 | else: :bar 436 | " 437 | 438 | " 439 | if condition, 440 | do: :foo, 441 | else: :bar 442 | ") 443 | 444 | (elixir-def-indentation-test indent-if-when-condition-is-a-named-function-on-a-module 445 | (:tags '(indentation)) 446 | " 447 | defmodule Whois do 448 | def lookup2(domain) do 449 | if Server.for(domain) do 450 | :ok 451 | else 452 | :error 453 | end 454 | end 455 | end 456 | " 457 | 458 | " 459 | defmodule Whois do 460 | def lookup2(domain) do 461 | if Server.for(domain) do 462 | :ok 463 | else 464 | :error 465 | end 466 | end 467 | end 468 | ") 469 | 470 | (elixir-def-indentation-test indent-try 471 | (:tags '(indentation)) 472 | " 473 | try do 474 | foo 475 | bar 476 | end 477 | " 478 | 479 | " 480 | try do 481 | foo 482 | bar 483 | end 484 | ") 485 | 486 | (elixir-def-indentation-test indent-try/after 487 | (:tags '(indentation)) 488 | " 489 | try do 490 | foo 491 | bar 492 | after 493 | after_everything() 494 | post_that() 495 | end 496 | " 497 | 498 | " 499 | try do 500 | foo 501 | bar 502 | after 503 | after_everything() 504 | post_that() 505 | end 506 | ") 507 | 508 | (elixir-def-indentation-test indent-try/catch/after 509 | (:tags '(indentation)) 510 | " 511 | try do 512 | foo 513 | bar 514 | catch 515 | baz -> 516 | nope 517 | [yeah] -> 518 | maybe 519 | after 520 | after_everything() 521 | post_that() 522 | end 523 | " 524 | 525 | " 526 | try do 527 | foo 528 | bar 529 | catch 530 | baz -> 531 | nope 532 | [yeah] -> 533 | maybe 534 | after 535 | after_everything() 536 | post_that() 537 | end 538 | ") 539 | 540 | (elixir-def-indentation-test indent-try/rescue/1 541 | (:tags '(indentation)) 542 | " 543 | try do 544 | raise 'some error' 545 | rescue 546 | RuntimeError -> 'rescued a runtime error' 547 | end 548 | " 549 | 550 | " 551 | try do 552 | raise 'some error' 553 | rescue 554 | RuntimeError -> 'rescued a runtime error' 555 | end 556 | ") 557 | 558 | (elixir-def-indentation-test indent-try/rescue/2 559 | (:tags '(indentation)) 560 | " 561 | try do 562 | raise 'some error' 563 | rescue 564 | x in [RuntimeError] -> 565 | x.message 566 | end 567 | " 568 | 569 | " 570 | try do 571 | raise 'some error' 572 | rescue 573 | x in [RuntimeError] -> 574 | x.message 575 | end 576 | ") 577 | 578 | (elixir-def-indentation-test indent-block-inside-fn-match 579 | (:tags '(indentation)) 580 | " 581 | defp into(stream, device, raw) do 582 | fn 583 | :ok, {:cont, x} -> 584 | case raw do 585 | true -> IO.binwrite(device, x) 586 | false -> IO.write(device, x) 587 | end 588 | :ok, _ -> stream 589 | end 590 | end 591 | " 592 | 593 | " 594 | defp into(stream, device, raw) do 595 | fn 596 | :ok, {:cont, x} -> 597 | case raw do 598 | true -> IO.binwrite(device, x) 599 | false -> IO.write(device, x) 600 | end 601 | :ok, _ -> stream 602 | end 603 | end 604 | ") 605 | 606 | (elixir-def-indentation-test indent-fn-in-assignment 607 | (:tags '(indentation)) 608 | " 609 | f = fn x, y -> 610 | x + y 611 | end 612 | " 613 | 614 | " 615 | f = fn x, y -> 616 | x + y 617 | end 618 | ") 619 | 620 | (elixir-def-indentation-test indent-fn-as-arguments 621 | (:tags '(indentation)) 622 | " 623 | Enum.map 1..10, fn x -> 624 | x + 1 625 | end 626 | " 627 | 628 | " 629 | Enum.map 1..10, fn x -> 630 | x + 1 631 | end 632 | ") 633 | 634 | (elixir-def-indentation-test indent-list-argument-continuation-lines-nicely 635 | (:tags '(indentation)) 636 | " 637 | to_process = [27, 33, 35, 11, 36, 29, 18, 37, 21, 31, 19, 10, 14, 30, 638 | 15, 17, 23, 28, 25, 34, 22, 20, 13, 16, 32, 12, 26, 24] 639 | " 640 | 641 | " 642 | to_process = [27, 33, 35, 11, 36, 29, 18, 37, 21, 31, 19, 10, 14, 30, 643 | 15, 17, 23, 28, 25, 34, 22, 20, 13, 16, 32, 12, 26, 24] 644 | ") 645 | 646 | (elixir-def-indentation-test indent-nested-fn 647 | (:tags '(indentation)) 648 | " 649 | defmodule FooModule do 650 | def foo do 651 | x = fn(a, b) -> a + b end 652 | end 653 | end 654 | " 655 | 656 | " 657 | defmodule FooModule do 658 | def foo do 659 | x = fn(a, b) -> a + b end 660 | end 661 | end 662 | ") 663 | 664 | (elixir-def-indentation-test indent-list-of-floats-aligns 665 | (:tags '(indentation)) 666 | " 667 | [1.2, 668 | 3.4] 669 | " 670 | 671 | " 672 | [1.2, 673 | 3.4] 674 | ") 675 | 676 | (elixir-def-indentation-test indent-after-operator 677 | (:tags '(indentation)) 678 | " 679 | defmodule Banana do 680 | def start do 681 | a = \"\" <> \"?\" 682 | 683 | case bar do 684 | z -> 1 685 | end 686 | 687 | case foo do 688 | ?x -> x 689 | end 690 | 691 | end 692 | end 693 | " 694 | 695 | " 696 | defmodule Banana do 697 | def start do 698 | a = \"\" <> \"?\" 699 | 700 | case bar do 701 | z -> 1 702 | end 703 | 704 | case foo do 705 | ?x -> x 706 | end 707 | 708 | end 709 | end 710 | ") 711 | 712 | (elixir-def-indentation-test nested-modules 713 | (:tags '(indentation)) 714 | " 715 | defmodule Mod1 do 716 | defmodule Mod1a do 717 | def start do 718 | foo() 719 | end 720 | end 721 | end 722 | " 723 | 724 | " 725 | defmodule Mod1 do 726 | defmodule Mod1a do 727 | def start do 728 | foo() 729 | end 730 | end 731 | end 732 | ") 733 | 734 | (elixir-def-indentation-test cond-comment 735 | (:tags '(indentation)) 736 | " 737 | def foo() do 738 | cond do 739 | yadda -> 740 | :ok 741 | badda -> # comment throws this off 742 | :what 743 | end 744 | end 745 | " 746 | 747 | " 748 | def foo() do 749 | cond do 750 | yadda -> 751 | :ok 752 | badda -> # comment throws this off 753 | :what 754 | end 755 | end 756 | ") 757 | 758 | (elixir-def-indentation-test cond-within-with 759 | (:expected-result :failed :tags '(indentation)) 760 | ;; https://github.com/elixir-editors/emacs-elixir/issues/319 761 | " 762 | with( 763 | foo <- 764 | cond do 765 | bar -> 766 | bar 767 | 768 | baz -> 769 | baz 770 | end, 771 | do: :ok 772 | ) 773 | " 774 | " 775 | with( 776 | foo <- 777 | cond do 778 | bar -> 779 | bar 780 | 781 | baz -> 782 | baz 783 | end, 784 | do: :ok 785 | ) 786 | ") 787 | 788 | (elixir-def-indentation-test with-statement 789 | (:tags '(indentation)) 790 | " 791 | defmodule Foo do 792 | 793 | def bar do 794 | with {:ok, width} <- Map.fetch(opts, :width), 795 | {:ok, height} <- Map.fetch(opts, :height) do 796 | {:ok, width * height} 797 | else 798 | :error -> 799 | {:error, :wrong_data} 800 | end 801 | end 802 | 803 | end 804 | " 805 | 806 | " 807 | defmodule Foo do 808 | 809 | def bar do 810 | with {:ok, width} <- Map.fetch(opts, :width), 811 | {:ok, height} <- Map.fetch(opts, :height) do 812 | {:ok, width * height} 813 | else 814 | :error -> 815 | {:error, :wrong_data} 816 | end 817 | end 818 | 819 | end 820 | ") 821 | 822 | (elixir-def-indentation-test with-statement/2 823 | (:tags '(indentation)) 824 | " 825 | defmodule Foo do 826 | def bar do 827 | with {:ok, width} <- Map.fetch(opts, :width), 828 | {:ok, height} <- Map.fetch(opts, :height), 829 | do: {:ok, width * height} 830 | 831 | with({:ok, width} <- Map.fetch(opts, :width), 832 | {:ok, height} <- Map.fetch(opts, :height), 833 | do: {:ok, width * height}) 834 | end 835 | end 836 | " 837 | 838 | " 839 | defmodule Foo do 840 | def bar do 841 | with {:ok, width} <- Map.fetch(opts, :width), 842 | {:ok, height} <- Map.fetch(opts, :height), 843 | do: {:ok, width * height} 844 | 845 | with({:ok, width} <- Map.fetch(opts, :width), 846 | {:ok, height} <- Map.fetch(opts, :height), 847 | do: {:ok, width * height}) 848 | end 849 | end 850 | ") 851 | 852 | (elixir-def-indentation-test indent-heredoc 853 | (:tags '(indentation)) 854 | " 855 | defmodule Foo do 856 | @doc \"\"\" 857 | this is a heredoc string 858 | 859 | \"\"\" 860 | def convert do 861 | x = 15 862 | end 863 | end 864 | " 865 | 866 | " 867 | defmodule Foo do 868 | @doc \"\"\" 869 | this is a heredoc string 870 | 871 | \"\"\" 872 | def convert do 873 | x = 15 874 | end 875 | end 876 | ") 877 | 878 | (elixir-def-indentation-test indent-heredoc/2 879 | (:tags '(indentation)) 880 | " 881 | defmodule Foo do 882 | @doc \"\"\" 883 | this is a heredoc string 884 | 885 | \"\"\" 886 | def convert do 887 | x = 15 888 | end 889 | 890 | defmodule Bar do 891 | @moduledoc \"\"\" 892 | this is a heredoc string 893 | 894 | last line 895 | \"\"\" 896 | end 897 | end 898 | " 899 | 900 | " 901 | defmodule Foo do 902 | @doc \"\"\" 903 | this is a heredoc string 904 | 905 | \"\"\" 906 | def convert do 907 | x = 15 908 | end 909 | 910 | defmodule Bar do 911 | @moduledoc \"\"\" 912 | this is a heredoc string 913 | 914 | last line 915 | \"\"\" 916 | end 917 | end 918 | ") 919 | 920 | (elixir-def-indentation-test indent-heredoc/3 921 | (:tags '(indentation)) 922 | " 923 | def foo() do 924 | \"\"\" 925 | heredoc 926 | \"\"\" 927 | end 928 | 929 | for x <- [1, 2, 3] do 930 | \"\"\" 931 | heredoc 932 | \"\"\" 933 | end 934 | " 935 | 936 | " 937 | def foo() do 938 | \"\"\" 939 | heredoc 940 | \"\"\" 941 | end 942 | 943 | for x <- [1, 2, 3] do 944 | \"\"\" 945 | heredoc 946 | \"\"\" 947 | end 948 | ") 949 | 950 | (elixir-def-indentation-test indent-multiline-pipes-after-call 951 | (:tags '(indentation)) 952 | " 953 | some_string 954 | |> String.downcase 955 | |> String.strip 956 | " 957 | 958 | " 959 | some_string 960 | |> String.downcase 961 | |> String.strip 962 | ") 963 | 964 | (elixir-def-indentation-test indent-multiline-on-the-right-of-pattern-match 965 | (:tags '(indentation)) 966 | " 967 | sanitized_string = 968 | some_string 969 | |> String.downcase 970 | |> String.strip 971 | " 972 | 973 | " 974 | sanitized_string = 975 | some_string 976 | |> String.downcase 977 | |> String.strip 978 | ") 979 | 980 | (elixir-def-indentation-test indent-pipes-after-assignment 981 | (:tags '(indentation)) 982 | " 983 | def foo(x) do 984 | a = x 985 | |> Enum.reverse 986 | end 987 | " 988 | 989 | " 990 | def foo(x) do 991 | a = x 992 | |> Enum.reverse 993 | end 994 | ") 995 | 996 | (elixir-def-indentation-test indent-pipes-inside-blocks 997 | (:tags '(indentation)) 998 | " 999 | defmodule Foo do 1000 | def bar do 1001 | baz = 1002 | [1,2,3,4,5,6,7,8,9] 1003 | |> Enum.reverse 1004 | |> Enum.filter(&(&1 > 5)) 1005 | end 1006 | end 1007 | " 1008 | 1009 | " 1010 | defmodule Foo do 1011 | def bar do 1012 | baz = 1013 | [1,2,3,4,5,6,7,8,9] 1014 | |> Enum.reverse 1015 | |> Enum.filter(&(&1 > 5)) 1016 | end 1017 | end 1018 | ") 1019 | 1020 | (elixir-def-indentation-test indent-inside-parens 1021 | (:tags '(indentation)) 1022 | " 1023 | x = do_something( 1024 | :foo, 1025 | :bar 1026 | ) 1027 | " 1028 | 1029 | " 1030 | x = do_something( 1031 | :foo, 1032 | :bar 1033 | ) 1034 | ") 1035 | 1036 | (elixir-def-indentation-test indent-inside-parens/2 1037 | (:tags '(indentation)) 1038 | " 1039 | x = do_something(:foo, 1040 | :bar) 1041 | " 1042 | 1043 | " 1044 | x = do_something(:foo, 1045 | :bar) 1046 | ") 1047 | 1048 | (elixir-def-indentation-test indent-inside-parens/3 1049 | (:tags '(indentation)) 1050 | " 1051 | x = do_something(:foo, fn (arg) -> 1052 | do_another(arg) 1053 | end) 1054 | " 1055 | 1056 | " 1057 | x = do_something(:foo, fn (arg) -> 1058 | do_another(arg) 1059 | end) 1060 | ") 1061 | 1062 | (elixir-def-indentation-test indent-inside-parens/4 1063 | (:tags '(indentation)) 1064 | " 1065 | defmodule Something do 1066 | def something do 1067 | x = do_something(:foo, fn (arg) -> 1068 | do_another(arg) 1069 | end) 1070 | end 1071 | end 1072 | " 1073 | 1074 | " 1075 | defmodule Something do 1076 | def something do 1077 | x = do_something(:foo, fn (arg) -> 1078 | do_another(arg) 1079 | end) 1080 | end 1081 | end 1082 | ") 1083 | 1084 | (elixir-def-indentation-test indent-inside-parens/5 1085 | (:tags '(indentation)) 1086 | " 1087 | defmodule IndentPlayground do 1088 | def my_func(arr) do 1089 | Enum.map(arr, fn(x) -> 1090 | x * 2 1091 | end) 1092 | #back here 1093 | end 1094 | end 1095 | " 1096 | 1097 | " 1098 | defmodule IndentPlayground do 1099 | def my_func(arr) do 1100 | Enum.map(arr, fn(x) -> 1101 | x * 2 1102 | end) 1103 | #back here 1104 | end 1105 | end 1106 | ") 1107 | 1108 | (elixir-def-indentation-test indent-lone-keyword 1109 | (:tags '(indentation)) 1110 | " 1111 | def foo do #comment 1112 | :bar 1113 | end 1114 | " 1115 | 1116 | " 1117 | def foo do #comment 1118 | :bar 1119 | end 1120 | ") 1121 | 1122 | (elixir-def-indentation-test indent-single-line-match 1123 | (:tags '(indentation)) 1124 | " 1125 | case x do 1126 | a -> b 1127 | c -> d 1128 | end 1129 | " 1130 | 1131 | " 1132 | case x do 1133 | a -> b 1134 | c -> d 1135 | end 1136 | ") 1137 | 1138 | (elixir-def-indentation-test indent-multiline-match 1139 | (:tags '(indentation)) 1140 | " 1141 | def foo do 1142 | case is_string(x) do 1143 | true -> 1144 | x2 = \" one\" 1145 | x <> x2 1146 | false -> 1147 | x2 = \" two\" 1148 | x <> x2 1149 | end 1150 | end 1151 | " 1152 | 1153 | " 1154 | def foo do 1155 | case is_string(x) do 1156 | true -> 1157 | x2 = \" one\" 1158 | x <> x2 1159 | false -> 1160 | x2 = \" two\" 1161 | x <> x2 1162 | end 1163 | end 1164 | ") 1165 | 1166 | (elixir-def-indentation-test indent-multiline-match/2 1167 | (:tags '(indentation)) 1168 | " 1169 | def foo do 1170 | case is_string(x) do 1171 | true -> 1172 | x2 = \" one\" 1173 | x <> x2 1174 | false -> 1175 | x2 = \" two\" 1176 | x <> x2 1177 | end 1178 | end 1179 | " 1180 | 1181 | " 1182 | def foo do 1183 | case is_string(x) do 1184 | true -> 1185 | x2 = \" one\" 1186 | x <> x2 1187 | false -> 1188 | x2 = \" two\" 1189 | x <> x2 1190 | end 1191 | end 1192 | ") 1193 | 1194 | (elixir-def-indentation-test indent-mixed-match 1195 | (:tags '(indentation)) 1196 | " 1197 | case x do 1198 | a -> b 1199 | c -> 1200 | d 1201 | e -> f 1202 | end 1203 | " 1204 | 1205 | " 1206 | case x do 1207 | a -> b 1208 | c -> 1209 | d 1210 | e -> f 1211 | end 1212 | ") 1213 | 1214 | (elixir-def-indentation-test indent-after-require-Record 1215 | (:tags '(indentation)) 1216 | ;; Mind the significant whitespace after `Record' in each case. There should 1217 | ;; be two spaces after `Record', otherwise this test is meaningless. 1218 | " 1219 | defmodule RSS do 1220 | require Record 1221 | 1222 | def zip(list1, list2) when length(list1) == length(list2) do 1223 | x = 1 1224 | end 1225 | end 1226 | " 1227 | 1228 | " 1229 | defmodule RSS do 1230 | require Record 1231 | 1232 | def zip(list1, list2) when length(list1) == length(list2) do 1233 | x = 1 1234 | end 1235 | end 1236 | ") 1237 | 1238 | (elixir-def-indentation-test indent-fn-in-multiline-assignment 1239 | (:expected-result :failed :tags '(indentation)) 1240 | " 1241 | variable = 1242 | fn -> 1243 | case :file.open(path, modes) do 1244 | {:ok, device} -> device 1245 | {:error, reason} -> 1246 | raise File.Error, reason: reason 1247 | end 1248 | end 1249 | " 1250 | 1251 | " 1252 | variable = 1253 | fn -> 1254 | case :file.open(path, modes) do 1255 | {:ok, device} -> device 1256 | {:error, reason} -> 1257 | raise File.Error, reason: reason 1258 | end 1259 | end 1260 | ") 1261 | 1262 | (elixir-def-indentation-test indent-after-def-do-online 1263 | (:tags '(indentation)) 1264 | " 1265 | defmodule Greeter do 1266 | def hello, do: IO.puts \"hello\" 1267 | def bye, do: IO.puts \"bye\" 1268 | 1269 | def hi(name) do 1270 | IO.puts \"Hi #{name}\" 1271 | end 1272 | end 1273 | " 1274 | 1275 | " 1276 | defmodule Greeter do 1277 | def hello, do: IO.puts \"hello\" 1278 | def bye, do: IO.puts \"bye\" 1279 | 1280 | def hi(name) do 1281 | IO.puts \"Hi #{name}\" 1282 | end 1283 | end 1284 | ") 1285 | 1286 | (elixir-def-indentation-test indent-after-def-do-online/2 1287 | (:tags '(indentation)) 1288 | 1289 | " 1290 | defmodule ControlFlow do 1291 | defmacro my_if(expr, do: if_block), do: if(expr, do: if_block, else: nil) 1292 | defmacro my_if(expr, do: if_block, else: else_block) do 1293 | quote do 1294 | case unquote(expr) do 1295 | result when result in [false, nil] -> unquote(else_block) 1296 | _ -> unquote(if_block) 1297 | end 1298 | end 1299 | end 1300 | end 1301 | " 1302 | 1303 | " 1304 | defmodule ControlFlow do 1305 | defmacro my_if(expr, do: if_block), do: if(expr, do: if_block, else: nil) 1306 | defmacro my_if(expr, do: if_block, else: else_block) do 1307 | quote do 1308 | case unquote(expr) do 1309 | result when result in [false, nil] -> unquote(else_block) 1310 | _ -> unquote(if_block) 1311 | end 1312 | end 1313 | end 1314 | end 1315 | ") 1316 | 1317 | (elixir-def-indentation-test indent-after-def-do-online/3 1318 | (:tags '(indentation)) 1319 | " 1320 | defmodule Foo do 1321 | def bar(baz, quun \\\\ nil) 1322 | def bar(baz, quun) when baz == quun, do: baz 1323 | def bar(baz, quun), do: quun 1324 | 1325 | defp bar(baz, quun \\\\ nil) 1326 | defp bar(baz, quun) when baz == quun, do: baz 1327 | defp bar(baz, quun), do: quun 1328 | 1329 | defmacro bar(baz, quun \\\\ nil) 1330 | defmacro bar(baz, quun) when baz == quun, do: baz 1331 | defmacro bar(baz, quun), do: quun 1332 | 1333 | defmacrop bar(baz, quun \\\\ nil) 1334 | defmacrop bar(baz, quun) when baz == quun, do: baz 1335 | defmacrop bar(baz, quun), do: quun 1336 | end 1337 | " 1338 | 1339 | " 1340 | defmodule Foo do 1341 | def bar(baz, quun \\\\ nil) 1342 | def bar(baz, quun) when baz == quun, do: baz 1343 | def bar(baz, quun), do: quun 1344 | 1345 | defp bar(baz, quun \\\\ nil) 1346 | defp bar(baz, quun) when baz == quun, do: baz 1347 | defp bar(baz, quun), do: quun 1348 | 1349 | defmacro bar(baz, quun \\\\ nil) 1350 | defmacro bar(baz, quun) when baz == quun, do: baz 1351 | defmacro bar(baz, quun), do: quun 1352 | 1353 | defmacrop bar(baz, quun \\\\ nil) 1354 | defmacrop bar(baz, quun) when baz == quun, do: baz 1355 | defmacrop bar(baz, quun), do: quun 1356 | end 1357 | ") 1358 | 1359 | (elixir-def-indentation-test indent-after-not-finished-one-line-def 1360 | (:tags '(indentation)) 1361 | " 1362 | defmodule Hello do 1363 | defp skip, 1364 | do: true 1365 | def on?, do: true 1366 | defmacro switch, 1367 | do: on! 1368 | defp self, do: value 1369 | defmacrop whatever, do: do_it! 1370 | end 1371 | " 1372 | 1373 | " 1374 | defmodule Hello do 1375 | defp skip, 1376 | do: true 1377 | def on?, do: true 1378 | defmacro switch, 1379 | do: on! 1380 | defp self, do: value 1381 | defmacrop whatever, do: do_it! 1382 | end 1383 | ") 1384 | 1385 | (elixir-def-indentation-test indent-correct-with-multiple-one-line-macro-calls 1386 | (:tags '(indentation)) 1387 | " 1388 | defmodule MyMacros do 1389 | mymacro x1, do: [:x1] 1390 | mymacro x2, do: [:x2] 1391 | mymacro x3, do: [:x3] 1392 | mymacro x1, do: [:x1] 1393 | mymacro x2, do: [:x2] 1394 | mymacro x3, do: [:x3] 1395 | end 1396 | " 1397 | 1398 | " 1399 | defmodule MyMacros do 1400 | mymacro x1, do: [:x1] 1401 | mymacro x2, do: [:x2] 1402 | mymacro x3, do: [:x3] 1403 | mymacro x1, do: [:x1] 1404 | mymacro x2, do: [:x2] 1405 | mymacro x3, do: [:x3] 1406 | end 1407 | ") 1408 | 1409 | (elixir-def-indentation-test indent-binary-sequence 1410 | (:tags '(indentation)) 1411 | " 1412 | defmodule ExampleTest do 1413 | test \"the truth\" do 1414 | assert <<1,2>> == <<1,2>> 1415 | assert 1 + 1 == 2 1416 | end 1417 | end 1418 | " 1419 | 1420 | " 1421 | defmodule ExampleTest do 1422 | test \"the truth\" do 1423 | assert <<1,2>> == <<1,2>> 1424 | assert 1 + 1 == 2 1425 | end 1426 | end 1427 | ") 1428 | 1429 | (elixir-def-indentation-test indent-binary-sequence-inside-match-block/2 1430 | (:tags '(indentation)) 1431 | " 1432 | case asd do 1433 | <> -> 1434 | <> 1437 | <> -> 1438 | <> 1441 | <> -> 1442 | <> 1445 | <> -> 1446 | <> 1449 | <<>> -> 1450 | main 1451 | end 1452 | " 1453 | 1454 | " 1455 | case asd do 1456 | <> -> 1457 | <> 1460 | <> -> 1461 | <> 1464 | <> -> 1465 | <> 1468 | <> -> 1469 | <> 1472 | <<>> -> 1473 | main 1474 | end 1475 | ") 1476 | 1477 | (elixir-def-indentation-test indent-inside-square-brackets 1478 | (:tags '(indentation)) 1479 | " 1480 | children = [ 1481 | supervisor(Task.Supervisor, [[name: KVServer.TaskSupervisor]]), 1482 | worker(Task, [KVServer, :accept, [4040]]) 1483 | ] 1484 | " 1485 | 1486 | " 1487 | children = [ 1488 | supervisor(Task.Supervisor, [[name: KVServer.TaskSupervisor]]), 1489 | worker(Task, [KVServer, :accept, [4040]]) 1490 | ] 1491 | ") 1492 | 1493 | (elixir-def-indentation-test indent-after-reserved-word/case 1494 | (:tags '(indentation)) 1495 | " 1496 | defmodule Application.Behavior do 1497 | def foo(test) do 1498 | test_case = test.case 1499 | run(test_case) 1500 | end 1501 | end 1502 | " 1503 | 1504 | " 1505 | defmodule Application.Behavior do 1506 | def foo(test) do 1507 | test_case = test.case 1508 | run(test_case) 1509 | end 1510 | end 1511 | ") 1512 | 1513 | (elixir-def-indentation-test indent-after-reserved-word/try 1514 | (:tags '(indentation)) 1515 | " 1516 | defmodule Application.Behavior do 1517 | def foo(test) do 1518 | test_case = test.try 1519 | run(test_case) 1520 | end 1521 | end 1522 | " 1523 | 1524 | " 1525 | defmodule Application.Behavior do 1526 | def foo(test) do 1527 | test_case = test.try 1528 | run(test_case) 1529 | end 1530 | end 1531 | ") 1532 | 1533 | (elixir-def-indentation-test indent-after-reserved-word/rescue 1534 | (:tags '(indentation)) 1535 | " 1536 | defmodule Application.Behavior do 1537 | def foo(test) do 1538 | test_case = test.rescue 1539 | run(test_case) 1540 | end 1541 | end 1542 | " 1543 | 1544 | " 1545 | defmodule Application.Behavior do 1546 | def foo(test) do 1547 | test_case = test.rescue 1548 | run(test_case) 1549 | end 1550 | end 1551 | ") 1552 | 1553 | 1554 | (elixir-def-indentation-test indent-after-reserved-word/if 1555 | (:tags '(indentation)) 1556 | " 1557 | defmodule Application.Behavior do 1558 | def foo(test) do 1559 | test_case = test.if 1560 | run(test_case) 1561 | end 1562 | end 1563 | " 1564 | 1565 | " 1566 | defmodule Application.Behavior do 1567 | def foo(test) do 1568 | test_case = test.if 1569 | run(test_case) 1570 | end 1571 | end 1572 | ") 1573 | 1574 | (elixir-def-indentation-test indent-after-bitstring/1 1575 | (:tags '(indentation)) 1576 | " 1577 | defmodule X do 1578 | def a, do: <<1 :: size(8)>> 1579 | def b, do: <<2 :: size(8)>> 1580 | def c, do: <<3 :: size(8)>> 1581 | end 1582 | " 1583 | 1584 | " 1585 | defmodule X do 1586 | def a, do: <<1 :: size(8)>> 1587 | def b, do: <<2 :: size(8)>> 1588 | def c, do: <<3 :: size(8)>> 1589 | end 1590 | ") 1591 | 1592 | (elixir-def-indentation-test indent-after-fn/1 1593 | (:tags '(indentation)) 1594 | " 1595 | defmodule X do 1596 | def func do 1597 | Enum.filter([1,2,3], 1598 | fn(1) -> true 1599 | (2) -> false 1600 | (_) -> true 1601 | end) 1602 | end 1603 | end 1604 | " 1605 | 1606 | " 1607 | defmodule X do 1608 | def func do 1609 | Enum.filter([1,2,3], 1610 | fn(1) -> true 1611 | (2) -> false 1612 | (_) -> true 1613 | end) 1614 | end 1615 | end 1616 | ") 1617 | 1618 | (elixir-def-indentation-test indent-outside-block 1619 | (:tags '(indentation)) 1620 | " 1621 | 1 + 1 # => 2 1622 | 1623 | sum = fn(a, b) -> 1624 | a + b 1625 | end 1626 | 1627 | sum.(1231, 3) 1628 | 1629 | a = 23 1630 | a = a 1631 | 1632 | 23 / 3 1633 | " 1634 | 1635 | " 1636 | 1 + 1 # => 2 1637 | 1638 | sum = fn(a, b) -> 1639 | a + b 1640 | end 1641 | 1642 | sum.(1231, 3) 1643 | 1644 | a = 23 1645 | a = a 1646 | 1647 | 23 / 3 1648 | ") 1649 | 1650 | (elixir-def-indentation-test indent-list-elements 1651 | (:tags '(indentation)) 1652 | " 1653 | defmodule Foo do 1654 | def bar do 1655 | [ 1656 | :foo, 1657 | :bar 1658 | ] 1659 | end 1660 | end 1661 | " 1662 | 1663 | " 1664 | defmodule Foo do 1665 | def bar do 1666 | [ 1667 | :foo, 1668 | :bar 1669 | ] 1670 | end 1671 | end 1672 | ") 1673 | 1674 | (elixir-def-indentation-test indent-tuple-elements 1675 | (:tags '(indentation)) 1676 | " 1677 | defmodule Foo do 1678 | def bar do 1679 | { 1680 | :foo, 1681 | :bar 1682 | } 1683 | end 1684 | end 1685 | " 1686 | 1687 | " 1688 | defmodule Foo do 1689 | def bar do 1690 | { 1691 | :foo, 1692 | :bar 1693 | } 1694 | end 1695 | end 1696 | ") 1697 | 1698 | (elixir-def-indentation-test indent-maps-with-stings-as-keys 1699 | (:tags '(indentation)) 1700 | " 1701 | %{ 1702 | \"data\" => %{ 1703 | \"foo\" => %{ 1704 | \"bar\" => nil 1705 | } 1706 | } 1707 | } 1708 | " 1709 | 1710 | " 1711 | %{ 1712 | \"data\" => %{ 1713 | \"foo\" => %{ 1714 | \"bar\" => nil 1715 | } 1716 | } 1717 | } 1718 | ") 1719 | 1720 | (elixir-def-indentation-test indent-maps-with-multiple-string-keys 1721 | (:expected-result :failed :tags '(indentation)) 1722 | " 1723 | Enum.map [], fn x -> 1724 | %{ 1725 | \"a\" => 5555, 1726 | \"b\" => 5555, 1727 | \"c\" => x, 1728 | } 1729 | end 1730 | " 1731 | 1732 | " 1733 | Enum.map [], fn x -> 1734 | %{ 1735 | \"a\" => 5555, 1736 | \"b\" => 5555, 1737 | \"c\" => x, 1738 | } 1739 | end 1740 | ") 1741 | 1742 | (elixir-def-indentation-test indent-maps-and-structs-elements 1743 | (:tags '(indentation)) 1744 | " 1745 | { 1746 | :foo, 1747 | :bar 1748 | } 1749 | 1750 | %GenEvent.Stream{ 1751 | manager: manager, 1752 | timeout: Keyword.get(options, :timeout, :infinity)} 1753 | " 1754 | 1755 | " 1756 | { 1757 | :foo, 1758 | :bar 1759 | } 1760 | 1761 | %GenEvent.Stream{ 1762 | manager: manager, 1763 | timeout: Keyword.get(options, :timeout, :infinity)} 1764 | ") 1765 | 1766 | (elixir-def-indentation-test indent-parenthesis-inside-block 1767 | (:tags '(indentation)) 1768 | " 1769 | defmodule Foo do 1770 | def bar do 1771 | () 1772 | end 1773 | end 1774 | " 1775 | 1776 | " 1777 | defmodule Foo do 1778 | def bar do 1779 | () 1780 | end 1781 | end 1782 | ") 1783 | 1784 | (elixir-def-indentation-test complex-case-with-matches/2 1785 | (:tags '(indentation)) 1786 | " 1787 | case parse do 1788 | { [ help: true ], _, _ } -> :help 1789 | { _, [ user, project, count ], _ } -> 1790 | { user, project, count } 1791 | { _, [ user, project ], _ } -> { user, project, @default_count } 1792 | _ -> :help 1793 | end 1794 | " 1795 | 1796 | " 1797 | case parse do 1798 | { [ help: true ], _, _ } -> :help 1799 | { _, [ user, project, count ], _ } -> 1800 | { user, project, count } 1801 | { _, [ user, project ], _ } -> { user, project, @default_count } 1802 | _ -> :help 1803 | end 1804 | ") 1805 | 1806 | (elixir-def-indentation-test complex-case-with-matches/3 1807 | (:tags '(indentation)) 1808 | " 1809 | defmodule MyModule do 1810 | case File.read(\"/usr/share/dict/words\") do 1811 | {:ok, contents} -> 1812 | {:something, contents} 1813 | {:error, reason} -> 1814 | {:error, reason} 1815 | end 1816 | end 1817 | " 1818 | 1819 | " 1820 | defmodule MyModule do 1821 | case File.read(\"/usr/share/dict/words\") do 1822 | {:ok, contents} -> 1823 | {:something, contents} 1824 | {:error, reason} -> 1825 | {:error, reason} 1826 | end 1827 | end 1828 | ") 1829 | 1830 | (elixir-def-indentation-test complex-case-with-matches/4 1831 | (:tags '(indentation)) 1832 | " 1833 | case :foo do 1834 | 1 -> 1835 | try true do 1836 | :foo 1837 | end 1838 | 2 -> 1839 | false 1840 | end 1841 | 1842 | fn x -> 1843 | if true do 1844 | end 1845 | end 1846 | " 1847 | 1848 | " 1849 | case :foo do 1850 | 1 -> 1851 | try true do 1852 | :foo 1853 | end 1854 | 2 -> 1855 | false 1856 | end 1857 | 1858 | fn x -> 1859 | if true do 1860 | end 1861 | end 1862 | ") 1863 | 1864 | (elixir-def-indentation-test complex-case-with-matches/5 1865 | (:tags '(indentation)) 1866 | " 1867 | case parse do 1868 | {} -> :help 1869 | {} -> :help 1870 | _ 1871 | end 1872 | " 1873 | 1874 | " 1875 | case parse do 1876 | {} -> :help 1877 | {} -> :help 1878 | _ 1879 | end 1880 | ") 1881 | 1882 | (elixir-def-indentation-test case-with-multiline-maps 1883 | (:tags '(indentation)) 1884 | " 1885 | case statement do 1886 | %{\"foo\" => \"foo\", 1887 | \"baz\" => \"baz\"} -> 1888 | :ok 1889 | _ -> 1890 | :ok 1891 | end 1892 | " 1893 | 1894 | " 1895 | case statement do 1896 | %{\"foo\" => \"foo\", 1897 | \"baz\" => \"baz\"} -> 1898 | :ok 1899 | _ -> 1900 | :ok 1901 | end 1902 | ") 1903 | 1904 | (elixir-def-indentation-test case-with-for-comprehension 1905 | (:tags '(indentation)) 1906 | " 1907 | case expression do 1908 | true -> 1909 | for _ <- [] do 1910 | :ok 1911 | end 1912 | end 1913 | " 1914 | 1915 | " 1916 | case expression do 1917 | true -> 1918 | for _ <- [] do 1919 | :ok 1920 | end 1921 | end 1922 | ") 1923 | 1924 | (elixir-def-indentation-test indent-case-when-condition-is-a-named-function-on-a-module 1925 | (:tags '(indentation)) 1926 | " 1927 | defmodule Whois do 1928 | def lookup1(domain) do 1929 | case Server.for(domain) do 1930 | {:ok, server} -> server 1931 | :error -> {:error, :unsupported} 1932 | end 1933 | end 1934 | end 1935 | " 1936 | 1937 | " 1938 | defmodule Whois do 1939 | def lookup1(domain) do 1940 | case Server.for(domain) do 1941 | {:ok, server} -> server 1942 | :error -> {:error, :unsupported} 1943 | end 1944 | end 1945 | end 1946 | ") 1947 | 1948 | (elixir-def-indentation-test close-map-curly-brackt 1949 | (:tags '(indentation)) 1950 | " 1951 | config = %{ 1952 | async_cases: [], 1953 | exclude: opts[:exclude], 1954 | include: opts[:include], 1955 | timeout: opts[:timeout], 1956 | trace: opts[:trace] 1957 | } 1958 | " 1959 | 1960 | " 1961 | config = %{ 1962 | async_cases: [], 1963 | exclude: opts[:exclude], 1964 | include: opts[:include], 1965 | timeout: opts[:timeout], 1966 | trace: opts[:trace] 1967 | } 1968 | ") 1969 | 1970 | 1971 | (elixir-def-indentation-test receive-after-block 1972 | (:tags '(indentation)) 1973 | " 1974 | receive do 1975 | {:hello} -> :ok 1976 | other -> 1977 | other 1978 | after 1979 | 2000 -> 1980 | IO.puts 'hello' 1981 | IO.puts 'status 2000 ends' 1982 | { :ok } -> 1983 | IO.puts 'ok' 1984 | _ -> whatever 1985 | end 1986 | " 1987 | 1988 | " 1989 | receive do 1990 | {:hello} -> :ok 1991 | other -> 1992 | other 1993 | after 1994 | 2000 -> 1995 | IO.puts 'hello' 1996 | IO.puts 'status 2000 ends' 1997 | { :ok } -> 1998 | IO.puts 'ok' 1999 | _ -> whatever 2000 | end 2001 | ") 2002 | 2003 | (elixir-def-indentation-test indent-after-for-online-definition 2004 | (:tags '(indentation)) 2005 | " 2006 | defmodule Hello do 2007 | def hi do 2008 | hi = for i <- list, do: i 2009 | # weird spacing now 2010 | 2011 | for i <- list, do: i 2012 | IO.puts 'WORKED' 2013 | end 2014 | end 2015 | 2016 | hi = for i <- list, do: i 2017 | # weird spacing now 2018 | " 2019 | 2020 | " 2021 | defmodule Hello do 2022 | def hi do 2023 | hi = for i <- list, do: i 2024 | # weird spacing now 2025 | 2026 | for i <- list, do: i 2027 | IO.puts 'WORKED' 2028 | end 2029 | end 2030 | 2031 | hi = for i <- list, do: i 2032 | # weird spacing now 2033 | ") 2034 | 2035 | (elixir-def-indentation-test indent-oneline-for-after-assignment 2036 | (:expected-result :failed :tags '(indentation)) 2037 | " 2038 | hi = 2039 | for i <- list, do: i 2040 | " 2041 | 2042 | " 2043 | hi = 2044 | for i <- list, do: i 2045 | ") 2046 | 2047 | (elixir-def-indentation-test indent-multiline-for 2048 | (:tags '(indentation)) 2049 | " 2050 | defmodule For do 2051 | def test do 2052 | for {k, v} <- keyword, 2053 | v = process_value(v), 2054 | into: %{} 2055 | do: {v, k} 2056 | 2057 | for {k, v} <- keyword, 2058 | v = process_value(v), 2059 | into: %{} do 2060 | {v, k} 2061 | end 2062 | end 2063 | end 2064 | " 2065 | 2066 | " 2067 | defmodule For do 2068 | def test do 2069 | for {k, v} <- keyword, 2070 | v = process_value(v), 2071 | into: %{} 2072 | do: {v, k} 2073 | 2074 | for {k, v} <- keyword, 2075 | v = process_value(v), 2076 | into: %{} do 2077 | {v, k} 2078 | end 2079 | end 2080 | end 2081 | ") 2082 | 2083 | (elixir-def-indentation-test indent-multiline-for-with-assignment 2084 | (:expected-result :failed :tags '(indentation)) 2085 | " 2086 | result = 2087 | for {k, v} <- keyword, 2088 | v = process_value(v), 2089 | into: %{} 2090 | do: {v, k} 2091 | " 2092 | 2093 | " 2094 | result = 2095 | for {k, v} <- keyword, 2096 | v = process_value(v), 2097 | into: %{} 2098 | do: {v, k} 2099 | ") 2100 | 2101 | (elixir-def-indentation-test indent-multiline-for-do-end-with-assignment 2102 | (:expected-result :failed :tags '(indentation)) 2103 | " 2104 | result = 2105 | for {k, v} <- keyword, 2106 | v = process_value(v), 2107 | into: %{} 2108 | do 2109 | {v, k} 2110 | end 2111 | " 2112 | 2113 | " 2114 | result = 2115 | for {k, v} <- keyword, 2116 | v = process_value(v), 2117 | into: %{} do 2118 | {v, k} 2119 | end 2120 | ") 2121 | 2122 | (elixir-def-indentation-test indent-multiline-function-calls-without-parenthesis 2123 | (:tags '(indentation)) 2124 | " 2125 | some_method :arg1, 2126 | :arg2 2127 | other_method 2128 | " 2129 | 2130 | " 2131 | some_method :arg1, 2132 | :arg2 2133 | other_method 2134 | ") 2135 | 2136 | (elixir-def-indentation-test indent-multiline-function-calls-without-parenthesis/2 2137 | (:tags '(indentation)) 2138 | " 2139 | some_method :arg1, 2140 | arg1: 1, 2141 | arg2: 2 2142 | other_method 2143 | " 2144 | 2145 | " 2146 | some_method :arg1, 2147 | arg1: 1, 2148 | arg2: 2 2149 | other_method 2150 | ") 2151 | 2152 | (elixir-def-indentation-test indent-correct-inside-fn-block 2153 | (:tags '(indentation)) 2154 | " 2155 | Enum.map(addresses, fn({mac_address, dbms}) -> 2156 | sum = Enum.reduce(dbms, fn(x, sum) -> x + sum end) 2157 | average_dbm = sum / length(addresses) 2158 | end) 2159 | " 2160 | 2161 | " 2162 | Enum.map(addresses, fn({mac_address, dbms}) -> 2163 | sum = Enum.reduce(dbms, fn(x, sum) -> x + sum end) 2164 | average_dbm = sum / length(addresses) 2165 | end) 2166 | ") 2167 | 2168 | (elixir-def-indentation-test indent-statement-with-anonymous-fn 2169 | (:tags '(indentation)) 2170 | " 2171 | cond do 2172 | is_nil(val) -> 2173 | IO.puts \"OK\" 2174 | Enum.any?(1..6, fn -> end) 2175 | true -> 2176 | end 2177 | " 2178 | 2179 | " 2180 | cond do 2181 | is_nil(val) -> 2182 | IO.puts \"OK\" 2183 | Enum.any?(1..6, fn -> end) 2184 | true -> 2185 | end 2186 | ") 2187 | 2188 | (elixir-def-indentation-test indent-maps-inside-list 2189 | (:tags '(indentation)) 2190 | " 2191 | [{:earmark, \"~> 0.1\", only: :dev}, 2192 | {:earmark, \"~> 0.1\", only: :dev}, 2193 | {:ex_doc, \"~> 0.11\", only: :dev}, 2194 | {:ex_doc, \"~> 0.11\", only: :dev}] 2195 | 2196 | [ 2197 | {1, 2, 3}, 2198 | {4, 5, 6}, 2199 | {7, 8, 9} 2200 | ] 2201 | 2202 | [ 2203 | %{ 2204 | name: \"John Doe\", 2205 | email: \"john@doe.org\" 2206 | }, 2207 | %{ 2208 | name: \"Jane Doe\", 2209 | email: \"jane@doe.org\", 2210 | }, 2211 | %{ 2212 | name: \"Josie Doe\", 2213 | email: \"josie@doe.org\", 2214 | }, 2215 | ] 2216 | " 2217 | 2218 | " 2219 | [{:earmark, \"~> 0.1\", only: :dev}, 2220 | {:earmark, \"~> 0.1\", only: :dev}, 2221 | {:ex_doc, \"~> 0.11\", only: :dev}, 2222 | {:ex_doc, \"~> 0.11\", only: :dev}] 2223 | 2224 | [ 2225 | {1, 2, 3}, 2226 | {4, 5, 6}, 2227 | {7, 8, 9} 2228 | ] 2229 | 2230 | [ 2231 | %{ 2232 | name: \"John Doe\", 2233 | email: \"john@doe.org\" 2234 | }, 2235 | %{ 2236 | name: \"Jane Doe\", 2237 | email: \"jane@doe.org\", 2238 | }, 2239 | %{ 2240 | name: \"Josie Doe\", 2241 | email: \"josie@doe.org\", 2242 | }, 2243 | ] 2244 | ") 2245 | 2246 | (elixir-def-indentation-test indent-before-comma 2247 | (:tags '(indentation)) 2248 | " 2249 | defmodule TestIndentation do 2250 | import Enum 2251 | 2252 | @att %{ab: 21, 2253 | de: 22} 2254 | IO.inspect @att 2255 | end 2256 | " 2257 | 2258 | " 2259 | defmodule TestIndentation do 2260 | import Enum 2261 | 2262 | @att %{ab: 21, 2263 | de: 22} 2264 | IO.inspect @att 2265 | end 2266 | ") 2267 | 2268 | (elixir-def-indentation-test indent-complex-comments 2269 | (:tags '(indentation)) 2270 | " 2271 | defmodule Foo do 2272 | @initial_state %{ 2273 | socket: nil, 2274 | opts: nil, 2275 | # TODO: x, 2276 | tail: \"\", 2277 | } 2278 | 2279 | def bar do 2280 | 2281 | first = { 2282 | asdasd, 2283 | asdasd, 2284 | asdad, 2285 | # comment 2286 | # another comment 2287 | value, 2288 | another 2289 | } 2290 | 2291 | another = [ 2292 | asdasd, 2293 | asdasd, 2294 | asdad, 2295 | # comment 2296 | # another comment 2297 | value, 2298 | another 2299 | ] 2300 | 2301 | end 2302 | 2303 | # one 2304 | # two 2305 | # three 2306 | end 2307 | " 2308 | 2309 | " 2310 | defmodule Foo do 2311 | @initial_state %{ 2312 | socket: nil, 2313 | opts: nil, 2314 | # TODO: x, 2315 | tail: \"\", 2316 | } 2317 | 2318 | def bar do 2319 | 2320 | first = { 2321 | asdasd, 2322 | asdasd, 2323 | asdad, 2324 | # comment 2325 | # another comment 2326 | value, 2327 | another 2328 | } 2329 | 2330 | another = [ 2331 | asdasd, 2332 | asdasd, 2333 | asdad, 2334 | # comment 2335 | # another comment 2336 | value, 2337 | another 2338 | ] 2339 | 2340 | end 2341 | 2342 | # one 2343 | # two 2344 | # three 2345 | end 2346 | ") 2347 | 2348 | (elixir-def-indentation-test indent-tuples-in-case-statement 2349 | (:tags '(indentation)) 2350 | " 2351 | defmodule Foo do 2352 | def bar do 2353 | case info do 2354 | :init -> 2355 | {:foo, :bar} 2356 | {:foo, :bar} 2357 | {:foo, :bar} 2358 | {:foo, :bar} 2359 | end 2360 | end 2361 | end 2362 | " 2363 | 2364 | " 2365 | defmodule Foo do 2366 | def bar do 2367 | case info do 2368 | :init -> 2369 | {:foo, :bar} 2370 | {:foo, :bar} 2371 | {:foo, :bar} 2372 | {:foo, :bar} 2373 | end 2374 | end 2375 | end 2376 | " 2377 | ) 2378 | 2379 | (elixir-def-indentation-test indent-multiline-function-specs 2380 | (:expected-result :failed :tags '(indentation)) 2381 | " 2382 | @callback init(args :: term) :: 2383 | {:ok, state} | 2384 | {:ok, state, timeout | :hibernate} | 2385 | :ignore | 2386 | {:stop, reason :: any} when state: any 2387 | " 2388 | 2389 | " 2390 | @callback init(args :: term) :: 2391 | {:ok, state} | 2392 | {:ok, state, timeout | :hibernate} | 2393 | :ignore | 2394 | {:stop, reason :: any} when state: any 2395 | ") 2396 | 2397 | (elixir-def-indentation-test indent-multiline-function-specs-followed-by-a-function-def 2398 | (:expected-result :failed :tags '(indentation)) 2399 | " 2400 | @spec foo(args :: term) :: 2401 | {:ok, state} | 2402 | {:ok, state, timeout | :hibernate} | 2403 | :ignore | 2404 | {:stop, reason :: any} when state: any 2405 | def foo(_opts) do 2406 | :ignore 2407 | end 2408 | " 2409 | 2410 | " 2411 | @spec foo(args :: term) :: 2412 | {:ok, state} | 2413 | {:ok, state, timeout | :hibernate} | 2414 | :ignore | 2415 | {:stop, reason :: any} when state: any 2416 | def foo(_opts) do 2417 | :ignore 2418 | end 2419 | ") 2420 | 2421 | ;; We don't want automatic whitespace cleanup here because of the significant 2422 | ;; whitespace after `Record' above. By setting `whitespace-action' to nil, 2423 | ;; `whitespace-mode' won't automatically clean up trailing whitespace (in my 2424 | ;; config, anyway). 2425 | 2426 | ;;; Local Variables: 2427 | ;;; whitespace-action: nil 2428 | ;;; End: 2429 | 2430 | (provide 'elixir-mode-indentation-test) 2431 | 2432 | ;;; elixir-mode-indentation-test.el ends here 2433 | -------------------------------------------------------------------------------- /tests/elixir-mode-moving-test.el: -------------------------------------------------------------------------------- 1 | ;;; elixir-mode-moving-test.el --- Tests for moving cursor functions 2 | 3 | ;;; Code: 4 | (require 'test-helper) 5 | 6 | (ert-deftest beginning-of-defun () 7 | :tags '(moving) 8 | (elixir-test-with-temp-buffer 9 | "def foo do 10 | :bar 11 | end 12 | " 13 | (search-forward ":bar") 14 | (call-interactively 'beginning-of-defun) 15 | (should (= (point) (point-min))))) 16 | 17 | (ert-deftest beginning-of-defun-nested () 18 | :tags '(moving) 19 | (elixir-test-with-temp-buffer 20 | " 21 | defmodule Foo do 22 | def bar do 23 | :baz 24 | end 25 | end 26 | " 27 | (search-forward ":baz") 28 | (call-interactively 'beginning-of-defun) 29 | (should (and (= (line-number-at-pos) 3) (bolp))))) 30 | 31 | (ert-deftest end-of-defun () 32 | :tags '(moving) 33 | (elixir-test-with-temp-buffer 34 | "def foo do 35 | :bar 36 | end 37 | " 38 | (search-forward ":bar") 39 | (call-interactively 'end-of-defun) 40 | (should (= (point) (point-max))))) 41 | 42 | (ert-deftest end-of-defun-oneline () 43 | :tags '(moving) 44 | (elixir-test-with-temp-buffer 45 | "def foo do: :bar" 46 | (search-forward ":bar") 47 | (call-interactively 'end-of-defun) 48 | (should (= (point) (line-end-position))))) 49 | 50 | (ert-deftest end-of-defun-nested () 51 | :tags '(moving) 52 | (elixir-test-with-temp-buffer 53 | " 54 | defmodule Foo do 55 | def bar do 56 | :baz 57 | end 58 | end 59 | " 60 | (forward-line 1) 61 | (call-interactively 'end-of-defun) 62 | (should (= (point) (point-max))) 63 | 64 | (goto-char (point-min)) 65 | (search-forward ":baz") 66 | (call-interactively 'end-of-defun) 67 | (should (and (= (line-number-at-pos) 6) (bolp))))) 68 | 69 | (ert-deftest end-of-mark-defun () 70 | :tags '(moving) 71 | (elixir-test-with-temp-buffer 72 | " 73 | defmodule Foo do 74 | def bar do 75 | :baz 76 | end 77 | end 78 | " 79 | (goto-char (point-min)) 80 | (search-forward ":baz") 81 | (call-interactively 'mark-defun) 82 | (should (= (count-lines (region-beginning) (region-end)) 3)))) 83 | 84 | (provide 'elixir-mode-moving-test) 85 | 86 | ;;; elixir-mode-helper-test.el ends here 87 | -------------------------------------------------------------------------------- /tests/test-helper.el: -------------------------------------------------------------------------------- 1 | ;;; test-helper.el --- Test helper 2 | 3 | ;;; Commentary: 4 | ;; 5 | 6 | ;;; Code: 7 | 8 | (require 'ert-x) ; `ert-with-test-buffer' 9 | (require 'cl-lib) ; `cl-defmacro' 10 | 11 | (message "Running tests on Emacs %s" emacs-version) 12 | 13 | ;; The test fixtures assume an indentation width of 2, so we need to set that 14 | ;; up for the tests. 15 | (setq-default default-tab-width 2 16 | indent-tabs-mode nil) 17 | 18 | ;; Load the elixir-mode under test 19 | (require 'elixir-mode) 20 | 21 | ;; Load elixir-format under test 22 | (require 'elixir-format) 23 | 24 | ;; Helpers 25 | 26 | (cl-defmacro elixir-deftest (name args &body body) 27 | (declare (indent 2)) 28 | `(ert-deftest ,(intern (format "elixir-ert-%s" name)) () 29 | "" 30 | ,@args 31 | (let ((elixir-smie-verbose-p t)) 32 | ,@body))) 33 | 34 | (cl-defmacro elixir-ert-with-test-buffer ((&rest args) initial-contents &body body) 35 | (declare (indent 2)) 36 | `(ert-with-test-buffer (,@args) 37 | (elixir-mode) 38 | (insert ,initial-contents) 39 | ,@body)) 40 | 41 | (defmacro elixir-test-with-temp-buffer (content &rest body) 42 | "Evaluate BODY in a temporary buffer with CONTENT." 43 | (declare (debug t) 44 | (indent 1)) 45 | `(with-temp-buffer 46 | (insert ,content) 47 | (elixir-mode) 48 | (font-lock-fontify-buffer) 49 | (goto-char (point-min)) 50 | ,@body)) 51 | 52 | (cl-defmacro elixir-def-indentation-test (name args initial-contents expected-output) 53 | (declare (indent 2)) 54 | `(elixir-deftest ,name ,args 55 | (elixir-ert-with-test-buffer (:name ,(format "(Expected)" name)) 56 | ,initial-contents 57 | (let ((indented (ert-buffer-string-reindented))) 58 | (delete-region (point-min) (point-max)) 59 | (insert ,expected-output) 60 | (ert-with-test-buffer (:name ,(format "(Actual)" name)) 61 | (elixir-mode) 62 | (insert indented) 63 | (should (equal indented ,expected-output))))))) 64 | 65 | (setq elixir-format-elixir-path (executable-find "elixir")) 66 | (setq elixir-format-mix-path (executable-find "mix")) 67 | 68 | (defconst elixir-format-test-example "defmodule Foo do 69 | use GenServer.Behaviour 70 | def foobar do 71 | if true, do: IO.puts \"yay\" 72 | end 73 | end") 74 | 75 | (defconst elixir-format-wrong-test-example "defmodule Foo do 76 | use GenServer.Behaviour 77 | def foobar do 78 | if true, do: IO.puts \"yay\" 79 | end") 80 | 81 | (setq elixir-version (let ((str (shell-command-to-string (concat elixir-format-elixir-path " --version")))) 82 | (car (when (string-match "\s\\(.+[.].+[.].+\\)[\s\n]" str) 83 | (list (match-string 1 str)))))) 84 | 85 | (defconst elixir-formatter-supported 86 | (>= (string-to-number elixir-version) (string-to-number "1.6")) 87 | ) 88 | 89 | (defconst elixir-format-formatted-test-example 90 | "defmodule Foo do 91 | use GenServer.Behaviour 92 | 93 | def foobar do 94 | if true, do: IO.puts(\"yay\") 95 | end 96 | end 97 | ") 98 | 99 | (provide 'test-helper) 100 | ;;; test-helper.el ends here 101 | --------------------------------------------------------------------------------