+ 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.
This commit is contained in:
Nikolay Nikolov
2024-03-15 19:20:10 +02:00
committed by GitHub
parent c2c00776e3
commit 899ba01ccf
7 changed files with 480 additions and 50 deletions

View File

@@ -11,9 +11,9 @@
## represents a complete Nim project. Single modules can either be kept in RAM
## or stored in a rod-file.
import std/[intsets, tables, hashes, strtabs]
import std/[intsets, tables, hashes, strtabs, algorithm]
import ../dist/checksums/src/checksums/md5
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
import ic / [packed_ast, ic]
@@ -55,11 +55,6 @@ type
concreteTypes*: seq[FullId]
inst*: PInstantiation
SymInfoPair* = object
sym*: PSym
info*: TLineInfo
isDecl*: bool
PipelinePass* = enum
NonePass
SemPass
@@ -108,7 +103,7 @@ type
doStopCompile*: proc(): bool {.closure.}
usageSym*: PSym # for nimsuggest
owners*: seq[PSym]
suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
suggestSymbols*: SuggestSymbolDatabase
suggestErrors*: Table[FileIndex, seq[Suggest]]
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
bucketTable*: CountTable[ItemId]
@@ -506,7 +501,7 @@ proc initModuleGraphFields(result: ModuleGraph) =
result.importStack = @[]
result.inclToMod = initTable[FileIndex, FileIndex]()
result.owners = @[]
result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]()
result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
result.methods = @[]
result.compilerprocs = initStrTable()
@@ -712,16 +707,14 @@ func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
## Check if symbol belongs to the 'stdlib' package.
sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
proc `==`*(a, b: SymInfoPair): bool =
result = a.sym == b.sym and a.info.exactEquals(b.info)
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): seq[SymInfoPair] =
result = graph.suggestSymbols.getOrDefault(fileIdx, @[])
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase =
result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions))
doAssert(result.fileIndex == fileIdx)
iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
for xs in g.suggestSymbols.values:
for x in xs:
yield x
for i in xs.lineInfo.low..xs.lineInfo.high:
yield xs.getSymInfoPair(i)
iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
for xs in g.suggestErrors.values:

View File

@@ -86,6 +86,7 @@ type # please make sure we have under 32 options
# also: generate header file
optIdeDebug # idetools: debug mode
optIdeTerse # idetools: use terse descriptions
optIdeExceptionInlayHints
optExcessiveStackTrace # fully qualified module filenames
optShowAllMismatches # show all overloading resolution candidates
optWholeProject # for 'doc': output any dependency
@@ -298,6 +299,7 @@ type
SuggestInlayHintKind* = enum
sihkType = "Type",
sihkParameter = "Parameter"
sihkException = "Exception"
SuggestInlayHint* = ref object
kind*: SuggestInlayHintKind

View File

@@ -11,9 +11,9 @@ import
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, options, guards, lineinfos, semfold, semdata,
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
semstrictfuncs
semstrictfuncs, suggestsymdb
import std/[tables, intsets, strutils]
import std/[tables, intsets, strutils, sequtils]
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -66,8 +66,12 @@ discard """
"""
type
CaughtExceptionsStack = object
nodes: seq[seq[PType]]
TEffects = object
exc: PNode # stack of exceptions
when defined(nimsuggest):
caughtExceptions: CaughtExceptionsStack
tags: PNode # list of tags
forbids: PNode # list of tags
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int
@@ -411,7 +415,7 @@ proc throws(tracked, n, orig: PNode) =
else:
tracked.add n
proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
proc getEbase*(g: ModuleGraph; info: TLineInfo): PType =
result = g.sysTypeFromName(info, "Exception")
proc excType(g: ModuleGraph; n: PNode): PType =
@@ -492,6 +496,18 @@ proc catchesAll(tracked: PEffects) =
if tracked.exc.len > 0:
setLen(tracked.exc.sons, tracked.bottom)
proc push(s: var CaughtExceptionsStack) =
s.nodes.add(@[])
proc pop(s: var CaughtExceptionsStack) =
s.nodes.del(high(s.nodes))
proc addCatch(s: var CaughtExceptionsStack, e: PType) =
s.nodes[high(s.nodes)].add(e)
proc addCatchAll(s: var CaughtExceptionsStack) =
s.nodes[high(s.nodes)].add(nil)
proc track(tracked: PEffects, n: PNode)
proc trackTryStmt(tracked: PEffects, n: PNode) =
let oldBottom = tracked.bottom
@@ -500,12 +516,33 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
let oldState = tracked.init.len
var inter: TIntersection = @[]
when defined(nimsuggest):
tracked.caughtExceptions.push
for i in 1..<n.len:
let b = n[i]
if b.kind == nkExceptBranch:
if b.len == 1:
tracked.caughtExceptions.addCatchAll
else:
for j in 0..<b.len - 1:
if b[j].isInfixAs():
assert(b[j][1].kind == nkType)
tracked.caughtExceptions.addCatch(b[j][1].typ)
else:
assert(b[j].kind == nkType)
tracked.caughtExceptions.addCatch(b[j].typ)
else:
assert b.kind == nkFinally
inc tracked.inTryStmt
track(tracked, n[0])
dec tracked.inTryStmt
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], bsNone)
when defined(nimsuggest):
tracked.caughtExceptions.pop
var branches = 1
var hasFinally = false
inc tracked.inExceptOrFinallyStmt
@@ -917,6 +954,19 @@ proc checkForSink(tracked: PEffects; n: PNode) =
if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options:
checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n)
proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
when defined(nimsuggest):
proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) =
var si = q.findSymInfoIndex(info)
if si != -1:
q.caughtExceptionsSet[si] = true
for w1 in tracked.caughtExceptions.nodes:
for w2 in w1:
q.caughtExceptions[si].add(w2)
if optIdeExceptionInlayHints in tracked.config.globalOptions:
internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)
proc trackCall(tracked: PEffects; n: PNode) =
template gcsafeAndSideeffectCheck() =
if notGcSafe(op) and not importedFromC(a):
@@ -937,6 +987,13 @@ proc trackCall(tracked: PEffects; n: PNode) =
if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
createTypeBoundOps(tracked, n.typ, n.info)
when defined(nimsuggest):
var actualLoc = a.info
if n.kind == nkHiddenCallConv:
actualLoc = n.info
if a.kind == nkSym:
markCaughtExceptions(tracked, tracked.graph, actualLoc, a.sym, tracked.graph.usageSym)
let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil
if notConstExpr:
if a.kind == nkCast and a[1].typ.kind == tyProc:

View File

@@ -32,7 +32,7 @@
# included from sigmatch.nim
import prefixmatches
import prefixmatches, suggestsymdb
from wordrecg import wDeprecated, wError, wAddr, wYield
import std/[algorithm, sets, parseutils, tables]
@@ -114,6 +114,10 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo; skip
result = skipUntil(line, '`', column)
if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0:
result = 0
elif column >= 0 and line[column] == '`' and isOpeningBacktick(column):
result = skipUntil(line, '`', column + 1) + 2
if cmpIgnoreStyle(line[column + 1..column + result - 2], ident) != 0:
result = 0
elif ident[0] in linter.Letters and ident[^1] != '=':
result = identLen(line, column)
if cmpIgnoreStyle(line[column..column + result - 1], ident[0..min(result-1,len(ident)-1)]) != 0:
@@ -265,7 +269,7 @@ proc `$`*(suggest: Suggest): string =
result.add(sep)
result.add($suggest.endCol)
proc suggestToSuggestInlayHint*(sug: Suggest): SuggestInlayHint =
proc suggestToSuggestInlayTypeHint*(sug: Suggest): SuggestInlayHint =
SuggestInlayHint(
kind: sihkType,
line: sug.line,
@@ -277,6 +281,30 @@ proc suggestToSuggestInlayHint*(sug: Suggest): SuggestInlayHint =
tooltip: ""
)
proc suggestToSuggestInlayExceptionHintLeft*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint =
SuggestInlayHint(
kind: sihkException,
line: sug.line,
column: sug.column,
label: "try ",
paddingLeft: false,
paddingRight: false,
allowInsert: false,
tooltip: "propagated exceptions: " & $propagatedExceptions
)
proc suggestToSuggestInlayExceptionHintRight*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint =
SuggestInlayHint(
kind: sihkException,
line: sug.line,
column: sug.column + sug.tokenLen,
label: "!",
paddingLeft: false,
paddingRight: false,
allowInsert: false,
tooltip: "propagated exceptions: " & $propagatedExceptions
)
proc suggestResult*(conf: ConfigRef; s: Suggest) =
if not isNil(conf.suggestionResultHook):
conf.suggestionResultHook(s)
@@ -534,6 +562,16 @@ proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool =
else:
result = false
proc isTracked*(current, trackPos: TinyLineInfo, tokenLen: int): bool =
if current.line==trackPos.line:
let col = trackPos.col
if col >= current.col and col <= current.col+tokenLen-1:
result = true
else:
result = false
else:
result = false
when defined(nimsuggest):
# Since TLineInfo defined a == operator that doesn't include the column,
# we map TLineInfo to a unique int here for this lookup table:
@@ -584,7 +622,7 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
## misnamed: should be 'symDeclared'
let conf = g.config
when defined(nimsuggest):
g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info, isDecl: isDecl)
g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions
if conf.suggestVersion == 0:
if s.allUsages.len == 0:

212
compiler/suggestsymdb.nim Normal file
View File

@@ -0,0 +1,212 @@
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)

View File

@@ -8,6 +8,10 @@
#
import compiler/renderer
import compiler/types
import compiler/trees
import compiler/wordrecg
import compiler/sempass2
import strformat
import algorithm
import tables
@@ -34,7 +38,7 @@ import compiler / [options, commands, modules,
passes, passaux, msgs,
sigmatch, ast,
idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
pathutils, condsyms, syntaxes]
pathutils, condsyms, syntaxes, suggestsymdb]
when defined(nimPreviewSlimSystem):
import std/typedthreads
@@ -74,6 +78,8 @@ Options:
--tester implies --stdin and outputs a line
'""" & DummyEof & """' for the tester
--find attempts to find the project file of the current project
--exceptionInlayHints:on|off
globally turn exception inlay hints on|off
The server then listens to the connection and takes line-based commands.
@@ -127,7 +133,8 @@ const
"type 'terse' to toggle terse mode on/off"
#List of currently supported capabilities. So lang servers/ides can iterate over and check for what's enabled
Capabilities = [
"con" #current NimSuggest supports the `con` commmand
"con", #current NimSuggest supports the `con` commmand
"exceptionInlayHints"
]
proc parseQuoted(cmd: string; outp: var string; start: int): int =
@@ -699,6 +706,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
quit 0
else:
processSwitch(pass, p, conf)
of "exceptioninlayhints":
case p.val.normalize
of "", "on": incl(conf.globalOptions, optIdeExceptionInlayHints)
of "off": excl(conf.globalOptions, optIdeExceptionInlayHints)
else: processSwitch(pass, p, conf)
of "tester":
gMode = mstdin
gEmitEof = true
@@ -802,25 +814,57 @@ func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair]
result.add(itm)
result.reverse()
func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDatabase =
# xs contains duplicate items and we want to filter them by range because the
# sym may not match. This can happen when xs contains the same definition but
# with different signature because suggestSym might be called multiple times
# for the same symbol (e. g. including/excluding the pragma)
result = SuggestFileSymbolDatabase(
lineInfo: newSeqOfCap[TinyLineInfo](xs.lineInfo.len),
sym: newSeqOfCap[PSym](xs.sym.len),
isDecl: newPackedBoolArray(),
caughtExceptions: newSeqOfCap[seq[PType]](xs.caughtExceptions.len),
caughtExceptionsSet: newPackedBoolArray(),
fileIndex: xs.fileIndex,
trackCaughtExceptions: xs.trackCaughtExceptions,
isSorted: false
)
var i = xs.lineInfo.high
while i >= 0:
let itm = xs.lineInfo[i]
var found = false
for res in result.lineInfo:
if res.exactEquals(itm):
found = true
break
if not found:
result.add(xs.getSymInfoPair(i))
dec i
result.reverse()
proc findSymData(graph: ModuleGraph, trackPos: TLineInfo):
ref SymInfoPair =
for s in graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair:
if isTracked(s.info, trackPos, s.sym.name.s.len):
let db = graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair
doAssert(db.fileIndex == trackPos.fileIndex)
for i in db.lineInfo.low..db.lineInfo.high:
if isTracked(db.lineInfo[i], TinyLineInfo(line: trackPos.line, col: trackPos.col), db.sym[i].name.s.len):
var res = db.getSymInfoPair(i)
new(result)
result[] = s
result[] = res
break
func isInRange*(current, startPos, endPos: TLineInfo, tokenLen: int): bool =
result = current.fileIndex == startPos.fileIndex and
func isInRange*(current, startPos, endPos: TinyLineInfo, tokenLen: int): bool =
result =
(current.line > startPos.line or (current.line == startPos.line and current.col>=startPos.col)) and
(current.line < endPos.line or (current.line == endPos.line and current.col <= endPos.col))
proc findSymDataInRange(graph: ModuleGraph, startPos, endPos: TLineInfo):
seq[SymInfoPair] =
result = newSeq[SymInfoPair]()
for s in graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair:
if isInRange(s.info, startPos, endPos, s.sym.name.s.len):
result.add(s)
let db = graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair
for i in db.lineInfo.low..db.lineInfo.high:
if isInRange(db.lineInfo[i], TinyLineInfo(line: startPos.line, col: startPos.col), TinyLineInfo(line: endPos.line, col: endPos.col), db.sym[i].name.s.len):
result.add(db.getSymInfoPair(i))
proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
ref SymInfoPair =
@@ -859,7 +903,7 @@ proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo,
endLine = endLine, endCol = endCol)
suggestResult(graph.config, suggest)
proc suggestInlayHintResult(graph: ModuleGraph, sym: PSym, info: TLineInfo,
proc suggestInlayHintResultType(graph: ModuleGraph, sym: PSym, info: TLineInfo,
defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) =
let section = if defaultSection != ideNone:
defaultSection
@@ -870,12 +914,61 @@ proc suggestInlayHintResult(graph: ModuleGraph, sym: PSym, info: TLineInfo,
var suggestDef = symToSuggest(graph, sym, isLocal=false, section,
info, 100, PrefixMatch.None, false, 0, true,
endLine = endLine, endCol = endCol)
suggestDef.inlayHintInfo = suggestToSuggestInlayHint(suggestDef)
suggestDef.inlayHintInfo = suggestToSuggestInlayTypeHint(suggestDef)
suggestDef.section = ideInlayHints
if sym.kind == skForVar:
suggestDef.inlayHintInfo.allowInsert = false
suggestResult(graph.config, suggestDef)
proc suggestInlayHintResultException(graph: ModuleGraph, sym: PSym, info: TLineInfo,
defaultSection = ideNone, caughtExceptions: seq[PType], caughtExceptionsSet: bool, endLine: uint16 = 0, endCol = 0) =
if not caughtExceptionsSet:
return
if sym.kind == skParam and sfEffectsDelayed in sym.flags:
return
var raisesList: seq[PType] = @[getEbase(graph, info)]
let t = sym.typ
if not isNil(t) and not isNil(t.n) and t.n.len > 0 and t.n[0].len > exceptionEffects:
let effects = t.n[0]
if effects.kind == nkEffectList and effects.len == effectListLen:
let effs = effects[exceptionEffects]
if not isNil(effs):
raisesList = @[]
for eff in items(effs):
if not isNil(eff):
raisesList.add(eff.typ)
var propagatedExceptionList: seq[PType] = @[]
for re in raisesList:
var exceptionIsPropagated = true
for ce in caughtExceptions:
if isNil(ce) or safeInheritanceDiff(re, ce) <= 0:
exceptionIsPropagated = false
break
if exceptionIsPropagated:
propagatedExceptionList.add(re)
if propagatedExceptionList.len == 0:
return
let section = if defaultSection != ideNone:
defaultSection
elif sym.info.exactEquals(info):
ideDef
else:
ideUse
var suggestDef = symToSuggest(graph, sym, isLocal=false, section,
info, 100, PrefixMatch.None, false, 0, true,
endLine = endLine, endCol = endCol)
suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintLeft(suggestDef, propagatedExceptionList)
suggestDef.section = ideInlayHints
suggestResult(graph.config, suggestDef)
suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintRight(suggestDef, propagatedExceptionList)
suggestResult(graph.config, suggestDef)
const
# kinds for ideOutline and ideGlobalSymbols
searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate}
@@ -893,15 +986,18 @@ proc findDef(n: PNode, line: uint16, col: int16): PNode =
let res = findDef(n[i], line, col)
if res != nil: return res
proc findByTLineInfo(trackPos: TLineInfo, infoPairs: seq[SymInfoPair]):
proc findByTLineInfo(trackPos: TLineInfo, infoPairs: SuggestFileSymbolDatabase):
ref SymInfoPair =
for s in infoPairs:
if s.info.exactEquals trackPos:
new(result)
result[] = s
break
result = nil
if infoPairs.fileIndex == trackPos.fileIndex:
for i in infoPairs.lineInfo.low..infoPairs.lineInfo.high:
let s = infoPairs.getSymInfoPair(i)
if s.info.exactEquals trackPos:
new(result)
result[] = s
break
proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool =
proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
proc checkSymbol(sym: PSym, info: TLineInfo): bool =
result = (sym.owner.kind in {skModule, skType} or sym.kind in {skProc, skMethod, skIterator, skTemplate, skType})
@@ -915,7 +1011,7 @@ proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: se
graph.suggestResult(sym, sym.info, ideOutline, endInfo.line, endInfo.col)
return true
proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool =
proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
for child in n:
if child.kind in {nkIdent, nkSym}:
if graph.outlineNode(child, endInfo, infoPairs):
@@ -924,7 +1020,7 @@ proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPair
if graph.handleIdentOrSym(child, endInfo, infoPairs):
return true
proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: seq[SymInfoPair]) =
proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: SuggestFileSymbolDatabase) =
var matched = true
if n.kind == nkIdent:
let symData = findByTLineInfo(n.info, infoPairs)
@@ -1028,7 +1124,11 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
of ideHighlight:
let sym = graph.findSymData(file, line, col)
if not sym.isNil:
let usages = graph.fileSymbols(fileIndex).filterIt(it.sym == sym.sym)
let fs = graph.fileSymbols(fileIndex)
var usages: seq[SymInfoPair] = @[]
for i in fs.lineInfo.low..fs.lineInfo.high:
if fs.sym[i] == sym.sym:
usages.add(fs.getSymInfoPair(i))
myLog fmt "Found {usages.len} usages in {file.string}"
for s in usages:
graph.suggestResult(s.sym, s.info)
@@ -1097,9 +1197,10 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
# find first mention of the symbol in the file containing the definition.
# It is either the definition or the declaration.
var first: SymInfoPair
for symbol in graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair:
if s.sym.symbolEqual(symbol.sym):
first = symbol
let db = graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair
for i in db.lineInfo.low..db.lineInfo.high:
if s.sym.symbolEqual(db.sym[i]):
first = db.getSymInfoPair(i)
break
if s.info.exactEquals(first.info):
@@ -1150,10 +1251,31 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
i += parseInt(tag, endLine, i)
i += skipWhile(tag, seps, i)
i += parseInt(tag, endCol, i)
i += skipWhile(tag, seps, i)
var typeHints = true
var exceptionHints = false
while i <= tag.high:
var token: string
i += parseUntil(tag, token, seps, i)
i += skipWhile(tag, seps, i)
case token:
of "+typeHints":
typeHints = true
of "-typeHints":
typeHints = false
of "+exceptionHints":
exceptionHints = true
of "-exceptionHints":
exceptionHints = false
else:
myLog fmt "Discarding unknown inlay hint parameter {token}"
let s = graph.findSymDataInRange(file, line, col, endLine, endCol)
for q in s:
if q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType:
graph.suggestInlayHintResult(q.sym, q.info, ideInlayHints)
if typeHints and q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType:
graph.suggestInlayHintResultType(q.sym, q.info, ideInlayHints)
if exceptionHints and q.sym.kind in {skProc, skFunc, skMethod, skVar, skLet, skParam} and not q.isDecl:
graph.suggestInlayHintResultException(q.sym, q.info, ideInlayHints, caughtExceptions = q.caughtExceptions, caughtExceptionsSet = q.caughtExceptionsSet)
else:
myLog fmt "Discarding {cmd}"

View File

@@ -279,7 +279,13 @@ proc runEpcTest(filename: string): int =
os.sleep(50)
inc i
let a = outp.readAll().strip()
let port = parseInt(a)
var port: int
try:
port = parseInt(a)
except ValueError:
echo "Error parsing port number: " & a
echo outp.readAll()
quit 1
socket.connect("localhost", Port(port))
for req, resp in items(s.script):