Пример #1
0
        /// <summary>
        /// WARNING: Be aware this code leaves things (Paper && Curs) in a state relied on by InsertProvidesConflicts
        /// See comments there before messing with this.
        /// But genrally speaking this function :
        /// Either adds one value to a Name : [ ... ] list
        /// Or creates the list
        /// Or fxies a Name : Value into and array as required.
        /// </summary>
        /// <param name="bar"> object holding Format and File</param>
        /// <param name="p"> Literally a Place where things are writtendown for use elsewhere... scratchpad</param>
        static void fixValueInList(CKanFormat bar, ref PieceOfPaper p)
        {
            if (bar.hasANameField(p.listToAddItTo) == true)
            { // we already have a provides entry
              // Check AND Add that we provide OldIdentifier
                bool hasOldIdentifierProvided = false;

                parseToValueFor(bar, ref p); // We are now here <"provides"> <:> >here< <[>  (where provides might also be author)
                                             // or worse here ...  <"author"> <:> >here< <"authors_handle"> <,>


                if (bar.isTokenTextUse("[") == false)
                { // yep its worse here ...  <"author"> <:> >here< <"authors_handle"> <,>
                  // there is no EOL trick we can use.
                    string ExistingAuthor = bar.Curs.TokenObj.theToken;
                    if (ExistingAuthor.Equals(p.valueToAdd) == false)
                    {  // The one item in the list is not the thign we wer meant to add.
                        //First valid date to expliclty double check. Whats been implied bythis beign a Valid File and thsi NOT beigna <[>.
                        TokenFile.Line L   = bar.Curs.Line;
                        int            len = L.TheLine.Count;
                        string         t   = "Huh?"; // if it prints huh then this line in the file is an very unexpected length. Sorry: lesson dont use ugly ckan files.
                        // This may be valid line in a ckan file <SOL>"author":"Axle","version":"1.2.9.1"<EOL> but youare SOL(not the star) if you want localiser to localise it.
                        if ((len != bar.Curs.TokNo + 3) || ((t = L.TheLine[L.TheLine.Count - 2].theToken).Equals(",") == false))
                        {  // Yeah nah that kinda cant happen.  and if it does I dont care... make your file prettier.
                           // TODO if the <,> is on another should we keep it and insert here?
                            throw new FormatException($"Fatal Format Error in {bar.TokFile.FilePath}:\n\t Expected <\"author\": \"Authorname\" , \"> got <\"author\": \"Authorname\" {t} >.");
                        }
                        // Okay so the file is still just like we already validated it to be ... a legal(ish) Json File...
                        // delete the Bits we cant keep.  >here< <"ExistingAuthor"> <,> <EOL>
                        L.TheLine.RemoveAt(bar.Curs.TokNo);
                        // delete the Bits we cant keep.  >here< <,> <EOL>
                        bar.Curs.TokenObj.theToken = "[";  // reuse the , token as a [
                        // bar.Curs.TokenObj.TokenCategory = TokenCategory.Token; // already verified as true as it was a ","
                        // delete the Bits we cant keep.  >here< <[> <EOL>
                        // Not strictly required but this code is now more like most of the otehr edit code  see reuse the white space code for why thats good diea.
                        bar.expectToken("[", TokenCategory.Token);
                        // we are here <[> >here< <EOL>
                        L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack + "    ", p.valueToAdd, TokenCategory.String, (int)'"'));
                        L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                        L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                        // we are here <"author"> <:> <[> >here< <EOL> <Author> <,> <EOL>
                        L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack + "    ", ExistingAuthor, TokenCategory.String, (int)'"'));
                        // nope no comma here L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                        L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                        // we are here <"author"> <:> <[> >here< <EOL> <Author> <,> <EOL> <ExistingAuthor> <,> <EOL>
                        L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack, "]", TokenCategory.Token, (int)']'));
                        // This is now the lexcial equivalent of the <,>  token we deleted above
                        L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                        L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                        // we are here <"author"> <:> <[> >here< <EOL> <Author> <,> <EOL> <ExistingAuthor> <EOL> <]> <,> <EOL>
                    }
                    else
                    {
                        bar.expectToken(p.valueToAdd, TokenCategory.String);
                        bar.expectToken(",", TokenCategory.Token);
                        CheckForEOLorThrow(bar);
                    }
                }
                else
                {
                    p.IndentationHack = p.IndentationHack + "    "; // yep I just did that....


                    // already used bar.expectToken("[", TokenCategory.Token);
                    // Remember where this is
                    int oldLineNo        = bar.Curs.LineNo;
                    int oldTokenNo       = bar.Curs.TokNo;
                    int NumOfIdentifiers = 0;
                    //We are here <"provides"> <:> <[> here <EOL>
                    bar.expectToken(TokenCategory.tokEOL); // We require one of these here. We fail if its missing.
                                                           // AKA this file is, for us, NOT valid   >>   "provides" : [ ],<EOL><<  as we require an EOL after the <[>
                    bar.skipEOL();                         // skips all EOLS and WS // be generous ?
                    while (true)
                    {
                        if (bar.isTokenTextUse("]"))
                        {
                            // We are here <"provides"> <:> <[>  <]> >here<
                            // or  possibly if <"provides"> <:> <[> ... <,>  <]> >here< if it passed validation phase
                            // we are NOT <"provides"> <:> <[> <name> <> <:> <value> [ <,> <name> <> <:> <value>]  <]> >here<
                            // the array list is either empty [] or comma  [ ... , ] terminated ...
                            // See ###ZZZ1 below
                            break;
                        }
                        // is it the end of the list
                        // rmember any indentation cues that we see.
                        // override the +4 space decison above and mirror any other provides?
                        if (p.IndentationHack.Length < bar.Curs.TokenObj.WhiteSpace.Length)
                        {   // But only if it is more indented than the earlier decision
                            // AKA we willindent 4 more spaces than provides and ignore the others...
                            // >>    "provides":[<EOL><<
                            // >> "Thing1Provided", "Thing2Provided" <EOL> <<
                            p.IndentationHack = bar.Curs.TokenObj.WhiteSpace;
                        }

                        // is it the one we are after?
                        if (bar.Curs.TokenObj.theToken.Equals(p.valueToAdd))
                        {
                            //We are here <"provides"> <:> <[> ... >here< <OldIdentifier> ... <]>
                            hasOldIdentifierProvided = true;
                        }
                        // Nope it some other thing that the mod  "provides"

                        bar.expectToken(TokenCategory.String); // skip it eitehr way
                        NumOfIdentifiers++;                    //count them
                        bar.skipEOL();                         // skips all EOLS and WS
                        if (bar.Curs.TokenObj.theToken.Equals(",") == false)
                        {                                      // So now we add our value for provides
                            // Failing this epctationThis probably should not have passed valdiation so it cant happen?
                            bar.expectToken("]", TokenCategory.Token);
                            bar.skipEOL(); // skips all EOLS and WS
                            bar.expectToken(",", TokenCategory.Token);
                            CheckForEOLorThrow(bar);
                            // see ###ZZZ1 above
                            // We are here <"provides"> <:> <[> ... <]> >here< <EOL>
                            // Non empty arrays (probably hopefully all the >valid< real one in the wild.) exit here
                            break;
                        }
                        bar.expectToken(",", TokenCategory.Token);
                        // there is (or better be as we were just promsied one). more provides values ...
                        // We are here <"provides"> <:> <[> ... <value> <,> >here< <value>
                        bar.skipEOL(); // skips all EOLS and WS
                    }
                    p.EndProvidesLineNo  = bar.Curs.LineNo;
                    p.EndProvidesTokenNo = bar.Curs.TokNo;
                    // we check for the EOL here a bit later.

                    if (hasOldIdentifierProvided == false)
                    {  // So now we go back and insert OldIdentifier at the front of the list/array of values
                        bar.Curs.setPosition(oldLineNo, oldTokenNo);
                        // as previously established.
                        // Cursor is at an EOL *and* it is the last token on a Line in File.
                        // insert our extra provides value here
                        /////////////////////////////////////////////////////////////////////
                        //   WARNING DANGER DANGER WILL ROBINSON.
                        /////////////////////////////////////////////////////////////////////
                        // After this operation the LINE object will have multiple output lines that it prints due to an <EOL> in the middle
                        // That is never true just after reading a file, and unless we edit it like this, never at all.
                        /////////////////////////////////////////////////////////////////////
                        // We are here <"provides"> <:> <[> ... <]> >here< <EOL>
                        // Which is cool so if we append our Stuff to the line object and end in an EOL too then its been inserted in the file
                        //  **AND**
                        // Every LineNo TokenNo pair that exists still points to the exact same stuff/token... as before the edit/insert

                        // add this <"provides"> <:> <[> >here< <EOL> <wsIndetation/OldIdentifier> [<,>] <EOL>
                        // where the optional <,> is added only if there is  follwing identifier in the array.
                        CheckForEOLorThrow(bar); // out of an abundance of caution. Check again.
                        TokenFile.Line L = bar.Curs.Line;
                        L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack, p.valueToAdd, TokenCategory.String, (int)'"'));
                        if (NumOfIdentifiers > 0)
                        {
                            //  it was NOT <"provides"> <:> <[> <]> it did have at ldast one idetifier, so we need a comma
                            L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                        }
                        // reuse whatever whites space it is that follows the cursor: see CheckForEOLorThrow above
                        L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                        // Line Object Now looks like this <"provides"> <:> <[> >here< <EOL> <wsIndetation/OldIdentifier> [<,>] <EOL>
                        // The comma is present if and only if there is another preexisting identifier in the Provides list
                    }
                    bar.Curs.setPosition(p.EndProvidesLineNo, p.EndProvidesTokenNo);
                    // File Now looks like this <"provides"> <:> <[> <EOL> <wsIndetation/OldIdentifier> [<,>] <EOL> ... <]> >here< <EOL>
                }
            }  // well that was fun
            else
            {  // Now for the easy more usual alternative There no existing provides name value insert one after license.
                // or insert author after abstract if we doing author
                if (bar.hasANameField(p.thingToAdditAfter[0]) == false)
                {
                    //Force an error to throw
                    bar.expectToken(p.thingToAdditAfter[0], TokenCategory.Token);
                    throw new FormatException("Programmer Error: This really cant (logically) happen.");
                }

                // This is a required by the schema field... so it better be there or who cares?
                // find where that nameField is move to the { that is just after it and the ":".
                string t = p.listToAddItTo;
                p.listToAddItTo = p.thingToAdditAfter[0];
                parseToValueFor(bar, ref p);                // find that name:value instead
                p.listToAddItTo   = t;
                p.IndentationHack = p.IndentationHack + ""; // yep, oops I just did that.... again
                // like <"author"> The schema specifies either a single value or an array for license (humans: see licenses at bottom of schema)
                //
                if (bar.isTokenTextUse("["))
                {
                    // already used bar.expectToken("[", TokenCategory.Token);
                    while (bar.Curs.TokenObj.theToken.Equals("]") == false)
                    {   // Accept anythign we find until we get one ]....
                        bar.expectToken(bar.Curs.TokenObj.theToken, bar.Curs.TokenObj.TokenCategory);
                    }
                    bar.expectToken("]", TokenCategory.Token);
                }
                else
                { // if is not a <[> it needs to be a single license string (or a single string abstract)
                    bar.expectToken(TokenCategory.String);
                }
                // Now we require a <,> after that name:value pair followed by an <EOL>
                bar.skipEOL();
                bar.expectToken(",", TokenCategory.Token);
                CheckForEOLorThrow(bar);
                // We are here <"license"> <:> {one_of String or Array} >here< <EOL>   (or equiv but for <"abstract">)
                // Now we add  <IndentationHack:"provides"> <:> <OldIdentifier> <,> <EOL>
                TokenFile.Line L = bar.Curs.Line;
                L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack, p.listToAddItTo, TokenCategory.String, (int)'"'));
                L.TheLine.Add(new TokenFile.TokenObject("", ":", TokenCategory.Token, (int)':'));
                L.TheLine.Add(new TokenFile.TokenObject(" ", "[", TokenCategory.Token, (int)'['));
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack + "    ", p.valueToAdd, TokenCategory.String, (int)'"'));
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                L.TheLine.Add(new TokenFile.TokenObject(p.IndentationHack, "]", TokenCategory.Token, (int)']'));
                L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                // reuse whatever whites space it is that follows the cursor: see CheckForEOLorThrow above
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));

                /////////////////////////////////////////////////////////////////////
                //   WARNING DANGER DANGER WILL ROBINSON.
                //   read carefully.
                /////////////////////////////////////////////////////////////////////
                p.EndProvidesLineNo  = bar.Curs.LineNo;
                p.EndProvidesTokenNo = L.TheLine.Count - 1; // Yep as we just added the EOL after "provides" we know right where we put it.
                bar.Curs.setPosition(p.EndProvidesLineNo, p.EndProvidesTokenNo);
            }
        }
Пример #2
0
        static void InsertProvidesConflicts(CKanFormat bar)
        {
            // These will get set to the place the <"conflicts"> <[> <"OldIdentifier"> <]> <EOL> entry should be added.

            CkanLocaliserClass.DoingWhat.Push("Fixing_Provides");

            PieceOfPaper p1;

            p1.valueToAdd        = OldIdentifier;
            p1.listToAddItTo     = "\"provides\"";
            p1.thingToAdditAfter = new List <string>();
            p1.thingToAdditAfter.Add("\"license\"");
            p1.EndProvidesLineNo  = 0;
            p1.EndProvidesTokenNo = 0;
            p1.IndentationHack    = "";
            fixValueInList(bar, ref p1);

            CkanLocaliserClass.DoingWhat.Pop();
            CkanLocaliserClass.DoingWhat.Push("Fixing_Conflicts");

            // The memory image of the File now has a provides statement that we put there or one we amended or checked...
            // BUT irrespective our cursor is at the last token of a Line object that follows
            // "provides" <:> [<[>] <"OldIdentifier"> [<,> <otherValue> <]>] >here< <EOL>
            // and hence we are ready to add the Conflicts value right here... unless there is one somewhere else....
            // And conflicts is worse it is an Array of Compound named values...  YAY hooray.
            if (bar.hasANameField("\"conflicts\"") == true)
            {  // Bugger...
               // There is an existing conflicts we will check if an exact copy of what we want is already there
               //   bool hasExactConflictsEntry = false;
                CkanLocaliserClass.DoingWhat.Push("Existing_Conflicts");

                PieceOfPaper p2;
                p2.valueToAdd        = "Foobar1";
                p2.listToAddItTo     = "\"conflicts\"";
                p2.thingToAdditAfter = new List <string>();
                p2.thingToAdditAfter.Add("\"FooBar2\"");
                p2.EndProvidesLineNo  = 0;
                p2.EndProvidesTokenNo = 0;
                p2.IndentationHack    = "";
                parseToValueFor(bar, ref p2); // We are now here <"conflicts"> <:> >here< <[>
// TODO is "conflicts" : { "name" : "ModID" }, legal without the []  do we care?
// validate various egs against schema using tools.
                p2.IndentationHack = p2.IndentationHack + "    "; // yep oops I just did that.... again but first?
                bar.expectToken("[", TokenCategory.Token);
                // Remember where this is
                int  oldConflictStartLineNo     = bar.Curs.LineNo;
                int  oldConflictStartTokenNo    = bar.Curs.TokNo;
                bool FileHasAGoodConflistsEntry = false;
                // Now follows asequence of <{> ... <}> [ <,> <{> ... <}> ]  blocks terminated by <]>
                // Check if any are exactly what we are about to add.
                while (true)
                {
                    bar.skipEOL(); // skips all EOLS and WS
                    if (bar.isTokenTextUse("]"))
                    {
                        // bar.expectToken("]", TokenCategory.Token);
                        // We are here <"conflicts"> <:> <[>  <]> >here<
                        // or  possibly if <"conflicts"> <:> <[> ... <,>  <]> >here< if it passed validation phase
                        // we are NOT <"conflicts"> <:> <[> <name> <> <:> <value> [ <,> <name> <> <:> <value>]  <]> >here<
                        // the array list is either empty [] or comma  [ ... , ] terminated ...
                        // See ###ZZZ2 below
                        break;
                    }
                    // is it the end of the list

                    // rmember any indentation cues that we see.
                    // override the +4 space decison above and mirror any other provides?
                    if (p2.IndentationHack.Length < bar.Curs.TokenObj.WhiteSpace.Length)
                    {   // But only if it is more indented than the earlier decision
                        // AKA we willindent 4 more spaces than provides and ignore the others...
                        // >>    "provides":[<EOL><<
                        // >> "Thing1Provided", "Thing2Provided" <EOL> <<
                        p2.IndentationHack = bar.Curs.TokenObj.WhiteSpace;
                    }

                    // is it the one we are after?
                    bool MatchesEntry = true; //so far it does

                    if (bar.isTokenTextUse("{"))
                    {                        // comma sep list of <name> <:> <value> pairs termianted by <}>
                        MatchesEntry = true; //set it to true and see it this fits the pattern
                        bar.skipEOL();       // skips all EOLS and WS
                        while (bar.isTokenTextUse("}") == false)
                        {                    // Wasnt a <}> didnt use it.
                            // So it ought to be  <name>
                            if (bar.Curs.TokenObj.theToken.Equals("\"name\"") == false)
                            {
                                MatchesEntry = false;
                            }
                            // We are here  <conflicts> : <[>  <{> [...] >here<  <name> <:> <value> [...] <}>
                            bar.expectToken(TokenCategory.String);     // No EOLs
                            bar.expectToken(":", TokenCategory.Token); // No EOLs
                            if (bar.Curs.TokenObj.theToken.Equals(OldIdentifier) == false)
                            {
                                MatchesEntry = false;
                            }
                            bar.expectToken(TokenCategory.String);
                            bar.skipEOL(); // skips all EOLS and WS
                            // ending a list with <,> is apprently OK ish?
                            // whatever if our validation step didnt balk we accept it here.

                            // We are here  <conflicts> : <[>  <{> [...] <name> <:> <value> >here< <,> [...] <}>
                            // or are here  <conflicts> : <[>  <{> [...] <name> <:> <value> >here< <}>
                            // or are here  <conflicts> : <[>  <{> [...] <name> <:> <value> >here< <,> <}>
                            bar.isTokenTextUse(","); // Eat the <,> iff its there...

                            // GROK AKA yes I know,
                            // Note: this code here permits  >> ... conflicts [ { "name" : "bar" "version" : "1.1.1." } ] ... <<
                            // which ought to have a missing comme eror but the validator will have flagegd that. Its not our job to do that again.
                            // trying to undertsand the ckan file with this code is enough.
                            bar.skipEOL(); // skips all EOLS and WS
                        }
                        // We are here  <conflicts> : <[>  <{> [...] <}> >here<  [<,>] [<{> [...] <}>]
                        if (MatchesEntry == true)
                        {                                      // There just right now was  { "name" : <OldIdentifier> }
                           // why that might exist in this file i have no idea but it does so were done
                            FileHasAGoodConflistsEntry = true; // just to be clear even if somethign elss would work.
                            break;
                        }
                    }
                    bar.skipEOL(); // skips all EOLS and WS
                    if (bar.isTokenTextUse("]"))
                    {
                        break;
                    }
                    bar.skipEOL();           // skips all EOLS and WS
                    bar.isTokenTextUse(","); // Eat the <,> iff its there...
                    // GROK AKA yes I still know,

                    bar.skipEOL(); // skips all EOLS and WS
                }
                // Either we found an existing conflictstatement or we want to add one.
                if (FileHasAGoodConflistsEntry == false)
                {
                    bar.Curs.setPosition(oldConflictStartLineNo, oldConflictStartTokenNo);
                    CheckForEOLorThrow(bar);
                    // Ok good to go same deal as the others:  We can append our insertion att he EOL here  <conflicts> : <[> >here< <EOL>
                    TokenFile.Line L = bar.Curs.Line;
                    L.TheLine.Add(new TokenFile.TokenObject(p2.IndentationHack, "{", TokenCategory.Token, (int)'{'));
                    L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                    L.TheLine.Add(new TokenFile.TokenObject(p2.IndentationHack + "    ", "\"name\"", TokenCategory.String, (int)'"'));
                    L.TheLine.Add(new TokenFile.TokenObject("", ":", TokenCategory.Token, (int)':'));
                    L.TheLine.Add(new TokenFile.TokenObject(" ", OldIdentifier, TokenCategory.String, (int)'"'));
                    L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                    L.TheLine.Add(new TokenFile.TokenObject(p2.IndentationHack, "}", TokenCategory.Token, (int)'}'));
                    L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                    L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                }
                CkanLocaliserClass.DoingWhat.Pop();
            }
            else
            {  // Deal with the case where we just append a new Conflicts statement
                CkanLocaliserClass.DoingWhat.Push("Adding_Conflicts");
                TokenFile.Line L = bar.Curs.Line;
                L.TheLine.Add(new TokenFile.TokenObject(p1.IndentationHack, "\"conflicts\"", TokenCategory.String, (int)'"'));
                L.TheLine.Add(new TokenFile.TokenObject("", ":", TokenCategory.Token, (int)':'));
                L.TheLine.Add(new TokenFile.TokenObject(" ", "[", TokenCategory.Token, (int)'['));
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                L.TheLine.Add(new TokenFile.TokenObject(p1.IndentationHack + "    ", "{", TokenCategory.Token, (int)'{'));
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                L.TheLine.Add(new TokenFile.TokenObject(p1.IndentationHack + "        ", "\"name\"", TokenCategory.String, (int)'"'));
                L.TheLine.Add(new TokenFile.TokenObject("", ":", TokenCategory.Token, (int)':'));
                L.TheLine.Add(new TokenFile.TokenObject(" ", OldIdentifier, TokenCategory.String, (int)'"'));
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                L.TheLine.Add(new TokenFile.TokenObject(p1.IndentationHack + "    ", "}", TokenCategory.Token, (int)'}'));
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                L.TheLine.Add(new TokenFile.TokenObject(p1.IndentationHack, "]", TokenCategory.Token, (int)']'));
                L.TheLine.Add(new TokenFile.TokenObject("", ",", TokenCategory.Token, (int)','));
                // reuse whatever whites space it is that follows the cursor: see CheckForEOLorThrow above
                L.TheLine.Add(new TokenFile.TokenObject(bar.Curs.TokenObj.WhiteSpace, "", TokenCategory.tokEOL, (int)'\n'));
                CkanLocaliserClass.DoingWhat.Pop();
            }
            CkanLocaliserClass.DoingWhat.Pop();
        }