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)); }
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); }
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); }
/// <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); }
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); }