@@ -17,13 +17,13 @@ defmodule Code.Fragment do
1717
1818 This function receives a string with an Elixir code fragment,
1919 representing a cursor position, and based on the string, it
20- provides contextual information about said position. The
21- return of this function can then be used to provide tips,
20+ provides contextual information about the latest token.
21+ The return of this function can then be used to provide tips,
2222 suggestions, and autocompletion functionality.
2323
24- This function provides a best-effort detection and may not be
25- accurate under all circumstances. See the "Limitations"
26- section below.
24+ This function performs its analyses on tokens. This means
25+ it does not understand how constructs are nested within each
26+ other. See the "Limitations" section below.
2727
2828 Consider adding a catch-all clause when handling the return
2929 type of this function as new cursor information may be added
@@ -110,11 +110,28 @@ defmodule Code.Fragment do
110110 * `{:unquoted_atom, charlist}` - the context is an unquoted atom. This
111111 can be any atom or an atom representing a module
112112
113+ We recommend looking at the test suite of this function for a complete list
114+ of examples and their return values.
115+
113116 ## Limitations
114117
115- The current algorithm only considers the last line of the input. This means
116- it will also show suggestions inside strings, heredocs, etc, which is
117- intentional as it helps with doctests, references, and more.
118+ The analysis is based on the current token, by analysing the last line of
119+ the input. For example, this code:
120+
121+ iex> Code.Fragment.cursor_context("%URI{")
122+ :expr
123+
124+ returns `:expr`, which suggests any variable, local function or alias
125+ could be used. However, given we are inside a struct, the best suggestion
126+ would be a struct field. In such cases, you can use
127+ `container_cursor_to_quoted`, which will return the container of the AST
128+ the cursor is currently within. You can then analyse this AST to provide
129+ completion of field names.
130+
131+ As a consequence of its token-based implementation, this function considers
132+ only the last line of the input. This means it will show suggestions inside
133+ strings, heredocs, etc, which is intentional as it helps with doctests,
134+ references, and more.
118135 """
119136 @ doc since: "1.13.0"
120137 @ spec cursor_context ( List.Chars . t ( ) , keyword ( ) ) ::
@@ -480,7 +497,8 @@ defmodule Code.Fragment do
480497 This function receives a string with an Elixir code fragment
481498 and a `position`. It returns a map containing the beginning
482499 and ending of the identifier alongside its context, or `:none`
483- if there is nothing with a known context.
500+ if there is nothing with a known context. This is useful to
501+ provide mouse-over and highlight functionality in editors.
484502
485503 The difference between `cursor_context/2` and `surround_context/3`
486504 is that the former assumes the expression in the code fragment
@@ -509,10 +527,10 @@ defmodule Code.Fragment do
509527 The returned map contains the column the expression starts and the
510528 first column after the expression ends.
511529
512- Similar to `cursor_context/2`, this function also provides a best-effort
513- detection and may not be accurate under all circumstances. See the
514- "Return values" and "Limitations" section under `cursor_context/2` for
515- more information.
530+ Similar to `cursor_context/2`, this function is also token-based
531+ and may not be accurate under all circumstances. See the
532+ "Return values" and "Limitations" section under `cursor_context/2`
533+ for more information.
516534
517535 ## Examples
518536
@@ -521,8 +539,8 @@ defmodule Code.Fragment do
521539
522540 ## Differences to `cursor_context/2`
523541
524- Because `surround_context/3` deals with complete code, it has some
525- difference to `cursor_context/2`:
542+ Because `surround_context/3` attempts to capture complex expressions,
543+ it has some differences to `cursor_context/2`:
526544
527545 * `dot_call`/`dot_arity` and `operator_call`/`operator_arity`
528546 are collapsed into `dot` and `operator` contexts respectively
@@ -542,6 +560,8 @@ defmodule Code.Fragment do
542560
543561 * This function never returns `:expr`
544562
563+ We recommend looking at the test suite of this function for a complete list
564+ of examples and their return values.
545565 """
546566 @ doc since: "1.13.0"
547567 @ spec surround_context ( List.Chars . t ( ) , position ( ) , keyword ( ) ) ::
@@ -937,17 +957,15 @@ defmodule Code.Fragment do
937957
938958 @ doc """
939959 Receives a string and returns a quoted expression
940- with a cursor at the nearest argument position .
960+ with the cursor AST position within its parent expression .
941961
942962 This function receives a string with an Elixir code fragment,
943963 representing a cursor position, and converts such string to
944- AST with the inclusion of special `__cursor__()` node based
945- on the position of the cursor with a container .
964+ AST with the inclusion of special `__cursor__()` node representing
965+ the cursor position within its parent .
946966
947- A container is any Elixir expression starting with `(`,
948- `{`, and `[`. This includes function calls, tuples, lists,
949- maps, and so on. For example, take this code, which would
950- be given as input:
967+ The parent node is any function call, tuple, list, map, and so on.
968+ For example, take this code, which would be given as input:
951969
952970 max(some_value,
953971
@@ -956,9 +974,9 @@ defmodule Code.Fragment do
956974 max(some_value, __cursor__())
957975
958976 In other words, this function is capable of closing any open
959- brackets and insert the cursor position. Any content at the
960- cursor position that is after a comma or an opening bracket
961- is discarded. For example, if this is given as input:
977+ brackets and insert the cursor position. Other content at the
978+ cursor position which is not a parent is discarded.
979+ For example, if this is given as input:
962980
963981 max(some_value, another_val
964982
@@ -1004,6 +1022,8 @@ defmodule Code.Fragment do
10041022
10051023 if(some_value, do: __cursor__())
10061024
1025+ For multi-line blocks, all previous lines are preserved.
1026+
10071027 The AST returned by this function is not safe to evaluate but
10081028 it can be analyzed and expanded.
10091029
@@ -1019,7 +1039,25 @@ defmodule Code.Fragment do
10191039 iex> Code.Fragment.container_cursor_to_quoted("[some, value")
10201040 {:ok, [{:some, [line: 1], nil}, {:__cursor__, [line: 1], []}]}
10211041
1022- For binaries, the `::` is exclusively kept as an operator:
1042+ If an expression is complete, then the whole expression is discarded
1043+ and only the parent is returned:
1044+
1045+ iex> Code.Fragment.container_cursor_to_quoted("if(is_atom(var)")
1046+ {:ok, {:if, [line: 1], [{:__cursor__, [line: 1], []}]}}
1047+
1048+ this means complete expressions themselves return only the cursor:
1049+
1050+ iex> Code.Fragment.container_cursor_to_quoted("if(is_atom(var))")
1051+ {:ok, {:__cursor__, [line: 1], []}}
1052+ iex> Code.Fragment.container_cursor_to_quoted("alias Foo.Bar")
1053+ {:ok, {:__cursor__, [line: 1], []}}
1054+
1055+ Operators are never considered containers:
1056+
1057+ iex> Code.Fragment.container_cursor_to_quoted("if(foo +")
1058+ {:ok, {:if, [line: 1], [{:__cursor__, [line: 1], []}]}}
1059+
1060+ with the exception of `::` inside binaries and `|` inside maps:
10231061
10241062 iex> Code.Fragment.container_cursor_to_quoted("<<some::integer")
10251063 {:ok, {:<<>>, [line: 1], [{:"::", [line: 1], [{:some, [line: 1], nil}, {:__cursor__, [line: 1], []}]}]}}
0 commit comments