nimsuggest: first version
This commit is contained in:
parent
217390d181
commit
26b853923c
21 changed files with 364 additions and 218 deletions
|
|
@ -428,6 +428,7 @@ type
|
||||||
nfExplicitCall # x.y() was used instead of x.y
|
nfExplicitCall # x.y() was used instead of x.y
|
||||||
nfExprCall # this is an attempt to call a regular expression
|
nfExprCall # this is an attempt to call a regular expression
|
||||||
nfIsRef # this node is a 'ref' node; used for the VM
|
nfIsRef # this node is a 'ref' node; used for the VM
|
||||||
|
nfIsCursor # this node is attached a cursor; used for idetools
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
TNodeFlags* = set[TNodeFlag]
|
||||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
|
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
|
||||||
|
|
@ -883,7 +884,7 @@ const
|
||||||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
||||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||||
nfDotSetter, nfDotField,
|
nfDotSetter, nfDotField,
|
||||||
nfIsRef}
|
nfIsRef, nfIsCursor}
|
||||||
namePos* = 0
|
namePos* = 0
|
||||||
patternPos* = 1 # empty except for term rewriting macros
|
patternPos* = 1 # empty except for term rewriting macros
|
||||||
genericParamsPos* = 2
|
genericParamsPos* = 2
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,14 @@ proc getCommandLineDesc(): string =
|
||||||
proc helpOnError(pass: TCmdLinePass) =
|
proc helpOnError(pass: TCmdLinePass) =
|
||||||
if pass == passCmd1:
|
if pass == passCmd1:
|
||||||
msgWriteln(getCommandLineDesc())
|
msgWriteln(getCommandLineDesc())
|
||||||
quit(0)
|
msgQuit(0)
|
||||||
|
|
||||||
proc writeAdvancedUsage(pass: TCmdLinePass) =
|
proc writeAdvancedUsage(pass: TCmdLinePass) =
|
||||||
if pass == passCmd1:
|
if pass == passCmd1:
|
||||||
msgWriteln(`%`(HelpMessage, [VersionAsString,
|
msgWriteln(`%`(HelpMessage, [VersionAsString,
|
||||||
platform.OS[platform.hostOS].name,
|
platform.OS[platform.hostOS].name,
|
||||||
CPU[platform.hostCPU].name]) & AdvancedUsage)
|
CPU[platform.hostCPU].name]) & AdvancedUsage)
|
||||||
quit(0)
|
msgQuit(0)
|
||||||
|
|
||||||
proc writeVersionInfo(pass: TCmdLinePass) =
|
proc writeVersionInfo(pass: TCmdLinePass) =
|
||||||
if pass == passCmd1:
|
if pass == passCmd1:
|
||||||
|
|
@ -87,7 +87,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
|
||||||
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
|
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
|
||||||
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
|
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
|
||||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
|
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
|
||||||
quit(0)
|
msgQuit(0)
|
||||||
|
|
||||||
var
|
var
|
||||||
helpWritten: bool
|
helpWritten: bool
|
||||||
|
|
@ -255,8 +255,7 @@ proc trackDirty(arg: string, info: TLineInfo) =
|
||||||
gDirtyBufferIdx = a[0].fileInfoIdx
|
gDirtyBufferIdx = a[0].fileInfoIdx
|
||||||
gDirtyOriginalIdx = a[1].fileInfoIdx
|
gDirtyOriginalIdx = a[1].fileInfoIdx
|
||||||
|
|
||||||
optTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
|
gTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
|
||||||
msgs.addCheckpoint(optTrackPos)
|
|
||||||
|
|
||||||
proc track(arg: string, info: TLineInfo) =
|
proc track(arg: string, info: TLineInfo) =
|
||||||
var a = arg.split(',')
|
var a = arg.split(',')
|
||||||
|
|
@ -266,8 +265,7 @@ proc track(arg: string, info: TLineInfo) =
|
||||||
localError(info, errInvalidNumber, a[1])
|
localError(info, errInvalidNumber, a[1])
|
||||||
if parseUtils.parseInt(a[2], column) <= 0:
|
if parseUtils.parseInt(a[2], column) <= 0:
|
||||||
localError(info, errInvalidNumber, a[2])
|
localError(info, errInvalidNumber, a[2])
|
||||||
optTrackPos = newLineInfo(a[0], line, column)
|
gTrackPos = newLineInfo(a[0], line, column)
|
||||||
msgs.addCheckpoint(optTrackPos)
|
|
||||||
|
|
||||||
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||||
if pass in {passCmd2, passPP}:
|
if pass in {passCmd2, passPP}:
|
||||||
|
|
@ -541,19 +539,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||||
trackDirty(arg, info)
|
trackDirty(arg, info)
|
||||||
of "suggest":
|
of "suggest":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optSuggest)
|
gIdeCmd = ideSug
|
||||||
of "def":
|
of "def":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optDef)
|
gIdeCmd = ideDef
|
||||||
of "eval":
|
of "eval":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
gEvalExpr = arg
|
gEvalExpr = arg
|
||||||
of "context":
|
of "context":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optContext)
|
gIdeCmd = ideCon
|
||||||
of "usages":
|
of "usages":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optUsages)
|
gIdeCmd = ideUse
|
||||||
of "stdout":
|
of "stdout":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optStdout)
|
incl(gGlobalOptions, optStdout)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type
|
||||||
next*: PIdent # for hash-table chaining
|
next*: PIdent # for hash-table chaining
|
||||||
h*: THash # hash value of s
|
h*: THash # hash value of s
|
||||||
|
|
||||||
var firstCharIsCS*: bool
|
var firstCharIsCS*: bool = true
|
||||||
var buckets*: array[0..4096 * 2 - 1, PIdent]
|
var buckets*: array[0..4096 * 2 - 1, PIdent]
|
||||||
|
|
||||||
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
||||||
|
|
|
||||||
|
|
@ -693,7 +693,15 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
||||||
when not defined(nimfix):
|
when not defined(nimfix):
|
||||||
assert buf[pos+1] == '#'
|
assert buf[pos+1] == '#'
|
||||||
if buf[pos+2] == '[':
|
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
|
tok.tokType = tkComment
|
||||||
# iNumber contains the number of '\n' in the token
|
# iNumber contains the number of '\n' in the token
|
||||||
tok.iNumber = 0
|
tok.iNumber = 0
|
||||||
|
|
|
||||||
|
|
@ -58,12 +58,7 @@ proc commandCompileToC =
|
||||||
registerPass(cgenPass)
|
registerPass(cgenPass)
|
||||||
rodPass()
|
rodPass()
|
||||||
#registerPass(cleanupPass())
|
#registerPass(cleanupPass())
|
||||||
if optCaasEnabled in gGlobalOptions:
|
|
||||||
# echo "BEFORE CHECK DEP"
|
|
||||||
# discard checkDepMem(gProjectMainIdx)
|
|
||||||
# echo "CHECK DEP COMPLETE"
|
|
||||||
discard
|
|
||||||
|
|
||||||
compileProject()
|
compileProject()
|
||||||
cgenWriteModules()
|
cgenWriteModules()
|
||||||
if gCmd != cmdRun:
|
if gCmd != cmdRun:
|
||||||
|
|
@ -177,17 +172,15 @@ proc commandSuggest =
|
||||||
if gDirtyBufferIdx != 0:
|
if gDirtyBufferIdx != 0:
|
||||||
discard compileModule(gDirtyBufferIdx, {sfDirty})
|
discard compileModule(gDirtyBufferIdx, {sfDirty})
|
||||||
resetModule(gDirtyBufferIdx)
|
resetModule(gDirtyBufferIdx)
|
||||||
if optDef in gGlobalOptions:
|
|
||||||
defFromSourceMap(optTrackPos)
|
|
||||||
else:
|
else:
|
||||||
msgs.gErrorMax = high(int) # do not stop after first error
|
msgs.gErrorMax = high(int) # do not stop after first error
|
||||||
semanticPasses()
|
semanticPasses()
|
||||||
rodPass()
|
rodPass()
|
||||||
# XXX: this handles the case when the dirty buffer is the main file,
|
# XXX: this handles the case when the dirty buffer is the main file,
|
||||||
# but doesn't handle the case when it's imported module
|
# but doesn't handle the case when it's imported module
|
||||||
var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
|
#var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
|
||||||
else: gProjectMainIdx
|
# else: gProjectMainIdx
|
||||||
compileProject(projFile)
|
compileProject() #(projFile)
|
||||||
|
|
||||||
proc resetMemory =
|
proc resetMemory =
|
||||||
resetCompilationLists()
|
resetCompilationLists()
|
||||||
|
|
@ -218,7 +211,6 @@ proc resetMemory =
|
||||||
# rodread.gMods
|
# rodread.gMods
|
||||||
|
|
||||||
# !! ropes.cache
|
# !! ropes.cache
|
||||||
# semthreads.computed?
|
|
||||||
#
|
#
|
||||||
# suggest.usageSym
|
# suggest.usageSym
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,6 @@ proc getModule(fileIdx: int32): PSym =
|
||||||
if fileIdx >= 0 and fileIdx < gCompiledModules.len:
|
if fileIdx >= 0 and fileIdx < gCompiledModules.len:
|
||||||
result = gCompiledModules[fileIdx]
|
result = gCompiledModules[fileIdx]
|
||||||
|
|
||||||
template compiledAt(x: PSym): expr =
|
|
||||||
gMemCacheData[x.position].compiledAt
|
|
||||||
|
|
||||||
template crc(x: PSym): expr =
|
template crc(x: PSym): expr =
|
||||||
gMemCacheData[x.position].crc
|
gMemCacheData[x.position].crc
|
||||||
|
|
||||||
|
|
@ -74,10 +71,12 @@ proc addDep(x: PSym, dep: int32) =
|
||||||
|
|
||||||
proc resetModule*(fileIdx: int32) =
|
proc resetModule*(fileIdx: int32) =
|
||||||
# echo "HARD RESETTING ", fileIdx.toFilename
|
# echo "HARD RESETTING ", fileIdx.toFilename
|
||||||
gMemCacheData[fileIdx].needsRecompile = Yes
|
if fileIdx <% gMemCacheData.len:
|
||||||
gCompiledModules[fileIdx] = nil
|
gMemCacheData[fileIdx].needsRecompile = Yes
|
||||||
cgendata.gModules[fileIdx] = nil
|
if fileIdx <% gCompiledModules.len:
|
||||||
resetSourceMap(fileIdx)
|
gCompiledModules[fileIdx] = nil
|
||||||
|
if fileIdx <% cgendata.gModules.len:
|
||||||
|
cgendata.gModules[fileIdx] = nil
|
||||||
|
|
||||||
proc resetAllModules* =
|
proc resetAllModules* =
|
||||||
for i in 0..gCompiledModules.high:
|
for i in 0..gCompiledModules.high:
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@
|
||||||
import
|
import
|
||||||
options, strutils, os, tables, ropes, platform
|
options, strutils, os, tables, ropes, platform
|
||||||
|
|
||||||
when useCaas:
|
|
||||||
import sockets
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TMsgKind* = enum
|
TMsgKind* = enum
|
||||||
errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
|
errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
|
||||||
|
|
@ -572,9 +569,6 @@ var
|
||||||
gWarnCounter*: int = 0
|
gWarnCounter*: int = 0
|
||||||
gErrorMax*: int = 1 # stop after gErrorMax errors
|
gErrorMax*: int = 1 # stop after gErrorMax errors
|
||||||
|
|
||||||
when useCaas:
|
|
||||||
var stdoutSocket*: Socket
|
|
||||||
|
|
||||||
proc unknownLineInfo*(): TLineInfo =
|
proc unknownLineInfo*(): TLineInfo =
|
||||||
result.line = int16(-1)
|
result.line = int16(-1)
|
||||||
result.col = int16(-1)
|
result.col = int16(-1)
|
||||||
|
|
@ -586,32 +580,35 @@ var
|
||||||
bufferedMsgs*: seq[string]
|
bufferedMsgs*: seq[string]
|
||||||
|
|
||||||
errorOutputs* = {eStdOut, eStdErr}
|
errorOutputs* = {eStdOut, eStdErr}
|
||||||
|
writelnHook*: proc (output: string) {.closure.}
|
||||||
|
|
||||||
proc clearBufferedMsgs* =
|
proc clearBufferedMsgs* =
|
||||||
bufferedMsgs = nil
|
bufferedMsgs = nil
|
||||||
|
|
||||||
proc suggestWriteln*(s: string) =
|
proc suggestWriteln*(s: string) =
|
||||||
if eStdOut in errorOutputs:
|
if eStdOut in errorOutputs:
|
||||||
when useCaas:
|
if isNil(writelnHook): writeln(stdout, s)
|
||||||
if isNil(stdoutSocket): writeln(stdout, s)
|
else: writelnHook(s)
|
||||||
else:
|
|
||||||
writeln(stdout, s)
|
|
||||||
stdoutSocket.send(s & "\c\L")
|
|
||||||
else:
|
|
||||||
writeln(stdout, s)
|
|
||||||
|
|
||||||
if eInMemory in errorOutputs:
|
if eInMemory in errorOutputs:
|
||||||
bufferedMsgs.safeAdd(s)
|
bufferedMsgs.safeAdd(s)
|
||||||
|
|
||||||
|
proc msgQuit*(x: int8) = quit x
|
||||||
|
proc msgQuit*(x: string) = quit x
|
||||||
|
|
||||||
proc suggestQuit*() =
|
proc suggestQuit*() =
|
||||||
if not isServing:
|
when true:
|
||||||
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")
|
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
|
# this format is understood by many text editors: it is the same that
|
||||||
# Borland and Freepascal use
|
# Borland and Freepascal use
|
||||||
|
|
@ -679,14 +676,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
|
||||||
# only for debugging purposes
|
# only for debugging purposes
|
||||||
result = filename in info.toFilename
|
result = filename in info.toFilename
|
||||||
|
|
||||||
var checkPoints*: seq[TLineInfo] = @[]
|
var gTrackPos*: TLineInfo
|
||||||
var optTrackPos*: TLineInfo
|
|
||||||
|
|
||||||
proc addCheckpoint*(info: TLineInfo) =
|
|
||||||
checkPoints.add(info)
|
|
||||||
|
|
||||||
proc addCheckpoint*(filename: string, line: int) =
|
|
||||||
addCheckpoint(newLineInfo(filename, line, - 1))
|
|
||||||
|
|
||||||
proc outWriteln*(s: string) =
|
proc outWriteln*(s: string) =
|
||||||
## Writes to stdout. Always.
|
## Writes to stdout. Always.
|
||||||
|
|
@ -694,7 +684,8 @@ proc outWriteln*(s: string) =
|
||||||
|
|
||||||
proc msgWriteln*(s: string) =
|
proc msgWriteln*(s: string) =
|
||||||
## Writes to stdout. If --stdout option is given, writes to stderr instead.
|
## 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 optStdout in gGlobalOptions:
|
||||||
if eStdErr in errorOutputs: writeln(stderr, s)
|
if eStdErr in errorOutputs: writeln(stderr, s)
|
||||||
|
|
@ -714,19 +705,6 @@ proc msgKindToString*(kind: TMsgKind): string =
|
||||||
proc getMessageStr(msg: TMsgKind, arg: string): string =
|
proc getMessageStr(msg: TMsgKind, arg: string): string =
|
||||||
result = msgKindToString(msg) % [arg]
|
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
|
type
|
||||||
TErrorHandling = enum doNothing, doAbort, doRaise
|
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),
|
result = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
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,
|
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
eh: TErrorHandling) =
|
eh: TErrorHandling) =
|
||||||
var frmt: string
|
var frmt: string
|
||||||
|
|
@ -821,7 +802,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
inc(gHintCounter)
|
inc(gHintCounter)
|
||||||
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
|
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
coordToStr(info.col), getMessageStr(msg, arg)]
|
||||||
if not ignoreMsg:
|
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
|
||||||
msgWriteln(s)
|
msgWriteln(s)
|
||||||
if optPrintSurroundingSrc and msg in errMin..errMax:
|
if optPrintSurroundingSrc and msg in errMin..errMax:
|
||||||
info.writeSurroundingSrc
|
info.writeSurroundingSrc
|
||||||
|
|
|
||||||
|
|
@ -89,4 +89,4 @@ condsyms.initDefines()
|
||||||
|
|
||||||
when not defined(selftest):
|
when not defined(selftest):
|
||||||
handleCmdLine()
|
handleCmdLine()
|
||||||
quit(int8(msgs.gErrorCounter > 0))
|
msgQuit(int8(msgs.gErrorCounter > 0))
|
||||||
|
|
|
||||||
187
compiler/nimsuggest/nimsuggest.nim
Normal file
187
compiler/nimsuggest/nimsuggest.nim
Normal 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()
|
||||||
|
|
@ -56,17 +56,14 @@ type # please make sure we have under 32 options
|
||||||
optNoMain, # do not generate a "main" proc
|
optNoMain, # do not generate a "main" proc
|
||||||
optThreads, # support for multi-threading
|
optThreads, # support for multi-threading
|
||||||
optStdout, # output to stdout
|
optStdout, # output to stdout
|
||||||
optSuggest, # ideTools: 'suggest'
|
|
||||||
optContext, # ideTools: 'context'
|
|
||||||
optDef, # ideTools: 'def'
|
|
||||||
optUsages, # ideTools: 'usages'
|
|
||||||
optThreadAnalysis, # thread analysis pass
|
optThreadAnalysis, # thread analysis pass
|
||||||
optTaintMode, # taint mode turned on
|
optTaintMode, # taint mode turned on
|
||||||
optTlsEmulation, # thread var emulation turned on
|
optTlsEmulation, # thread var emulation turned on
|
||||||
optGenIndex # generate index file for documentation;
|
optGenIndex # generate index file for documentation;
|
||||||
optEmbedOrigSrc # embed the original source in the generated code
|
optEmbedOrigSrc # embed the original source in the generated code
|
||||||
# also: generate header file
|
# also: generate header file
|
||||||
|
optIdeDebug # idetools: debug mode
|
||||||
|
optIdeTerse # idetools: use terse descriptions
|
||||||
TGlobalOptions* = set[TGlobalOption]
|
TGlobalOptions* = set[TGlobalOption]
|
||||||
TCommands* = enum # Nim's commands
|
TCommands* = enum # Nim's commands
|
||||||
# **keep binary compatible**
|
# **keep binary compatible**
|
||||||
|
|
@ -86,6 +83,12 @@ type # please make sure we have under 32 options
|
||||||
TGCMode* = enum # the selected GC
|
TGCMode* = enum # the selected GC
|
||||||
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
|
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
|
||||||
|
|
||||||
|
TIdeCmd* = enum
|
||||||
|
ideNone, ideSug, ideCon, ideDef, ideUse
|
||||||
|
|
||||||
|
var
|
||||||
|
gIdeCmd*: TIdeCmd
|
||||||
|
|
||||||
const
|
const
|
||||||
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
|
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
|
||||||
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
|
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,12 @@ proc rawSkipComment(p: var TParser, node: PNode) =
|
||||||
if p.tok.tokType == tkComment:
|
if p.tok.tokType == tkComment:
|
||||||
if node != nil:
|
if node != nil:
|
||||||
if node.comment == nil: node.comment = ""
|
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:
|
else:
|
||||||
parMessage(p, errInternal, "skipComment")
|
parMessage(p, errInternal, "skipComment")
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -379,7 +384,8 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
|
||||||
#| dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
|
#| dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
|
||||||
var info = p.parLineInfo
|
var info = p.parLineInfo
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optInd(p, a)
|
result = newNodeI(nkDotExpr, info)
|
||||||
|
optInd(p, result)
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
of tkType:
|
of tkType:
|
||||||
result = newNodeP(nkTypeOfExpr, p)
|
result = newNodeP(nkTypeOfExpr, p)
|
||||||
|
|
@ -390,7 +396,6 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
else:
|
else:
|
||||||
result = newNodeI(nkDotExpr, info)
|
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
addSon(result, parseSymbol(p))
|
addSon(result, parseSymbol(p))
|
||||||
|
|
||||||
|
|
@ -737,7 +742,7 @@ proc parseOperators(p: var TParser, headNode: PNode,
|
||||||
var a = newNodeP(nkInfix, p)
|
var a = newNodeP(nkInfix, p)
|
||||||
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
|
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optInd(p, opNode)
|
optInd(p, a)
|
||||||
# read sub-expression with higher priority:
|
# read sub-expression with higher priority:
|
||||||
var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
|
var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
|
||||||
addSon(a, opNode)
|
addSon(a, opNode)
|
||||||
|
|
@ -1735,8 +1740,8 @@ proc parseObject(p: var TParser): PNode =
|
||||||
|
|
||||||
proc parseTypeClassParam(p: var TParser): PNode =
|
proc parseTypeClassParam(p: var TParser): PNode =
|
||||||
if p.tok.tokType == tkVar:
|
if p.tok.tokType == tkVar:
|
||||||
|
result = newNodeP(nkVarTy, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = newNode(nkVarTy)
|
|
||||||
result.addSon(p.parseSymbol)
|
result.addSon(p.parseSymbol)
|
||||||
else:
|
else:
|
||||||
result = p.parseSymbol
|
result = p.parseSymbol
|
||||||
|
|
@ -1747,7 +1752,7 @@ proc parseTypeClass(p: var TParser): PNode =
|
||||||
#| &IND{>} stmt
|
#| &IND{>} stmt
|
||||||
result = newNodeP(nkTypeClassTy, p)
|
result = newNodeP(nkTypeClassTy, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
var args = newNode(nkArgList)
|
var args = newNodeP(nkArgList, p)
|
||||||
addSon(result, args)
|
addSon(result, args)
|
||||||
addSon(args, p.parseTypeClassParam)
|
addSon(args, p.parseTypeClassParam)
|
||||||
while p.tok.tokType == tkComma:
|
while p.tok.tokType == tkComma:
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
|
||||||
if s == nil:
|
if s == nil:
|
||||||
rawMessage(errCannotOpenFile, filename)
|
rawMessage(errCannotOpenFile, filename)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
s = stream
|
s = stream
|
||||||
while true:
|
while true:
|
||||||
openParsers(p, fileIdx, s)
|
openParsers(p, fileIdx, s)
|
||||||
|
|
|
||||||
|
|
@ -411,12 +411,6 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
|
||||||
proc pragmaBreakpoint(c: PContext, n: PNode) =
|
proc pragmaBreakpoint(c: PContext, n: PNode) =
|
||||||
discard getOptionalStr(c, n, "")
|
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) =
|
proc pragmaWatchpoint(c: PContext, n: PNode) =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind == nkExprColonExpr:
|
||||||
n.sons[1] = c.semExpr(c, n.sons[1])
|
n.sons[1] = c.semExpr(c, n.sons[1])
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,9 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||||
if c.inCompilesContext > 0:
|
if c.inCompilesContext > 0:
|
||||||
# fail fast:
|
# fail fast:
|
||||||
globalError(n.info, errTypeMismatch, "")
|
globalError(n.info, errTypeMismatch, "")
|
||||||
if errors.len == 0:
|
if errors.isNil or errors.len == 0:
|
||||||
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
||||||
|
return
|
||||||
|
|
||||||
# to avoid confusing errors like:
|
# to avoid confusing errors like:
|
||||||
# got (SslPtr, SocketHandle)
|
# got (SslPtr, SocketHandle)
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
||||||
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
|
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
|
||||||
debug t
|
debug t
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
quit 1
|
|
||||||
|
|
||||||
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = replaceTypeVarsTAux(cl, t)
|
result = replaceTypeVarsTAux(cl, t)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nim Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -14,7 +14,7 @@ import
|
||||||
extccomp, strutils, os, platform, parseopt
|
extccomp, strutils, os, platform, parseopt
|
||||||
|
|
||||||
when useCaas:
|
when useCaas:
|
||||||
import sockets
|
import net
|
||||||
|
|
||||||
# We cache modules and the dependency graph. However, we don't check for
|
# 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
|
# file changes but expect the client to tell us about them, otherwise the
|
||||||
|
|
@ -61,14 +61,16 @@ proc serve*(action: proc (){.nimcall.}) =
|
||||||
|
|
||||||
of "tcp", "":
|
of "tcp", "":
|
||||||
when useCaas:
|
when useCaas:
|
||||||
var server = socket()
|
var server = newSocket()
|
||||||
if server == invalidSocket: raiseOSError(osLastError())
|
|
||||||
let p = getConfigVar("server.port")
|
let p = getConfigVar("server.port")
|
||||||
let port = if p.len > 0: parseInt(p).Port else: 6000.Port
|
let port = if p.len > 0: parseInt(p).Port else: 6000.Port
|
||||||
server.bindAddr(port, getConfigVar("server.address"))
|
server.bindAddr(port, getConfigVar("server.address"))
|
||||||
var inp = "".TaintedString
|
var inp = "".TaintedString
|
||||||
server.listen()
|
server.listen()
|
||||||
new(stdoutSocket)
|
var stdoutSocket = newSocket()
|
||||||
|
msgs.writelnHook = proc (line: string) =
|
||||||
|
stdoutSocket.send(line & "\c\L")
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
accept(server, stdoutSocket)
|
accept(server, stdoutSocket)
|
||||||
stdoutSocket.readLine(inp)
|
stdoutSocket.readLine(inp)
|
||||||
|
|
@ -76,7 +78,7 @@ proc serve*(action: proc (){.nimcall.}) =
|
||||||
stdoutSocket.send("\c\L")
|
stdoutSocket.send("\c\L")
|
||||||
stdoutSocket.close()
|
stdoutSocket.close()
|
||||||
else:
|
else:
|
||||||
quit "server.type not supported; compiler built without caas support"
|
msgQuit "server.type not supported; compiler built without caas support"
|
||||||
else:
|
else:
|
||||||
echo "Invalid server.type:", typ
|
echo "Invalid server.type:", typ
|
||||||
quit 1
|
msgQuit 1
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nim Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# 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 =
|
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
|
||||||
result = section
|
result = section
|
||||||
result.add(sep)
|
result.add(sep)
|
||||||
result.add($s.kind)
|
if optIdeTerse in gGlobalOptions:
|
||||||
result.add(sep)
|
if s.kind in routineKinds:
|
||||||
if not isLocal and s.kind != skModule:
|
result.add renderTree(s.ast, {renderNoBody, renderNoComments,
|
||||||
let ow = s.owner
|
renderDocComments, renderNoPragmas})
|
||||||
if ow.kind != skModule and ow.owner != nil:
|
else:
|
||||||
let ow2 = ow.owner
|
result.add s.name.s
|
||||||
result.add(ow2.origModuleName)
|
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('.')
|
||||||
result.add(ow.origModuleName)
|
result.add(s.name.s)
|
||||||
result.add('.')
|
result.add(sep)
|
||||||
result.add(s.name.s)
|
if s.typ != nil:
|
||||||
result.add(sep)
|
result.add(typeToString(s.typ))
|
||||||
if s.typ != nil:
|
result.add(sep)
|
||||||
result.add(typeToString(s.typ))
|
result.add(toFullPath(li))
|
||||||
result.add(sep)
|
result.add(sep)
|
||||||
result.add(toFullPath(li))
|
result.add($toLinenumber(li))
|
||||||
result.add(sep)
|
result.add(sep)
|
||||||
result.add($toLinenumber(li))
|
result.add($toColumn(li))
|
||||||
result.add(sep)
|
result.add(sep)
|
||||||
result.add($toColumn(li))
|
when not defined(noDocgen):
|
||||||
result.add(sep)
|
result.add(s.extractDocComment.escape)
|
||||||
when not defined(noDocgen):
|
|
||||||
result.add(s.extractDocComment.escape)
|
|
||||||
|
|
||||||
proc symToStr(s: PSym, isLocal: bool, section: string): string =
|
proc symToStr(s: PSym, isLocal: bool, section: string): string =
|
||||||
result = symToStr(s, isLocal, section, s.info)
|
result = symToStr(s, isLocal, section, s.info)
|
||||||
|
|
||||||
proc filterSym(s: PSym): bool {.inline.} =
|
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.} =
|
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||||
let fmoduleId = getModule(f).id
|
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.} =
|
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:
|
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)
|
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
|
||||||
|
|
||||||
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
|
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
|
||||||
assert typ != nil
|
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) =
|
proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
|
||||||
# do not produce too many symbols:
|
# do not produce too many symbols:
|
||||||
|
|
@ -191,19 +217,28 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
|
||||||
else:
|
else:
|
||||||
suggestOperations(c, n, typ, outputs)
|
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 =
|
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
|
result = n
|
||||||
else:
|
else:
|
||||||
for i in 0.. <safeLen(n):
|
for i in 0.. <safeLen(n):
|
||||||
result = findClosestDot(n.sons[i])
|
result = findClosestDot(n.sons[i])
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
|
|
||||||
const
|
|
||||||
CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit}
|
|
||||||
|
|
||||||
proc findClosestCall(n: PNode): PNode =
|
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
|
result = n
|
||||||
else:
|
else:
|
||||||
for i in 0.. <safeLen(n):
|
for i in 0.. <safeLen(n):
|
||||||
|
|
@ -211,15 +246,14 @@ proc findClosestCall(n: PNode): PNode =
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
|
|
||||||
proc isTracked(current: TLineInfo, tokenLen: int): bool =
|
proc isTracked(current: TLineInfo, tokenLen: int): bool =
|
||||||
for i in countup(0, high(checkPoints)):
|
if current.fileIndex == gTrackPos.fileIndex:
|
||||||
if current.fileIndex == checkPoints[i].fileIndex:
|
if current.line == gTrackPos.line:
|
||||||
if current.line == checkPoints[i].line:
|
let col = gTrackPos.col
|
||||||
let col = checkPoints[i].col
|
if col >= current.col and col <= current.col+tokenLen-1:
|
||||||
if col >= current.col and col <= current.col+tokenLen-1:
|
return true
|
||||||
return true
|
|
||||||
|
|
||||||
proc findClosestSym(n: PNode): PNode =
|
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
|
result = n
|
||||||
elif n.kind notin {nkNone..nkNilLit}:
|
elif n.kind notin {nkNone..nkNilLit}:
|
||||||
for i in 0.. <sonsLen(n):
|
for i in 0.. <sonsLen(n):
|
||||||
|
|
@ -258,69 +292,18 @@ proc findDefinition(info: TLineInfo; s: PSym) =
|
||||||
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
|
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
|
||||||
suggestQuit()
|
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) =
|
proc ensureIdx[T](x: var T, y: int) =
|
||||||
if x.len <= y: x.setLen(y+1)
|
if x.len <= y: x.setLen(y+1)
|
||||||
|
|
||||||
proc ensureSeq[T](x: var seq[T]) =
|
proc ensureSeq[T](x: var seq[T]) =
|
||||||
if x == nil: newSeq(x, 0)
|
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.} =
|
proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
|
||||||
## misnamed: should be 'symDeclared'
|
## misnamed: should be 'symDeclared'
|
||||||
if optUsages in gGlobalOptions:
|
if gIdeCmd == ideUse:
|
||||||
findUsages(info, s)
|
findUsages(info, s)
|
||||||
if optDef in gGlobalOptions:
|
elif gIdeCmd == ideDef:
|
||||||
findDefinition(info, s)
|
findDefinition(info, s)
|
||||||
if isServing:
|
|
||||||
addToSourceMap(s, info)
|
|
||||||
|
|
||||||
proc markUsed(info: TLineInfo; s: PSym) =
|
proc markUsed(info: TLineInfo; s: PSym) =
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
|
|
@ -334,30 +317,28 @@ proc useSym*(sym: PSym): PNode =
|
||||||
markUsed(result.info, sym)
|
markUsed(result.info, sym)
|
||||||
|
|
||||||
proc suggestExpr*(c: PContext, node: PNode) =
|
proc suggestExpr*(c: PContext, node: PNode) =
|
||||||
var cp = msgs.inCheckpoint(node.info)
|
if nfIsCursor notin node.flags:
|
||||||
if cp == cpNone: return
|
if gTrackPos.line < 0: return
|
||||||
|
var cp = inCheckpoint(node.info)
|
||||||
|
if cp == cpNone: return
|
||||||
var outputs = 0
|
var outputs = 0
|
||||||
# This keeps semExpr() from coming here recursively:
|
# This keeps semExpr() from coming here recursively:
|
||||||
if c.inCompilesContext > 0: return
|
if c.inCompilesContext > 0: return
|
||||||
inc(c.inCompilesContext)
|
inc(c.inCompilesContext)
|
||||||
|
|
||||||
if optSuggest in gGlobalOptions:
|
if gIdeCmd == ideSug:
|
||||||
var n = findClosestDot(node)
|
var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
|
||||||
if n == nil: n = node
|
if n == nil: n = node
|
||||||
else: cp = cpExact
|
if n.kind == nkDotExpr:
|
||||||
if n.kind == nkDotExpr and cp == cpExact:
|
|
||||||
var obj = safeSemExpr(c, n.sons[0])
|
var obj = safeSemExpr(c, n.sons[0])
|
||||||
suggestFieldAccess(c, obj, outputs)
|
suggestFieldAccess(c, obj, outputs)
|
||||||
else:
|
else:
|
||||||
#debug n
|
|
||||||
suggestEverything(c, n, outputs)
|
suggestEverything(c, n, outputs)
|
||||||
|
|
||||||
if optContext in gGlobalOptions:
|
elif gIdeCmd == ideCon:
|
||||||
var n = findClosestCall(node)
|
var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
|
||||||
if n == nil: n = node
|
if n == nil: n = node
|
||||||
else: cp = cpExact
|
if n.kind in nkCallKinds:
|
||||||
|
|
||||||
if n.kind in CallNodes:
|
|
||||||
var a = copyNode(n)
|
var a = copyNode(n)
|
||||||
var x = safeSemExpr(c, n.sons[0])
|
var x = safeSemExpr(c, n.sons[0])
|
||||||
if x.kind == nkEmpty or x.typ == nil: x = 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
|
if x.kind == nkEmpty or x.typ == nil: break
|
||||||
addSon(a, x)
|
addSon(a, x)
|
||||||
suggestCall(c, a, n, outputs)
|
suggestCall(c, a, n, outputs)
|
||||||
|
|
||||||
dec(c.inCompilesContext)
|
dec(c.inCompilesContext)
|
||||||
if outputs > 0 and optUsages notin gGlobalOptions: suggestQuit()
|
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
|
||||||
|
|
||||||
proc suggestStmt*(c: PContext, n: PNode) =
|
proc suggestStmt*(c: PContext, n: PNode) =
|
||||||
suggestExpr(c, n)
|
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])
|
|
||||||
|
|
|
||||||
|
|
@ -1012,7 +1012,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
of opcQuit:
|
of opcQuit:
|
||||||
if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
|
if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
|
||||||
message(c.debug[pc], hintQuitCalled)
|
message(c.debug[pc], hintQuitCalled)
|
||||||
quit(int(getOrdValue(regs[ra].regToNode)))
|
msgQuit(int8(getOrdValue(regs[ra].regToNode)))
|
||||||
else:
|
else:
|
||||||
return TFullReg(kind: rkNone)
|
return TFullReg(kind: rkNone)
|
||||||
of opcSetLenStr:
|
of opcSetLenStr:
|
||||||
|
|
|
||||||
|
|
@ -599,7 +599,7 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
|
||||||
|
|
||||||
proc parentDirPos(path: string): int =
|
proc parentDirPos(path: string): int =
|
||||||
var q = 1
|
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):
|
for i in countdown(len(path)-q, 0):
|
||||||
if path[i] in {DirSep, AltSep}: return i
|
if path[i] in {DirSep, AltSep}: return i
|
||||||
result = -1
|
result = -1
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -9,7 +9,6 @@ version 0.10
|
||||||
- iterators always require a return type
|
- iterators always require a return type
|
||||||
|
|
||||||
- make nimble part of the distribution
|
- make nimble part of the distribution
|
||||||
- split idetools into separate tool
|
|
||||||
- split docgen into separate tool
|
- split docgen into separate tool
|
||||||
- special rule for ``[]=``, items, pairs
|
- special rule for ``[]=``, items, pairs
|
||||||
- BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info)
|
- BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ News
|
||||||
- ``logging.level`` and ``logging.handlers`` are no longer exported.
|
- ``logging.level`` and ``logging.handlers`` are no longer exported.
|
||||||
``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
|
``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
|
||||||
should be used instead.
|
should be used instead.
|
||||||
|
- ``nim idetools`` has been replaced by a separate tool `nimsuggest`_.
|
||||||
|
|
||||||
|
|
||||||
Language Additions
|
Language Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue