diff options
author | Araq <rumpf_a@web.de> | 2015-01-26 21:38:39 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2015-01-27 01:36:19 +0100 |
commit | 26b853923cbbd92b9b74cf1b9dce773b0ff39cf3 (patch) | |
tree | 6ccfe4b728b2469b69fe4a884f393b51dc22ab26 /compiler | |
parent | 217390d18192e87534e61b9d15c55d44b7e163f7 (diff) | |
download | Nim-26b853923cbbd92b9b74cf1b9dce773b0ff39cf3.tar.gz |
nimsuggest: first version
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/commands.nim | 20 | ||||
-rw-r--r-- | compiler/idents.nim | 2 | ||||
-rw-r--r-- | compiler/lexer.nim | 10 | ||||
-rw-r--r-- | compiler/main.nim | 16 | ||||
-rw-r--r-- | compiler/modules.nim | 13 | ||||
-rw-r--r-- | compiler/msgs.nim | 67 | ||||
-rw-r--r-- | compiler/nim.nim | 2 | ||||
-rw-r--r-- | compiler/nimsuggest/nimsuggest.nim | 187 | ||||
-rw-r--r-- | compiler/options.nim | 13 | ||||
-rw-r--r-- | compiler/parser.nim | 17 | ||||
-rw-r--r-- | compiler/passes.nim | 2 | ||||
-rw-r--r-- | compiler/pragmas.nim | 6 | ||||
-rw-r--r-- | compiler/semcall.nim | 3 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 1 | ||||
-rw-r--r-- | compiler/service.nim | 16 | ||||
-rw-r--r-- | compiler/suggest.nim | 197 | ||||
-rw-r--r-- | compiler/vm.nim | 2 |
18 files changed, 361 insertions, 216 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 883b68d71..00515875f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/commands.nim b/compiler/commands.nim index 9fa221de0..a438ea566 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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) diff --git a/compiler/idents.nim b/compiler/idents.nim index 775bffa00..0cca18929 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -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 = diff --git a/compiler/lexer.nim b/compiler/lexer.nim index d856063cb..4fbac2d5c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -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 diff --git a/compiler/main.nim b/compiler/main.nim index 05209fa80..4cf9adc0d 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -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 # diff --git a/compiler/modules.nim b/compiler/modules.nim index 3dedc4d18..db05ccc6c 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -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: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index d94b8ade8..e19e6f618 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -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) @@ -715,19 +706,6 @@ 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 proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = @@ -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 diff --git a/compiler/nim.nim b/compiler/nim.nim index a87e0a1ac..b23d438e0 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -89,4 +89,4 @@ condsyms.initDefines() when not defined(selftest): handleCmdLine() - quit(int8(msgs.gErrorCounter > 0)) + msgQuit(int8(msgs.gErrorCounter > 0)) diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim new file mode 100644 index 000000000..b9d167beb --- /dev/null +++ b/compiler/nimsuggest/nimsuggest.nim @@ -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() diff --git a/compiler/options.nim b/compiler/options.nim index 415ac8430..0a1fbebc1 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -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} diff --git a/compiler/parser.nim b/compiler/parser.nim index 76285b7ec..adf3b72a3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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: diff --git a/compiler/passes.nim b/compiler/passes.nim index a63e29b35..95c944410 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1b04d5296..735460906 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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]) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 7f7005633..cdfdfc9d0 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d8efdbb23..7b8f478ec 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -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) diff --git a/compiler/service.nim b/compiler/service.nim index 4a1b296cd..d84fdf060 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -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 diff --git a/compiler/suggest.nim b/compiler/suggest.nim index f7b00c8f8..bd8341936 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -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]) diff --git a/compiler/vm.nim b/compiler/vm.nim index ae5fcb43f..7edbb3fd2 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -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: |