-
-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a38bd7c
Showing
157 changed files
with
8,976 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.{md, markdown, eex}] | ||
trim_trailing_whitespace = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Used by "mix format" | ||
[ | ||
inputs: ["mix.exs", "config/*.exs"], | ||
subdirectories: ["apps/*"] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# The directory Mix will write compiled artifacts to. | ||
/_build/ | ||
|
||
# If you run "mix test --cover", coverage assets end up here. | ||
/cover/ | ||
|
||
# The directory Mix downloads your dependencies sources to. | ||
/deps/ | ||
|
||
# Where third-party dependencies like ExDoc output generated docs. | ||
/doc/ | ||
|
||
# Ignore .fetch files in case you like to edit your project deps locally. | ||
/.fetch | ||
|
||
# If the VM crashes, it generates a dump, let's ignore it too. | ||
erl_crash.dump | ||
|
||
# Also ignore archive artifacts (built via "mix archive.build"). | ||
*.ez | ||
|
||
# Temporary files, for example, from tests. | ||
/tmp/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Node.start(:"[email protected]") | ||
# Node.set_cookie(:lexical) | ||
# Node.connect(:"[email protected]") | ||
|
||
project = Lexical.Project.new("file://#{File.cwd!()}/../ex_ls/") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
elixir 1.14.3-otp-25 | ||
erlang 25.2.1 | ||
nodejs 12.16.3 | ||
yarn 1.22.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Lexical | ||
|
||
**TODO: Add description** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Used by "mix format" | ||
[ | ||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# The directory Mix will write compiled artifacts to. | ||
/_build/ | ||
|
||
# If you run "mix test --cover", coverage assets end up here. | ||
/cover/ | ||
|
||
# The directory Mix downloads your dependencies sources to. | ||
/deps/ | ||
|
||
# Where third-party dependencies like ExDoc output generated docs. | ||
/doc/ | ||
|
||
# Ignore .fetch files in case you like to edit your project deps locally. | ||
/.fetch | ||
|
||
# If the VM crashes, it generates a dump, let's ignore it too. | ||
erl_crash.dump | ||
|
||
# Also ignore archive artifacts (built via "mix archive.build"). | ||
*.ez | ||
|
||
# Ignore package tarball (built via "mix hex.build"). | ||
common-*.tar | ||
|
||
# Temporary files, for example, from tests. | ||
/tmp/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Common | ||
|
||
**TODO: Add description** | ||
|
||
## Installation | ||
|
||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed | ||
by adding `common` to your list of dependencies in `mix.exs`: | ||
|
||
```elixir | ||
def deps do | ||
[ | ||
{:common, "~> 0.1.0"} | ||
] | ||
end | ||
``` | ||
|
||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) | ||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can | ||
be found at <https://hexdocs.pm/common>. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
defmodule Lexical.CodeUnit do | ||
@moduledoc """ | ||
Code unit and offset conversions | ||
The LSP protocol speaks in positions, which defines where something happens in a document. | ||
Positions have a start and an end, which are defined as code unit _offsets_ from the beginning | ||
of a line. this module helps to convert between utf8, which most of the world speaks | ||
natively, and utf16, which has been forced upon us by microsoft. | ||
Converting between offsets and code units is 0(n), and allocations only happen if a | ||
multi-byte character is detected, at which point, only that character is allocated. | ||
This exploits the fact that most source code consists of ascii characters, with at best, | ||
sporadic multi-byte characters in it. Thus, the vast majority of documents will not require | ||
any allocations at all. | ||
""" | ||
@type utf8_code_unit :: non_neg_integer() | ||
@type utf16_code_unit :: non_neg_integer() | ||
@type utf8_offset :: non_neg_integer() | ||
@type utf16_offset :: non_neg_integer() | ||
|
||
@type error :: {:error, :misaligned} | {:error, :out_of_bounds} | ||
|
||
# public | ||
|
||
@doc """ | ||
Converts a utf8 character offset into a utf16 character offset. This implementation | ||
clamps the maximum size of an offset so that any initial character position can be | ||
passed in and the offset returned will reflect the end of the line. | ||
""" | ||
@spec utf16_offset(String.t(), utf8_offset()) :: utf16_offset() | ||
def utf16_offset(binary, character_position) do | ||
do_utf16_offset(binary, character_position, 0) | ||
end | ||
|
||
@doc """ | ||
Converts a utf16 character offset into a utf8 character offset. This implementation | ||
clamps the maximum size of an offset so that any initial character position can be | ||
passed in and the offset returned will reflect the end of the line. | ||
""" | ||
@spec utf8_offset(String.t(), utf16_offset()) :: utf8_offset() | ||
def utf8_offset(binary, character_position) do | ||
do_utf8_offset(binary, character_position, 0) | ||
end | ||
|
||
@spec to_utf8(String.t(), utf16_code_unit()) :: {:ok, utf8_code_unit()} | error | ||
def to_utf8(binary, utf16_unit) do | ||
do_to_utf8(binary, utf16_unit, 0) | ||
end | ||
|
||
@spec to_utf16(String.t(), utf8_code_unit()) :: {:ok, utf16_code_unit()} | error | ||
def to_utf16(binary, utf16_unit) do | ||
do_to_utf16(binary, utf16_unit, 0) | ||
end | ||
|
||
def count(:utf16, binary) do | ||
do_count_utf16(binary, 0) | ||
end | ||
|
||
# Private | ||
|
||
# UTF-16 | ||
|
||
def do_count_utf16(<<>>, count) do | ||
count | ||
end | ||
|
||
def do_count_utf16(<<c, rest::binary>>, count) when c < 128 do | ||
do_count_utf16(rest, count + 1) | ||
end | ||
|
||
def do_count_utf16(<<c::utf8, rest::binary>>, count) do | ||
increment = | ||
<<c::utf16>> | ||
|> byte_size() | ||
|> div(2) | ||
|
||
do_count_utf16(rest, count + increment) | ||
end | ||
|
||
defp do_utf16_offset(_, 0, offset) do | ||
offset | ||
end | ||
|
||
defp do_utf16_offset(<<>>, _, offset) do | ||
# this clause pegs the offset at the end of the string | ||
# no matter the character index | ||
offset | ||
end | ||
|
||
defp do_utf16_offset(<<c, rest::binary>>, remaining, offset) when c < 128 do | ||
do_utf16_offset(rest, remaining - 1, offset + 1) | ||
end | ||
|
||
defp do_utf16_offset(<<c::utf8, rest::binary>>, remaining, offset) do | ||
s = <<c::utf8>> | ||
increment = utf16_size(s) | ||
do_utf16_offset(rest, remaining - 1, offset + increment) | ||
end | ||
|
||
defp do_to_utf16(_, 0, utf16_unit) do | ||
{:ok, utf16_unit} | ||
end | ||
|
||
defp do_to_utf16(_, utf8_unit, _) when utf8_unit < 0 do | ||
{:error, :misaligned} | ||
end | ||
|
||
defp do_to_utf16(<<>>, _remaining, _utf16_unit) do | ||
{:error, :out_of_bounds} | ||
end | ||
|
||
defp do_to_utf16(<<c, rest::binary>>, utf8_unit, utf16_unit) when c < 128 do | ||
do_to_utf16(rest, utf8_unit - 1, utf16_unit + 1) | ||
end | ||
|
||
defp do_to_utf16(<<c::utf8, rest::binary>>, utf8_unit, utf16_unit) do | ||
utf8_string = <<c::utf8>> | ||
increment = utf16_size(utf8_string) | ||
decrement = byte_size(utf8_string) | ||
|
||
do_to_utf16(rest, utf8_unit - decrement, utf16_unit + increment) | ||
end | ||
|
||
defp utf16_size(binary) when is_binary(binary) do | ||
binary | ||
|> :unicode.characters_to_binary(:utf8, :utf16) | ||
|> byte_size() | ||
|> div(2) | ||
end | ||
|
||
# UTF-8 | ||
|
||
defp do_utf8_offset(_, 0, offset) do | ||
offset | ||
end | ||
|
||
defp do_utf8_offset(<<>>, _, offset) do | ||
# this clause pegs the offset at the end of the string | ||
# no matter the character index | ||
offset | ||
end | ||
|
||
defp do_utf8_offset(<<c, rest::binary>>, remaining, offset) when c < 128 do | ||
do_utf8_offset(rest, remaining - 1, offset + 1) | ||
end | ||
|
||
defp do_utf8_offset(<<c::utf8, rest::binary>>, remaining, offset) do | ||
s = <<c::utf8>> | ||
increment = utf8_size(s) | ||
decrement = utf16_size(s) | ||
do_utf8_offset(rest, remaining - decrement, offset + increment) | ||
end | ||
|
||
defp do_to_utf8(_, 0, utf8_unit) do | ||
{:ok, utf8_unit} | ||
end | ||
|
||
defp do_to_utf8(_, utf_16_units, _) when utf_16_units < 0 do | ||
{:error, :misaligned} | ||
end | ||
|
||
defp do_to_utf8(<<>>, _remaining, _utf8_unit) do | ||
{:error, :out_of_bounds} | ||
end | ||
|
||
defp do_to_utf8(<<c, rest::binary>>, utf16_unit, utf8_unit) when c < 128 do | ||
do_to_utf8(rest, utf16_unit - 1, utf8_unit + 1) | ||
end | ||
|
||
defp do_to_utf8(<<c::utf8, rest::binary>>, utf16_unit, utf8_unit) do | ||
utf8_code_units = byte_size(<<c::utf8>>) | ||
utf16_code_units = utf16_size(<<c::utf8>>) | ||
|
||
do_to_utf8(rest, utf16_unit - utf16_code_units, utf8_unit + utf8_code_units) | ||
end | ||
|
||
defp utf8_size(binary) when is_binary(binary) do | ||
byte_size(binary) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
defmodule Common do | ||
@moduledoc """ | ||
Documentation for `Common`. | ||
""" | ||
|
||
@doc """ | ||
Hello world. | ||
## Examples | ||
iex> Common.hello() | ||
:world | ||
""" | ||
def hello do | ||
:world | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
defmodule Lexical do | ||
@type uri :: String.t() | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
defmodule Lexical.ProcessCache do | ||
@moduledoc """ | ||
A simple cache with a timeout that lives in the process dictionary | ||
""" | ||
|
||
defmodule Entry do | ||
defstruct [:value, :expiry] | ||
|
||
def new(value, timeout_ms) do | ||
expiry_ts = now_ts() + timeout_ms | ||
%__MODULE__{value: value, expiry: expiry_ts} | ||
end | ||
|
||
def valid?(%__MODULE__{} = entry) do | ||
now_ts() < entry.expiry | ||
end | ||
|
||
defp now_ts do | ||
System.os_time(:millisecond) | ||
end | ||
end | ||
|
||
@type key :: term() | ||
@type fetch_result :: {:ok, term()} | :error | ||
|
||
@doc """ | ||
Retrieves a value from the cache | ||
If the value is not found, the default is returned | ||
""" | ||
@spec get(key()) :: term() | nil | ||
@spec get(key(), term()) :: term() | nil | ||
def get(key, default \\ nil) do | ||
case fetch(key) do | ||
{:ok, val} -> val | ||
:error -> default | ||
end | ||
end | ||
|
||
@doc """ | ||
Retrieves a value from the cache | ||
If the value is not found, the default is returned | ||
""" | ||
@spec fetch(key()) :: fetch_result() | ||
def fetch(key) do | ||
case Process.get(key, :unset) do | ||
%Entry{} = entry -> | ||
if Entry.valid?(entry) do | ||
{:ok, entry.value} | ||
else | ||
Process.delete(key) | ||
:error | ||
end | ||
|
||
:unset -> | ||
:error | ||
end | ||
end | ||
|
||
@doc """ | ||
Retrieves and optionally sets a value in the cache. | ||
Trans looks up a value in the cache under key. If that value isn't | ||
found, the compute_fn is then executed, and its return value is set | ||
in the cache. The cached value will live in the cache for `timeout` | ||
milliseconds | ||
""" | ||
def trans(key, timeout_ms \\ 5000, compute_fn) do | ||
case fetch(key) do | ||
:error -> | ||
set(key, timeout_ms, compute_fn) | ||
|
||
{:ok, result} -> | ||
result | ||
end | ||
end | ||
|
||
defp set(key, timeout_ms, compute_fn) do | ||
value = compute_fn.() | ||
Process.put(key, Entry.new(value, timeout_ms)) | ||
value | ||
end | ||
end |
Oops, something went wrong.