jaclang 0.0.5__py3-none-any.whl → 0.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jaclang might be problematic. Click here for more details.
- jaclang/__init__.py +2 -1
- jaclang/cli/__jac_gen__/__init__.py +0 -0
- jaclang/cli/__jac_gen__/cli.py +175 -0
- jaclang/cli/__jac_gen__/cmds.py +132 -0
- jaclang/cli/cli.jac +2 -2
- jaclang/cli/cmds.jac +8 -2
- jaclang/cli/impl/__jac_gen__/__init__.py +0 -0
- jaclang/cli/impl/__jac_gen__/cli_impl.py +16 -0
- jaclang/cli/impl/__jac_gen__/cmds_impl.py +26 -0
- jaclang/cli/impl/cli_impl.jac +25 -8
- jaclang/cli/impl/cmds_impl.jac +35 -6
- jaclang/core/__jac_gen__/__init__.py +0 -0
- jaclang/core/__jac_gen__/primitives.py +567 -0
- jaclang/core/impl/__jac_gen__/__init__.py +0 -0
- jaclang/core/impl/__jac_gen__/arch_impl.py +24 -0
- jaclang/core/impl/__jac_gen__/element_impl.py +26 -0
- jaclang/core/impl/__jac_gen__/exec_ctx_impl.py +12 -0
- jaclang/core/impl/__jac_gen__/memory_impl.py +14 -0
- jaclang/core/impl/element_impl.jac +3 -3
- jaclang/core/impl/exec_ctx_impl.jac +3 -6
- jaclang/core/primitives.jac +4 -3
- jaclang/jac/absyntree.py +555 -180
- jaclang/jac/constant.py +6 -0
- jaclang/jac/importer.py +34 -56
- jaclang/jac/langserve.py +26 -0
- jaclang/jac/lexer.py +35 -3
- jaclang/jac/parser.py +146 -115
- jaclang/jac/passes/blue/__init__.py +8 -3
- jaclang/jac/passes/blue/ast_build_pass.py +454 -305
- jaclang/jac/passes/blue/blue_pygen_pass.py +112 -74
- jaclang/jac/passes/blue/decl_def_match_pass.py +49 -277
- jaclang/jac/passes/blue/import_pass.py +1 -1
- jaclang/jac/passes/blue/pyout_pass.py +74 -0
- jaclang/jac/passes/blue/semantic_check_pass.py +37 -0
- jaclang/jac/passes/blue/sym_tab_build_pass.py +1045 -0
- jaclang/jac/passes/blue/tests/test_ast_build_pass.py +2 -2
- jaclang/jac/passes/blue/tests/test_blue_pygen_pass.py +9 -28
- jaclang/jac/passes/blue/tests/test_decl_def_match_pass.py +13 -22
- jaclang/jac/passes/blue/tests/test_sym_tab_build_pass.py +22 -0
- jaclang/jac/passes/ir_pass.py +8 -6
- jaclang/jac/passes/purple/__jac_gen__/__init__.py +0 -0
- jaclang/jac/passes/purple/__jac_gen__/analyze_pass.py +37 -0
- jaclang/jac/passes/purple/__jac_gen__/purple_pygen_pass.py +305 -0
- jaclang/jac/passes/purple/impl/__jac_gen__/__init__.py +0 -0
- jaclang/jac/passes/purple/impl/__jac_gen__/purple_pygen_pass_impl.py +23 -0
- jaclang/jac/passes/purple/impl/purple_pygen_pass_impl.jac +2 -5
- jaclang/jac/symtable.py +154 -0
- jaclang/jac/tests/fixtures/__jac_gen__/__init__.py +0 -0
- jaclang/jac/tests/fixtures/__jac_gen__/hello_world.py +16 -0
- jaclang/jac/tests/fixtures/fam.jac +7 -8
- jaclang/jac/tests/fixtures/mod_doc_test.jac +1 -0
- jaclang/jac/tests/test_parser.py +8 -0
- jaclang/jac/transform.py +41 -14
- jaclang/jac/transpiler.py +18 -9
- jaclang/utils/fstring_parser.py +2 -2
- jaclang/utils/helpers.py +41 -0
- jaclang/utils/lang_tools.py +12 -2
- jaclang/utils/test.py +41 -0
- jaclang/vendor/__init__.py +1 -0
- jaclang/vendor/pygls/__init__.py +25 -0
- jaclang/vendor/pygls/capabilities.py +502 -0
- jaclang/vendor/pygls/client.py +176 -0
- jaclang/vendor/pygls/constants.py +26 -0
- jaclang/vendor/pygls/exceptions.py +220 -0
- jaclang/vendor/pygls/feature_manager.py +241 -0
- jaclang/vendor/pygls/lsp/__init__.py +139 -0
- jaclang/vendor/pygls/lsp/client.py +2224 -0
- jaclang/vendor/pygls/lsprotocol/__init__.py +2 -0
- jaclang/vendor/pygls/lsprotocol/_hooks.py +1233 -0
- jaclang/vendor/pygls/lsprotocol/converters.py +17 -0
- jaclang/vendor/pygls/lsprotocol/types.py +12820 -0
- jaclang/vendor/pygls/lsprotocol/validators.py +47 -0
- jaclang/vendor/pygls/progress.py +79 -0
- jaclang/vendor/pygls/protocol.py +1184 -0
- jaclang/vendor/pygls/server.py +620 -0
- jaclang/vendor/pygls/uris.py +184 -0
- jaclang/vendor/pygls/workspace/__init__.py +81 -0
- jaclang/vendor/pygls/workspace/position.py +204 -0
- jaclang/vendor/pygls/workspace/text_document.py +234 -0
- jaclang/vendor/pygls/workspace/workspace.py +311 -0
- {jaclang-0.0.5.dist-info → jaclang-0.0.8.dist-info}/METADATA +1 -1
- jaclang-0.0.8.dist-info/RECORD +118 -0
- jaclang/core/jaclang.jac +0 -62
- jaclang/jac/passes/blue/tests/test_type_analyze_pass.py +0 -53
- jaclang/jac/passes/blue/type_analyze_pass.py +0 -728
- jaclang/jac/sym_table.py +0 -127
- jaclang-0.0.5.dist-info/RECORD +0 -73
- /jaclang/{utils → vendor}/sly/__init__.py +0 -0
- /jaclang/{utils → vendor}/sly/docparse.py +0 -0
- /jaclang/{utils → vendor}/sly/lex.py +0 -0
- /jaclang/{utils → vendor}/sly/yacc.py +0 -0
- {jaclang-0.0.5.dist-info → jaclang-0.0.8.dist-info}/WHEEL +0 -0
- {jaclang-0.0.5.dist-info → jaclang-0.0.8.dist-info}/entry_points.txt +0 -0
- {jaclang-0.0.5.dist-info → jaclang-0.0.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# Original work Copyright 2017 Palantir Technologies, Inc. #
|
|
3
|
+
# Original work licensed under the MIT License. #
|
|
4
|
+
# See ThirdPartyNotices.txt in the project root for license information. #
|
|
5
|
+
# All modifications Copyright (c) Open Law Library. All rights reserved. #
|
|
6
|
+
# #
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License") #
|
|
8
|
+
# you may not use this file except in compliance with the License. #
|
|
9
|
+
# You may obtain a copy of the License at #
|
|
10
|
+
# #
|
|
11
|
+
# http: // www.apache.org/licenses/LICENSE-2.0 #
|
|
12
|
+
# #
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software #
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS, #
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
|
16
|
+
# See the License for the specific language governing permissions and #
|
|
17
|
+
# limitations under the License. #
|
|
18
|
+
############################################################################
|
|
19
|
+
"""
|
|
20
|
+
A collection of URI utilities with logic built on the VSCode URI library.
|
|
21
|
+
|
|
22
|
+
https://github.com/Microsoft/vscode-uri/blob/e59cab84f5df6265aed18ae5f43552d3eef13bb9/lib/index.ts
|
|
23
|
+
"""
|
|
24
|
+
from typing import Optional, Tuple
|
|
25
|
+
|
|
26
|
+
import re
|
|
27
|
+
from urllib import parse
|
|
28
|
+
|
|
29
|
+
from jaclang.vendor.pygls import IS_WIN
|
|
30
|
+
|
|
31
|
+
RE_DRIVE_LETTER_PATH = re.compile(r"^\/[a-zA-Z]:")
|
|
32
|
+
|
|
33
|
+
URLParts = Tuple[str, str, str, str, str, str]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _normalize_win_path(path: str):
|
|
37
|
+
netloc = ""
|
|
38
|
+
|
|
39
|
+
# normalize to fwd-slashes on windows,
|
|
40
|
+
# on other systems bwd-slashes are valid
|
|
41
|
+
# filename character, eg /f\oo/ba\r.txt
|
|
42
|
+
if IS_WIN:
|
|
43
|
+
path = path.replace("\\", "/")
|
|
44
|
+
|
|
45
|
+
# check for authority as used in UNC shares
|
|
46
|
+
# or use the path as given
|
|
47
|
+
if path[:2] == "//":
|
|
48
|
+
idx = path.index("/", 2)
|
|
49
|
+
if idx == -1:
|
|
50
|
+
netloc = path[2:]
|
|
51
|
+
else:
|
|
52
|
+
netloc = path[2:idx]
|
|
53
|
+
path = path[idx:]
|
|
54
|
+
|
|
55
|
+
# Ensure that path starts with a slash
|
|
56
|
+
# or that it is at least a slash
|
|
57
|
+
if not path.startswith("/"):
|
|
58
|
+
path = "/" + path
|
|
59
|
+
|
|
60
|
+
# Normalize drive paths to lower case
|
|
61
|
+
if RE_DRIVE_LETTER_PATH.match(path):
|
|
62
|
+
path = path[0] + path[1].lower() + path[2:]
|
|
63
|
+
|
|
64
|
+
return path, netloc
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def from_fs_path(path: str):
|
|
68
|
+
"""Returns a URI for the given filesystem path."""
|
|
69
|
+
try:
|
|
70
|
+
scheme = "file"
|
|
71
|
+
params, query, fragment = "", "", ""
|
|
72
|
+
path, netloc = _normalize_win_path(path)
|
|
73
|
+
return urlunparse((scheme, netloc, path, params, query, fragment))
|
|
74
|
+
except (AttributeError, TypeError):
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def to_fs_path(uri: str):
|
|
79
|
+
"""
|
|
80
|
+
Returns the filesystem path of the given URI.
|
|
81
|
+
|
|
82
|
+
Will handle UNC paths and normalize windows drive letters to lower-case.
|
|
83
|
+
Also uses the platform specific path separator. Will *not* validate the
|
|
84
|
+
path for invalid characters and semantics.
|
|
85
|
+
Will *not* look at the scheme of this URI.
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
# scheme://netloc/path;parameters?query#fragment
|
|
89
|
+
scheme, netloc, path, _, _, _ = urlparse(uri)
|
|
90
|
+
|
|
91
|
+
if netloc and path and scheme == "file":
|
|
92
|
+
# unc path: file://shares/c$/far/boo
|
|
93
|
+
value = f"//{netloc}{path}"
|
|
94
|
+
|
|
95
|
+
elif RE_DRIVE_LETTER_PATH.match(path):
|
|
96
|
+
# windows drive letter: file:///C:/far/boo
|
|
97
|
+
value = path[1].lower() + path[2:]
|
|
98
|
+
|
|
99
|
+
else:
|
|
100
|
+
# Other path
|
|
101
|
+
value = path
|
|
102
|
+
|
|
103
|
+
if IS_WIN:
|
|
104
|
+
value = value.replace("/", "\\")
|
|
105
|
+
|
|
106
|
+
return value
|
|
107
|
+
except TypeError:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def uri_scheme(uri: str):
|
|
112
|
+
try:
|
|
113
|
+
return urlparse(uri)[0]
|
|
114
|
+
except (TypeError, IndexError):
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# TODO: Use `URLParts` type
|
|
119
|
+
def uri_with(
|
|
120
|
+
uri: str,
|
|
121
|
+
scheme: Optional[str] = None,
|
|
122
|
+
netloc: Optional[str] = None,
|
|
123
|
+
path: Optional[str] = None,
|
|
124
|
+
params: Optional[str] = None,
|
|
125
|
+
query: Optional[str] = None,
|
|
126
|
+
fragment: Optional[str] = None,
|
|
127
|
+
):
|
|
128
|
+
"""
|
|
129
|
+
Return a URI with the given part(s) replaced.
|
|
130
|
+
Parts are decoded / encoded.
|
|
131
|
+
"""
|
|
132
|
+
old_scheme, old_netloc, old_path, old_params, old_query, old_fragment = urlparse(
|
|
133
|
+
uri
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if path is None:
|
|
137
|
+
raise Exception("`path` must not be None")
|
|
138
|
+
|
|
139
|
+
path, _ = _normalize_win_path(path)
|
|
140
|
+
return urlunparse(
|
|
141
|
+
(
|
|
142
|
+
scheme or old_scheme,
|
|
143
|
+
netloc or old_netloc,
|
|
144
|
+
path or old_path,
|
|
145
|
+
params or old_params,
|
|
146
|
+
query or old_query,
|
|
147
|
+
fragment or old_fragment,
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def urlparse(uri: str):
|
|
153
|
+
"""Parse and decode the parts of a URI."""
|
|
154
|
+
scheme, netloc, path, params, query, fragment = parse.urlparse(uri)
|
|
155
|
+
return (
|
|
156
|
+
parse.unquote(scheme),
|
|
157
|
+
parse.unquote(netloc),
|
|
158
|
+
parse.unquote(path),
|
|
159
|
+
parse.unquote(params),
|
|
160
|
+
parse.unquote(query),
|
|
161
|
+
parse.unquote(fragment),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def urlunparse(parts: URLParts) -> str:
|
|
166
|
+
"""Unparse and encode parts of a URI."""
|
|
167
|
+
scheme, netloc, path, params, query, fragment = parts
|
|
168
|
+
|
|
169
|
+
# Avoid encoding the windows drive letter colon
|
|
170
|
+
if RE_DRIVE_LETTER_PATH.match(path):
|
|
171
|
+
quoted_path = path[:3] + parse.quote(path[3:])
|
|
172
|
+
else:
|
|
173
|
+
quoted_path = parse.quote(path)
|
|
174
|
+
|
|
175
|
+
return parse.urlunparse(
|
|
176
|
+
(
|
|
177
|
+
parse.quote(scheme),
|
|
178
|
+
parse.quote(netloc),
|
|
179
|
+
quoted_path,
|
|
180
|
+
parse.quote(params),
|
|
181
|
+
parse.quote(query),
|
|
182
|
+
parse.quote(fragment),
|
|
183
|
+
)
|
|
184
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
from jaclang.vendor.pygls.lsprotocol import types
|
|
5
|
+
|
|
6
|
+
from .workspace import Workspace
|
|
7
|
+
from .text_document import TextDocument
|
|
8
|
+
from .position import Position
|
|
9
|
+
|
|
10
|
+
Workspace = Workspace
|
|
11
|
+
TextDocument = TextDocument
|
|
12
|
+
Position = Position
|
|
13
|
+
|
|
14
|
+
# For backwards compatibility
|
|
15
|
+
Document = TextDocument
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def utf16_unit_offset(chars: str):
|
|
19
|
+
warnings.warn(
|
|
20
|
+
"'utf16_unit_offset' has been deprecated, use "
|
|
21
|
+
"'Position.utf16_unit_offset' instead",
|
|
22
|
+
DeprecationWarning,
|
|
23
|
+
stacklevel=2,
|
|
24
|
+
)
|
|
25
|
+
_position = Position()
|
|
26
|
+
return _position.utf16_unit_offset(chars)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def utf16_num_units(chars: str):
|
|
30
|
+
warnings.warn(
|
|
31
|
+
"'utf16_num_units' has been deprecated, use "
|
|
32
|
+
"'Position.client_num_units' instead",
|
|
33
|
+
DeprecationWarning,
|
|
34
|
+
stacklevel=2,
|
|
35
|
+
)
|
|
36
|
+
_position = Position()
|
|
37
|
+
return _position.client_num_units(chars)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def position_from_utf16(lines: List[str], position: types.Position):
|
|
41
|
+
warnings.warn(
|
|
42
|
+
"'position_from_utf16' has been deprecated, use "
|
|
43
|
+
"'Position.position_from_client_units' instead",
|
|
44
|
+
DeprecationWarning,
|
|
45
|
+
stacklevel=2,
|
|
46
|
+
)
|
|
47
|
+
_position = Position()
|
|
48
|
+
return _position.position_from_client_units(lines, position)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def position_to_utf16(lines: List[str], position: types.Position):
|
|
52
|
+
warnings.warn(
|
|
53
|
+
"'position_to_utf16' has been deprecated, use "
|
|
54
|
+
"'Position.position_to_client_units' instead",
|
|
55
|
+
DeprecationWarning,
|
|
56
|
+
stacklevel=2,
|
|
57
|
+
)
|
|
58
|
+
_position = Position()
|
|
59
|
+
return _position.position_to_client_units(lines, position)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def range_from_utf16(lines: List[str], range: types.Range):
|
|
63
|
+
warnings.warn(
|
|
64
|
+
"'range_from_utf16' has been deprecated, use "
|
|
65
|
+
"'Position.range_from_client_units' instead",
|
|
66
|
+
DeprecationWarning,
|
|
67
|
+
stacklevel=2,
|
|
68
|
+
)
|
|
69
|
+
_position = Position()
|
|
70
|
+
return _position.range_from_client_units(lines, range)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def range_to_utf16(lines: List[str], range: types.Range):
|
|
74
|
+
warnings.warn(
|
|
75
|
+
"'range_to_utf16' has been deprecated, use "
|
|
76
|
+
"'Position.range_to_client_units' instead",
|
|
77
|
+
DeprecationWarning,
|
|
78
|
+
stacklevel=2,
|
|
79
|
+
)
|
|
80
|
+
_position = Position()
|
|
81
|
+
return _position.range_to_client_units(lines, range)
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# Original work Copyright 2017 Palantir Technologies, Inc. #
|
|
3
|
+
# Original work licensed under the MIT License. #
|
|
4
|
+
# See ThirdPartyNotices.txt in the project root for license information. #
|
|
5
|
+
# All modifications Copyright (c) Open Law Library. All rights reserved. #
|
|
6
|
+
# #
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License") #
|
|
8
|
+
# you may not use this file except in compliance with the License. #
|
|
9
|
+
# You may obtain a copy of the License at #
|
|
10
|
+
# #
|
|
11
|
+
# http: // www.apache.org/licenses/LICENSE-2.0 #
|
|
12
|
+
# #
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software #
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS, #
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
|
16
|
+
# See the License for the specific language governing permissions and #
|
|
17
|
+
# limitations under the License. #
|
|
18
|
+
############################################################################
|
|
19
|
+
import logging
|
|
20
|
+
from typing import List, Optional, Union
|
|
21
|
+
|
|
22
|
+
from jaclang.vendor.pygls.lsprotocol import types
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
log = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Position:
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
encoding: Optional[
|
|
32
|
+
Union[types.PositionEncodingKind, str]
|
|
33
|
+
] = types.PositionEncodingKind.Utf16,
|
|
34
|
+
):
|
|
35
|
+
self.encoding = encoding
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def is_char_beyond_multilingual_plane(cls, char: str) -> bool:
|
|
39
|
+
return ord(char) > 0xFFFF
|
|
40
|
+
|
|
41
|
+
def utf16_unit_offset(self, chars: str):
|
|
42
|
+
"""
|
|
43
|
+
Calculate the number of characters which need two utf-16 code units.
|
|
44
|
+
|
|
45
|
+
Arguments:
|
|
46
|
+
chars (str): The string to count occurrences of utf-16 code units for.
|
|
47
|
+
"""
|
|
48
|
+
return sum(self.is_char_beyond_multilingual_plane(ch) for ch in chars)
|
|
49
|
+
|
|
50
|
+
def client_num_units(self, chars: str):
|
|
51
|
+
"""
|
|
52
|
+
Calculate the length of `str` in utf-16 code units.
|
|
53
|
+
|
|
54
|
+
Arguments:
|
|
55
|
+
chars (str): The string to return the length in utf-16 code units for.
|
|
56
|
+
"""
|
|
57
|
+
utf32_units = len(chars)
|
|
58
|
+
if self.encoding == types.PositionEncodingKind.Utf32:
|
|
59
|
+
return utf32_units
|
|
60
|
+
|
|
61
|
+
if self.encoding == types.PositionEncodingKind.Utf8:
|
|
62
|
+
return utf32_units + (self.utf16_unit_offset(chars) * 2)
|
|
63
|
+
|
|
64
|
+
return utf32_units + self.utf16_unit_offset(chars)
|
|
65
|
+
|
|
66
|
+
def position_from_client_units(
|
|
67
|
+
self, lines: List[str], position: types.Position
|
|
68
|
+
) -> types.Position:
|
|
69
|
+
"""
|
|
70
|
+
Convert the position.character from UTF-[32|16|8] code units to UTF-32.
|
|
71
|
+
|
|
72
|
+
A python application can't use the character member of `Position`
|
|
73
|
+
directly. As per specification it is represented as a zero-based line and
|
|
74
|
+
character offset based on posible a UTF-[32|16|8] string representation.
|
|
75
|
+
|
|
76
|
+
All characters whose code point exceeds the Basic Multilingual Plane are
|
|
77
|
+
represented by 2 UTF-16 or 4 UTF-8 code units.
|
|
78
|
+
|
|
79
|
+
The offset of the closing quotation mark in x="😋" is
|
|
80
|
+
- 7 in UTF-8 representation
|
|
81
|
+
- 5 in UTF-16 representation
|
|
82
|
+
- 4 in UTF-32 representation
|
|
83
|
+
|
|
84
|
+
see: https://github.com/microsoft/language-server-protocol/issues/376
|
|
85
|
+
|
|
86
|
+
Arguments:
|
|
87
|
+
lines (list):
|
|
88
|
+
The content of the document which the position refers to.
|
|
89
|
+
position (Position):
|
|
90
|
+
The line and character offset in UTF-[32|16|8] code units.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
The position with `character` being converted to UTF-32 code units.
|
|
94
|
+
"""
|
|
95
|
+
if len(lines) == 0:
|
|
96
|
+
return types.Position(0, 0)
|
|
97
|
+
if position.line >= len(lines):
|
|
98
|
+
return types.Position(len(lines) - 1, self.client_num_units(lines[-1]))
|
|
99
|
+
|
|
100
|
+
_line = lines[position.line]
|
|
101
|
+
_line = _line.replace("\r\n", "\n") # TODO: it's a bit of a hack
|
|
102
|
+
_client_len = self.client_num_units(_line)
|
|
103
|
+
_utf32_len = len(_line)
|
|
104
|
+
|
|
105
|
+
if _client_len == 0:
|
|
106
|
+
return types.Position(position.line, 0)
|
|
107
|
+
|
|
108
|
+
_client_end_of_line = self.client_num_units(_line)
|
|
109
|
+
if position.character > _client_end_of_line:
|
|
110
|
+
position.character = _client_end_of_line - 1
|
|
111
|
+
|
|
112
|
+
_client_index = 0
|
|
113
|
+
utf32_index = 0
|
|
114
|
+
while True:
|
|
115
|
+
_is_searching_queried_position = _client_index < position.character
|
|
116
|
+
_is_before_end_of_line = utf32_index < _utf32_len
|
|
117
|
+
_is_searching_for_position = (
|
|
118
|
+
_is_searching_queried_position and _is_before_end_of_line
|
|
119
|
+
)
|
|
120
|
+
if not _is_searching_for_position:
|
|
121
|
+
break
|
|
122
|
+
|
|
123
|
+
_current_char = _line[utf32_index]
|
|
124
|
+
_is_double_width = Position.is_char_beyond_multilingual_plane(_current_char)
|
|
125
|
+
if _is_double_width:
|
|
126
|
+
if self.encoding == types.PositionEncodingKind.Utf32:
|
|
127
|
+
_client_index += 1
|
|
128
|
+
if self.encoding == types.PositionEncodingKind.Utf8:
|
|
129
|
+
_client_index += 4
|
|
130
|
+
_client_index += 2
|
|
131
|
+
else:
|
|
132
|
+
_client_index += 1
|
|
133
|
+
utf32_index += 1
|
|
134
|
+
|
|
135
|
+
position = types.Position(line=position.line, character=utf32_index)
|
|
136
|
+
return position
|
|
137
|
+
|
|
138
|
+
def position_to_client_units(
|
|
139
|
+
self, lines: List[str], position: types.Position
|
|
140
|
+
) -> types.Position:
|
|
141
|
+
"""
|
|
142
|
+
Convert the position.character from its internal UTF-32 representation
|
|
143
|
+
to client-supported UTF-[32|16|8] code units.
|
|
144
|
+
|
|
145
|
+
Arguments:
|
|
146
|
+
lines (list):
|
|
147
|
+
The content of the document which the position refers to.
|
|
148
|
+
position (Position):
|
|
149
|
+
The line and character offset in UTF-32 code units.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
The position with `character` being converted to UTF-[32|16|8] code units.
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
character = self.client_num_units(
|
|
156
|
+
lines[position.line][: position.character]
|
|
157
|
+
)
|
|
158
|
+
return types.Position(
|
|
159
|
+
line=position.line,
|
|
160
|
+
character=character,
|
|
161
|
+
)
|
|
162
|
+
except IndexError:
|
|
163
|
+
return types.Position(line=len(lines), character=0)
|
|
164
|
+
|
|
165
|
+
def range_from_client_units(
|
|
166
|
+
self, lines: List[str], range: types.Range
|
|
167
|
+
) -> types.Range:
|
|
168
|
+
"""
|
|
169
|
+
Convert range.[start|end].character from UTF-[32|16|8] code units to UTF-32.
|
|
170
|
+
|
|
171
|
+
Arguments:
|
|
172
|
+
lines (list):
|
|
173
|
+
The content of the document which the range refers to.
|
|
174
|
+
range (Range):
|
|
175
|
+
The line and character offset in UTF-[32|16|8] code units.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
The range with `character` offsets being converted to UTF-32 code units.
|
|
179
|
+
"""
|
|
180
|
+
range_new = types.Range(
|
|
181
|
+
start=self.position_from_client_units(lines, range.start),
|
|
182
|
+
end=self.position_from_client_units(lines, range.end),
|
|
183
|
+
)
|
|
184
|
+
return range_new
|
|
185
|
+
|
|
186
|
+
def range_to_client_units(
|
|
187
|
+
self, lines: List[str], range: types.Range
|
|
188
|
+
) -> types.Range:
|
|
189
|
+
"""
|
|
190
|
+
Convert range.[start|end].character from UTF-32 to UTF-[32|16|8] code units.
|
|
191
|
+
|
|
192
|
+
Arguments:
|
|
193
|
+
lines (list):
|
|
194
|
+
The content of the document which the range refers to.
|
|
195
|
+
range (Range):
|
|
196
|
+
The line and character offset in code units.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
The range with `character` offsets being converted to UTF-[32|16|8] code units.
|
|
200
|
+
"""
|
|
201
|
+
return types.Range(
|
|
202
|
+
start=self.position_to_client_units(lines, range.start),
|
|
203
|
+
end=self.position_to_client_units(lines, range.end),
|
|
204
|
+
)
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# Original work Copyright 2017 Palantir Technologies, Inc. #
|
|
3
|
+
# Original work licensed under the MIT License. #
|
|
4
|
+
# See ThirdPartyNotices.txt in the project root for license information. #
|
|
5
|
+
# All modifications Copyright (c) Open Law Library. All rights reserved. #
|
|
6
|
+
# #
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License") #
|
|
8
|
+
# you may not use this file except in compliance with the License. #
|
|
9
|
+
# You may obtain a copy of the License at #
|
|
10
|
+
# #
|
|
11
|
+
# http: // www.apache.org/licenses/LICENSE-2.0 #
|
|
12
|
+
# #
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software #
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS, #
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
|
16
|
+
# See the License for the specific language governing permissions and #
|
|
17
|
+
# limitations under the License. #
|
|
18
|
+
############################################################################
|
|
19
|
+
import io
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
from typing import List, Optional, Pattern, Union
|
|
24
|
+
|
|
25
|
+
from jaclang.vendor.pygls.lsprotocol import types
|
|
26
|
+
|
|
27
|
+
from jaclang.vendor.pygls.uris import to_fs_path
|
|
28
|
+
from .position import Position
|
|
29
|
+
|
|
30
|
+
# TODO: this is not the best e.g. we capture numbers
|
|
31
|
+
RE_END_WORD = re.compile("^[A-Za-z_0-9]*")
|
|
32
|
+
RE_START_WORD = re.compile("[A-Za-z_0-9]*$")
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TextDocument(object):
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
uri: str,
|
|
41
|
+
source: Optional[str] = None,
|
|
42
|
+
version: Optional[int] = None,
|
|
43
|
+
language_id: Optional[str] = None,
|
|
44
|
+
local: bool = True,
|
|
45
|
+
sync_kind: types.TextDocumentSyncKind = types.TextDocumentSyncKind.Incremental,
|
|
46
|
+
position_encoding: Optional[
|
|
47
|
+
Union[types.PositionEncodingKind, str]
|
|
48
|
+
] = types.PositionEncodingKind.Utf16,
|
|
49
|
+
):
|
|
50
|
+
self.uri = uri
|
|
51
|
+
self.version = version
|
|
52
|
+
path = to_fs_path(uri)
|
|
53
|
+
if path is None:
|
|
54
|
+
raise Exception("`path` cannot be None")
|
|
55
|
+
self.path = path
|
|
56
|
+
self.language_id = language_id
|
|
57
|
+
self.filename: Optional[str] = os.path.basename(self.path)
|
|
58
|
+
|
|
59
|
+
self._local = local
|
|
60
|
+
self._source = source
|
|
61
|
+
|
|
62
|
+
self._is_sync_kind_full = sync_kind == types.TextDocumentSyncKind.Full
|
|
63
|
+
self._is_sync_kind_incremental = (
|
|
64
|
+
sync_kind == types.TextDocumentSyncKind.Incremental
|
|
65
|
+
)
|
|
66
|
+
self._is_sync_kind_none = sync_kind == types.TextDocumentSyncKind.None_
|
|
67
|
+
|
|
68
|
+
self.position = Position(encoding=position_encoding)
|
|
69
|
+
|
|
70
|
+
def __str__(self):
|
|
71
|
+
return str(self.uri)
|
|
72
|
+
|
|
73
|
+
def _apply_incremental_change(
|
|
74
|
+
self, change: types.TextDocumentContentChangeEvent_Type1
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Apply an ``Incremental`` text change to the document"""
|
|
77
|
+
lines = self.lines
|
|
78
|
+
text = change.text
|
|
79
|
+
change_range = change.range
|
|
80
|
+
|
|
81
|
+
range = self.position.range_from_client_units(lines, change_range)
|
|
82
|
+
start_line = range.start.line
|
|
83
|
+
start_col = range.start.character
|
|
84
|
+
end_line = range.end.line
|
|
85
|
+
end_col = range.end.character
|
|
86
|
+
|
|
87
|
+
# Check for an edit occurring at the very end of the file
|
|
88
|
+
if start_line == len(lines):
|
|
89
|
+
self._source = self.source + text
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
new = io.StringIO()
|
|
93
|
+
|
|
94
|
+
# Iterate over the existing document until we hit the edit range,
|
|
95
|
+
# at which point we write the new text, then loop until we hit
|
|
96
|
+
# the end of the range and continue writing.
|
|
97
|
+
for i, line in enumerate(lines):
|
|
98
|
+
if i < start_line:
|
|
99
|
+
new.write(line)
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
if i > end_line:
|
|
103
|
+
new.write(line)
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
if i == start_line:
|
|
107
|
+
new.write(line[:start_col])
|
|
108
|
+
new.write(text)
|
|
109
|
+
|
|
110
|
+
if i == end_line:
|
|
111
|
+
new.write(line[end_col:])
|
|
112
|
+
|
|
113
|
+
self._source = new.getvalue()
|
|
114
|
+
|
|
115
|
+
def _apply_full_change(self, change: types.TextDocumentContentChangeEvent) -> None:
|
|
116
|
+
"""Apply a ``Full`` text change to the document."""
|
|
117
|
+
self._source = change.text
|
|
118
|
+
|
|
119
|
+
def _apply_none_change(self, _: types.TextDocumentContentChangeEvent) -> None:
|
|
120
|
+
"""Apply a ``None`` text change to the document
|
|
121
|
+
|
|
122
|
+
Currently does nothing, provided for consistency.
|
|
123
|
+
"""
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
def apply_change(self, change: types.TextDocumentContentChangeEvent) -> None:
|
|
127
|
+
"""Apply a text change to a document, considering TextDocumentSyncKind
|
|
128
|
+
|
|
129
|
+
Performs either
|
|
130
|
+
:attr:`~lsprotocol.types.TextDocumentSyncKind.Incremental`,
|
|
131
|
+
:attr:`~lsprotocol.types.TextDocumentSyncKind.Full`, or no synchronization
|
|
132
|
+
based on both the client request and server capabilities.
|
|
133
|
+
|
|
134
|
+
.. admonition:: ``Incremental`` versus ``Full`` synchronization
|
|
135
|
+
|
|
136
|
+
Even if a server accepts ``Incremantal`` SyncKinds, clients may request
|
|
137
|
+
a ``Full`` SyncKind. In LSP 3.x, clients make this request by omitting
|
|
138
|
+
both Range and RangeLength from their request. Consequently, the
|
|
139
|
+
attributes "range" and "rangeLength" will be missing from ``Full``
|
|
140
|
+
content update client requests in the pygls Python library.
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
if isinstance(change, types.TextDocumentContentChangeEvent_Type1):
|
|
144
|
+
if self._is_sync_kind_incremental:
|
|
145
|
+
self._apply_incremental_change(change)
|
|
146
|
+
return
|
|
147
|
+
# Log an error, but still perform full update to preserve existing
|
|
148
|
+
# assumptions in test_document/test_document_full_edit. Test breaks
|
|
149
|
+
# otherwise, and fixing the tests would require a broader fix to
|
|
150
|
+
# protocol.py.
|
|
151
|
+
logger.error(
|
|
152
|
+
"Unsupported client-provided TextDocumentContentChangeEvent. "
|
|
153
|
+
"Please update / submit a Pull Request to your LSP client."
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if self._is_sync_kind_none:
|
|
157
|
+
self._apply_none_change(change)
|
|
158
|
+
else:
|
|
159
|
+
self._apply_full_change(change)
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def lines(self) -> List[str]:
|
|
163
|
+
return self.source.splitlines(True)
|
|
164
|
+
|
|
165
|
+
def offset_at_position(self, client_position: types.Position) -> int:
|
|
166
|
+
"""Return the character offset pointed at by the given client_position."""
|
|
167
|
+
lines = self.lines
|
|
168
|
+
server_position = self.position.position_from_client_units(
|
|
169
|
+
lines, client_position
|
|
170
|
+
)
|
|
171
|
+
row, col = server_position.line, server_position.character
|
|
172
|
+
return col + sum(self.position.client_num_units(line) for line in lines[:row])
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def source(self) -> str:
|
|
176
|
+
if self._source is None:
|
|
177
|
+
with io.open(self.path, "r", encoding="utf-8") as f:
|
|
178
|
+
return f.read()
|
|
179
|
+
return self._source
|
|
180
|
+
|
|
181
|
+
def word_at_position(
|
|
182
|
+
self,
|
|
183
|
+
client_position: types.Position,
|
|
184
|
+
re_start_word: Pattern[str] = RE_START_WORD,
|
|
185
|
+
re_end_word: Pattern[str] = RE_END_WORD,
|
|
186
|
+
) -> str:
|
|
187
|
+
"""Return the word at position.
|
|
188
|
+
|
|
189
|
+
The word is constructed in two halves, the first half is found by taking
|
|
190
|
+
the first match of ``re_start_word`` on the line up until
|
|
191
|
+
``position.character``.
|
|
192
|
+
|
|
193
|
+
The second half is found by taking ``position.character`` up until the
|
|
194
|
+
last match of ``re_end_word`` on the line.
|
|
195
|
+
|
|
196
|
+
:func:`python:re.findall` is used to find the matches.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
position
|
|
201
|
+
The line and character offset.
|
|
202
|
+
|
|
203
|
+
re_start_word
|
|
204
|
+
The regular expression for extracting the word backward from
|
|
205
|
+
position. The default pattern is ``[A-Za-z_0-9]*$``.
|
|
206
|
+
|
|
207
|
+
re_end_word
|
|
208
|
+
The regular expression for extracting the word forward from
|
|
209
|
+
position. The default pattern is ``^[A-Za-z_0-9]*``.
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
str
|
|
214
|
+
The word (obtained by concatenating the two matches) at position.
|
|
215
|
+
"""
|
|
216
|
+
lines = self.lines
|
|
217
|
+
if client_position.line >= len(lines):
|
|
218
|
+
return ""
|
|
219
|
+
|
|
220
|
+
server_position = self.position.position_from_client_units(
|
|
221
|
+
lines, client_position
|
|
222
|
+
)
|
|
223
|
+
row, col = server_position.line, server_position.character
|
|
224
|
+
line = lines[row]
|
|
225
|
+
# Split word in two
|
|
226
|
+
start = line[:col]
|
|
227
|
+
end = line[col:]
|
|
228
|
+
|
|
229
|
+
# Take end of start and start of end to find word
|
|
230
|
+
# These are guaranteed to match, even if they match the empty string
|
|
231
|
+
m_start = re_start_word.findall(start)
|
|
232
|
+
m_end = re_end_word.findall(end)
|
|
233
|
+
|
|
234
|
+
return m_start[0] + m_end[-1]
|