Skip to content

Commit

Permalink
[move] Provide code completion via language server
Browse files Browse the repository at this point in the history
Implement a completion provider in the move-analyzer language server,
so users of the Visual Studio Code extension see code completion suggestions
as they type.

This first iteration of completion in the language server is as simple as
possible, and performs no semantic analysis. However, it must still lex the
source program. That is because, as soon as move-analyzer informs VS Code that
it provides completions, VS Code stops its internal completion engine entirely.
Originally, the intent of this change to move-analyzer was to *supplement*
VS Code's completion items (for example, if you typed `foo` anywhere in the
source file, VS Code would surface `foo` as a completion item everywhere else)
with Move's keywords and built-in operators. Since there is no easy way to
add to VS Code's simple textual completion engine, this commit implements such
an engine on its own, and adds to that the Move keywords and operators.

Doing so resulted in several decisions and future work items:

* Users spend most of their time editing source code that is not saved to disk.
  They only save that code to disk at certain times, such as when they feel it
  is worth saving. To provide completions, move-analyzer cannot just rely on
  what's on disk, and so must receive notifications from the language server
  client as to what source buffers the user is editing. (This could be improved
  by processing incremental updates from the client.)
* Language servers can define "trigger characters" that prompt the client to
  present completions. For Move and many other languages, `.` and `::` make
  sense for these (for example, `foo.` and `foo::`). Move keywords (`if`,
  `fun`, etc.) and operators (`move` and `copy`) can't appear in these
  positions, so they are filtered out when the user's cursor appears in
  these positions. (This could be improved by performing semantic analysis
  and filtering completion items based on whether the cursor is within a
  `address`, `module`, or `fun` block, based on what identifiers are in
  scope, etc.)
* Currently, the source file being edited is lexed whenever completions are
  requested for that file. Instead, move-analyzer could maintain an
  ever-ready syntax tree view of the entire package being edited, that is
  updated as files are modified. When completions are suggested, they ought
  to be readily available based on this tree, instead of being computed
  reactively as requested.
* I noticed that certain contextual keywords were not included in the
  regex-based TextMate grammar for Move, so I added them. (Once the
  language server begins relying on parsed, semantic analyzed views of
  Move programs, it can begin providing semantically-aware syntax
  highlighting that ensures these are not colored as keywords unless
  appropriate.)

Closes: aptos-labs#9589
  • Loading branch information
modocache authored and bors-libra committed Nov 30, 2021
1 parent 20b2b8b commit a5d1c18
Show file tree
Hide file tree
Showing 120 changed files with 2,857 additions and 2,477 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ and MultiEd25519 (K-of-N multisig).
A multi-ed25519 public key


<pre><code><b>struct</b> <a href="Authenticator.md#0x1_Authenticator_MultiEd25519PublicKey">MultiEd25519PublicKey</a> has <b>copy</b>, drop, store
<pre><code><b>struct</b> <a href="Authenticator.md#0x1_Authenticator_MultiEd25519PublicKey">MultiEd25519PublicKey</a> <b>has</b> <b>copy</b>, drop, store
</code></pre>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ min_nonce min_nonce + k - 1
```


<pre><code><b>struct</b> <a href="CRSN.md#0x1_CRSN">CRSN</a> has key
<pre><code><b>struct</b> <a href="CRSN.md#0x1_CRSN">CRSN</a> <b>has</b> key
</code></pre>


Expand Down Expand Up @@ -94,7 +94,7 @@ Whenever a force shift is performed a <code><a href="CRSN.md#0x1_CRSN_ForceShift
This is used to prove the absence of a transaction at a specific sequence nonce.


<pre><code><b>struct</b> <a href="CRSN.md#0x1_CRSN_ForceShiftEvent">ForceShiftEvent</a> has drop, store
<pre><code><b>struct</b> <a href="CRSN.md#0x1_CRSN_ForceShiftEvent">ForceShiftEvent</a> <b>has</b> drop, store
</code></pre>


Expand Down Expand Up @@ -139,7 +139,7 @@ This is used to prove the absence of a transaction at a specific sequence nonce.
Flag stored in memory to turn on CRSNs


<pre><code><b>struct</b> <a href="CRSN.md#0x1_CRSN_CRSNsAllowed">CRSNsAllowed</a> has key
<pre><code><b>struct</b> <a href="CRSN.md#0x1_CRSN_CRSNsAllowed">CRSNsAllowed</a> <b>has</b> key
</code></pre>


Expand Down Expand Up @@ -262,7 +262,7 @@ The size given to the CRSN at the time of publishing was zero, which is not supp
<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_allow_crsns">allow_crsns</a>(account: &signer) {
<a href="Roles.md#0x1_Roles_assert_diem_root">Roles::assert_diem_root</a>(account);
<b>assert</b>!(!<b>exists</b>&lt;<a href="CRSN.md#0x1_CRSN_CRSNsAllowed">CRSNsAllowed</a>&gt;(<a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account)), <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_EALREADY_INITIALIZED">EALREADY_INITIALIZED</a>));
move_to(account, <a href="CRSN.md#0x1_CRSN_CRSNsAllowed">CRSNsAllowed</a> { })
<b>move_to</b>(account, <a href="CRSN.md#0x1_CRSN_CRSNsAllowed">CRSNsAllowed</a> { })
}
</code></pre>

Expand Down Expand Up @@ -291,7 +291,7 @@ Publish a DSN under <code>account</code>. Cannot already have a DSN published.
<b>assert</b>!(size &gt; 0, <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(<a href="CRSN.md#0x1_CRSN_EZERO_SIZE_CRSN">EZERO_SIZE_CRSN</a>));
<b>assert</b>!(size &lt;= <a href="CRSN.md#0x1_CRSN_MAX_CRSN_SIZE">MAX_CRSN_SIZE</a>, <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(<a href="CRSN.md#0x1_CRSN_ECRSN_SIZE_TOO_LARGE">ECRSN_SIZE_TOO_LARGE</a>));
<b>assert</b>!(<b>exists</b>&lt;<a href="CRSN.md#0x1_CRSN_CRSNsAllowed">CRSNsAllowed</a>&gt;(@DiemRoot), <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_ENOT_INITIALIZED">ENOT_INITIALIZED</a>));
move_to(account, <a href="CRSN.md#0x1_CRSN">CRSN</a> {
<b>move_to</b>(account, <a href="CRSN.md#0x1_CRSN">CRSN</a> {
min_nonce,
size,
slots: <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/BitVector.md#0x1_BitVector_new">BitVector::new</a>(size),
Expand Down Expand Up @@ -343,14 +343,14 @@ Record <code>sequence_nonce</code> under the <code>account</code>. Returns true
<b>let</b> addr = <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account);
<b>if</b> (<a href="CRSN.md#0x1_CRSN_check">check</a>(account, sequence_nonce)) {
// <a href="CRSN.md#0x1_CRSN">CRSN</a> <b>exists</b> by `check`.
<b>let</b> crsn = borrow_global_mut&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>let</b> crsn = <b>borrow_global_mut</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
// accept nonce
<b>let</b> scaled_nonce = sequence_nonce - crsn.min_nonce;
<a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/BitVector.md#0x1_BitVector_set">BitVector::set</a>(&<b>mut</b> crsn.slots, scaled_nonce);
<a href="CRSN.md#0x1_CRSN_shift_window_right">shift_window_right</a>(crsn);
<b>return</b> <b>true</b>
} <b>else</b> <b>if</b> (<b>exists</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr)) { // window was force shifted in this transaction
<b>let</b> crsn = borrow_global&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>let</b> crsn = <b>borrow_global</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>if</b> (crsn.min_nonce &gt; sequence_nonce) <b>return</b> <b>true</b>
};

Expand Down Expand Up @@ -383,7 +383,7 @@ will be accepted, and <code><b>false</b></code> otherwise.
<b>acquires</b> <a href="CRSN.md#0x1_CRSN">CRSN</a> {
<b>let</b> addr = <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account);
<b>assert</b>!(<a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr), <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_ENO_CRSN">ENO_CRSN</a>));
<b>let</b> crsn = borrow_global_mut&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>let</b> crsn = <b>borrow_global_mut</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);

// Don't accept <b>if</b> it's outside of the window
<b>if</b> ((sequence_nonce &lt; crsn.min_nonce) ||
Expand Down Expand Up @@ -418,7 +418,7 @@ will be accepted, and <code><b>false</b></code> otherwise.


<pre><code><b>schema</b> <a href="CRSN.md#0x1_CRSN_CheckAbortsIf">CheckAbortsIf</a> {
addr: address;
addr: <b>address</b>;
sequence_nonce: u64;
<b>let</b> crsn = <b>global</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>let</b> scaled_nonce = sequence_nonce - crsn.min_nonce;
Expand All @@ -436,7 +436,7 @@ will be accepted, and <code><b>false</b></code> otherwise.
<a name="0x1_CRSN_spec_check"></a>


<pre><code><b>fun</b> <a href="CRSN.md#0x1_CRSN_spec_check">spec_check</a>(addr: address, sequence_nonce: u64): bool {
<pre><code><b>fun</b> <a href="CRSN.md#0x1_CRSN_spec_check">spec_check</a>(addr: <b>address</b>, sequence_nonce: u64): bool {
<b>let</b> crsn = <b>global</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>if</b> ((sequence_nonce &lt; crsn.min_nonce) ||
(sequence_nonce &gt;= crsn.min_nonce + <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/BitVector.md#0x1_BitVector_length">BitVector::length</a>(crsn.slots))) {
Expand Down Expand Up @@ -475,7 +475,7 @@ then shifted over set bits as define by the <code>shift_window_right</code> func
<b>assert</b>!(shift_amount &gt; 0, <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(<a href="CRSN.md#0x1_CRSN_EINVALID_SHIFT">EINVALID_SHIFT</a>));
<b>let</b> addr = <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account);
<b>assert</b>!(<a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr), <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_ENO_CRSN">ENO_CRSN</a>));
<b>let</b> crsn = borrow_global_mut&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);
<b>let</b> crsn = <b>borrow_global_mut</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr);

<a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Event.md#0x1_Event_emit_event">Event::emit_event</a>(&<b>mut</b> crsn.force_shift_events, <a href="CRSN.md#0x1_CRSN_ForceShiftEvent">ForceShiftEvent</a> {
current_min_nonce: crsn.min_nonce,
Expand All @@ -502,7 +502,7 @@ then shifted over set bits as define by the <code>shift_window_right</code> func
Return whether this address has a CRSN resource published under it.


<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr: address): bool
<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr: <b>address</b>): bool
</code></pre>


Expand All @@ -511,7 +511,7 @@ Return whether this address has a CRSN resource published under it.
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr: address): bool {
<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr: <b>address</b>): bool {
<b>exists</b>&lt;<a href="CRSN.md#0x1_CRSN">CRSN</a>&gt;(addr)
}
</code></pre>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This code provides a container for storing a chain id and functions to initializ



<pre><code><b>struct</b> <a href="ChainId.md#0x1_ChainId">ChainId</a> has key
<pre><code><b>struct</b> <a href="ChainId.md#0x1_ChainId">ChainId</a> <b>has</b> key
</code></pre>


Expand Down Expand Up @@ -87,7 +87,7 @@ Publish the chain ID <code>id</code> of this Diem instance under the DiemRoot ac
<a href="DiemTimestamp.md#0x1_DiemTimestamp_assert_genesis">DiemTimestamp::assert_genesis</a>();
<a href="CoreAddresses.md#0x1_CoreAddresses_assert_diem_root">CoreAddresses::assert_diem_root</a>(dr_account);
<b>assert</b>!(!<b>exists</b>&lt;<a href="ChainId.md#0x1_ChainId">ChainId</a>&gt;(<a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(dr_account)), <a href="../../../../../../../DPN/releases/artifacts/current/build/MoveStdlib/docs/Errors.md#0x1_Errors_already_published">Errors::already_published</a>(<a href="ChainId.md#0x1_ChainId_ECHAIN_ID">ECHAIN_ID</a>));
move_to(dr_account, <a href="ChainId.md#0x1_ChainId">ChainId</a> { id })
<b>move_to</b>(dr_account, <a href="ChainId.md#0x1_ChainId">ChainId</a> { id })
}
</code></pre>

Expand Down Expand Up @@ -131,7 +131,7 @@ Return the chain ID of this Diem instance

<pre><code><b>public</b> <b>fun</b> <a href="ChainId.md#0x1_ChainId_get">get</a>(): u8 <b>acquires</b> <a href="ChainId.md#0x1_ChainId">ChainId</a> {
<a href="DiemTimestamp.md#0x1_DiemTimestamp_assert_operating">DiemTimestamp::assert_operating</a>();
borrow_global&lt;<a href="ChainId.md#0x1_ChainId">ChainId</a>&gt;(@DiemRoot).id
<b>borrow_global</b>&lt;<a href="ChainId.md#0x1_ChainId">ChainId</a>&gt;(@DiemRoot).id
}
</code></pre>

Expand Down
Loading

0 comments on commit a5d1c18

Please sign in to comment.