nimsuggest: first version

This commit is contained in:
Araq
2015-01-26 21:38:39 +01:00
parent 217390d181
commit 26b853923c
21 changed files with 364 additions and 218 deletions

View File

@@ -428,6 +428,7 @@ type
nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM
nfIsCursor # this node is attached a cursor; used for idetools
TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
@@ -883,7 +884,7 @@ const
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField,
nfIsRef}
nfIsRef, nfIsCursor}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2

View File

@@ -65,14 +65,14 @@ proc getCommandLineDesc(): string =
proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(getCommandLineDesc())
quit(0)
msgQuit(0)
proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & AdvancedUsage)
quit(0)
msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1:
@@ -87,7 +87,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
quit(0)
msgQuit(0)
var
helpWritten: bool
@@ -255,8 +255,7 @@ proc trackDirty(arg: string, info: TLineInfo) =
gDirtyBufferIdx = a[0].fileInfoIdx
gDirtyOriginalIdx = a[1].fileInfoIdx
optTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
msgs.addCheckpoint(optTrackPos)
gTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
proc track(arg: string, info: TLineInfo) =
var a = arg.split(',')
@@ -266,8 +265,7 @@ proc track(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[1])
if parseUtils.parseInt(a[2], column) <= 0:
localError(info, errInvalidNumber, a[2])
optTrackPos = newLineInfo(a[0], line, column)
msgs.addCheckpoint(optTrackPos)
gTrackPos = newLineInfo(a[0], line, column)
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}:
@@ -541,19 +539,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
trackDirty(arg, info)
of "suggest":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSuggest)
gIdeCmd = ideSug
of "def":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optDef)
gIdeCmd = ideDef
of "eval":
expectArg(switch, arg, pass, info)
gEvalExpr = arg
of "context":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optContext)
gIdeCmd = ideCon
of "usages":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optUsages)
gIdeCmd = ideUse
of "stdout":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optStdout)

View File

@@ -25,7 +25,7 @@ type
next*: PIdent # for hash-table chaining
h*: THash # hash value of s
var firstCharIsCS*: bool
var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent]
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =

View File

@@ -693,7 +693,15 @@ proc scanComment(L: var TLexer, tok: var TToken) =
when not defined(nimfix):
assert buf[pos+1] == '#'
if buf[pos+2] == '[':
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
if buf[pos+3] == ']':
# ##[] is the (rather complex) "cursor token" for idetools
tok.tokType = tkComment
tok.literal = "[]"
inc(L.bufpos, 4)
return
else:
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
tok.tokType = tkComment
# iNumber contains the number of '\n' in the token
tok.iNumber = 0

View File

@@ -58,12 +58,7 @@ proc commandCompileToC =
registerPass(cgenPass)
rodPass()
#registerPass(cleanupPass())
if optCaasEnabled in gGlobalOptions:
# echo "BEFORE CHECK DEP"
# discard checkDepMem(gProjectMainIdx)
# echo "CHECK DEP COMPLETE"
discard
compileProject()
cgenWriteModules()
if gCmd != cmdRun:
@@ -177,17 +172,15 @@ proc commandSuggest =
if gDirtyBufferIdx != 0:
discard compileModule(gDirtyBufferIdx, {sfDirty})
resetModule(gDirtyBufferIdx)
if optDef in gGlobalOptions:
defFromSourceMap(optTrackPos)
else:
msgs.gErrorMax = high(int) # do not stop after first error
semanticPasses()
rodPass()
# XXX: this handles the case when the dirty buffer is the main file,
# but doesn't handle the case when it's imported module
var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
else: gProjectMainIdx
compileProject(projFile)
#var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
# else: gProjectMainIdx
compileProject() #(projFile)
proc resetMemory =
resetCompilationLists()
@@ -218,7 +211,6 @@ proc resetMemory =
# rodread.gMods
# !! ropes.cache
# semthreads.computed?
#
# suggest.usageSym
#

View File

@@ -34,9 +34,6 @@ proc getModule(fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < gCompiledModules.len:
result = gCompiledModules[fileIdx]
template compiledAt(x: PSym): expr =
gMemCacheData[x.position].compiledAt
template crc(x: PSym): expr =
gMemCacheData[x.position].crc
@@ -74,10 +71,12 @@ proc addDep(x: PSym, dep: int32) =
proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename
gMemCacheData[fileIdx].needsRecompile = Yes
gCompiledModules[fileIdx] = nil
cgendata.gModules[fileIdx] = nil
resetSourceMap(fileIdx)
if fileIdx <% gMemCacheData.len:
gMemCacheData[fileIdx].needsRecompile = Yes
if fileIdx <% gCompiledModules.len:
gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil
proc resetAllModules* =
for i in 0..gCompiledModules.high:

View File

@@ -10,9 +10,6 @@
import
options, strutils, os, tables, ropes, platform
when useCaas:
import sockets
type
TMsgKind* = enum
errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
@@ -572,9 +569,6 @@ var
gWarnCounter*: int = 0
gErrorMax*: int = 1 # stop after gErrorMax errors
when useCaas:
var stdoutSocket*: Socket
proc unknownLineInfo*(): TLineInfo =
result.line = int16(-1)
result.col = int16(-1)
@@ -586,32 +580,35 @@ var
bufferedMsgs*: seq[string]
errorOutputs* = {eStdOut, eStdErr}
writelnHook*: proc (output: string) {.closure.}
proc clearBufferedMsgs* =
bufferedMsgs = nil
proc suggestWriteln*(s: string) =
if eStdOut in errorOutputs:
when useCaas:
if isNil(stdoutSocket): writeln(stdout, s)
else:
writeln(stdout, s)
stdoutSocket.send(s & "\c\L")
else:
writeln(stdout, s)
if isNil(writelnHook): writeln(stdout, s)
else: writelnHook(s)
if eInMemory in errorOutputs:
bufferedMsgs.safeAdd(s)
proc msgQuit*(x: int8) = quit x
proc msgQuit*(x: string) = quit x
proc suggestQuit*() =
if not isServing:
quit(0)
elif isWorkingWithDirtyBuffer:
# No need to compile the rest if we are working with a
# throw-away buffer. Incomplete dot expressions frequently
# found in dirty buffers will result in errors few steps
# from now anyway.
when true:
raise newException(ESuggestDone, "suggest done")
else:
if not isServing:
assert false
quit(0)
elif isWorkingWithDirtyBuffer:
# No need to compile the rest if we are working with a
# throw-away buffer. Incomplete dot expressions frequently
# found in dirty buffers will result in errors few steps
# from now anyway.
raise newException(ESuggestDone, "suggest done")
# this format is understood by many text editors: it is the same that
# Borland and Freepascal use
@@ -679,14 +676,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes
result = filename in info.toFilename
var checkPoints*: seq[TLineInfo] = @[]
var optTrackPos*: TLineInfo
proc addCheckpoint*(info: TLineInfo) =
checkPoints.add(info)
proc addCheckpoint*(filename: string, line: int) =
addCheckpoint(newLineInfo(filename, line, - 1))
var gTrackPos*: TLineInfo
proc outWriteln*(s: string) =
## Writes to stdout. Always.
@@ -694,7 +684,8 @@ proc outWriteln*(s: string) =
proc msgWriteln*(s: string) =
## Writes to stdout. If --stdout option is given, writes to stderr instead.
if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
#if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if optStdout in gGlobalOptions:
if eStdErr in errorOutputs: writeln(stderr, s)
@@ -714,19 +705,6 @@ proc msgKindToString*(kind: TMsgKind): string =
proc getMessageStr(msg: TMsgKind, arg: string): string =
result = msgKindToString(msg) % [arg]
type
TCheckPointResult* = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
for i in countup(0, high(checkPoints)):
if current.fileIndex == checkPoints[i].fileIndex:
if current.line == checkPoints[i].line and
abs(current.col-checkPoints[i].col) < 4:
return cpExact
if current.line >= checkPoints[i].line:
return cpFuzzy
type
TErrorHandling = enum doNothing, doAbort, doRaise
@@ -798,6 +776,9 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
result = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)]
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) =
var frmt: string
@@ -821,7 +802,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
inc(gHintCounter)
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)]
if not ignoreMsg:
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s)
if optPrintSurroundingSrc and msg in errMin..errMax:
info.writeSurroundingSrc

View File

@@ -89,4 +89,4 @@ condsyms.initDefines()
when not defined(selftest):
handleCmdLine()
quit(int8(msgs.gErrorCounter > 0))
msgQuit(int8(msgs.gErrorCounter > 0))

View File

@@ -0,0 +1,187 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
import strutils, os, parseopt, parseUtils
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
extccomp, condsyms, lists, net, rdstdin
const Usage = """
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
Usage:
nimsuggest [options] projectfile.nim
Options:
--port:PORT port, by default 6000
--address:HOST binds to that address, by default ""
--stdin read commands from stdin and write results to
stdout instead of using sockets
The server then listens to the connection and takes line-based commands.
In addition, all command line options of Nim that do not affect code generation
are supported.
"""
var
gPort = 6000.Port
gAddress = ""
gUseStdin: bool
const
seps = {':', ';', ' ', '\t'}
Help = "usage: sug|con|def|use dirtybuffer.nim[;originalfile.nim]:line:col\n"&
"type 'quit' to quit\n" &
"type 'debug' to toggle debug mode on/off\n" &
"type 'terse' to toggle terse mode on/off"
proc parseQuoted(cmd: string; outp: var string; start: int): int =
var i = start
i += skipWhitespace(cmd, i)
if cmd[i] == '"':
i += parseUntil(cmd, outp, '"', i+1)+1
else:
i += parseUntil(cmd, outp, seps, i)
result = i
proc action(cmd: string) =
template toggle(sw) =
if sw in gGlobalOptions:
excl(gGlobalOptions, sw)
else:
incl(gGlobalOptions, sw)
return
var opc = ""
var i = parseIdent(cmd, opc, 0)
case opc.normalize
of "sug": gIdeCmd = ideSug
of "con": gIdeCmd = ideCon
of "def": gIdeCmd = ideDef
of "use":
modules.resetAllModules()
gIdeCmd = ideUse
of "quit": quit()
of "debug": toggle optIdeDebug
of "terse": toggle optIdeTerse
else:
echo Help
return
var dirtyfile = ""
var orig = ""
i = parseQuoted(cmd, dirtyfile, i)
if cmd[i] == ';':
i = parseQuoted(cmd, orig, i+1)
i += skipWhile(cmd, seps, i)
var line, col = -1
i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i)
if dirtyfile.len != 0:
gDirtyBufferIdx = dirtyfile.fileInfoIdx
gDirtyOriginalIdx = if orig.len != 0: orig.fileInfoIdx else: gDirtyBufferIdx
else:
discard "use the same filename as in the last command"
resetModule gDirtyBufferIdx
if gDirtyBufferIdx != gProjectMainIdx:
resetModule gProjectMainIdx
gTrackPos = newLineInfo(gDirtyBufferIdx, line, col)
#echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
gErrorCounter = 0
compileProject()
proc serve() =
gDirtyBufferIdx = gProjectMainIdx
gDirtyOriginalIdx = gProjectMainIdx
# do not stop after the first error:
msgs.gErrorMax = high(int)
if gUseStdin:
echo Help
var line = ""
while readLineFromStdin("> ", line):
action line
echo ""
flushFile(stdout)
else:
var server = newSocket()
server.bindAddr(gPort, gAddress)
var inp = "".TaintedString
server.listen()
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
while true:
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
action inp.string
stdoutSocket.send("\c\L")
stdoutSocket.close()
proc mainCommand =
registerPass verbosePass
registerPass semPass
gCmd = cmdIdeTools
incl gGlobalOptions, optCaasEnabled
isServing = true
wantMainModule()
appendStr(searchPaths, options.libpath)
if gProjectFull.len != 0:
# current path is always looked first for modules
prependStr(searchPaths, gProjectPath)
serve()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption:
case p.key.normalize
of "port": gPort = parseInt(p.val).Port
of "address": gAddress = p.val
of "stdin": gUseStdin = true
else: processSwitch(pass, p)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine() =
if paramCount() == 0:
stdout.writeln(Usage)
else:
processCmdLine(passCmd1, "")
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
except OSError:
gProjectFull = gProjectName
var p = splitFile(gProjectFull)
gProjectPath = p.dir
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
mainCommand()
when false:
proc quitCalled() {.noconv.} =
writeStackTrace()
addQuitProc(quitCalled)
condsyms.initDefines()
defineSymbol "nimsuggest"
handleCmdline()

View File

@@ -56,17 +56,14 @@ type # please make sure we have under 32 options
optNoMain, # do not generate a "main" proc
optThreads, # support for multi-threading
optStdout, # output to stdout
optSuggest, # ideTools: 'suggest'
optContext, # ideTools: 'context'
optDef, # ideTools: 'def'
optUsages, # ideTools: 'usages'
optThreadAnalysis, # thread analysis pass
optTaintMode, # taint mode turned on
optTlsEmulation, # thread var emulation turned on
optGenIndex # generate index file for documentation;
optEmbedOrigSrc # embed the original source in the generated code
# also: generate header file
optIdeDebug # idetools: debug mode
optIdeTerse # idetools: use terse descriptions
TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nim's commands
# **keep binary compatible**
@@ -86,6 +83,12 @@ type # please make sure we have under 32 options
TGCMode* = enum # the selected GC
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
TIdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse
var
gIdeCmd*: TIdeCmd
const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}

View File

@@ -111,7 +111,12 @@ proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment:
if node != nil:
if node.comment == nil: node.comment = ""
add(node.comment, p.tok.literal)
if p.tok.literal == "[]":
node.flags.incl nfIsCursor
#echo "parser: "
#debug node
else:
add(node.comment, p.tok.literal)
else:
parMessage(p, errInternal, "skipComment")
getTok(p)
@@ -379,7 +384,8 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
#| dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
var info = p.parLineInfo
getTok(p)
optInd(p, a)
result = newNodeI(nkDotExpr, info)
optInd(p, result)
case p.tok.tokType
of tkType:
result = newNodeP(nkTypeOfExpr, p)
@@ -390,7 +396,6 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
getTok(p)
addSon(result, a)
else:
result = newNodeI(nkDotExpr, info)
addSon(result, a)
addSon(result, parseSymbol(p))
@@ -737,7 +742,7 @@ proc parseOperators(p: var TParser, headNode: PNode,
var a = newNodeP(nkInfix, p)
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
getTok(p)
optInd(p, opNode)
optInd(p, a)
# read sub-expression with higher priority:
var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
addSon(a, opNode)
@@ -1735,8 +1740,8 @@ proc parseObject(p: var TParser): PNode =
proc parseTypeClassParam(p: var TParser): PNode =
if p.tok.tokType == tkVar:
result = newNodeP(nkVarTy, p)
getTok(p)
result = newNode(nkVarTy)
result.addSon(p.parseSymbol)
else:
result = p.parseSymbol
@@ -1747,7 +1752,7 @@ proc parseTypeClass(p: var TParser): PNode =
#| &IND{>} stmt
result = newNodeP(nkTypeClassTy, p)
getTok(p)
var args = newNode(nkArgList)
var args = newNodeP(nkArgList, p)
addSon(result, args)
addSon(args, p.parseTypeClassParam)
while p.tok.tokType == tkComma:

View File

@@ -174,7 +174,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
if s == nil:
rawMessage(errCannotOpenFile, filename)
return
else:
else:
s = stream
while true:
openParsers(p, fileIdx, s)

View File

@@ -411,12 +411,6 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "")
proc pragmaCheckpoint(c: PContext, n: PNode) =
# checkpoints can be used to debug the compiler; they are not documented
var info = n.info
inc(info.line) # next line is affected!
msgs.addCheckpoint(info)
proc pragmaWatchpoint(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr:
n.sons[1] = c.semExpr(c, n.sons[1])

View File

@@ -84,8 +84,9 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if c.inCompilesContext > 0:
# fail fast:
globalError(n.info, errTypeMismatch, "")
if errors.len == 0:
if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
# to avoid confusing errors like:
# got (SslPtr, SocketHandle)

View File

@@ -101,7 +101,6 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
debug t
writeStackTrace()
quit 1
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -14,7 +14,7 @@ import
extccomp, strutils, os, platform, parseopt
when useCaas:
import sockets
import net
# We cache modules and the dependency graph. However, we don't check for
# file changes but expect the client to tell us about them, otherwise the
@@ -61,14 +61,16 @@ proc serve*(action: proc (){.nimcall.}) =
of "tcp", "":
when useCaas:
var server = socket()
if server == invalidSocket: raiseOSError(osLastError())
var server = newSocket()
let p = getConfigVar("server.port")
let port = if p.len > 0: parseInt(p).Port else: 6000.Port
server.bindAddr(port, getConfigVar("server.address"))
var inp = "".TaintedString
server.listen()
new(stdoutSocket)
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
while true:
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
@@ -76,7 +78,7 @@ proc serve*(action: proc (){.nimcall.}) =
stdoutSocket.send("\c\L")
stdoutSocket.close()
else:
quit "server.type not supported; compiler built without caas support"
msgQuit "server.type not supported; compiler built without caas support"
else:
echo "Invalid server.type:", typ
quit 1
msgQuit 1

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -31,35 +31,52 @@ proc origModuleName(m: PSym): string =
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
result = section
result.add(sep)
result.add($s.kind)
result.add(sep)
if not isLocal and s.kind != skModule:
let ow = s.owner
if ow.kind != skModule and ow.owner != nil:
let ow2 = ow.owner
result.add(ow2.origModuleName)
if optIdeTerse in gGlobalOptions:
if s.kind in routineKinds:
result.add renderTree(s.ast, {renderNoBody, renderNoComments,
renderDocComments, renderNoPragmas})
else:
result.add s.name.s
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
else:
result.add($s.kind)
result.add(sep)
if not isLocal and s.kind != skModule:
let ow = s.owner
if ow.kind != skModule and ow.owner != nil:
let ow2 = ow.owner
result.add(ow2.origModuleName)
result.add('.')
result.add(ow.origModuleName)
result.add('.')
result.add(ow.origModuleName)
result.add('.')
result.add(s.name.s)
result.add(sep)
if s.typ != nil:
result.add(typeToString(s.typ))
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
result.add(sep)
when not defined(noDocgen):
result.add(s.extractDocComment.escape)
result.add(s.name.s)
result.add(sep)
if s.typ != nil:
result.add(typeToString(s.typ))
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
result.add(sep)
when not defined(noDocgen):
result.add(s.extractDocComment.escape)
proc symToStr(s: PSym, isLocal: bool, section: string): string =
result = symToStr(s, isLocal, section, s.info)
proc filterSym(s: PSym): bool {.inline.} =
result = s.name.s[0] in lexer.SymChars and s.kind != skModule
result = s.kind != skModule
proc filterSymNoOpr(s: PSym): bool {.inline.} =
result = s.kind != skModule and s.name.s[0] in lexer.SymChars and
not isKeyword(s.name)
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
let fmoduleId = getModule(f).id
@@ -131,11 +148,20 @@ proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
# special rule: if system and some weird generic match via 'tyExpr'
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
# wants 'system.`-|` as suggestion
let m = s.getModule()
if m != nil and sfSystemModule in m.flags:
if s.kind == skType: return
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar})
if exp.kind == tyVarargs: exp = elemType(exp)
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
assert typ != nil
wholeSymTab(filterSym(it) and typeFits(c, it, typ), sectionSuggest)
wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
# do not produce too many symbols:
@@ -191,19 +217,28 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
else:
suggestOperations(c, n, typ, outputs)
type
TCheckPointResult = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line and
abs(current.col-gTrackPos.col) < 4:
return cpExact
if current.line >= gTrackPos.line:
return cpFuzzy
proc findClosestDot(n: PNode): PNode =
if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact:
if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
result = n
else:
for i in 0.. <safeLen(n):
result = findClosestDot(n.sons[i])
if result != nil: return
const
CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit}
proc findClosestCall(n: PNode): PNode =
if n.kind in CallNodes and msgs.inCheckpoint(n.info) == cpExact:
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
result = n
else:
for i in 0.. <safeLen(n):
@@ -211,15 +246,14 @@ proc findClosestCall(n: PNode): PNode =
if result != nil: return
proc isTracked(current: TLineInfo, tokenLen: int): bool =
for i in countup(0, high(checkPoints)):
if current.fileIndex == checkPoints[i].fileIndex:
if current.line == checkPoints[i].line:
let col = checkPoints[i].col
if col >= current.col and col <= current.col+tokenLen-1:
return true
if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line:
let col = gTrackPos.col
if col >= current.col and col <= current.col+tokenLen-1:
return true
proc findClosestSym(n: PNode): PNode =
if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact:
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
result = n
elif n.kind notin {nkNone..nkNilLit}:
for i in 0.. <sonsLen(n):
@@ -258,69 +292,18 @@ proc findDefinition(info: TLineInfo; s: PSym) =
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
suggestQuit()
type
TSourceMap = object
lines: seq[TLineMap]
TEntry = object
pos: int
sym: PSym
TLineMap = object
entries: seq[TEntry]
var
gSourceMaps: seq[TSourceMap] = @[]
proc ensureIdx[T](x: var T, y: int) =
if x.len <= y: x.setLen(y+1)
proc ensureSeq[T](x: var seq[T]) =
if x == nil: newSeq(x, 0)
proc resetSourceMap*(fileIdx: int32) =
ensureIdx(gSourceMaps, fileIdx)
gSourceMaps[fileIdx].lines = @[]
proc addToSourceMap(sym: PSym, info: TLineInfo) =
ensureIdx(gSourceMaps, info.fileIndex)
ensureSeq(gSourceMaps[info.fileIndex].lines)
ensureIdx(gSourceMaps[info.fileIndex].lines, info.line)
ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries)
gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym))
proc defFromLine(entries: var seq[TEntry], col: int32) =
if entries == nil: return
# The sorting is done lazily here on purpose.
# No need to pay the price for it unless the user requests
# "goto definition" on a particular line
sort(entries) do (a,b: TEntry) -> int:
return cmp(a.pos, b.pos)
for e in entries:
# currently, the line-infos for most expressions point to
# one position past the end of the expression. This means
# that the first expr that ends after the cursor column is
# the one we are looking for.
if e.pos >= col:
suggestWriteln(symToStr(e.sym, isLocal=false, sectionDef))
return
proc defFromSourceMap*(i: TLineInfo) =
if not ((i.fileIndex < gSourceMaps.len) and
(gSourceMaps[i.fileIndex].lines != nil) and
(i.line < gSourceMaps[i.fileIndex].lines.len)): return
defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
## misnamed: should be 'symDeclared'
if optUsages in gGlobalOptions:
if gIdeCmd == ideUse:
findUsages(info, s)
if optDef in gGlobalOptions:
elif gIdeCmd == ideDef:
findDefinition(info, s)
if isServing:
addToSourceMap(s, info)
proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed)
@@ -334,30 +317,28 @@ proc useSym*(sym: PSym): PNode =
markUsed(result.info, sym)
proc suggestExpr*(c: PContext, node: PNode) =
var cp = msgs.inCheckpoint(node.info)
if cp == cpNone: return
if nfIsCursor notin node.flags:
if gTrackPos.line < 0: return
var cp = inCheckpoint(node.info)
if cp == cpNone: return
var outputs = 0
# This keeps semExpr() from coming here recursively:
if c.inCompilesContext > 0: return
inc(c.inCompilesContext)
if optSuggest in gGlobalOptions:
var n = findClosestDot(node)
if gIdeCmd == ideSug:
var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
if n == nil: n = node
else: cp = cpExact
if n.kind == nkDotExpr and cp == cpExact:
if n.kind == nkDotExpr:
var obj = safeSemExpr(c, n.sons[0])
suggestFieldAccess(c, obj, outputs)
else:
#debug n
suggestEverything(c, n, outputs)
if optContext in gGlobalOptions:
var n = findClosestCall(node)
elif gIdeCmd == ideCon:
var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
if n == nil: n = node
else: cp = cpExact
if n.kind in CallNodes:
if n.kind in nkCallKinds:
var a = copyNode(n)
var x = safeSemExpr(c, n.sons[0])
if x.kind == nkEmpty or x.typ == nil: x = n.sons[0]
@@ -368,15 +349,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
if x.kind == nkEmpty or x.typ == nil: break
addSon(a, x)
suggestCall(c, a, n, outputs)
dec(c.inCompilesContext)
if outputs > 0 and optUsages notin gGlobalOptions: suggestQuit()
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n)
proc findSuggest*(c: PContext, n: PNode) =
if n == nil: return
suggestExpr(c, n)
for i in 0.. <safeLen(n):
findSuggest(c, n.sons[i])

View File

@@ -1012,7 +1012,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcQuit:
if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
message(c.debug[pc], hintQuitCalled)
quit(int(getOrdValue(regs[ra].regToNode)))
msgQuit(int8(getOrdValue(regs[ra].regToNode)))
else:
return TFullReg(kind: rkNone)
of opcSetLenStr:

View File

@@ -599,7 +599,7 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
proc parentDirPos(path: string): int =
var q = 1
if path[len(path)-1] in {DirSep, AltSep}: q = 2
if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
for i in countdown(len(path)-q, 0):
if path[i] in {DirSep, AltSep}: return i
result = -1

View File

@@ -9,7 +9,6 @@ version 0.10
- iterators always require a return type
- make nimble part of the distribution
- split idetools into separate tool
- split docgen into separate tool
- special rule for ``[]=``, items, pairs
- BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info)

View File

@@ -19,6 +19,8 @@ News
- ``logging.level`` and ``logging.handlers`` are no longer exported.
``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
should be used instead.
- ``nim idetools`` has been replaced by a separate tool `nimsuggest`_.
Language Additions
------------------