Skip to content

Commit

Permalink
tool: LSP support
Browse files Browse the repository at this point in the history
  • Loading branch information
bung87 committed Sep 7, 2023
1 parent 7eb3b5b commit 59c722a
Show file tree
Hide file tree
Showing 32 changed files with 2,885 additions and 81 deletions.
4 changes: 2 additions & 2 deletions compiler/ast/ast_parsed_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ type
pnkExprEqExpr
pnkExprColonExpr
pnkIdentDefs
pnkConstDef
pnkVarTuple
pnkPar
pnkSqrBracket
Expand Down Expand Up @@ -175,6 +174,7 @@ type
pnkExportStmt
pnkExportExceptStmt
pnkConstSection
pnkTypeSection
pnkLetSection
pnkVarSection
pnkProcDef
Expand All @@ -184,7 +184,7 @@ type
pnkIteratorDef
pnkMacroDef
pnkTemplateDef
pnkTypeSection
pnkConstDef
pnkTypeDef
pnkEnumTy
pnkEnumFieldDef
Expand Down
6 changes: 3 additions & 3 deletions compiler/ast/astalgo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
else: return nil
result = nil

proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
proc sameIgnoreBacktickGensymInfo*(a, b: string): bool =
if a[0] != b[0]: return false
var alen = a.len - 1
while alen > 0 and a[alen] != '`': dec(alen)
Expand Down Expand Up @@ -588,6 +588,6 @@ proc listSymbolNames*(symbols: openArray[PSym]): string =
result.add sym.name.s

proc isDiscriminantField*(n: PNode): bool =
if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags
elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags
if n.kind == nkCheckedFieldExpr: n[0][1].kind == nkSym and sfDiscriminant in n[0][1].sym.flags
elif n.kind == nkDotExpr: n[1].kind == nkSym and sfDiscriminant in n[1].sym.flags
else: false
2 changes: 1 addition & 1 deletion compiler/ast/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2321,7 +2321,7 @@ proc parseAll(p: var Parser): ParsedNode =
if p.tok.indent != 0:
p.invalidIndentation()

proc parseTopLevelStmt(p: var Parser): ParsedNode =
proc parseTopLevelStmt*(p: var Parser): ParsedNode =
## Implements an iterator which, when called repeatedly, returns the next
## top-level statement or emptyNode if end of stream.
result = p.emptyNode
Expand Down
9 changes: 1 addition & 8 deletions compiler/front/msgs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,7 @@ proc errorActions(
elif eh == doRaise:
result = (doRaise, false)


proc `==`*(a, b: TLineInfo): bool =
result = a.line == b.line and a.fileIndex == b.fileIndex

proc exactEquals*(a, b: TLineInfo): bool =
result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col

proc getContext*(conf: ConfigRef; lastinfo: TLineInfo): seq[ReportContext] =
Expand All @@ -342,17 +338,14 @@ proc getContext*(conf: ConfigRef; lastinfo: TLineInfo): seq[ReportContext] =

info = context.info

proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
conf[fileIdx].lines.add line

proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
## xxx there's an off by 1 error that should be fixed; if a file ends with "foo" or "foo\n"
## it will return same number of lines (ie, a trailing empty line is discounted)
result = conf[fileIdx].lines.len
if result == 0:
try:
for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
addSourceLine conf, fileIdx, line
conf[fileIdx].lines.add line
except IOError:
discard
result = conf[fileIdx].lines.len
Expand Down
1 change: 0 additions & 1 deletion compiler/modules/modules.nim
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fr
initStrTables(graph, result)
result.ast = nil
processModuleAux("import(dirty)")
graph.markClientsDirty(fileIdx)


proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
Expand Down
6 changes: 4 additions & 2 deletions compiler/sem/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
when defined(nimsuggest):
if c.config.cmd == cmdIdeTools:
suggestExpr(c, n)
if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n)
if c.config.m.trackPos == n[1].info: suggestExprNoCheck(c, n)

let s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
if s.isError:
Expand Down Expand Up @@ -2891,7 +2891,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = semOverloadedCallAnalyseEffects(c, n, flags)
if result == nil:
result = errorNode(c, n)
else:
elif result.safeLen != 0:
let callee = result[0].sym
if callee.magic == mNone:
semFinishOperands(c, result)
Expand All @@ -2900,6 +2900,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = fixVarArgumentsAndAnalyse(c, result)
if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
else:
unreachable()
of mRunnableExamples:
markUsed(c, n.info, s)
if c.config.cmd in cmdDocLike and n.len >= 2 and n.lastSon.kind == nkStmtList:
Expand Down
2 changes: 1 addition & 1 deletion compiler/sem/sempass2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,7 @@ proc track(tracked: PEffects, n: PNode) =
of nkVarSection, nkLetSection:
for child in n:
let last = lastSon(child)
if child.kind == nkIdentDefs and sfCompileTime in child[0].sym.flags:
if child.kind == nkIdentDefs and child[0].kind == nkSym and sfCompileTime in child[0].sym.flags:
# don't analyse the definition of ``.compileTime`` globals. They
# don't "exist" in the context (i.e., run time) we're analysing
# in
Expand Down
2 changes: 1 addition & 1 deletion compiler/sem/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1691,7 +1691,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
setCaseContextIdx(c, i)
var x = n[i]
when defined(nimsuggest):
if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
if c.config.ideCmd == ideSug and c.config.m.trackPos == x.info and caseTyp.kind == tyEnum:
suggestEnum(c, x, caseTyp)
case x.kind
of nkOfBranch:
Expand Down
59 changes: 13 additions & 46 deletions compiler/tools/suggest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const
template origModuleName(m: PSym): string = m.name.s

proc findDocComment(n: PNode): PNode =
if n == nil: return nil
if n == nil or n.kind == nkError: return nil
if n.comment.len > 0: return n
if n.kind in {nkStmtList, nkStmtListExpr, nkObjectTy, nkRecList} and n.len > 0:
result = findDocComment(n[0])
Expand Down Expand Up @@ -389,7 +389,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
var typ = n.typ
var pm: PrefixMatch
when defined(nimsuggest):
if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0:
if n.kind == nkSym and n.sym.kind == skError:
# consider 'foo.|' where 'foo' is some not imported module.
let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info))
if fullPath.isEmpty:
Expand Down Expand Up @@ -463,40 +463,24 @@ proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult =
return cpFuzzy

proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool =
if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line:
if current.fileIndex == trackPos.fileIndex and
current.line == trackPos.line:
let col = trackPos.col
if col >= current.col and col <= current.col+tokenLen-1:
if col >= current.col and col <= current.col + tokenLen - 1:
return true

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:
proc infoToInt(info: TLineInfo): int64 =
info.fileIndex.int64 + info.line.int64 shl 32 + info.col.int64 shl 48

proc addNoDup(s: PSym; info: TLineInfo) =
# ensure nothing gets too slow:
if s.allUsages.len > 500: return
let infoAsInt = info.infoToInt
for infoB in s.allUsages:
if infoB.infoToInt == infoAsInt: return
if infoB == info: return
s.allUsages.add(info)

proc findUsages(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
if g.config.suggestVersion == 1:
if usageSym == nil and isTracked(info, g.config.m.trackPos, s.name.s.len):
usageSym = s
suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
elif s == usageSym:
if g.config.lastLineInfo != info:
suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
g.config.lastLineInfo = info

when defined(nimsuggest):
proc listUsages*(g: ModuleGraph; s: PSym) =
#echo "usages ", s.allUsages.len
for info in s.allUsages:
let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
let x = if info == s.info: ideDef else: ideUse
suggestResult(g.config, symToSuggest(g, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))

proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
Expand All @@ -518,35 +502,18 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
## misnamed: should be 'symDeclared'
let conf = g.config
when defined(nimsuggest):
if conf.suggestVersion == 0:
if s.allUsages.len == 0:
s.allUsages = @[info]
else:
s.addNoDup(info)
if s.allUsages.len == 0:
s.allUsages = @[info]
else:
s.addNoDup(info)

if conf.ideCmd == ideUse:
findUsages(g, info, s, usageSym)
elif conf.ideCmd == ideDef:
if conf.ideCmd == ideDef:
findDefinition(g, info, s, usageSym)
elif conf.ideCmd == ideDus and s != nil:
if isTracked(info, conf.m.trackPos, s.name.s.len):
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
findUsages(g, info, s, usageSym)
elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
elif conf.ideCmd == ideOutline and isDecl:
# if a module is included then the info we have is inside the include and
# we need to walk up the owners until we find the outer most module,
# which will be the last skModule prior to an skPackage.
var
parentFileIndex = info.fileIndex # assume we're in the correct module
parentModule = s.owner
while parentModule != nil and parentModule.kind == skModule:
parentFileIndex = parentModule.info.fileIndex
parentModule = parentModule.owner

if parentFileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))

proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support!
Expand Down Expand Up @@ -608,7 +575,7 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
suggestQuit()

proc suggestExpr*(c: PContext, n: PNode) =
if exactEquals(c.config.m.trackPos, n.info): suggestExprNoCheck(c, n)
if c.config.m.trackPos == n.info: suggestExprNoCheck(c, n)

proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
let attached = c.config.m.trackPosAttached
Expand Down
21 changes: 11 additions & 10 deletions compiler/vm/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import
idents,
typesrenderer,
types,
astalgo,
],
compiler/modules/[
modulegraphs
Expand Down Expand Up @@ -2714,11 +2715,11 @@ proc rawExecute(c: var TCtx, pc: var int): YieldReason =
of opcEqIdent:
decodeBC(rkInt)

func asCString(a: TFullReg): cstring =
func getName(a: TFullReg): string =
case a.kind
of rkLocation, rkHandle:
if a.handle.typ.kind == akString:
result = deref(a.handle).strVal.asCString()
result = $ deref(a.handle).strVal
of rkNimNode:
var aNode = a.nimNode

Expand All @@ -2735,11 +2736,11 @@ proc rawExecute(c: var TCtx, pc: var int): YieldReason =

case aNode.kind
of nkIdent:
result = aNode.ident.s.cstring
result = aNode.ident.s
of nkSym:
result = aNode.sym.name.s.cstring
result = aNode.sym.name.s
of nkOpenSymChoice, nkClosedSymChoice:
result = aNode[0].sym.name.s.cstring
result = aNode[0].sym.name.s
else:
discard
else:
Expand All @@ -2750,12 +2751,12 @@ proc rawExecute(c: var TCtx, pc: var int): YieldReason =

# These vars are of type `cstring` to prevent unnecessary string copy.
let
aStrVal = asCString(regs[rb])
bStrVal = asCString(regs[rc])
aStrVal = getName(regs[rb])
bStrVal = getName(regs[rc])

regs[ra].intVal =
if aStrVal != nil and bStrVal != nil:
ord(idents.cmpIgnoreStyle(aStrVal, bStrVal, high(int)) == 0)
regs[ra].intVal =
if aStrVal != "" and bStrVal != "":
ord(sameIgnoreBacktickGensymInfo(aStrVal, $bStrVal))
else:
0
of opcStrToIdent:
Expand Down
12 changes: 7 additions & 5 deletions compiler/vm/vmjit.nim
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,15 @@ proc genProc(jit: var JitState, c: var TCtx, s: PSym): VmGenResult =
c.removeLastEof()

let body =
if s.kind == skMacro:
if isCompileTimeProc(s) and not defined(nimsuggest):
# no need to go through the transformation cache
transformBody(c.graph, c.idgen, s, s.ast[bodyPos])
else:
# watch out! While compile-time only procedures don't need to be cached
# here, we still need to retrieve their already cached body (if one
# exists). Lifted inner procedures would otherwise not work.
transformBody(c.graph, c.idgen, s, cache = not isCompileTimeProc(s))
# watch out! Since transforming a procedure body permanently alters
# the state of inner procedures, we need to both cache and later
# retrieve the transformed body for non-compile-only routines or
# when in suggest mode
transformBody(c.graph, c.idgen, s, cache = true)

echoInput(c.config, s, body)
var (tree, sourceMap) = generateCode(c.graph, s, selectOptions(c), body)
Expand Down
File renamed without changes.
Loading

0 comments on commit 59c722a

Please sign in to comment.