summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-05-07 18:44:24 +0200
committerAraq <rumpf_a@web.de>2013-05-07 18:44:24 +0200
commit9fc98cefda91d7199e46d271a466a07867354d91 (patch)
tree4e8abce285ad9f5c145ec9f660966f88604ab422
parentd97504d6ef7ad6c30adc5cd64872f600fd4ec14d (diff)
parentf0be93bfa2cb49ab88595478efb4457391d49a31 (diff)
downloadNim-9fc98cefda91d7199e46d271a466a07867354d91.tar.gz
Merge branch 'master' into newparser
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/cgen.nim34
-rw-r--r--compiler/commands.nim22
-rw-r--r--compiler/extccomp.nim2
-rw-r--r--compiler/main.nim57
-rw-r--r--compiler/msgs.nim15
-rw-r--r--compiler/options.nim6
-rw-r--r--compiler/rodread.nim6
-rw-r--r--compiler/rodwrite.nim3
-rw-r--r--compiler/semstmts.nim3
-rw-r--r--compiler/semtypes.nim3
-rw-r--r--compiler/service.nim3
-rw-r--r--compiler/sigmatch.nim9
-rw-r--r--compiler/suggest.nim61
-rw-r--r--tests/caas/compile-suggest.txt7
-rw-r--r--tests/caas/def-def-compile.txt10
-rw-r--r--tests/caas/main_dirty.nim14
-rw-r--r--tests/caas/suggest-compile.txt7
18 files changed, 196 insertions, 69 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 523831c04..eb12c977f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -275,6 +275,7 @@ const
 
   sfDirty* = sfPure
     # template is not hygienic (old styled template)
+    # module, compiled from a dirty-buffer
 
   sfAnon* = sfDiscardable
     # symbol name that was generated by the compiler
@@ -680,7 +681,6 @@ type
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
     align*: int               # the type's alignment requirements
-    containerID*: int         # used for type checking of generics
     loc*: TLoc
 
   TPair*{.final.} = object 
@@ -1020,7 +1020,6 @@ proc assignType(dest, src: PType) =
   dest.n = src.n
   dest.size = src.size
   dest.align = src.align
-  dest.containerID = src.containerID
   dest.destructor = src.destructor
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index f09252b48..f034f6675 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1279,10 +1279,6 @@ proc updateCachedModule(m: BModule) =
 
   addFileToLink(cfilenoext)
 
-proc updateCachedModules* =
-  for m in cgenModules():
-    if m.fromCache: m.updateCachedModule
-
 proc myClose(b: PPassContext, n: PNode): PNode = 
   result = n
   if b == nil or passes.skipCodegen(n): return 
@@ -1292,24 +1288,28 @@ proc myClose(b: PPassContext, n: PNode): PNode =
     genStmts(m.initProc, n)
   # cached modules need to registered too: 
   registerModuleToMain(m.module)
-  
+
   if sfMainModule in m.module.flags: 
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
-    genMainProc(m) 
-    # we need to process the transitive closure because recursive module
-    # deps are allowed (and the system module is processed in the wrong
-    # order anyway)
-    if generatedHeader != nil: finishModule(generatedHeader)
-    while gForwardedProcsCounter > 0:
-      for m in cgenModules():
-        if not m.fromCache:
-          finishModule(m)
+    genMainProc(m)
+
+proc cgenWriteModules* =
+  # we need to process the transitive closure because recursive module
+  # deps are allowed (and the system module is processed in the wrong
+  # order anyway)
+  if generatedHeader != nil: finishModule(generatedHeader)
+  while gForwardedProcsCounter > 0:
     for m in cgenModules():
       if not m.fromCache:
-        writeModule(m, pending=true)
-    writeMapping(gMapping)
-    if generatedHeader != nil: writeHeader(generatedHeader)
+        finishModule(m)
+  for m in cgenModules():
+    if m.fromCache:
+      m.updateCachedModule
+    else:
+      m.writeModule(pending=true)
+  writeMapping(gMapping)
+  if generatedHeader != nil: writeHeader(generatedHeader)
 
 const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 27da03bbe..63704b328 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -207,6 +207,22 @@ proc processPath(path: string, notRelativeToProj = false): string =
     "projectname", options.gProjectName,
     "projectpath", options.gProjectPath])
 
+proc trackDirty(arg: string, info: TLineInfo) =
+  var a = arg.split(',')
+  if a.len != 4: LocalError(info, errTokenExpected,
+                            "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN")
+  var line, column: int
+  if parseUtils.parseInt(a[2], line) <= 0:
+    LocalError(info, errInvalidNumber, a[1])
+  if parseUtils.parseInt(a[3], column) <= 0:
+    LocalError(info, errInvalidNumber, a[2])
+  
+  gDirtyBufferIdx = a[0].fileInfoIdx
+  gDirtyOriginalIdx = a[1].fileInfoIdx
+ 
+  optTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
+  msgs.addCheckpoint(optTrackPos)
+
 proc track(arg: string, info: TLineInfo) = 
   var a = arg.split(',')
   if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN")
@@ -215,7 +231,8 @@ proc track(arg: string, info: TLineInfo) =
     LocalError(info, errInvalidNumber, a[1])
   if parseUtils.parseInt(a[2], column) <= 0:
     LocalError(info, errInvalidNumber, a[2])
-  msgs.addCheckpoint(newLineInfo(a[0], line, column))
+  optTrackPos = newLineInfo(a[0], line, column)
+  msgs.addCheckpoint(optTrackPos)
 
 proc dynlibOverride(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   if pass in {passCmd2, passPP}:
@@ -469,6 +486,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   of "track":
     expectArg(switch, arg, pass, info)
     track(arg, info)
+  of "trackdirty":
+    expectArg(switch, arg, pass, info)
+    trackDirty(arg, info)
   of "suggest": 
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optSuggest)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 8107fcd82..44396100b 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -579,7 +579,7 @@ proc CallCCompiler*(projectfile: string) =
       else:
         rawMessage(errGenerated, " execution of an external program failed; " &
                    "rerun with --parallelBuild:1 to see the error message")
-  if optNoLinking notin gGlobalOptions:
+  if optNoLinking notin gGlobalOptions and cmds.len > 0:
     # call the linker:
     var it = PStrEntry(toLink.head)
     var objfiles = ""
diff --git a/compiler/main.nim b/compiler/main.nim
index 46ef65e81..2ff7691d8 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -16,7 +16,7 @@ import
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, cgendata, json, nversion,
   platform, nimconf, importer, passaux, depends, evals, types, idgen,
-  tables, docgen2, service, magicsys, parser, crc, ccgutils
+  tables, docgen2, service, magicsys, parser, crc, ccgutils, sigmatch
 
 const
   has_LLVM_Backend = false
@@ -64,7 +64,7 @@ proc crcChanged(fileIdx: int32): bool =
     gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged
                                        else: crcNotChanged
     # echo "TESTING CRC: ", fileIdx.toFilename, " ", result
-    
+  
   case gMemCacheData[fileIdx].crcStatus:
   of crcHasChanged:
     result = true
@@ -89,38 +89,38 @@ proc addDep(x: Psym, dep: int32) =
   growCache gMemCacheData, dep
   gMemCacheData[x.position].deps.safeAdd(dep)
 
-proc ResetModule(fileIdx: int32) =
-  echo "HARD RESETTING ", fileIdx.toFilename
+proc resetModule(fileIdx: int32) =
+  # echo "HARD RESETTING ", fileIdx.toFilename
   gMemCacheData[fileIdx].needsRecompile = Yes
   gCompiledModules[fileIdx] = nil
   cgendata.gModules[fileIdx] = nil
+  resetSourceMap(fileIdx)
 
-proc ResetAllModules =
+proc resetAllModules =
   for i in 0..gCompiledModules.high:
     if gCompiledModules[i] != nil:
-      ResetModule(i.int32)
+      resetModule(i.int32)
 
-  for m in cgenModules():
-    echo "CGEN MODULE FOUND"
+  # for m in cgenModules(): echo "CGEN MODULE FOUND"
 
 proc checkDepMem(fileIdx: int32): TNeedRecompile  =
   template markDirty =
-    ResetModule(fileIdx)
+    resetModule(fileIdx)
     return Yes
 
   if gMemCacheData[fileIdx].needsRecompile != Maybe:
     return gMemCacheData[fileIdx].needsRecompile
 
   if optForceFullMake in gGlobalOptions or
-     curCaasCmd != lastCaasCmd or
-     crcChanged(fileIdx): markDirty
+     crcChanged(fileIdx):
+       markDirty
   
   if gMemCacheData[fileIdx].deps != nil:
     gMemCacheData[fileIdx].needsRecompile = Probing
     for dep in gMemCacheData[fileIdx].deps:
       let d = checkDepMem(dep)
       if d in { Yes, Recompiled }:
-        echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
+        # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
         markDirty
   
   gMemCacheData[fileIdx].needsRecompile = No
@@ -213,6 +213,9 @@ proc rodPass =
   if optSymbolFiles in gGlobalOptions:
     registerPass(rodwritePass)
 
+proc codegenPass =
+  registerPass cgenPass
+
 proc semanticPasses =
   registerPass verbosePass
   registerPass semPass
@@ -251,14 +254,11 @@ proc CommandCompileToC =
     # echo "CHECK DEP COMPLETE"
 
   compileProject()
-
-  if compilationCachePresent:
-    updateCachedModules()
-
+  cgenWriteModules()
   if gCmd != cmdRun:
     extccomp.CallCCompiler(changeFileExt(gProjectFull, ""))
 
-  if optCaasEnabled in gGlobalOptions:
+  if isServing:
     # caas will keep track only of the compilation commands
     lastCaasCmd = curCaasCmd
     resetCgenModules()
@@ -377,10 +377,23 @@ proc CommandScan =
     rawMessage(errCannotOpenFile, f)
   
 proc CommandSuggest =
-  msgs.gErrorMax = high(int)  # do not stop after first error
-  semanticPasses()
-  rodPass()
-  compileProject()
+  if isServing:
+    # XXX: hacky work-around ahead
+    # Currently, it's possible to issue a idetools command, before
+    # issuing the first compile command. This will leave the compiler
+    # cache in a state where "no recompilation is necessary", but the
+    # cgen pass was never executed at all.
+    CommandCompileToC()
+    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()
+    compileProject()
 
 proc wantMainModule =
   if gProjectFull.len == 0:
@@ -404,7 +417,7 @@ proc requireMainModuleOption =
 proc resetMemory =
   resetCompilationLists()
   ccgutils.resetCaches()
-  ResetAllModules()
+  resetAllModules()
   resetRopeCache()
   resetSysTypes()
   gOwners = @[]
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index e11e47bac..16ef06b61 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -526,13 +526,17 @@ proc SuggestWriteln*(s: string) =
     else: 
       Writeln(stdout, s)
       stdoutSocket.send(s & "\c\L")
-    
+
 proc SuggestQuit*() =
-  if not isServing: quit(0)
-  elif not isNil(stdoutSocket):
-    stdoutSocket.send("\c\L")
+  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.
     raise newException(ESuggestDone, "suggest done")
-  
+
 # this format is understood by many text editors: it is the same that
 # Borland and Freepascal use
 const
@@ -606,6 +610,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
   result = filename in info.toFilename
 
 var checkPoints*: seq[TLineInfo] = @[]
+var optTrackPos*: TLineInfo
 
 proc addCheckpoint*(info: TLineInfo) = 
   checkPoints.add(info)
diff --git a/compiler/options.nim b/compiler/options.nim
index 08a5ba010..f67ded163 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -107,10 +107,16 @@ var
   gLastCmdTime*: float        # when caas is enabled, we measure each command
   gListFullPaths*: bool
   isServing*: bool = false
+  gDirtyBufferIdx* = 0'i32    # indicates the fileIdx of the dirty version of
+                              # the tracked source X, saved by the CAAS client.
+  gDirtyOriginalIdx* = 0'i32  # the original source file of the dirtified buffer.
 
 proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
 proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
 
+template isWorkingWithDirtyBuffer*: expr =
+  gDirtyBufferIdx != 0
+
 template compilationCachePresent*: expr =
   {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
 
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index d0a63cd99..562eaebab 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -328,9 +328,6 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
     result.align = decodeVInt(r.s, r.pos)
   else: 
     result.align = 2
-  if r.s[r.pos] == '@': 
-    inc(r.pos)
-    result.containerID = decodeVInt(r.s, r.pos)
   decodeLoc(r, result.loc, info)
   while r.s[r.pos] == '^': 
     inc(r.pos)
@@ -1012,9 +1009,6 @@ proc writeType(f: TFile; t: PType) =
   if t.align != 2:
     f.write('=')
     f.write($t.align)
-  if t.containerID != 0: 
-    f.write('@')
-    f.write($t.containerID)
   for i in countup(0, sonsLen(t) - 1): 
     if t.sons[i] == nil: 
       f.write("^()")
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index b64347ba9..0221977bf 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -233,9 +233,6 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   if t.align != 2: 
     add(result, '=')
     encodeVInt(t.align, result)
-  if t.containerID != 0: 
-    add(result, '@')
-    encodeVInt(t.containerID, result)
   encodeLoc(w, t.loc, result)
   for i in countup(0, sonsLen(t) - 1): 
     if t.sons[i] == nil: 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f19b79c6a..2c3adfeda 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -693,9 +693,6 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       openScope(c.tab)
       pushOwner(s)
       if s.magic == mNone: s.typ.kind = tyGenericBody
-      if s.typ.containerID != 0: 
-        InternalError(a.info, "semTypeSection: containerID")
-      s.typ.containerID = s.typ.id
       # XXX for generic type aliases this is not correct! We need the
       # underlying Id really: 
       #
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index b3a82c413..3abc994ef 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -770,9 +770,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     return newOrPrevType(tyError, prev, c)
   elif s.typ.kind != tyGenericBody:
     isConcrete = false
-  elif s.typ.containerID == 0: 
-    InternalError(n.info, "semtypes.semGeneric")
-    return newOrPrevType(tyError, prev, c)
   elif sonsLen(n) != sonsLen(s.typ): 
     LocalError(n.info, errWrongNumberOfArguments)
     return newOrPrevType(tyError, prev, c)
diff --git a/compiler/service.nim b/compiler/service.nim
index b3c7fbc83..8e8fe20bf 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -65,6 +65,9 @@ proc serve*(action: proc (){.nimcall.}) =
     curCaasCmd = cmd
     processCmdLine(passCmd2, cmd)
     action()
+    gDirtyBufferIdx = 0
+    gDirtyOriginalIdx = 0
+    gErrorCounter = 0
 
   let typ = getConfigVar("server.type")
   case typ
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index ddda493ee..e1882e1fb 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -559,11 +559,10 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       # simply no match for now:
       nil
     elif x.kind == tyGenericInst and 
-          (f.sons[0].containerID == x.sons[0].containerID) and
-          (sonsLen(x) - 1 == sonsLen(f)): 
-      assert(x.sons[0].kind == tyGenericBody)
-      for i in countup(1, sonsLen(f) - 1): 
-        if x.sons[i].kind == tyGenericParam: 
+          (f.sons[0] == x.sons[0]) and
+          (sonsLen(x) - 1 == sonsLen(f)):
+      for i in countup(1, sonsLen(f) - 1):
+        if x.sons[i].kind == tyGenericParam:
           InternalError("wrong instantiated type!")
         elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return 
       result = isGeneric
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 130666f4d..18e6dbddf 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -9,7 +9,9 @@
 
 ## This file implements features required for IDE support.
 
-# imported from sigmatch.nim
+# included from sigmatch.nim
+
+import algorithm
 
 const
   sep = '\t'
@@ -238,12 +240,69 @@ proc findDefinition(node: PNode, 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*(n: PNode, s: PSym) {.inline.} =
   ## misnamed: should be 'symDeclared'
   if optUsages in gGlobalOptions:
     findUsages(n, s)
   if optDef in gGlobalOptions:
     findDefinition(n, s)
+  if isServing:
+    addToSourceMap(s, n.info)
 
 proc markUsed(n: PNode, s: PSym) =
   incl(s.flags, sfUsed)
diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt
new file mode 100644
index 000000000..4e2ab9729
--- /dev/null
+++ b/tests/caas/compile-suggest.txt
@@ -0,0 +1,7 @@
+main.nim
+> c
+SuccessX
+> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest main.nim
+skField\tx
+skField\ty
+
diff --git a/tests/caas/def-def-compile.txt b/tests/caas/def-def-compile.txt
new file mode 100644
index 000000000..64002aff1
--- /dev/null
+++ b/tests/caas/def-def-compile.txt
@@ -0,0 +1,10 @@
+main.nim
+> idetools --track:main.nim,5,18 --def main.nim
+strutils.toUpper
+SuccessX
+> idetools --track:main.nim,5,18 --def main.nim
+strutils.toUpper
+SuccessX
+> c
+SuccessX
+
diff --git a/tests/caas/main_dirty.nim b/tests/caas/main_dirty.nim
new file mode 100644
index 000000000..95fb6c624
--- /dev/null
+++ b/tests/caas/main_dirty.nim
@@ -0,0 +1,14 @@
+import imported, strutils
+
+type
+  TFoo = object
+    x: int
+    y: string
+
+proc main =
+  var t1 = "text"
+  var t2 = t1.toUpper
+  var foo = TFoo(x: 10, y: "test")
+  foo.
+  echo(t1 +++ t2)
+
diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt
new file mode 100644
index 000000000..49d0dc431
--- /dev/null
+++ b/tests/caas/suggest-compile.txt
@@ -0,0 +1,7 @@
+main.nim
+> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest main.nim
+skField\tx
+skField\ty
+> c
+SuccessX
+