public TableStreamRun Append(TableStreamRun run, ModelDelta token, int length) { var parentIndex = GetParentIndex(run.PointerSources); var parent = model.GetNextRun(parentIndex) as ITableRun; if (parent == null) { return(run); } var segmentIndex = GetSegmentIndex(parent.ElementContent, parentFieldForLength); if (segmentIndex == -1) { return(run); } UpdateParents(token, parent, segmentIndex, run.ElementCount + length, run.PointerSources); var naturalLength = run.Length; var newRun = (TableStreamRun)model.RelocateForExpansion(token, run, naturalLength + length * run.ElementLength); for (int i = 0; i < run.ElementLength * length; i++) { token.ChangeData(model, newRun.Start + naturalLength + i, 0x00); } for (int i = naturalLength + length * run.ElementLength; i < naturalLength; i++) { if (model[newRun.Start + i] != 0xFF) { token.ChangeData(model, newRun.Start + i, 0xFF); } } return(new TableStreamRun(model, newRun.Start, run.PointerSources, run.FormatString, run.ElementContent, this)); }
public override bool Write(IDataModel model, ModelDelta token, int start, ref string data) { if (data.StartsWith("(") && data.EndsWith(")")) { data = data.Substring(1, data.Length - 2); } var tokens = TableStreamRun.Tokenize(data); var remainder = ", ".Join(tokens.Skip(1)); if (tokens.Count == 0) { return(false); } data = tokens[0]; bool result; if (!TryParse(model, data, out int value)) { result = base.Write(model, token, start, ref data); } else { data = value.ToString(); result = base.Write(model, token, start, ref data); } data = remainder; return(result); }
public override bool Write(IDataModel model, ModelDelta token, int start, ref string data) { if (data.StartsWith("(") && data.EndsWith(")")) { data = data.Substring(1, data.Length - 2); } var remainder = string.Empty; if (Type != ElementContentType.PCS) { var tokens = TableStreamRun.Tokenize(data); remainder = ", ".Join(tokens.Skip(1)); data = tokens[0]; } data = data.Trim(); if (data.StartsWith("0x")) { data = data.Substring(2); } if (!int.TryParse(data, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out var intValue)) { intValue = 0; } data = remainder; return(model.WriteMultiByteValue(start, Length, token, intValue)); }
public TableStreamRun Append(TableStreamRun run, ModelDelta token, int length) { var naturalLength = run.Length - EndCode.Count; var newRun = model.RelocateForExpansion(token, run, naturalLength + length * run.ElementLength + EndCode.Count); if (naturalLength == 0) { for (int i = 0; i < run.ElementLength * length; i++) { token.ChangeData(model, newRun.Start + naturalLength + i, 0); } } else { for (int i = 0; i < run.ElementLength * length; i++) { token.ChangeData(model, newRun.Start + naturalLength + i, model[newRun.Start + naturalLength + i - run.ElementLength]); } } for (int i = naturalLength + length * run.ElementLength; i < naturalLength; i++) { if (model[newRun.Start + i] != 0xFF) { token.ChangeData(model, newRun.Start + i, 0xFF); } } for (int i = 0; i < EndCode.Count; i++) { token.ChangeData(model, newRun.Start + naturalLength + length * run.ElementLength + i, EndCode[i]); } return(new TableStreamRun(model, newRun.Start, run.PointerSources, run.FormatString, run.ElementContent, this, run.ElementCount + length)); }
public override int LengthForNewRun(IDataModel model, int pointerAddress) { var tableRun = GetTable(model, pointerAddress); var pointerSegment = GetSegment(tableRun, pointerAddress); TableStreamRun.TryParseTableStream(model, -1, new SortedSpan <int>(pointerAddress), pointerSegment.Name, pointerSegment.InnerFormat, tableRun.ElementContent, out var newStream); return(newStream.Length); }
public void StreamElementViewModel_CallAutocomplete_ZIndexChanges() { Model.SetList("options", new[] { "PoisonPowder", "\"Poison Gas\"", "other" }); Model.WritePointer(ViewPort.CurrentChange, 0x100, 0); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.options]", null, new FixedLengthStreamStrategy(2)); Model.ObserveRunWritten(new NoDataChangeDeltaModel(), stream); var vm = new TextStreamElementViewModel(ViewPort, default, 0x100, stream.FormatString);
public void StreamWithEnum_RequestAutocompleteAtEnum_GetOptions() { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[a:options b:]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("match, 3", caretLineIndex: 0, caretCharacterIndex: 5).Select(option => option.Text).ToArray(); Assert.Equal(new[] { "Xmatch", "matchX", "matXch" }, options); }
public override void UpdateNewRunFromPointerFormat(IDataModel model, ModelDelta token, string name, ref IFormattedRun run) { if (!TableStreamRun.TryParseTableStream(model, run.Start, run.PointerSources, name, Format, null, out var runAttempt)) { return; } model.ClearFormat(token, run.Start, runAttempt.Length); run = runAttempt; }
public override bool Write(IDataModel model, ModelDelta token, int start, ref string data) { var parts = data.Split(new[] { "(", ")", " " }, StringSplitOptions.RemoveEmptyEntries).ToList(); TableStreamRun.Recombine(parts, "\"", "\""); data = string.Empty; if (parts.Count != VisibleElementCount) { return(false); } int bitOffset = 0; int partIndex = 0; bool anyChanges = false; for (int i = 0; i < Elements.Count; i++) { if (string.IsNullOrEmpty(Elements[i].Name)) { // Unnamed segments. I should increment, and bitOffset should increase, but the value should change to zero. anyChanges |= Elements[i].Write(model, token, start, bitOffset, 0); partIndex -= 1; } else if (!string.IsNullOrEmpty(Elements[i].SourceName)) { if (ArrayRunEnumSegment.TryParse(Elements[i].SourceName, model, parts[partIndex], out int value)) { anyChanges |= Elements[i].Write(model, token, start, bitOffset, value); } } else if (Elements[i].BitWidth == 1) { if (bool.TryParse(parts[partIndex], out bool value)) { anyChanges |= Elements[i].Write(model, token, start, bitOffset, value ? 1 : 0); } } else { if (int.TryParse(parts[partIndex], out int value)) { anyChanges |= Elements[i].Write(model, token, start, bitOffset, value); } } partIndex += 1; bitOffset += Elements[i].BitWidth; } var remainingBits = Length * 8 - bitOffset; if (remainingBits > 0) { anyChanges |= new TupleSegment(string.Empty, remainingBits).Write(model, token, start, bitOffset, 0); } return(anyChanges); }
public void TableStreamRunAutoCompleteOption_MoreElementsNeededOnLine_MovesToNextElement() { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[a:options b:]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("match", caretLineIndex: 0, caretCharacterIndex: 5).ToArray(); Assert.Equal("matXch, ", options[2].LineText); }
public void SingleElementTableStreamRun_AutoCompleteField_OptionsMakeSense() { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc: xyz:options]", null, new FixedLengthStreamStrategy(1)); var options = stream.GetAutoCompleteOptions("xyz: match", caretLineIndex: 0, caretCharacterIndex: 10).ToArray(); Assert.Equal("xyz: matchX", options[1].LineText); }
public void TupleInTableStream_AutoCompleteExtraField_NoOptions() { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc:|t|i:options|j:options]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("(Xmatch match ma", 0, 16).ToArray(); Assert.Empty(options); }
public override void UpdateNewRunFromPointerFormat(IDataModel model, ModelDelta token, string name, IReadOnlyList <ArrayRunElementSegment> sourceSegments, int parentIndex, ref IFormattedRun run) { if (!TableStreamRun.TryParseTableStream(model, run.Start, run.PointerSources, name, Format, null, out var runAttempt)) { return; } model.ClearFormat(token, run.Start, runAttempt.Length); run = runAttempt; }
public void TableStreamRunAutoCompleteOption_Execute_TextChanges() { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[a:options b:]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("match, 3", caretLineIndex: 0, caretCharacterIndex: 5).ToArray(); Assert.Equal("matchX, 3", options[1].LineText); }
public override IFormattedRun WriteNewRun(IDataModel owner, ModelDelta token, int source, int destination, string name, IReadOnlyList <ArrayRunElementSegment> sourceSegments) { // don't bother checking the TryParse result: we very much expect that the data originally in the run won't fit the parse. TableStreamRun.TryParseTableStream(owner, destination, new SortedSpan <int>(source), name, Format, sourceSegments, out var tableStream); if (TableStreamRun.TryWriteNewEndToken(token, ref tableStream)) { return(tableStream); } return(tableStream.DeserializeRun("", token)); }
public void SingleElementStream_AutocompleteWithLeadingWhitespace_Works() { Model.SetList("options", new[] { "\"Poison Gas\"", "\"Poison Sting\"", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc:options xyz:]", null, new FixedLengthStreamStrategy(1)); var options = stream.GetAutoCompleteOptions(" abc: Poison", 0, 13).ToArray(); Assert.Equal("\"Poison Gas\"", options[0].Text); Assert.Equal("\"Poison Sting\"", options[1].Text); }
public void TupleInTableStream_AutoCompleteBoolean_BooleanOptions() { var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.|t|i:|j.]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("(2 e", 0, 4).ToArray(); Assert.Equal(2, options.Length); Assert.Equal("false", options[0].Text); Assert.Equal("(2 true)", options[1].LineText); }
public void TupleInSingleElementTableStream_AutoCompleteInitialField_OptionsMatch(string inputLine, string outputText, string outputLine) { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.|t|i:options|j:options|k.]", null, new FixedLengthStreamStrategy(1)); var options = stream.GetAutoCompleteOptions(inputLine, 0, inputLine.Length).ToArray(); Assert.Equal(outputText, options[0].Text); Assert.Equal(outputLine, options[0].LineText); }
public void TupleInTableStream_AutoCompleteSecondField_OptionCompletesSubContent() { Model.SetList("options", new[] { "Xmatch", "matchX", "matXch", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc:|t|i:options|j:options]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("(Xmatch match", 0, 13).ToArray(); Assert.Equal("Xmatch", options[0].Text); Assert.Equal("(Xmatch Xmatch)", options[0].LineText); }
public void ParenthesisAndTupleElementWithQuotes_Autocomplete_OptionsAreFiltered() { Model.SetList("options", new[] { "\"Poison Gas\"", "\"Poison Sting\"", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.|t|a:options|b:]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("(\"Poison sti 4)", 0, 12).ToArray(); Assert.Single(options); Assert.Equal("\"Poison Sting\"", options[0].Text); Assert.Equal("(\"Poison Sting\" 4)", options[0].LineText); }
public void OptionsWithNoQuote_StartEnumWithLeadingQuote_StillFindAutocompleteOption() { Model.SetList("options", new[] { "PoisonPowder", "\"Poison Gas\"", "other" }); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.options]", null, new FixedLengthStreamStrategy(2)); var options = stream.GetAutoCompleteOptions("\"Poison", 0, 7).ToArray(); Assert.Equal(2, options.Length); Assert.Equal("PoisonPowder", options[0].Text); Assert.Equal("\"Poison Gas\"", options[1].Text); }
public override bool TryAddFormatAtDestination(IDataModel owner, ModelDelta token, int source, int destination, string name, IReadOnlyList <ArrayRunElementSegment> sourceSegments) { if (TableStreamRun.TryParseTableStream(owner, destination, new SortedSpan <int>(source), name, Format, sourceSegments, out var tsRun)) { if (!(token is NoDataChangeDeltaModel)) { owner.ObserveRunWritten(token, tsRun); } return(true); } return(false); }
public void StreamElementViewModel_AutocompleteWithNoCompletion_ZIndexDoesNotChange() { Model.SetList("options", new[] { "PoisonPowder", "\"Poison Gas\"", "other" }); Model.WritePointer(ViewPort.CurrentChange, 0x100, 0); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.options]", null, new FixedLengthStreamStrategy(2)); Model.ObserveRunWritten(new NoDataChangeDeltaModel(), stream); var vm = new TextStreamElementViewModel(ViewPort, 0x100, stream.FormatString); vm.GetAutoCompleteOptions("xzy", 0, 3); Assert.Equal(0, vm.ZIndex); }
public void SingleElementStreamRunWithTuple_Serialize_TupleAppearsAsTuple() { var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[index::|t|:.|i::::::. unknown:|h unused:|h]", null, new FixedLengthStreamStrategy(1)); var lines = stream.SerializeRun().SplitLines(); var tokenLines = lines.Select(line => line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); lines = tokenLines.Select(tokens => " ".Join(tokens)).ToArray(); Assert.Equal("index: (0)", lines[0]); Assert.Equal("unknown: 0x0000", lines[1]); Assert.Equal("unused: 0x0000", lines[2]); }
public void TupleSkip3Bits_Write23_Write184() { SetFullModel(0xFF); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[index::|t|:.|i::::::.]", null, new FixedLengthStreamStrategy(2)); Model.ObserveRunWritten(ViewPort.CurrentChange, stream); ViewPort.Refresh(); ViewPort.Edit("23 "); Assert.Equal(23 * 8, Model[0]); Assert.Equal(0, Model[1]); Assert.Equal(0, Model[2]); Assert.Equal(0, Model[3]); }
public TableStreamRun UpdateFromParentStream(TableStreamRun run, ModelDelta token, int parentSegmentIndex) { var parentAddress = GetParentIndex(run.PointerSources); var parent = model.GetNextRun(parentAddress) as ITableRun; if (parent == null) { return(run); } var segmentIndex = GetSegmentIndex(parent.ElementContent, parentFieldForLength); if (segmentIndex == -1 || segmentIndex != parentSegmentIndex) { return(run); } var segmentOffset = parent.ElementContent.Take(segmentIndex).Sum(segment => segment.Length); var offsets = parent.ConvertByteOffsetToArrayOffset(parentAddress); var newElementCount = model.ReadMultiByteValue(parent.Start + offsets.ElementIndex * parent.ElementLength + segmentOffset, parent.ElementContent[segmentIndex].Length); var newRun = run; if (newElementCount != newRun.ElementCount) { var endOfCurrentRun = run.Start + run.Length; var nextRunMinimumStart = newRun.Start + newRun.ElementLength * newElementCount; if ( TableStreamRun.DataMatches(model, newRun, newElementCount) && model.GetNextRun(nextRunMinimumStart).Start >= nextRunMinimumStart && run.ElementCount <= 1 ) { // no need to repoint: the next data matches // this is important for when we're pasting pointers to existing formats before pasting those formats' lengths. // example: paste ^newanchor <pointer> count, where pointer points to existing data. We don't want writing the 'count' to cause a repoint model.ClearFormat(token, endOfCurrentRun, nextRunMinimumStart - endOfCurrentRun); UpdateParents(token, parent, segmentIndex, newElementCount, newRun.PointerSources); newRun = new TableStreamRun(model, newRun.Start, newRun.PointerSources, newRun.FormatString, newRun.ElementContent, this); } else { newRun = (TableStreamRun)newRun.Append(token, newElementCount - newRun.ElementCount); UpdateParents(token, parent, segmentIndex, newElementCount, newRun.PointerSources); } } return(newRun); }
public override bool TryAddFormatAtDestination(IDataModel owner, ModelDelta token, int source, int destination, string name, IReadOnlyList <ArrayRunElementSegment> sourceSegments, int parentIndex) { if (TableStreamRun.TryParseTableStream(owner, destination, new SortedSpan <int>(source), name, Format, sourceSegments, out var tsRun)) { if (token is not NoDataChangeDeltaModel) { // we know that the data format matches, but there may already be a run there that starts sooner if (owner.GetNextRun(tsRun.Start) is ITableRun existingTable && existingTable.Start < tsRun.Start) { // there is already a format that starts sooner: do nothing, but return true because the format matches } else { owner.ClearFormat(token, tsRun.Start + 1, tsRun.Length - 1); owner.ObserveRunWritten(token, tsRun); } } return(true); }
public void StreamElementViewModel_CallAutocomplete_ZIndexChanges() { Model.SetList("options", new[] { "PoisonPowder", "\"Poison Gas\"", "other" }); Model.WritePointer(ViewPort.CurrentChange, 0x100, 0); var stream = new TableStreamRun(Model, 0, SortedSpan <int> .None, "[abc.options]", null, new FixedLengthStreamStrategy(2)); Model.ObserveRunWritten(new NoDataChangeDeltaModel(), stream); var vm = new TextStreamElementViewModel(ViewPort, 0x100, stream.FormatString); var view = new StubView(vm); Assert.Equal(0, vm.ZIndex); vm.GetAutoCompleteOptions(string.Empty, 0, 0); Assert.Equal(1, vm.ZIndex); vm.ClearAutocomplete(); Assert.Equal(0, vm.ZIndex); Assert.Equal(2, view.PropertyNotifications.Count(pname => pname == nameof(vm.ZIndex))); }
public void Visit(DataFormats.Tuple tuple, byte data) { Result = CurrentText.EndsWith(")"); if (CurrentText.EndsWith(" ")) { var tokens = CurrentText.Split(new[] { ' ', '(', ')' }, StringSplitOptions.RemoveEmptyEntries).ToList(); TableStreamRun.Recombine(tokens, "\"", "\""); if (tokens.Count == tuple.Model.VisibleElementCount) { Result = true; } } if (Result) { tuple.Model.Write(Model, CurrentChange, memoryLocation, CurrentText); NewDataIndex = memoryLocation + tuple.Length; } }
public void AnimationScript_LinkToBody_EncodeCorrectly() { // createSprite <100> 2 (1arg) 0 end var compiledScript = new byte[] { 2, 128, 0, 0, 8, 2, 1, 0, 0, 8 }; Array.Copy(compiledScript, 0, Model.RawData, 0, compiledScript.Length); var spriteData = new byte[] { /*tileTag*/ 1, 0, /*paletteTag*/ 2, 0, /*oam*/ 3, 1, 0, 8, /*anims*/ 4, 1, 0, 8, /*images*/ 5, 1, 0, 8, /*affineAnims*/ 6, 1, 0, 8, /*callback*/ 7, 1, 0, 8 }; Array.Copy(spriteData, 0, Model.RawData, 0x80, spriteData.Length); Model.ObserveRunWritten(ViewPort.CurrentChange, new PointerRun(1)); var stream = new TableStreamRun( Model, 0x80, new SortedSpan <int>(1), "[tileTag: paletteTag: oam<> anims<> images<> affineAnims<> callback<>]", null, new FixedLengthStreamStrategy(1)); Model.ObserveRunWritten(ViewPort.CurrentChange, stream); var script = ViewPort.Tools.CodeTool.AnimationScriptParser.Parse(Model, 0, compiledScript.Length); Model.ResetChanges(); ViewPort.Tools.CodeTool.AnimationScriptParser.Compile(ViewPort.CurrentChange, Model, 0, ref script, out var movedData); Assert.Contains("{", script); Assert.Equal(0, Model.ChangeCount); }