define 宏函数与调用点使用 macro token;常量保持 variable+readonly;多行字符串按行拆分;修正符号/出现点合并顺序导致整行 token 丢失;大文档诊断 scratch 按源大小扩容。
225 lines
7.4 KiB
Plaintext
225 lines
7.4 KiB
Plaintext
from stdint import *
|
|
import string
|
|
import memhub
|
|
import lib.lsp.LspPools as LspPools
|
|
import lib.lsp.LspPositions as LspPositions
|
|
|
|
|
|
define LSP_DOC_POOL_SIZE = 4194304
|
|
# Parse scratch arena shared by diagnostics: reset before each publish so ASTs and token lists do
|
|
# not accumulate across edits. It lives in the per-documents object, whose owner is the permanent
|
|
# pool, because the message pool is reset after every request.
|
|
# The arena grows with the largest published document: a 4 MiB arena is exhausted by a few hundred
|
|
# KiB of source, and allocation failure aborts the parse.
|
|
define LSP_SCRATCH_POOL_SIZE = 4194304
|
|
define LSP_SCRATCH_BYTES_PER_SOURCE = 128
|
|
define LSP_SCRATCH_POOL_MAX = 134217728
|
|
|
|
|
|
class LspDocument:
|
|
uri: str
|
|
text: str
|
|
version: int
|
|
index: LspPositions.LspLineIndex*
|
|
pool: LspPools.LspPool*
|
|
spare: LspPools.LspPool*
|
|
|
|
|
|
class LspDocuments:
|
|
pool: memhub.MemManager*
|
|
docs: list[LspDocument*]*
|
|
count: int
|
|
scratch: LspPools.LspPool*
|
|
|
|
def __new__(self, pool: memhub.MemManager*) -> rawptr:
|
|
p: LspDocuments* = (LspDocuments*)(pool.alloc(LspDocuments.__sizeof__()))
|
|
if p:
|
|
string.memset(p, 0, LspDocuments.__sizeof__())
|
|
return rawptr(p)
|
|
|
|
def __init__(self, pool: memhub.MemManager*):
|
|
self.pool = pool
|
|
self.docs = list[LspDocument*](pool, 8)
|
|
self.count = 0
|
|
self.scratch = LspPools.Create(pool, sizet(LSP_SCRATCH_POOL_SIZE))
|
|
|
|
def Scratch(self) -> LspPools.LspPool*:
|
|
return self.scratch
|
|
|
|
def ScratchFor(self, source_len: sizet) -> LspPools.LspPool*:
|
|
"""Return a scratch arena large enough to parse a document of source_len bytes."""
|
|
want: sizet = source_len * sizet(LSP_SCRATCH_BYTES_PER_SOURCE)
|
|
if want < sizet(LSP_SCRATCH_POOL_SIZE):
|
|
want = sizet(LSP_SCRATCH_POOL_SIZE)
|
|
if want > sizet(LSP_SCRATCH_POOL_MAX):
|
|
want = sizet(LSP_SCRATCH_POOL_MAX)
|
|
if self.scratch is not None and self.scratch.size >= want:
|
|
return self.scratch
|
|
if self.scratch is not None:
|
|
self.scratch.Destroy()
|
|
self.scratch = None
|
|
self.scratch = LspPools.Create(self.pool, want)
|
|
return self.scratch
|
|
|
|
def Find(self, uri: str) -> LspDocument*:
|
|
return self._Find(uri)
|
|
|
|
def _Find(self, uri: str) -> LspDocument*:
|
|
if uri is None:
|
|
return None
|
|
i: int = 0
|
|
while i < self.count:
|
|
doc: LspDocument* = self.docs[i]
|
|
if doc is not None and doc.uri is not None:
|
|
if string.strcmp(doc.uri, uri) == 0:
|
|
return doc
|
|
i += 1
|
|
return None
|
|
|
|
def _DestroyDoc(self, doc: LspDocument*) -> int:
|
|
if doc is None:
|
|
return -1
|
|
if doc.pool is not None:
|
|
doc.pool.Destroy()
|
|
doc.pool = None
|
|
if doc.spare is not None:
|
|
doc.spare.Destroy()
|
|
doc.spare = None
|
|
return 0
|
|
|
|
def Open(self, uri: str, text: str, version: int) -> LspDocument*:
|
|
if uri is None or text is None:
|
|
return None
|
|
existing: LspDocument* = self._Find(uri)
|
|
if existing is not None:
|
|
self.Close(uri)
|
|
|
|
doc: LspDocument* = (LspDocument*)(self.pool.alloc(LspDocument.__sizeof__()))
|
|
if doc is None:
|
|
return None
|
|
string.memset(doc, 0, LspDocument.__sizeof__())
|
|
doc.uri = LspPools.DupStr(self.pool, uri)
|
|
if doc.uri is None:
|
|
return None
|
|
|
|
p1: LspPools.LspPool* = LspPools.Create(self.pool, sizet(LSP_DOC_POOL_SIZE))
|
|
p2: LspPools.LspPool* = LspPools.Create(self.pool, sizet(LSP_DOC_POOL_SIZE))
|
|
if p1 is None or p2 is None:
|
|
if p1 is not None:
|
|
p1.Destroy()
|
|
if p2 is not None:
|
|
p2.Destroy()
|
|
return None
|
|
doc.pool = p1
|
|
doc.spare = p2
|
|
doc.text = LspPools.DupStr(p1.mb, text)
|
|
if doc.text is None:
|
|
self._DestroyDoc(doc)
|
|
return None
|
|
doc.index = LspPositions.LspLineIndex(p1.mb)
|
|
if doc.index is None or doc.index.Build(doc.text) != 0:
|
|
self._DestroyDoc(doc)
|
|
return None
|
|
doc.version = version
|
|
self.docs.append(doc)
|
|
self.count += 1
|
|
return doc
|
|
|
|
def ApplyChange(self, uri: str, text: str, has_range: int,
|
|
start_line: int, start_char: int,
|
|
end_line: int, end_char: int,
|
|
version: int) -> int:
|
|
doc: LspDocument* = self._Find(uri)
|
|
if doc is None or doc.index is None or doc.spare is None:
|
|
return -1
|
|
new_pool: LspPools.LspPool* = doc.spare
|
|
if new_pool.Reset() < 0:
|
|
return -1
|
|
|
|
old_len: sizet = doc.index.Length()
|
|
start: sizet = 0
|
|
end: sizet = old_len
|
|
if has_range != 0:
|
|
if doc.index.RangeToOffsets(start_line, start_char, end_line, end_char,
|
|
&start, &end) != 0:
|
|
return -1
|
|
if end > old_len:
|
|
return -1
|
|
|
|
add_len: sizet = 0
|
|
if text is not None:
|
|
add_len = string.strlen(text)
|
|
new_len: sizet = start + add_len + (old_len - end)
|
|
buf: bytes = new_pool.mb.alloc(new_len + 1)
|
|
if buf is None:
|
|
return -1
|
|
if start > 0:
|
|
string.memcpy(buf, doc.text, start)
|
|
if add_len > 0:
|
|
string.memcpy(buf + start, text, add_len)
|
|
if old_len > end:
|
|
string.memcpy(buf + start + add_len, doc.text + end, old_len - end)
|
|
buf[new_len] = 0
|
|
|
|
new_index: LspPositions.LspLineIndex* = LspPositions.LspLineIndex(new_pool.mb)
|
|
if new_index is None or new_index.Build(str(buf)) != 0:
|
|
return -1
|
|
|
|
old_pool: LspPools.LspPool* = doc.pool
|
|
doc.pool = new_pool
|
|
doc.spare = old_pool
|
|
doc.text = str(buf)
|
|
doc.index = new_index
|
|
doc.version = version
|
|
if old_pool is not None:
|
|
old_pool.Reset()
|
|
return 0
|
|
|
|
def Close(self, uri: str) -> int:
|
|
i: int = 0
|
|
while i < self.count:
|
|
doc: LspDocument* = self.docs[i]
|
|
if doc is not None and doc.uri is not None:
|
|
if string.strcmp(doc.uri, uri) == 0:
|
|
self._DestroyDoc(doc)
|
|
self.docs[i] = self.docs[self.count - 1]
|
|
self.docs.pop()
|
|
self.count -= 1
|
|
return 0
|
|
i += 1
|
|
return -1
|
|
|
|
def Count(self) -> int:
|
|
return self.count
|
|
|
|
def DocAt(self, index: int) -> LspDocument*:
|
|
if index < 0 or index >= self.count:
|
|
return None
|
|
return self.docs[index]
|
|
|
|
def UsedBytes(self) -> sizet:
|
|
total: sizet = 0
|
|
i: int = 0
|
|
while i < self.count:
|
|
doc: LspDocument* = self.docs[i]
|
|
if doc is not None:
|
|
if doc.pool is not None:
|
|
total += doc.pool.UsedBytes()
|
|
if doc.spare is not None:
|
|
total += doc.spare.UsedBytes()
|
|
i += 1
|
|
return total
|
|
|
|
def FreeBytes(self) -> sizet:
|
|
total: sizet = 0
|
|
i: int = 0
|
|
while i < self.count:
|
|
doc: LspDocument* = self.docs[i]
|
|
if doc is not None:
|
|
if doc.pool is not None:
|
|
total += doc.pool.FreeBytes()
|
|
if doc.spare is not None:
|
|
total += doc.spare.FreeBytes()
|
|
i += 1
|
|
return total
|