1Code.require_file("test_helper.exs", __DIR__) 2 3defmodule CodeFragmentTest do 4 use ExUnit.Case, async: true 5 6 doctest Code.Fragment 7 alias Code.Fragment, as: CF 8 9 describe "cursor_context/2" do 10 test "expressions" do 11 assert CF.cursor_context([]) == :expr 12 assert CF.cursor_context(",") == :expr 13 assert CF.cursor_context("[") == :expr 14 assert CF.cursor_context("<<") == :expr 15 assert CF.cursor_context("=>") == :expr 16 assert CF.cursor_context("->") == :expr 17 assert CF.cursor_context("foo(<<") == :expr 18 assert CF.cursor_context("hello: ") == :expr 19 assert CF.cursor_context("\n") == :expr 20 assert CF.cursor_context('\n') == :expr 21 assert CF.cursor_context("\n\n") == :expr 22 assert CF.cursor_context('\n\n') == :expr 23 end 24 25 test "local_or_var" do 26 assert CF.cursor_context("hello_wo") == {:local_or_var, 'hello_wo'} 27 assert CF.cursor_context("hello_world?") == {:local_or_var, 'hello_world?'} 28 assert CF.cursor_context("hello_world!") == {:local_or_var, 'hello_world!'} 29 assert CF.cursor_context("hello/wor") == {:local_or_var, 'wor'} 30 assert CF.cursor_context("hello..wor") == {:local_or_var, 'wor'} 31 assert CF.cursor_context("hello::wor") == {:local_or_var, 'wor'} 32 assert CF.cursor_context("[hello_wo") == {:local_or_var, 'hello_wo'} 33 assert CF.cursor_context("'hello_wo") == {:local_or_var, 'hello_wo'} 34 assert CF.cursor_context("hellò_wó") == {:local_or_var, 'hellò_wó'} 35 assert CF.cursor_context("hello? world") == {:local_or_var, 'world'} 36 assert CF.cursor_context("hello! world") == {:local_or_var, 'world'} 37 assert CF.cursor_context("hello: world") == {:local_or_var, 'world'} 38 end 39 40 test "dot" do 41 assert CF.cursor_context("hello.") == {:dot, {:var, 'hello'}, ''} 42 assert CF.cursor_context(":hello.") == {:dot, {:unquoted_atom, 'hello'}, ''} 43 assert CF.cursor_context("nested.map.") == {:dot, {:dot, {:var, 'nested'}, 'map'}, ''} 44 45 assert CF.cursor_context("Hello.") == {:dot, {:alias, 'Hello'}, ''} 46 assert CF.cursor_context("Hello.World.") == {:dot, {:alias, 'Hello.World'}, ''} 47 assert CF.cursor_context("Hello.wor") == {:dot, {:alias, 'Hello'}, 'wor'} 48 assert CF.cursor_context("hello.wor") == {:dot, {:var, 'hello'}, 'wor'} 49 assert CF.cursor_context("Hello.++") == {:dot, {:alias, 'Hello'}, '++'} 50 assert CF.cursor_context(":hello.wor") == {:dot, {:unquoted_atom, 'hello'}, 'wor'} 51 assert CF.cursor_context(":hell@o.wor") == {:dot, {:unquoted_atom, 'hell@o'}, 'wor'} 52 assert CF.cursor_context(":he@ll@o.wor") == {:dot, {:unquoted_atom, 'he@ll@o'}, 'wor'} 53 assert CF.cursor_context(":hell@@o.wor") == {:dot, {:unquoted_atom, 'hell@@o'}, 'wor'} 54 assert CF.cursor_context("@hello.wor") == {:dot, {:module_attribute, 'hello'}, 'wor'} 55 56 assert CF.cursor_context("nested.map.wor") == 57 {:dot, {:dot, {:var, 'nested'}, 'map'}, 'wor'} 58 end 59 60 test "local_arity" do 61 assert CF.cursor_context("hello/") == {:local_arity, 'hello'} 62 end 63 64 test "local_call" do 65 assert CF.cursor_context("hello\s") == {:local_call, 'hello'} 66 assert CF.cursor_context("hello\t") == {:local_call, 'hello'} 67 assert CF.cursor_context("hello(") == {:local_call, 'hello'} 68 assert CF.cursor_context("hello(\s") == {:local_call, 'hello'} 69 assert CF.cursor_context("hello(\t") == {:local_call, 'hello'} 70 end 71 72 test "dot_arity" do 73 assert CF.cursor_context("Foo.hello/") == {:dot_arity, {:alias, 'Foo'}, 'hello'} 74 assert CF.cursor_context("Foo.+/") == {:dot_arity, {:alias, 'Foo'}, '+'} 75 assert CF.cursor_context("Foo . hello /") == {:dot_arity, {:alias, 'Foo'}, 'hello'} 76 assert CF.cursor_context("Foo . + /") == {:dot_arity, {:alias, 'Foo'}, '+'} 77 assert CF.cursor_context("foo.hello/") == {:dot_arity, {:var, 'foo'}, 'hello'} 78 assert CF.cursor_context(":foo.hello/") == {:dot_arity, {:unquoted_atom, 'foo'}, 'hello'} 79 assert CF.cursor_context("@f.hello/") == {:dot_arity, {:module_attribute, 'f'}, 'hello'} 80 end 81 82 test "dot_call" do 83 assert CF.cursor_context("Foo.hello\s") == {:dot_call, {:alias, 'Foo'}, 'hello'} 84 assert CF.cursor_context("Foo.hello\t") == {:dot_call, {:alias, 'Foo'}, 'hello'} 85 assert CF.cursor_context("Foo.hello(") == {:dot_call, {:alias, 'Foo'}, 'hello'} 86 assert CF.cursor_context("Foo.hello(\s") == {:dot_call, {:alias, 'Foo'}, 'hello'} 87 assert CF.cursor_context("Foo.hello(\t") == {:dot_call, {:alias, 'Foo'}, 'hello'} 88 assert CF.cursor_context("Foo . hello (") == {:dot_call, {:alias, 'Foo'}, 'hello'} 89 assert CF.cursor_context("Foo . hello (\s") == {:dot_call, {:alias, 'Foo'}, 'hello'} 90 assert CF.cursor_context("Foo . hello (\t") == {:dot_call, {:alias, 'Foo'}, 'hello'} 91 92 assert CF.cursor_context(":foo.hello\s") == {:dot_call, {:unquoted_atom, 'foo'}, 'hello'} 93 assert CF.cursor_context(":foo.hello\t") == {:dot_call, {:unquoted_atom, 'foo'}, 'hello'} 94 assert CF.cursor_context(":foo.hello(") == {:dot_call, {:unquoted_atom, 'foo'}, 'hello'} 95 assert CF.cursor_context(":foo.hello(\s") == {:dot_call, {:unquoted_atom, 'foo'}, 'hello'} 96 assert CF.cursor_context(":foo.hello(\t") == {:dot_call, {:unquoted_atom, 'foo'}, 'hello'} 97 assert CF.cursor_context(":foo.hello\s") == {:dot_call, {:unquoted_atom, 'foo'}, 'hello'} 98 99 assert CF.cursor_context("foo.hello\s") == {:dot_call, {:var, 'foo'}, 'hello'} 100 assert CF.cursor_context("foo.hello\t") == {:dot_call, {:var, 'foo'}, 'hello'} 101 assert CF.cursor_context("foo.hello(") == {:dot_call, {:var, 'foo'}, 'hello'} 102 assert CF.cursor_context("foo.hello(\s") == {:dot_call, {:var, 'foo'}, 'hello'} 103 assert CF.cursor_context("foo.hello(\t") == {:dot_call, {:var, 'foo'}, 'hello'} 104 105 assert CF.cursor_context("@f.hello\s") == {:dot_call, {:module_attribute, 'f'}, 'hello'} 106 assert CF.cursor_context("@f.hello\t") == {:dot_call, {:module_attribute, 'f'}, 'hello'} 107 assert CF.cursor_context("@f.hello(") == {:dot_call, {:module_attribute, 'f'}, 'hello'} 108 assert CF.cursor_context("@f.hello(\s") == {:dot_call, {:module_attribute, 'f'}, 'hello'} 109 assert CF.cursor_context("@f.hello(\t") == {:dot_call, {:module_attribute, 'f'}, 'hello'} 110 111 assert CF.cursor_context("Foo.+\s") == {:dot_call, {:alias, 'Foo'}, '+'} 112 assert CF.cursor_context("Foo.+\t") == {:dot_call, {:alias, 'Foo'}, '+'} 113 assert CF.cursor_context("Foo.+(") == {:dot_call, {:alias, 'Foo'}, '+'} 114 assert CF.cursor_context("Foo.+(\s") == {:dot_call, {:alias, 'Foo'}, '+'} 115 assert CF.cursor_context("Foo.+(\t") == {:dot_call, {:alias, 'Foo'}, '+'} 116 assert CF.cursor_context("Foo . + (") == {:dot_call, {:alias, 'Foo'}, '+'} 117 assert CF.cursor_context("Foo . + (\s") == {:dot_call, {:alias, 'Foo'}, '+'} 118 assert CF.cursor_context("Foo . + (\t") == {:dot_call, {:alias, 'Foo'}, '+'} 119 end 120 121 test "alias" do 122 assert CF.cursor_context("HelloWor") == {:alias, 'HelloWor'} 123 assert CF.cursor_context("Hello.Wor") == {:alias, 'Hello.Wor'} 124 assert CF.cursor_context("Hello . Wor") == {:alias, 'Hello.Wor'} 125 assert CF.cursor_context("Hello::Wor") == {:alias, 'Wor'} 126 assert CF.cursor_context("Hello..Wor") == {:alias, 'Wor'} 127 end 128 129 test "structs" do 130 assert CF.cursor_context("%") == {:struct, ''} 131 assert CF.cursor_context(":%") == {:unquoted_atom, '%'} 132 assert CF.cursor_context("::%") == {:struct, ''} 133 134 assert CF.cursor_context("%HelloWor") == {:struct, 'HelloWor'} 135 assert CF.cursor_context("%Hello.") == {:struct, 'Hello.'} 136 assert CF.cursor_context("%Hello.Wor") == {:struct, 'Hello.Wor'} 137 assert CF.cursor_context("% Hello . Wor") == {:struct, 'Hello.Wor'} 138 end 139 140 test "unquoted atom" do 141 assert CF.cursor_context(":") == {:unquoted_atom, ''} 142 assert CF.cursor_context(":HelloWor") == {:unquoted_atom, 'HelloWor'} 143 assert CF.cursor_context(":HelloWór") == {:unquoted_atom, 'HelloWór'} 144 assert CF.cursor_context(":hello_wor") == {:unquoted_atom, 'hello_wor'} 145 assert CF.cursor_context(":Óla_mundo") == {:unquoted_atom, 'Óla_mundo'} 146 assert CF.cursor_context(":Ol@_mundo") == {:unquoted_atom, 'Ol@_mundo'} 147 assert CF.cursor_context(":Ol@") == {:unquoted_atom, 'Ol@'} 148 assert CF.cursor_context("foo:hello_wor") == {:unquoted_atom, 'hello_wor'} 149 150 # Operators from atoms 151 assert CF.cursor_context(":+") == {:unquoted_atom, '+'} 152 assert CF.cursor_context(":or") == {:unquoted_atom, 'or'} 153 assert CF.cursor_context(":<") == {:unquoted_atom, '<'} 154 assert CF.cursor_context(":.") == {:unquoted_atom, '.'} 155 assert CF.cursor_context(":..") == {:unquoted_atom, '..'} 156 assert CF.cursor_context(":->") == {:unquoted_atom, '->'} 157 assert CF.cursor_context(":%") == {:unquoted_atom, '%'} 158 end 159 160 test "operators" do 161 assert CF.cursor_context("+") == {:operator, '+'} 162 assert CF.cursor_context("++") == {:operator, '++'} 163 assert CF.cursor_context("!") == {:operator, '!'} 164 assert CF.cursor_context("<") == {:operator, '<'} 165 assert CF.cursor_context("<<<") == {:operator, '<<<'} 166 assert CF.cursor_context("..") == {:operator, '..'} 167 assert CF.cursor_context("<~") == {:operator, '<~'} 168 assert CF.cursor_context("=~") == {:operator, '=~'} 169 assert CF.cursor_context("<~>") == {:operator, '<~>'} 170 assert CF.cursor_context("::") == {:operator, '::'} 171 172 assert CF.cursor_context("+ ") == {:operator_call, '+'} 173 assert CF.cursor_context("++ ") == {:operator_call, '++'} 174 assert CF.cursor_context("! ") == {:operator_call, '!'} 175 assert CF.cursor_context("< ") == {:operator_call, '<'} 176 assert CF.cursor_context("<<< ") == {:operator_call, '<<<'} 177 assert CF.cursor_context(".. ") == {:operator_call, '..'} 178 assert CF.cursor_context("<~ ") == {:operator_call, '<~'} 179 assert CF.cursor_context("=~ ") == {:operator_call, '=~'} 180 assert CF.cursor_context("<~> ") == {:operator_call, '<~>'} 181 assert CF.cursor_context(":: ") == {:operator_call, '::'} 182 183 assert CF.cursor_context("+/") == {:operator_arity, '+'} 184 assert CF.cursor_context("++/") == {:operator_arity, '++'} 185 assert CF.cursor_context("!/") == {:operator_arity, '!'} 186 assert CF.cursor_context("</") == {:operator_arity, '<'} 187 assert CF.cursor_context("<<</") == {:operator_arity, '<<<'} 188 assert CF.cursor_context("../") == {:operator_arity, '..'} 189 assert CF.cursor_context("<~/") == {:operator_arity, '<~'} 190 assert CF.cursor_context("=~/") == {:operator_arity, '=~'} 191 assert CF.cursor_context("<~>/") == {:operator_arity, '<~>'} 192 assert CF.cursor_context("::/") == {:operator_arity, '::'} 193 194 # Unknown operators altogether 195 assert CF.cursor_context("***") == :none 196 197 # Textual operators are shown as local_or_var UNLESS there is space 198 assert CF.cursor_context("when") == {:local_or_var, 'when'} 199 assert CF.cursor_context("when ") == {:operator_call, 'when'} 200 assert CF.cursor_context("when.") == :none 201 202 assert CF.cursor_context("not") == {:local_or_var, 'not'} 203 assert CF.cursor_context("not ") == {:operator_call, 'not'} 204 assert CF.cursor_context("not.") == :none 205 end 206 207 test "incomplete operators" do 208 assert CF.cursor_context("~~") == {:operator, '~~'} 209 assert CF.cursor_context("~~ ") == :none 210 assert CF.cursor_context("^^") == {:operator, '^^'} 211 assert CF.cursor_context("^^ ") == :none 212 213 assert CF.cursor_context("Foo.~") == {:dot, {:alias, 'Foo'}, '~'} 214 assert CF.cursor_context("Foo . ~") == {:dot, {:alias, 'Foo'}, '~'} 215 assert CF.cursor_context("Foo.~~") == {:dot, {:alias, 'Foo'}, '~~'} 216 assert CF.cursor_context("Foo . ~~") == {:dot, {:alias, 'Foo'}, '~~'} 217 assert CF.cursor_context("Foo.~ ") == :none 218 assert CF.cursor_context("Foo.~~ ") == :none 219 assert CF.cursor_context("Foo.^^") == {:dot, {:alias, 'Foo'}, '^^'} 220 assert CF.cursor_context("Foo . ^^") == {:dot, {:alias, 'Foo'}, '^^'} 221 assert CF.cursor_context("Foo.^^ ") == :none 222 end 223 224 test "sigil" do 225 assert CF.cursor_context("~") == {:sigil, ''} 226 assert CF.cursor_context("~ ") == :none 227 228 assert CF.cursor_context("~r") == {:sigil, 'r'} 229 assert CF.cursor_context("~r/") == :none 230 assert CF.cursor_context("~r<") == :none 231 232 assert CF.cursor_context("~R") == {:sigil, 'R'} 233 assert CF.cursor_context("~R/") == :none 234 assert CF.cursor_context("~R<") == :none 235 end 236 237 test "module attribute" do 238 assert CF.cursor_context("@") == {:module_attribute, ''} 239 assert CF.cursor_context("@hello_wo") == {:module_attribute, 'hello_wo'} 240 end 241 242 test "none" do 243 # Punctuation 244 assert CF.cursor_context(")") == :none 245 assert CF.cursor_context("}") == :none 246 assert CF.cursor_context(">>") == :none 247 assert CF.cursor_context("'") == :none 248 assert CF.cursor_context("\"") == :none 249 250 # Numbers 251 assert CF.cursor_context("123") == :none 252 assert CF.cursor_context("123?") == :none 253 assert CF.cursor_context("123!") == :none 254 assert CF.cursor_context("123var?") == :none 255 assert CF.cursor_context("0x") == :none 256 257 # Codepoints 258 assert CF.cursor_context("?") == :none 259 assert CF.cursor_context("?a") == :none 260 assert CF.cursor_context("?foo") == :none 261 262 # Dots 263 assert CF.cursor_context(".") == :none 264 assert CF.cursor_context("Mundo.Óla") == :none 265 assert CF.cursor_context(":hello.World") == :none 266 267 # Aliases 268 assert CF.cursor_context("Hello::Wór") == :none 269 assert CF.cursor_context("ÓlaMundo") == :none 270 assert CF.cursor_context("HelloWór") == :none 271 assert CF.cursor_context("@Hello") == :none 272 assert CF.cursor_context("Hello(") == :none 273 assert CF.cursor_context("Hello ") == :none 274 assert CF.cursor_context("hello.World") == :none 275 276 # Identifier 277 assert CF.cursor_context("foo@bar") == :none 278 assert CF.cursor_context("@foo@bar") == :none 279 end 280 281 test "newlines" do 282 assert CF.cursor_context("this+does-not*matter\nHello.") == {:dot, {:alias, 'Hello'}, ''} 283 assert CF.cursor_context('this+does-not*matter\nHello.') == {:dot, {:alias, 'Hello'}, ''} 284 end 285 end 286 287 describe "surround_context/2" do 288 test "newlines" do 289 for i <- 1..8 do 290 assert CF.surround_context("\n\nhello_wo\n", {3, i}) == %{ 291 context: {:local_or_var, 'hello_wo'}, 292 begin: {3, 1}, 293 end: {3, 9} 294 } 295 end 296 end 297 298 test "column out of range" do 299 assert CF.surround_context("hello", {1, 20}) == :none 300 end 301 302 test "local_or_var" do 303 for i <- 1..8 do 304 assert CF.surround_context("hello_wo", {1, i}) == %{ 305 context: {:local_or_var, 'hello_wo'}, 306 begin: {1, 1}, 307 end: {1, 9} 308 } 309 end 310 311 assert CF.surround_context("hello_wo", {1, 9}) == :none 312 313 for i <- 2..9 do 314 assert CF.surround_context(" hello_wo", {1, i}) == %{ 315 context: {:local_or_var, 'hello_wo'}, 316 begin: {1, 2}, 317 end: {1, 10} 318 } 319 end 320 321 assert CF.surround_context(" hello_wo", {1, 10}) == :none 322 323 for i <- 1..6 do 324 assert CF.surround_context("hello!", {1, i}) == %{ 325 context: {:local_or_var, 'hello!'}, 326 begin: {1, 1}, 327 end: {1, 7} 328 } 329 end 330 331 assert CF.surround_context("hello!", {1, 7}) == :none 332 333 for i <- 1..5 do 334 assert CF.surround_context("안녕_세상", {1, i}) == %{ 335 context: {:local_or_var, '안녕_세상'}, 336 begin: {1, 1}, 337 end: {1, 6} 338 } 339 end 340 341 assert CF.surround_context("안녕_세상", {1, 6}) == :none 342 343 # Keywords are not local or var 344 for keyword <- ~w(do end after catch else rescue) do 345 assert CF.surround_context(keyword, {1, 1}) == :none 346 end 347 end 348 349 test "local call" do 350 for i <- 1..8 do 351 assert CF.surround_context("hello_wo(", {1, i}) == %{ 352 context: {:local_call, 'hello_wo'}, 353 begin: {1, 1}, 354 end: {1, 9} 355 } 356 end 357 358 assert CF.surround_context("hello_wo(", {1, 9}) == :none 359 360 for i <- 1..8 do 361 assert CF.surround_context("hello_wo (", {1, i}) == %{ 362 context: {:local_call, 'hello_wo'}, 363 begin: {1, 1}, 364 end: {1, 9} 365 } 366 end 367 368 assert CF.surround_context("hello_wo (", {1, 9}) == :none 369 370 for i <- 1..6 do 371 assert CF.surround_context("hello!(", {1, i}) == %{ 372 context: {:local_call, 'hello!'}, 373 begin: {1, 1}, 374 end: {1, 7} 375 } 376 end 377 378 assert CF.surround_context("hello!(", {1, 7}) == :none 379 380 for i <- 1..5 do 381 assert CF.surround_context("안녕_세상(", {1, i}) == %{ 382 context: {:local_call, '안녕_세상'}, 383 begin: {1, 1}, 384 end: {1, 6} 385 } 386 end 387 388 assert CF.surround_context("안녕_세상(", {1, 6}) == :none 389 end 390 391 test "local arity" do 392 for i <- 1..8 do 393 assert CF.surround_context("hello_wo/", {1, i}) == %{ 394 context: {:local_arity, 'hello_wo'}, 395 begin: {1, 1}, 396 end: {1, 9} 397 } 398 end 399 400 assert CF.surround_context("hello_wo/", {1, 9}) == :none 401 402 for i <- 1..8 do 403 assert CF.surround_context("hello_wo /", {1, i}) == %{ 404 context: {:local_arity, 'hello_wo'}, 405 begin: {1, 1}, 406 end: {1, 9} 407 } 408 end 409 410 assert CF.surround_context("hello_wo /", {1, 9}) == :none 411 412 for i <- 1..6 do 413 assert CF.surround_context("hello!/", {1, i}) == %{ 414 context: {:local_arity, 'hello!'}, 415 begin: {1, 1}, 416 end: {1, 7} 417 } 418 end 419 420 assert CF.surround_context("hello!/", {1, 7}) == :none 421 422 for i <- 1..5 do 423 assert CF.surround_context("안녕_세상/", {1, i}) == %{ 424 context: {:local_arity, '안녕_세상'}, 425 begin: {1, 1}, 426 end: {1, 6} 427 } 428 end 429 430 assert CF.surround_context("안녕_세상/", {1, 6}) == :none 431 end 432 433 test "textual operators" do 434 for op <- ~w(when not or and in), i <- 1..byte_size(op) do 435 assert CF.surround_context("#{op}", {1, i}) == %{ 436 context: {:operator, String.to_charlist(op)}, 437 begin: {1, 1}, 438 end: {1, byte_size(op) + 1} 439 } 440 end 441 end 442 443 test "dot" do 444 for i <- 1..5 do 445 assert CF.surround_context("Hello.wor", {1, i}) == %{ 446 context: {:alias, 'Hello'}, 447 begin: {1, 1}, 448 end: {1, 6} 449 } 450 end 451 452 for i <- 6..9 do 453 assert CF.surround_context("Hello.wor", {1, i}) == %{ 454 context: {:dot, {:alias, 'Hello'}, 'wor'}, 455 begin: {1, 1}, 456 end: {1, 10} 457 } 458 end 459 460 assert CF.surround_context("Hello.", {1, 6}) == :none 461 462 for i <- 1..5 do 463 assert CF.surround_context("Hello . wor", {1, i}) == %{ 464 context: {:alias, 'Hello'}, 465 begin: {1, 1}, 466 end: {1, 6} 467 } 468 end 469 470 for i <- 6..11 do 471 assert CF.surround_context("Hello . wor", {1, i}) == %{ 472 context: {:dot, {:alias, 'Hello'}, 'wor'}, 473 begin: {1, 1}, 474 end: {1, 12} 475 } 476 end 477 478 assert CF.surround_context("Hello .", {1, 6}) == :none 479 480 for i <- 1..5 do 481 assert CF.surround_context("hello.wor", {1, i}) == %{ 482 context: {:local_or_var, 'hello'}, 483 begin: {1, 1}, 484 end: {1, 6} 485 } 486 end 487 488 for i <- 6..9 do 489 assert CF.surround_context("hello.wor", {1, i}) == %{ 490 context: {:dot, {:var, 'hello'}, 'wor'}, 491 begin: {1, 1}, 492 end: {1, 10} 493 } 494 end 495 end 496 497 test "alias" do 498 for i <- 1..8 do 499 assert CF.surround_context("HelloWor", {1, i}) == %{ 500 context: {:alias, 'HelloWor'}, 501 begin: {1, 1}, 502 end: {1, 9} 503 } 504 end 505 506 assert CF.surround_context("HelloWor", {1, 9}) == :none 507 508 for i <- 2..9 do 509 assert CF.surround_context(" HelloWor", {1, i}) == %{ 510 context: {:alias, 'HelloWor'}, 511 begin: {1, 2}, 512 end: {1, 10} 513 } 514 end 515 516 assert CF.surround_context(" HelloWor", {1, 10}) == :none 517 518 for i <- 1..9 do 519 assert CF.surround_context("Hello.Wor", {1, i}) == %{ 520 context: {:alias, 'Hello.Wor'}, 521 begin: {1, 1}, 522 end: {1, 10} 523 } 524 end 525 526 assert CF.surround_context("Hello.Wor", {1, 10}) == :none 527 528 for i <- 1..11 do 529 assert CF.surround_context("Hello . Wor", {1, i}) == %{ 530 context: {:alias, 'Hello.Wor'}, 531 begin: {1, 1}, 532 end: {1, 12} 533 } 534 end 535 536 assert CF.surround_context("Hello . Wor", {1, 12}) == :none 537 538 for i <- 1..15 do 539 assert CF.surround_context("Foo . Bar . Baz", {1, i}) == %{ 540 context: {:alias, 'Foo.Bar.Baz'}, 541 begin: {1, 1}, 542 end: {1, 16} 543 } 544 end 545 end 546 547 test "struct" do 548 assert CF.surround_context("%", {1, 1}) == :none 549 assert CF.surround_context("::%", {1, 1}) == :none 550 assert CF.surround_context("::%", {1, 2}) == :none 551 assert CF.surround_context("::%Hello", {1, 1}) == :none 552 assert CF.surround_context("::%Hello", {1, 2}) == :none 553 554 assert CF.surround_context("::%Hello", {1, 3}) == %{ 555 context: {:struct, 'Hello'}, 556 begin: {1, 3}, 557 end: {1, 9} 558 } 559 560 assert CF.surround_context("::% Hello", {1, 3}) == %{ 561 context: {:struct, 'Hello'}, 562 begin: {1, 3}, 563 end: {1, 10} 564 } 565 566 assert CF.surround_context("::% Hello", {1, 4}) == %{ 567 context: {:struct, 'Hello'}, 568 begin: {1, 3}, 569 end: {1, 10} 570 } 571 572 # Alias 573 assert CF.surround_context("%HelloWor", {1, 1}) == %{ 574 context: {:struct, 'HelloWor'}, 575 begin: {1, 1}, 576 end: {1, 10} 577 } 578 579 for i <- 2..9 do 580 assert CF.surround_context("%HelloWor", {1, i}) == %{ 581 context: {:struct, 'HelloWor'}, 582 begin: {1, 1}, 583 end: {1, 10} 584 } 585 end 586 587 assert CF.surround_context("%HelloWor", {1, 10}) == :none 588 589 # With dot 590 assert CF.surround_context("%Hello.Wor", {1, 1}) == %{ 591 context: {:struct, 'Hello.Wor'}, 592 begin: {1, 1}, 593 end: {1, 11} 594 } 595 596 for i <- 2..10 do 597 assert CF.surround_context("%Hello.Wor", {1, i}) == %{ 598 context: {:struct, 'Hello.Wor'}, 599 begin: {1, 1}, 600 end: {1, 11} 601 } 602 end 603 604 assert CF.surround_context("%Hello.Wor", {1, 11}) == :none 605 606 # With spaces 607 assert CF.surround_context("% Hello . Wor", {1, 1}) == %{ 608 context: {:struct, 'Hello.Wor'}, 609 begin: {1, 1}, 610 end: {1, 14} 611 } 612 613 for i <- 2..13 do 614 assert CF.surround_context("% Hello . Wor", {1, i}) == %{ 615 context: {:struct, 'Hello.Wor'}, 616 begin: {1, 1}, 617 end: {1, 14} 618 } 619 end 620 621 assert CF.surround_context("% Hello . Wor", {1, 14}) == :none 622 end 623 624 test "module attributes" do 625 for i <- 1..10 do 626 assert CF.surround_context("@hello_wor", {1, i}) == %{ 627 context: {:module_attribute, 'hello_wor'}, 628 begin: {1, 1}, 629 end: {1, 11} 630 } 631 end 632 633 assert CF.surround_context("@Hello", {1, 1}) == :none 634 end 635 636 test "operators" do 637 for i <- 2..4 do 638 assert CF.surround_context("1<<<3", {1, i}) == %{ 639 context: {:operator, '<<<'}, 640 begin: {1, 2}, 641 end: {1, 5} 642 } 643 end 644 645 for i <- 3..5 do 646 assert CF.surround_context("1 <<< 3", {1, i}) == %{ 647 context: {:operator, '<<<'}, 648 begin: {1, 3}, 649 end: {1, 6} 650 } 651 end 652 653 for i <- 2..3 do 654 assert CF.surround_context("1::3", {1, i}) == %{ 655 context: {:operator, '::'}, 656 begin: {1, 2}, 657 end: {1, 4} 658 } 659 end 660 661 for i <- 3..4 do 662 assert CF.surround_context("1 :: 3", {1, i}) == %{ 663 context: {:operator, '::'}, 664 begin: {1, 3}, 665 end: {1, 5} 666 } 667 end 668 669 for i <- 2..3 do 670 assert CF.surround_context("x..y", {1, i}) == %{ 671 context: {:operator, '..'}, 672 begin: {1, 2}, 673 end: {1, 4} 674 } 675 end 676 677 for i <- 3..4 do 678 assert CF.surround_context("x .. y", {1, i}) == %{ 679 context: {:operator, '..'}, 680 begin: {1, 3}, 681 end: {1, 5} 682 } 683 end 684 685 assert CF.surround_context("@", {1, 1}) == %{ 686 context: {:operator, '@'}, 687 begin: {1, 1}, 688 end: {1, 2} 689 } 690 691 assert CF.surround_context("!", {1, 1}) == %{ 692 context: {:operator, '!'}, 693 begin: {1, 1}, 694 end: {1, 2} 695 } 696 697 assert CF.surround_context("!foo", {1, 1}) == %{ 698 context: {:operator, '!'}, 699 begin: {1, 1}, 700 end: {1, 2} 701 } 702 703 assert CF.surround_context("foo !bar", {1, 5}) == %{ 704 context: {:operator, '!'}, 705 begin: {1, 5}, 706 end: {1, 6} 707 } 708 end 709 710 test "sigil" do 711 assert CF.surround_context("~", {1, 1}) == :none 712 assert CF.surround_context("~~r", {1, 1}) == :none 713 assert CF.surround_context("~~r", {1, 2}) == :none 714 715 assert CF.surround_context("~~~", {1, 1}) == %{ 716 begin: {1, 1}, 717 context: {:operator, '~~~'}, 718 end: {1, 4} 719 } 720 721 assert CF.surround_context("~r/foo/", {1, 1}) == %{ 722 begin: {1, 1}, 723 context: {:sigil, 'r'}, 724 end: {1, 3} 725 } 726 727 assert CF.surround_context("~r/foo/", {1, 2}) == %{ 728 begin: {1, 1}, 729 context: {:sigil, 'r'}, 730 end: {1, 3} 731 } 732 733 assert CF.surround_context("~r/foo/", {1, 3}) == :none 734 735 assert CF.surround_context("~R<foo>", {1, 1}) == %{ 736 begin: {1, 1}, 737 context: {:sigil, 'R'}, 738 end: {1, 3} 739 } 740 741 assert CF.surround_context("~R<foo>", {1, 2}) == %{ 742 begin: {1, 1}, 743 context: {:sigil, 'R'}, 744 end: {1, 3} 745 } 746 747 assert CF.surround_context("~R<foo>", {1, 3}) == :none 748 end 749 750 test "dot operator" do 751 for i <- 4..7 do 752 assert CF.surround_context("Foo.<<<", {1, i}) == %{ 753 context: {:dot, {:alias, 'Foo'}, '<<<'}, 754 begin: {1, 1}, 755 end: {1, 8} 756 } 757 end 758 759 for i <- 4..9 do 760 assert CF.surround_context("Foo . <<<", {1, i}) == %{ 761 context: {:dot, {:alias, 'Foo'}, '<<<'}, 762 begin: {1, 1}, 763 end: {1, 10} 764 } 765 end 766 767 for i <- 4..6 do 768 assert CF.surround_context("Foo.::", {1, i}) == %{ 769 context: {:dot, {:alias, 'Foo'}, '::'}, 770 begin: {1, 1}, 771 end: {1, 7} 772 } 773 end 774 775 for i <- 4..8 do 776 assert CF.surround_context("Foo . ::", {1, i}) == %{ 777 context: {:dot, {:alias, 'Foo'}, '::'}, 778 begin: {1, 1}, 779 end: {1, 9} 780 } 781 end 782 end 783 784 test "unquoted atom" do 785 for i <- 1..10 do 786 assert CF.surround_context(":hello_wor", {1, i}) == %{ 787 context: {:unquoted_atom, 'hello_wor'}, 788 begin: {1, 1}, 789 end: {1, 11} 790 } 791 end 792 793 for i <- 1..10 do 794 assert CF.surround_context(":Hello@Wor", {1, i}) == %{ 795 context: {:unquoted_atom, 'Hello@Wor'}, 796 begin: {1, 1}, 797 end: {1, 11} 798 } 799 end 800 801 assert CF.surround_context(":", {1, 1}) == :none 802 end 803 end 804 805 describe "container_cursor_to_quoted/2" do 806 def s2q(arg, opts \\ []), do: Code.string_to_quoted(arg, opts) 807 def cc2q(arg, opts \\ []), do: CF.container_cursor_to_quoted(arg, opts) 808 809 test "completes terminators" do 810 assert cc2q("(") == s2q("(__cursor__())") 811 assert cc2q("[") == s2q("[__cursor__()]") 812 assert cc2q("{") == s2q("{__cursor__()}") 813 assert cc2q("<<") == s2q("<<__cursor__()>>") 814 assert cc2q("foo do") == s2q("foo do __cursor__() end") 815 assert cc2q("foo do true else") == s2q("foo do true else __cursor__() end") 816 end 817 818 test "inside interpolation" do 819 assert cc2q(~S|"foo #{(|) == s2q(~S|"foo #{(__cursor__())}"|) 820 assert cc2q(~S|"foo #{"bar #{{|) == s2q(~S|"foo #{"bar #{{__cursor__()}}"}"|) 821 end 822 823 test "do-end blocks" do 824 assert cc2q("foo(bar do baz") == s2q("foo(bar do __cursor__() end)") 825 assert cc2q("foo(bar do baz ") == s2q("foo(bar do baz(__cursor__()) end)") 826 assert cc2q("foo(bar do baz(") == s2q("foo(bar do baz(__cursor__()) end)") 827 assert cc2q("foo(bar do baz bat,") == s2q("foo(bar do baz(bat, __cursor__()) end)") 828 829 assert {:error, {_, "syntax error before: ", "'end'"}} = cc2q("foo(bar do baz, bat") 830 end 831 832 test "keyword lists" do 833 assert cc2q("[bar: ") == s2q("[bar: __cursor__()]") 834 assert cc2q("[bar: baz,") == s2q("[bar: baz, __cursor__()]") 835 assert cc2q("[arg, bar: baz,") == s2q("[arg, bar: baz, __cursor__()]") 836 assert cc2q("[arg: val, bar: baz,") == s2q("[arg: val, bar: baz, __cursor__()]") 837 838 assert cc2q("{arg, bar: ") == s2q("{arg, bar: __cursor__()}") 839 assert cc2q("{arg, bar: baz,") == s2q("{arg, bar: baz, __cursor__()}") 840 assert cc2q("{arg: val, bar: baz,") == s2q("{arg: val, bar: baz, __cursor__()}") 841 842 assert cc2q("foo(bar: ") == s2q("foo(bar: __cursor__())") 843 assert cc2q("foo(bar: baz,") == s2q("foo([bar: baz, __cursor__()])") 844 assert cc2q("foo(arg, bar: ") == s2q("foo(arg, bar: __cursor__())") 845 assert cc2q("foo(arg, bar: baz,") == s2q("foo(arg, [bar: baz, __cursor__()])") 846 assert cc2q("foo(arg: val, bar: ") == s2q("foo(arg: val, bar: __cursor__())") 847 assert cc2q("foo(arg: val, bar: baz,") == s2q("foo([arg: val, bar: baz, __cursor__()])") 848 849 assert cc2q("foo bar: ") == s2q("foo(bar: __cursor__())") 850 assert cc2q("foo bar: baz,") == s2q("foo([bar: baz, __cursor__()])") 851 assert cc2q("foo arg, bar: ") == s2q("foo(arg, bar: __cursor__())") 852 assert cc2q("foo arg, bar: baz,") == s2q("foo(arg, [bar: baz, __cursor__()])") 853 assert cc2q("foo arg: val, bar: ") == s2q("foo(arg: val, bar: __cursor__())") 854 assert cc2q("foo arg: val, bar: baz,") == s2q("foo([arg: val, bar: baz, __cursor__()])") 855 end 856 857 test "maps and structs" do 858 assert cc2q("%") == s2q("__cursor__()") 859 assert cc2q("%{") == s2q("%{__cursor__()}") 860 assert cc2q("%{bar:") == s2q("%{__cursor__()}") 861 assert cc2q("%{bar: ") == s2q("%{bar: __cursor__()}") 862 assert cc2q("%{bar: baz,") == s2q("%{bar: baz, __cursor__()}") 863 864 assert cc2q("%Foo") == s2q("__cursor__()") 865 assert cc2q("%Foo{") == s2q("%Foo{__cursor__()}") 866 assert cc2q("%Foo{bar: ") == s2q("%Foo{bar: __cursor__()}") 867 assert cc2q("%Foo{bar: baz,") == s2q("%Foo{bar: baz, __cursor__()}") 868 end 869 870 test "removes tokens until opening" do 871 assert cc2q("(123") == s2q("(__cursor__())") 872 assert cc2q("[foo") == s2q("[__cursor__()]") 873 assert cc2q("{'foo'") == s2q("{__cursor__()}") 874 assert cc2q("<<1+2") == s2q("<<__cursor__()>>") 875 assert cc2q("foo do :atom") == s2q("foo do __cursor__() end") 876 assert cc2q("foo(:atom") == s2q("foo(__cursor__())") 877 end 878 879 test "removes tokens until comma" do 880 assert cc2q("[bar, 123") == s2q("[bar, __cursor__()]") 881 assert cc2q("{bar, 'foo'") == s2q("{bar, __cursor__()}") 882 assert cc2q("<<bar, 1+2") == s2q("<<bar, __cursor__()>>") 883 assert cc2q("foo(bar, :atom") == s2q("foo(bar, __cursor__())") 884 assert cc2q("foo bar, :atom") == s2q("foo(bar, __cursor__())") 885 end 886 887 test "removes functions" do 888 assert cc2q("(fn") == s2q("(__cursor__())") 889 assert cc2q("(fn x") == s2q("(__cursor__())") 890 assert cc2q("(fn x ->") == s2q("(__cursor__())") 891 assert cc2q("(fn x -> x") == s2q("(__cursor__())") 892 assert cc2q("(fn x, y -> x + y") == s2q("(__cursor__())") 893 assert cc2q("(fn x, y -> x + y end") == s2q("(__cursor__())") 894 end 895 896 test "removes captures" do 897 assert cc2q("[& &1") == s2q("[__cursor__()]") 898 assert cc2q("[&(&1") == s2q("[__cursor__()]") 899 end 900 901 test "removes closed terminators" do 902 assert cc2q("foo([1, 2, 3] |>") == s2q("foo(__cursor__())") 903 assert cc2q("foo({1, 2, 3} |>") == s2q("foo(__cursor__())") 904 assert cc2q("foo((1, 2, 3) |>") == s2q("foo(__cursor__())") 905 assert cc2q("foo(<<1, 2, 3>> |>") == s2q("foo(__cursor__())") 906 assert cc2q("foo(bar do :done end |>") == s2q("foo(__cursor__())") 907 end 908 909 test "incomplete expressions" do 910 assert cc2q("foo(123, :") == s2q("foo(123, __cursor__())") 911 assert cc2q("foo(123, %") == s2q("foo(123, __cursor__())") 912 assert cc2q("foo(123, 0x") == s2q("foo(123, __cursor__())") 913 assert cc2q("foo(123, ~") == s2q("foo(123, __cursor__())") 914 assert cc2q("foo(123, ~r") == s2q("foo(123, __cursor__())") 915 assert cc2q("foo(123, ~r/") == s2q("foo(123, __cursor__())") 916 end 917 918 test "options" do 919 opts = [columns: true] 920 assert cc2q("foo(", opts) == s2q("foo(__cursor__())", opts) 921 assert cc2q("foo(123,", opts) == s2q("foo(123,__cursor__())", opts) 922 923 opts = [token_metadata: true] 924 assert cc2q("foo(", opts) == s2q("foo(__cursor__())", opts) 925 assert cc2q("foo(123,", opts) == s2q("foo(123,__cursor__())", opts) 926 end 927 end 928end 929