Beispiel #1
0
            public override int VisitHeader([NotNull] YarnSpinnerV1Parser.HeaderContext context)
            {
                // When we encounter a "title:" header, replace any periods in
                // it with underscores.
                if (context.header_key.Text != "title")
                {
                    return(base.VisitHeader(context));
                }

                var nodeName = context.header_value.Text;

                if (nodeName.Contains("."))
                {
                    var newNodeName = nodeName.Replace(".", "_");

                    var replacement = new TextReplacement
                    {
                        Start           = context.header_value.StartIndex,
                        StartLine       = context.header_value.Line,
                        OriginalText    = nodeName,
                        ReplacementText = newNodeName,
                        Comment         = NodesWereRenamedDescription,
                    };

                    this.replacements.Add(replacement);
                }

                return(base.VisitHeader(context));
            }
Beispiel #2
0
            public override int VisitOptionJump([NotNull] YarnSpinnerV1Parser.OptionJumpContext context)
            {
                var destination = context.NodeName.Text;

                var nodesWereRenamed = false;

                if (destination.Contains("."))
                {
                    destination      = destination.Replace(".", "_");
                    nodesWereRenamed = true;
                }

                var comment = JumpSyntaxWasUpgradedDescription + (nodesWereRenamed ? " " + OptionDestinationsWereRenamedDescription : string.Empty);

                var replacement = new TextReplacement {
                    OriginalText    = GetContextTextWithWhitespace(context),
                    ReplacementText = $"<<jump {destination}>>",
                    Start           = context.Start.StartIndex,
                    StartLine       = context.Start.Line,
                    Comment         = comment,
                };

                this.replacements.Add(replacement);

                return(0);
            }
Beispiel #3
0
            public override int VisitOptionJump([NotNull] YarnSpinnerV1Parser.OptionJumpContext context)
            {
                var destination = context.NodeName.Text;

                var replacement = new TextReplacement {
                    OriginalText    = GetContextTextWithWhitespace(context),
                    ReplacementText = $"<<jump {destination}>>",
                    Start           = context.Start.StartIndex,
                    StartLine       = context.Start.Line,
                    Comment         = "A jump was upgraded to use updated syntax.",
                };

                this.replacements.Add(replacement);

                return(0);
            }
            public override void ExitFormat_function(YarnSpinnerV1Parser.Format_functionContext context)
            {
                // V1: [select {$gender} male="male" female="female" other="other"]
                //  function_name: "select" variable: "$gender" key_value_pair="male="male"..."
                //
                // V2: [select value={$gender} male="male" female="female" other="other"/]
                var formatFunctionType = context.function_name?.Text;
                var variableContext    = context.variable();

                if (formatFunctionType == null || variableContext == null)
                {
                    // Not actually a format function, but the parser may
                    // have misinterpreted it? Do nothing here.
                    return;
                }

                var variableName = variableContext.GetText();

                StringBuilder sb = new StringBuilder();

                sb.Append($"{formatFunctionType} value={{{variableName}}}");

                foreach (var kvp in context.key_value_pair())
                {
                    sb.Append($" {kvp.GetText()}");
                }

                sb.Append(" /");

                // '[' and ']' are tokens that wrap this format_function,
                // so we're just replacing its innards
                var originalLength = context.Stop.StopIndex + 1 - context.Start.StartIndex;
                var originalStart  = context.Start.StartIndex;
                var originalText   = this.contents.Substring(originalStart, originalLength);

                var replacement = new TextReplacement()
                {
                    Start           = context.Start.StartIndex,
                    StartLine       = context.Start.Line,
                    OriginalText    = originalText,
                    ReplacementText = sb.ToString(),
                    Comment         = "Format functions have been replaced with markup.",
                };

                // Deliver the replacement!
                this.replacementCallback(replacement);
            }
Beispiel #5
0
            /// <inheritdoc/>
            public override void ExitLine_statement([NotNull] YarnSpinnerParser.Line_statementContext context)
            {
                // We're looking at a complete line statement.

                // First, figure out if this line statement already has a line
                // tag. Start by taking the hashtags...
                var hashtags = context.hashtag();

                // Get the text for all of these hashtags...
                var texts = StringTableGeneratorVisitor.GetHashtagTexts(hashtags);

                // And then look for a line ID hashtag.
                foreach (var text in texts)
                {
                    if (text.StartsWith("line:"))
                    {
                        // This line contains a line code. Nothing left to do.
                        return;
                    }
                }

                // Find the index of the first token on the default channel to
                // the left of the newline.
                var previousTokenIndex = IndexOfPreviousTokenOnChannel(
                    TokenStream,
                    context.NEWLINE().Symbol.TokenIndex,
                    YarnSpinnerLexer.DefaultTokenChannel
                    );

                // Did we find one?
                if (previousTokenIndex == -1)
                {
                    // No token was found before this newline. This is an
                    // internal error - there must be at least one symbol
                    // besides the terminating newline.
                    throw new InvalidOperationException($"Internal error: failed to find any tokens before the newline in line statement on line {context.Start.Line}");
                }

                // Get the token at this index. We'll put our tag after it.
                var previousToken = TokenStream.Get(previousTokenIndex);

                // Generate a new, unique line ID.
                string newLineID = Utility.GenerateString(existingStrings);

                // Record that we've used this new line ID, so that we don't
                // accidentally use it twice.
                existingStrings.Add(newLineID);

                // Create a text replacement that inserts a space followed by
                // the line tag at the end of the line.
                var replacement = new Upgrader.TextReplacement()
                {
                    Start           = previousToken.StopIndex + 1,
                    StartLine       = previousToken.Line,
                    OriginalText    = "",
                    ReplacementText = $" #{newLineID} ",
                    Comment         = "Added line tag"
                };

                // Add this replacement to the list.
                this.Replacements.Add(replacement);
            }
Beispiel #6
0
            public override int VisitNode([NotNull] YarnSpinnerV1Parser.NodeContext context)
            {
                this.currentNodeOptionLinks.Clear();

                this.VisitChildren(context);

                if (this.currentNodeOptionLinks.Count == 0)
                {
                    // No options in this node. Early out.
                    return(0);
                }

                // CurrentNodeOptionLinks now contains all options
                // that were encountered; create amendments to delete them
                // and add shortcut options
                var newShortcutOptionEntries = new List <string>();

                bool optionsWereRenamed = false;

                foreach (var optionLink in this.currentNodeOptionLinks)
                {
                    // If this option link has any hashtags, the newline at
                    // the end of the line is captured, so we'll need to
                    // generate a new one in our replacement text. If not,
                    // then the replacement text is empty.
                    var needsNewline = optionLink.Context.hashtag().Length > 0;

                    string replacementText = $@"// Option ""{GetContextTextWithWhitespace(optionLink.Context.option_formatted_text())}"" moved to the end of this node";
                    replacementText += needsNewline ? "\n" : string.Empty;

                    // Create a replacement to remove it
                    var replacement = new TextReplacement
                    {
                        Start           = optionLink.Context.Start.StartIndex,
                        StartLine       = optionLink.Context.Start.Line,
                        OriginalText    = GetContextTextWithWhitespace(optionLink.Context),
                        ReplacementText = replacementText,
                        Comment         = OptionsWereMovedDescription,
                    };

                    this.replacements.Add(replacement);

                    // And create a replacement at the end to add the
                    // shortcut replacement
                    var optionLine        = GetContextTextWithWhitespace(optionLink.Context.option_formatted_text());
                    var optionDestination = optionLink.Context.NodeName?.Text ?? "<ERROR: invalid destination>";

                    if (optionDestination.Contains("."))
                    {
                        optionDestination  = optionDestination.Replace(".", "_");
                        optionsWereRenamed = true;
                    }

                    var hashtags = optionLink.Context.hashtag().Select(hashtag => GetContextTextWithWhitespace(hashtag));

                    var conditions = optionLink.Conditions.Select(c =>
                    {
                        if (c.requiredTruthValue == true)
                        {
                            return($"({GetContextTextWithWhitespace(c.expression)})");
                        }
                        else
                        {
                            return($"!({GetContextTextWithWhitespace(c.expression)})");
                        }
                    }).Reverse();

                    var allConditions = string.Join(" && ", conditions);

                    var sb = new System.Text.StringBuilder();

                    // Create the shortcut option
                    sb.Append("-> ");
                    sb.Append(optionLine);

                    // If this option had any conditions, emit the computed
                    // line condition
                    if (allConditions.Count() > 0)
                    {
                        sb.Append($" <<if {allConditions}>>");
                    }

                    // Emit all hashtags that the option had
                    foreach (var hashtag in hashtags)
                    {
                        sb.Append($" {hashtag}");
                    }

                    // Now start creating the jump instruction
                    sb.AppendLine();

                    // Indent one level; we know we're at the end of a node
                    // so we're at the zero indentation level
                    sb.Append("    ");

                    // Emit the jump instruction itself
                    sb.Append($"<<jump {optionDestination}>>");
                    sb.AppendLine();

                    // We're done!
                    newShortcutOptionEntries.Add(sb.ToString());
                }

                // Finally, create a replacement that injects the newly created shortcut options
                var endOfNode = context.BODY_END().Symbol;

                string replacementDescription = $"{OptionSyntaxChangedDescription}{(optionsWereRenamed ? " " + OptionDestinationsWereRenamedDescription : string.Empty)}";

                var newOptionsReplacement = new TextReplacement
                {
                    Start           = endOfNode.StartIndex,
                    OriginalText    = string.Empty,
                    ReplacementText = string.Join(string.Empty, newShortcutOptionEntries),
                    StartLine       = endOfNode.Line,
                    Comment         = replacementDescription,
                };

                this.replacements.Add(newOptionsReplacement);

                return(0);
            }