Files
Nim/compiler/suggestsymdb.nim
Nikolay Nikolov 899ba01ccf + added nimsuggest support for exception inlay hints (#23202)
This adds nimsuggest support for displaying inlay hints for exceptions.
An inlay hint is displayed around function calls, that can raise an
exception, which isn't handled in the current subroutine (in other
words, exceptions that can propagate back to the caller). On mouse hover
on top of the hint, a list of exceptions that could propagate is shown.

The changes, required to support this are already commited to
nimlangserver and the VS code extension. The extension and the server
allow configuration for whether these new exception hints are enabled
(they can be enabled or disabled independently from the type hints), as
well as the inlay strings that are inserted before and after the name of
the function, around the function call. Potentially, one of these
strings can be empty, for example, the user can choose to add an inlay
hint only before the name of the function, or only after the name of the
function.
2024-03-15 18:20:10 +01:00

213 lines
5.2 KiB
Nim

import std/[intsets, tables, algorithm, assertions]
import ast, lineinfos, msgs
type
PackedBoolArray* = object
s: IntSet
len: int
TinyLineInfo* = object
line*: uint16
col*: int16
SymInfoPair* = object
sym*: PSym
info*: TLineInfo
caughtExceptions*: seq[PType]
caughtExceptionsSet*: bool
isDecl*: bool
SuggestFileSymbolDatabase* = object
lineInfo*: seq[TinyLineInfo]
sym*: seq[PSym]
caughtExceptions*: seq[seq[PType]]
caughtExceptionsSet*: PackedBoolArray
isDecl*: PackedBoolArray
fileIndex*: FileIndex
trackCaughtExceptions*: bool
isSorted*: bool
SuggestSymbolDatabase* = Table[FileIndex, SuggestFileSymbolDatabase]
func newPackedBoolArray*(): PackedBoolArray =
PackedBoolArray(
s: initIntSet(),
len: 0
)
func low*(s: PackedBoolArray): int =
0
func high*(s: PackedBoolArray): int =
s.len - 1
func `[]`*(s: PackedBoolArray; idx: int): bool =
s.s.contains(idx)
proc `[]=`*(s: var PackedBoolArray; idx: int; v: bool) =
if v:
s.s.incl(idx)
else:
s.s.excl(idx)
proc add*(s: var PackedBoolArray; v: bool) =
inc(s.len)
if v:
s.s.incl(s.len - 1)
proc reverse*(s: var PackedBoolArray) =
var
reversedSet = initIntSet()
for i in 0..s.high:
if s.s.contains(i):
reversedSet.incl(s.high - i)
s.s = reversedSet
proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair =
SymInfoPair(
sym: s.sym[idx],
info: TLineInfo(
line: s.lineInfo[idx].line,
col: s.lineInfo[idx].col,
fileIndex: s.fileIndex
),
caughtExceptions:
if s.trackCaughtExceptions:
s.caughtExceptions[idx]
else:
@[],
caughtExceptionsSet:
if s.trackCaughtExceptions:
s.caughtExceptionsSet[idx]
else:
false,
isDecl: s.isDecl[idx]
)
proc reverse*(s: var SuggestFileSymbolDatabase) =
s.lineInfo.reverse()
s.sym.reverse()
s.caughtExceptions.reverse()
s.caughtExceptionsSet.reverse()
s.isDecl.reverse()
proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase =
SuggestFileSymbolDatabase(
lineInfo: @[],
sym: @[],
caughtExceptions: @[],
caughtExceptionsSet: newPackedBoolArray(),
isDecl: newPackedBoolArray(),
fileIndex: aFileIndex,
trackCaughtExceptions: aTrackCaughtExceptions,
isSorted: true
)
proc exactEquals*(a, b: TinyLineInfo): bool =
result = a.line == b.line and a.col == b.col
proc `==`*(a, b: SymInfoPair): bool =
result = a.sym == b.sym and a.info.exactEquals(b.info)
func cmp*(a: TinyLineInfo; b: TinyLineInfo): int =
result = cmp(a.line, b.line)
if result == 0:
result = cmp(a.col, b.col)
func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int =
result = cmp(s.lineInfo[i], s.lineInfo[j])
if result == 0:
result = cmp(s.isDecl[i], s.isDecl[j])
proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
if i == j:
return
var tmp1 = s.lineInfo[i]
s.lineInfo[i] = s.lineInfo[j]
s.lineInfo[j] = tmp1
if s.trackCaughtExceptions:
var tmp2 = s.caughtExceptions[i]
s.caughtExceptions[i] = s.caughtExceptions[j]
s.caughtExceptions[j] = tmp2
var tmp3 = s.caughtExceptionsSet[i]
s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j]
s.caughtExceptionsSet[j] = tmp3
var tmp4 = s.isDecl[i]
s.isDecl[i] = s.isDecl[j]
s.isDecl[j] = tmp4
var tmp5 = s.sym[i]
s.sym[i] = s.sym[j]
s.sym[j] = tmp5
proc quickSort(s: var SuggestFileSymbolDatabase; ll, rr: int) =
var
i, j, pivotIdx: int
l = ll
r = rr
while true:
i = l
j = r
pivotIdx = l + ((r - l) shr 1)
while true:
while (i < pivotIdx) and (s.compare(pivotIdx, i) > 0):
inc i
while (j > pivotIdx) and (s.compare(pivotIdx, j) < 0):
dec j
if i < j:
s.exchange(i, j)
if pivotIdx == i:
pivotIdx = j
inc i
elif pivotIdx == j:
pivotIdx = i
dec j
else:
inc i
dec j
else:
break
if (pivotIdx - l) < (r - pivotIdx):
if (l + 1) < pivotIdx:
s.quickSort(l, pivotIdx - 1)
l = pivotIdx + 1
else:
if (pivotIdx + 1) < r:
s.quickSort(pivotIdx + 1, r)
if (l + 1) < pivotIdx:
r = pivotIdx - 1
else:
break
if l >= r:
break
proc sort*(s: var SuggestFileSymbolDatabase) =
s.quickSort(s.lineInfo.low, s.lineInfo.high)
s.isSorted = true
proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) =
doAssert(v.info.fileIndex == s.fileIndex)
s.lineInfo.add(TinyLineInfo(
line: v.info.line,
col: v.info.col
))
s.sym.add(v.sym)
s.isDecl.add(v.isDecl)
if s.trackCaughtExceptions:
s.caughtExceptions.add(v.caughtExceptions)
s.caughtExceptionsSet.add(v.caughtExceptionsSet)
s.isSorted = false
proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) =
s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v)
proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int =
doAssert(li.fileIndex == s.fileIndex)
if not s.isSorted:
s.sort()
var q = TinyLineInfo(
line: li.line,
col: li.col
)
result = binarySearch(s.lineInfo, q, cmp)