From 1401390c81dc3f0165b59a2bd078e211f29f49a3 Mon Sep 17 00:00:00 2001 From: Dan Schulte Date: Fri, 9 Nov 2018 10:33:05 -0800 Subject: [PATCH] Unify readme generation processes and add auth installation instructions --- src/DSL/Builder.cs | 307 +++++++ src/DSL/{TSPosition.cs => BuilderPosition.cs} | 6 +- src/DSL/HTMLBuilder.cs | 171 ++++ src/DSL/HTMLElement.cs | 212 +++++ src/DSL/IBuilder.cs | 88 ++ src/DSL/JSArgumentList.cs | 151 ++++ src/DSL/JSArray.cs | 107 +++ src/DSL/JSBlock.cs | 127 +++ src/DSL/JSBuilder.cs | 454 ++++++++++ src/DSL/JSClass.cs | 98 +++ src/DSL/JSDocumentationComment.cs | 125 +++ src/DSL/JSIfBlock.cs | 25 + src/DSL/JSObject.cs | 242 ++++++ src/DSL/JSParameter.cs | 47 + src/DSL/JSParameterList.cs | 75 ++ src/DSL/JSTryBlock.cs | 20 + src/DSL/JSValue.cs | 127 +++ src/DSL/MarkdownBuilder.cs | 301 +++++++ src/DSL/TSArgumentList.cs | 8 +- src/DSL/TSBlock.cs | 2 +- src/DSL/TSBuilder.cs | 453 +--------- src/DSL/TSValue.cs | 2 +- src/TemplateFactory.cs | 2 - src/azure/AzureTemplateFactory.cs | 5 - .../Templates/AzureReadmeTemplate.cshtml | 84 -- src/vanilla/CodeGeneratorTS.cs | 6 +- src/vanilla/Model/CodeModelTS.cs | 259 ++++-- src/vanilla/Templates/ReadmeTemplate.cshtml | 53 -- src/vanilla/VanillaTemplateFactory.cs | 5 - test/azuremetadata/generated/Lro/README.md | 41 +- test/metadata/generated/BodyComplex/README.md | 51 +- .../multiapi-test-2017-10-01/README.md | 41 +- .../multiapi-test-2018-02-01/README.md | 41 +- .../multiapi/packages/multiapi-test/README.md | 47 +- unittests/AssertEx.cs | 4 +- unittests/DSL/BuilderTests.cs | 397 +++++++++ unittests/DSL/HTMLBuilderTests.cs | 166 ++++ unittests/DSL/JSBuilderTests.cs | 804 ++++++++++++++++++ unittests/DSL/MarkdownBuilderTests.cs | 104 +++ unittests/DSL/TSBuilderTests.cs | 75 +- 40 files changed, 4542 insertions(+), 791 deletions(-) create mode 100644 src/DSL/Builder.cs rename src/DSL/{TSPosition.cs => BuilderPosition.cs} (83%) create mode 100644 src/DSL/HTMLBuilder.cs create mode 100644 src/DSL/HTMLElement.cs create mode 100644 src/DSL/IBuilder.cs create mode 100644 src/DSL/JSArgumentList.cs create mode 100644 src/DSL/JSArray.cs create mode 100644 src/DSL/JSBlock.cs create mode 100644 src/DSL/JSBuilder.cs create mode 100644 src/DSL/JSClass.cs create mode 100644 src/DSL/JSDocumentationComment.cs create mode 100644 src/DSL/JSIfBlock.cs create mode 100644 src/DSL/JSObject.cs create mode 100644 src/DSL/JSParameter.cs create mode 100644 src/DSL/JSParameterList.cs create mode 100644 src/DSL/JSTryBlock.cs create mode 100644 src/DSL/JSValue.cs create mode 100644 src/DSL/MarkdownBuilder.cs delete mode 100644 src/azure/Templates/AzureReadmeTemplate.cshtml delete mode 100644 src/vanilla/Templates/ReadmeTemplate.cshtml create mode 100644 unittests/DSL/BuilderTests.cs create mode 100644 unittests/DSL/HTMLBuilderTests.cs create mode 100644 unittests/DSL/JSBuilderTests.cs create mode 100644 unittests/DSL/MarkdownBuilderTests.cs diff --git a/src/DSL/Builder.cs b/src/DSL/Builder.cs new file mode 100644 index 000000000000..d7df84cd5e92 --- /dev/null +++ b/src/DSL/Builder.cs @@ -0,0 +1,307 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using AutoRest.Core.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AutoRest.TypeScript.DSL +{ + public class Builder : IBuilder + { + private const string newLine = "\n"; + private const string singleIndent = " "; + + private readonly StringBuilder contents = new StringBuilder(); + private readonly StringBuilder linePrefix = new StringBuilder(); + private readonly List positions = new List(); + + /// + /// Whether or not a newline will be written before the next text. + /// + public bool WriteNewLineBeforeNextText { get; set; } + + /// + /// The word wrap width. A null wordWrapWidth indicates that no word wrapping should take place. + /// + public int? WordWrapWidth { get; set; } + + /// + /// Create a position object that will track a certain position within the TSBuilder's content. + /// + /// + public BuilderPosition CreatePosition() + { + BuilderPosition position; + int contentLength = contents.Length; + if (!positions.Any()) + { + position = new BuilderPosition(null, contentLength); + positions.Add(position); + } + else + { + position = positions.Last(); + int lastPositionIndexInBuilder = position.GetIndexInBuilder(); + if (lastPositionIndexInBuilder != contentLength) + { + position = new BuilderPosition(position, contentLength - lastPositionIndexInBuilder); + positions.Add(position); + } + } + return position; + } + + /// + /// Get whether or not a new line character has been added to this TSBuilder since the provided character index. + /// + /// The character index to begin the search at. + /// + public bool HasChangedLinesSince(int index) + { + bool result = false; + for (int i = index; i < contents.Length; ++i) + { + if (contents[i] == '\n') + { + result = true; + break; + } + } + return result; + } + + public void Insert(int index, string text) + { + if (positions.Any()) + { + int positionIndex = 0; + foreach (BuilderPosition position in positions) + { + if (index <= positionIndex + position.CharactersAfterPreviousPosition) + { + position.CharactersAfterPreviousPosition += text.Length; + break; + } + else + { + positionIndex += position.CharactersAfterPreviousPosition; + } + } + } + contents.Insert(index, text); + } + + public void InsertNewLine(int index) + { + Insert(index, newLine + linePrefix); + } + + public void AddIndentToLinesAfter(int index) + { + for (int i = index; i < contents.Length; ++i) + { + if (contents[i] == '\n' && i + 1 < contents.Length && contents[i + 1] != '\n') + { + contents.Insert(i + 1, singleIndent); + } + } + } + + /// + /// Get the text that has been added to this TSBuilder. + /// + /// The text that has been added to this TSBuilder. + public override string ToString() + { + return contents.ToString(); + } + + /// + /// Add the provided value to end of the line prefix. + /// + /// The value to add to the line prefix. + public void AddToPrefix(string toAdd) + { + linePrefix.Append(toAdd); + } + + /// + /// Remove the provided value from the end of the line prefix. + /// + /// The value to remove from the end of the line prefix. + public void RemoveFromPrefix(string toRemove) + { + int toRemoveLength = toRemove.Length; + if (linePrefix.Length <= toRemoveLength) + { + linePrefix.Clear(); + } + else + { + linePrefix.Remove(linePrefix.Length - toRemoveLength, toRemoveLength); + } + } + + /// + /// Invoke the provided action with the provided additional prefix. + /// + /// The additional text to add to the line prefix for the duration of the provided action. + /// The action to invoke with the provided additional line prefix text. + public void WithAddedPrefix(string toAdd, Action action) + { + AddToPrefix(toAdd); + try + { + action.Invoke(); + } + finally + { + RemoveFromPrefix(toAdd); + } + } + + /// + /// Add a single indentation for the context of the provided action. + /// + /// The action to invoke with an extra level of indentation. + public void Indent(Action action) + { + IncreaseIndent(); + try + { + action.Invoke(); + } + finally + { + DecreaseIndent(); + } + } + + /// + /// Add a new level of indentation to the line prefix. + /// + public void IncreaseIndent() + => AddToPrefix(singleIndent); + + /// + /// Remove a level of indentation from the line prefix. + /// + public void DecreaseIndent() + => RemoveFromPrefix(singleIndent); + + /// + /// Wrap the provided line using the existing wordWrapWidth. + /// + /// The line to wrap. + /// Whether or not to add the line prefix to the wrapped lines. + /// The wrapped lines. + internal IEnumerable WordWrap(string line, bool addPrefix) + { + List lines = new List(); + + if (!string.IsNullOrEmpty(line)) + { + if (WordWrapWidth == null) + { + lines.Add(line); + } + else + { + // Subtract an extra column from the word wrap width because columns generally are + // 1 -based instead of 0-based. + int wordWrapIndexMinusLinePrefixLength = WordWrapWidth.Value - (addPrefix ? linePrefix.Length : 0) - 1; + + IEnumerable wrappedLines = line.WordWrap(wordWrapIndexMinusLinePrefixLength); + foreach (string wrappedLine in wrappedLines.SkipLast(1)) + { + lines.Add(wrappedLine + newLine); + } + + string lastWrappedLine = wrappedLines.Last(); + if (!string.IsNullOrEmpty(lastWrappedLine)) + { + lines.Add(lastWrappedLine); + } + } + } + return lines; + } + + /// + /// Add the provided text to this TSBuilder. + /// + /// The text to add. + public void Text(string text, params object[] formattedArguments) + { + if (!string.IsNullOrEmpty(text) && formattedArguments != null && formattedArguments.Length > 0) + { + text = string.Format(text, formattedArguments); + } + + bool addPrefix = WriteNewLineBeforeNextText; + + List lines = new List(); + + if (WriteNewLineBeforeNextText) + { + WriteNewLineBeforeNextText = false; + contents.Append(newLine); + } + + if (string.IsNullOrEmpty(text)) + { + lines.Add(""); + } + else + { + int lineStartIndex = 0; + int textLength = text.Length; + while (lineStartIndex < textLength) + { + int newLineCharacterIndex = text.IndexOf(newLine, lineStartIndex); + if (newLineCharacterIndex == -1) + { + string line = text.Substring(lineStartIndex); + IEnumerable wrappedLines = WordWrap(line, addPrefix); + lines.AddRange(wrappedLines); + lineStartIndex = textLength; + } + else + { + int nextLineStartIndex = newLineCharacterIndex + 1; + string line = text.Substring(lineStartIndex, nextLineStartIndex - lineStartIndex); + IEnumerable wrappedLines = WordWrap(line, addPrefix); + lines.AddRange(wrappedLines); + lineStartIndex = nextLineStartIndex; + } + } + } + + string prefix = addPrefix ? linePrefix.ToString() : null; + foreach (string line in lines) + { + if (addPrefix && !string.IsNullOrWhiteSpace(prefix) || (!string.IsNullOrEmpty(prefix) && !string.IsNullOrWhiteSpace(line))) + { + contents.Append(prefix); + } + + contents.Append(line); + } + } + + /// + /// Add the provided line of the text to this TSBuilder. + /// + /// The line of text to add to this TSBuilder. + /// Any optional formatted arguments that will be inserted into the text if provided. + public void Line(string text = "", params object[] formattedArguments) + { + Text(text, formattedArguments); + WriteNewLineBeforeNextText = true; + } + } +} diff --git a/src/DSL/TSPosition.cs b/src/DSL/BuilderPosition.cs similarity index 83% rename from src/DSL/TSPosition.cs rename to src/DSL/BuilderPosition.cs index b936f3c3db12..2e9532dd929d 100644 --- a/src/DSL/TSPosition.cs +++ b/src/DSL/BuilderPosition.cs @@ -7,11 +7,11 @@ namespace AutoRest.TypeScript.DSL /// /// A position within a TSBuilder. /// - public class TSPosition + public class BuilderPosition { - private readonly TSPosition previousPosition; + private readonly BuilderPosition previousPosition; - public TSPosition(TSPosition previousPosition, int charactersAfterPreviousPosition) + public BuilderPosition(BuilderPosition previousPosition, int charactersAfterPreviousPosition) { this.previousPosition = previousPosition; this.CharactersAfterPreviousPosition = charactersAfterPreviousPosition; diff --git a/src/DSL/HTMLBuilder.cs b/src/DSL/HTMLBuilder.cs new file mode 100644 index 000000000000..07cbcc8ad9bb --- /dev/null +++ b/src/DSL/HTMLBuilder.cs @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace AutoRest.TypeScript.DSL +{ + public class HTMLBuilder : IBuilder + { + private readonly IBuilder builder; + + public HTMLBuilder() + : this(new Builder()) + { + } + + public HTMLBuilder(IBuilder builder) + { + this.builder = builder; + } + + public bool WriteNewLineBeforeNextText { get => builder.WriteNewLineBeforeNextText; set => builder.WriteNewLineBeforeNextText = value; } + public int? WordWrapWidth { get => builder.WordWrapWidth; set => builder.WordWrapWidth = value; } + + public void AddIndentToLinesAfter(int index) + { + builder.AddIndentToLinesAfter(index); + } + + public void AddToPrefix(string toAdd) + { + builder.AddToPrefix(toAdd); + } + + public BuilderPosition CreatePosition() + { + return builder.CreatePosition(); + } + + public void DecreaseIndent() + { + builder.DecreaseIndent(); + } + + public bool HasChangedLinesSince(int index) + { + return builder.HasChangedLinesSince(index); + } + + public void IncreaseIndent() + { + builder.IncreaseIndent(); + } + + public void Indent(Action action) + { + builder.Indent(action); + } + + public void Insert(int index, string text) + { + builder.Insert(index, text); + } + + public void InsertNewLine(int index) + { + builder.InsertNewLine(index); + } + + public void Line(string text = "", params object[] formattedArguments) + { + builder.Line(text, formattedArguments); + } + + public void RemoveFromPrefix(string toRemove) + { + builder.RemoveFromPrefix(toRemove); + } + + public void Text(string text, params object[] formattedArguments) + { + builder.Text(text, formattedArguments); + } + + public void WithAddedPrefix(string toAdd, Action action) + { + builder.WithAddedPrefix(toAdd, action); + } + + public override string ToString() + { + return builder.ToString(); + } + + public void DOCTYPE() + { + Line(""); + } + + public void Element(string elementName, bool canBeEmptyElement) + { + Element(elementName, canBeEmptyElement, element => + { + }); + } + + public void Element(string elementName, string elementValue) + { + Element(elementName, childElement => + { + childElement.Text(elementValue); + }); + } + + public void Element(string elementName, Action elementAction) + { + Element(elementName, true, elementAction); + } + + public void Element(string elementName, bool canBeEmptyElement, Action elementAction) + { + using (HTMLElement element = new HTMLElement(this, elementName, canBeEmptyElement)) + { + elementAction?.Invoke(element); + } + } + + public void Html(Action action) + { + Element("html", html => + { + html.Attribute("lang", "en"); + action.Invoke(html); + }); + } + + public void Head(Action action = null) + { + Element("head", action); + } + + public void Title(string title) + { + Element("title", title); + } + + public void Script(string src) + { + Element("script", false, script => script.Attribute("src", src)); + } + + public void Script(Action action) + { + Element("script", false, script => + { + script.Attribute("type", "text/javascript"); + script.JavaScript(action); + }); + } + + public void Body() + { + Element("body", false); + } + + public void JavaScript(Action action) + { + action.Invoke(new JSBuilder(this)); + } + } +} diff --git a/src/DSL/HTMLElement.cs b/src/DSL/HTMLElement.cs new file mode 100644 index 000000000000..ffe6fe7dc989 --- /dev/null +++ b/src/DSL/HTMLElement.cs @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace AutoRest.TypeScript.DSL +{ + public class HTMLElement : IDisposable + { + private readonly HTMLBuilder builder; + private readonly string elementName; + private readonly bool canBeEmptyElement; + + public enum State + { + Start, + Attributes, + ChildElements, + ChildText, + End + } + + public HTMLElement(HTMLBuilder builder, string elementName, bool canBeEmptyElement) + { + this.builder = builder; + this.elementName = elementName; + this.canBeEmptyElement = canBeEmptyElement; + this.CurrentState = State.Start; + builder.Text($"<{elementName}"); + } + + public State CurrentState { get; private set; } + + + private void SetCurrentState(State newState) + { + switch (newState) + { + case State.Attributes: + switch (CurrentState) + { + case State.ChildElements: + throw new Exception("Can't add HTML element attributes after a child element has been added."); + + case State.ChildText: + throw new Exception("Can't add HTML element attributes after element text has been added."); + + case State.End: + throw new Exception("Can't add HTML element attributes after the element has been closed."); + } + break; + + case State.ChildElements: + switch (CurrentState) + { + case State.Start: + case State.Attributes: + builder.Line(">"); + break; + + case State.End: + throw new Exception("Can't add HTML child elements after the element has been closed."); + } + break; + + case State.ChildText: + switch (CurrentState) + { + case State.Start: + case State.Attributes: + builder.Text(">"); + break; + + case State.End: + throw new Exception("Can't add HTML child elements after the element has been closed."); + } + break; + + case State.End: + switch (CurrentState) + { + case State.Start: + case State.Attributes: + if (canBeEmptyElement) + { + builder.Line("/>"); + } + else + { + builder.Line($">"); + } + break; + + case State.ChildElements: + case State.ChildText: + builder.Line($""); + break; + } + break; + } + CurrentState = newState; + } + + public void Attribute(string attributeName, string attributeValue) + { + SetCurrentState(State.Attributes); + builder.Text($" {attributeName}=\"{attributeValue}\""); + } + + public void Text(string text) + { + if (!string.IsNullOrEmpty(text)) + { + SetCurrentState(State.ChildText); + builder.Text(text); + } + } + + public void ChildElement(string elementName, bool canBeEmptyElement = true) + { + ChildElement(elementName, canBeEmptyElement, element => + { + }); + } + + public void ChildElement(string elementName, string elementValue) + { + ChildElement(elementName, childElement => + { + childElement.Text(elementValue); + }); + } + + public void ChildElement(string elementName, Action childElementAction) + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Element(elementName, childElementAction); + }); + } + + public void ChildElement(string elementName, bool canBeEmptyElement, Action childElementAction) + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Element(elementName, canBeEmptyElement, childElementAction); + }); + } + + public void Head(Action action = null) + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Head(action); + }); + } + + public void Title(string title) + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Title(title); + }); + } + + public void Script(string src) + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Script(src); + }); + } + + public void Script(Action action) + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Script(action); + }); + } + + public void JavaScript(Action action) + { + SetCurrentState(State.ChildText); + builder.Indent(() => + { + builder.Line(); + builder.JavaScript(action); + }); + } + + public void Body() + { + SetCurrentState(State.ChildElements); + builder.Indent(() => + { + builder.Body(); + }); + } + + public void Dispose() + { + SetCurrentState(State.End); + } + } +} diff --git a/src/DSL/IBuilder.cs b/src/DSL/IBuilder.cs new file mode 100644 index 000000000000..2efc54b8f7e7 --- /dev/null +++ b/src/DSL/IBuilder.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; + +namespace AutoRest.TypeScript.DSL +{ + public interface IBuilder + { + /// + /// Whether or not a newline will be written before the next text. + /// + bool WriteNewLineBeforeNextText { get; set; } + + /// + /// The word wrap width. A null wordWrapWidth indicates that no word wrapping should take place. + /// + int? WordWrapWidth { get; set; } + + /// + /// Create a position object that will track a certain position within the TSBuilder's content. + /// + /// + BuilderPosition CreatePosition(); + + /// + /// Get whether or not a new line character has been added to this TSBuilder since the provided character index. + /// + /// The character index to begin the search at. + /// + bool HasChangedLinesSince(int index); + + void Insert(int index, string text); + + void InsertNewLine(int index); + + void AddIndentToLinesAfter(int index); + + /// + /// Add the provided value to end of the line prefix. + /// + /// The value to add to the line prefix. + void AddToPrefix(string toAdd); + + /// + /// Remove the provided value from the end of the line prefix. + /// + /// The value to remove from the end of the line prefix. + void RemoveFromPrefix(string toRemove); + + /// + /// Invoke the provided action with the provided additional prefix. + /// + /// The additional text to add to the line prefix for the duration of the provided action. + /// The action to invoke with the provided additional line prefix text. + void WithAddedPrefix(string toAdd, Action action); + + /// + /// Add a single indentation for the context of the provided action. + /// + /// The action to invoke with an extra level of indentation. + void Indent(Action action); + + /// + /// Add a new level of indentation to the line prefix. + /// + void IncreaseIndent(); + + /// + /// Remove a level of indentation from the line prefix. + /// + void DecreaseIndent(); + + /// + /// Add the provided text to this TSBuilder. + /// + /// The text to add. + void Text(string text, params object[] formattedArguments); + + /// + /// Add the provided line of the text to this TSBuilder. + /// + /// The line of text to add to this TSBuilder. + /// Any optional formatted arguments that will be inserted into the text if provided. + void Line(string text = "", params object[] formattedArguments); + } +} diff --git a/src/DSL/JSArgumentList.cs b/src/DSL/JSArgumentList.cs new file mode 100644 index 000000000000..d0fa74a7edcf --- /dev/null +++ b/src/DSL/JSArgumentList.cs @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// The TypeScript DSL representation for adding arguments to a function call's argument list. + /// + public class JSArgumentList : JSValue, IDisposable + { + private List argumentPositions; + + /// + /// Create a new TSArgumentList that will emit text to the provided JSBuilder. + /// + /// The JSBuilder to emit text to. + public JSArgumentList(JSBuilder builder) + : base(builder) + { + argumentPositions = new List(); + } + + public void Dispose() + { + if (argumentPositions.Count >= 2) + { + int firstArgumentPositionIndex = argumentPositions.First().GetIndexInBuilder(); + if (HasChangedLinesSince(firstArgumentPositionIndex)) + { + foreach (BuilderPosition argumentPosition in argumentPositions) + { + InsertNewLine(argumentPosition.GetIndexInBuilder()); + } + AddIndentToLinesAfter(firstArgumentPositionIndex); + } + else + { + foreach (BuilderPosition argumentPosition in argumentPositions.Skip(1)) + { + Insert(argumentPosition.GetIndexInBuilder(), " "); + } + } + } + } + + /// + /// Perform any necessary actions before an argument is added to this argument list. + /// + private void BeforeArgumentAdded() + { + if (argumentPositions.Any()) + { + base.Text(","); + } + argumentPositions.Add(CreatePosition()); + } + + /// + /// Add a quoted-string argument to this argument list. + /// + /// The text that will be wrapped in double-quotes and then added. + public override void QuotedString(string text) + { + BeforeArgumentAdded(); + base.QuotedString(text); + } + + /// + /// Add a function call argument to this argument list. + /// + /// The name of the function to invoke. + /// The action that will be used to populate the arguments of the function call. + public override void FunctionCall(string functionName, Action argumentListAction) + { + BeforeArgumentAdded(); + base.FunctionCall(functionName, argumentListAction); + } + + /// + /// Add a text (not a quoted-string) argument to this argument list. + /// + /// The raw text argument to add. + public override void Text(string text) + { + BeforeArgumentAdded(); + base.Text(text); + } + + /// + /// Add a JSON array to this TSValue. + /// + /// The action that will be invoked to produce the elements of the JSON array. + public override void Array(Action arrayAction) + { + BeforeArgumentAdded(); + base.Array(arrayAction); + } + + /// + /// Add an object argument to this argument list. + /// + /// The action that will be used to populate the properties of the object. + public override void Object(Action objectAction = null) + { + BeforeArgumentAdded(); + base.Object(objectAction); + } + + /// + /// Add a boolean value to this TSValue. + /// + /// The boolean value to add to this TSValue. + public override void Boolean(bool value) + { + BeforeArgumentAdded(); + base.Boolean(value); + } + + /// + /// Add a null value to this TSValue. + /// + public override void Null() + { + BeforeArgumentAdded(); + base.Null(); + } + + /// + /// Add an undefined value to this TSValue. + /// + public override void Undefined() + { + BeforeArgumentAdded(); + base.Undefined(); + } + + /// + /// Add a lambda to this TSArgumentList. + /// + public override void Lambda(string paramName, Action lambdaBodyAction) + { + BeforeArgumentAdded(); + base.Lambda(paramName, lambdaBodyAction); + } + } +} diff --git a/src/DSL/JSArray.cs b/src/DSL/JSArray.cs new file mode 100644 index 000000000000..8b4408ba0c6d --- /dev/null +++ b/src/DSL/JSArray.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// A TypeScript DSL representation for a JSON array. + /// + public class JSArray : IDisposable + { + private readonly JSBuilder builder; + private State currentState = State.Start; + + public enum State + { + Start, + Element + } + + /// + /// Create a new JSON array. + /// + /// The JSBuilder that this TSArray will emit to. + public JSArray(JSBuilder builder) + { + this.builder = builder; + } + + private void SetCurrentState(State newState) + { + switch (currentState) + { + case State.Start: + builder.Line(); + break; + + case State.Element: + builder.Line(","); + break; + + default: + throw new Exception($"Unrecognized current state: {currentState}"); + } + currentState = newState; + } + + /// + /// Mark the end of this TSArray. If the current state is not Start, then a newline will be added. + /// + public void Dispose() + { + if (currentState != State.Start) + { + builder.Line(); + } + } + + /// + /// Invoke the provided action to create an element value, and then add the generated value to this TSArray. + /// + /// The action to invoke to add the element's value. + public void Value(Action valueAction) + { + SetCurrentState(State.Element); + builder.Value(valueAction); + } + + /// + /// Add an element to this TSArray with the provided boolean value. + /// + /// The boolean value to add. + public void Boolean(bool value) + { + Value(tsValue => tsValue.Boolean(value)); + } + + /// + /// Add an element to this TSArray with the provided text value. + /// + /// The text value to add. + public void Text(string value) + { + Value(tsValue => tsValue.Text(value)); + } + + /// + /// Add an element to this TSArray with the provided quoted-string value. + /// + /// The value to quote and then add. + public void QuotedString(string value) + { + Value(tsValue => tsValue.QuotedString(value)); + } + + /// + /// Invoke the provided action to create an element object, and then add the generated object to this TSArray. + /// + /// The action to invoke to add the element's value. + public void Object(Action valueAction) + { + Value(tsValue => tsValue.Object(valueAction)); + } + } +} diff --git a/src/DSL/JSBlock.cs b/src/DSL/JSBlock.cs new file mode 100644 index 000000000000..4579d9acaa84 --- /dev/null +++ b/src/DSL/JSBlock.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace AutoRest.TypeScript.DSL +{ + public class JSBlock : IDisposable + { + protected readonly JSBuilder builder; + private State currentState; + + protected enum State + { + Start, + Statement, + Comment, + If, + Try, + Threw, + Returned + } + + public JSBlock(JSBuilder builder) + { + this.builder = builder; + currentState = State.Start; + } + + protected void SetCurrentState(State newState) + { + switch (currentState) + { + case State.Threw: + throw new Exception("Once a block has a throw statement emitted, no further statements can be emitted."); + + case State.Returned: + throw new Exception("Once a block's return statement has been emitted, no further statements can be emitted."); + } + currentState = newState; + } + + public void Dispose() + { + } + + public void Text(string text) + { + SetCurrentState(State.Statement); + builder.Text(text); + } + + public void Line() + { + builder.Line(); + } + + public void Line(string text) + { + SetCurrentState(State.Statement); + builder.Line(text); + } + + public void LineComment(string text) + { + SetCurrentState(State.Comment); + builder.LineComment(text); + } + + public void FunctionCall(string functionName, Action argumentListAction) + { + SetCurrentState(State.Statement); + builder.FunctionCall(functionName, argumentListAction); + } + + public void Indent(Action action) + { + builder.Indent(action); + } + + public void ConstObjectVariable(string variableName, Action valueAction) + { + SetCurrentState(State.Statement); + builder.ConstObjectVariable(variableName, valueAction); + } + + public void ConstObjectVariable(string variableName, string value) + { + SetCurrentState(State.Statement); + builder.ConstObjectVariable(variableName, value); + } + + public JSIfBlock If(string condition, Action thenAction) + { + SetCurrentState(State.If); + return builder.If(condition, thenAction); + } + + public JSTryBlock Try(Action tryAction) + { + SetCurrentState(State.Try); + return builder.Try(tryAction); + } + + public void Return(string text) + { + Return(value => value.Text(text)); + } + + public void Return(Action returnValueAction) + { + SetCurrentState(State.Returned); + builder.Return(returnValueAction); + } + + public void Throw(string valueToThrow) + { + SetCurrentState(State.Threw); + builder.Throw(valueToThrow); + } + + public void Value(Action valueAction) + { + builder.Value(valueAction); + } + } +} diff --git a/src/DSL/JSBuilder.cs b/src/DSL/JSBuilder.cs new file mode 100644 index 000000000000..0417c28e71c6 --- /dev/null +++ b/src/DSL/JSBuilder.cs @@ -0,0 +1,454 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// A StringBuilder that has helper methods for building TypeScript code. + /// + public class JSBuilder : IBuilder + { + protected const int defaultCommentWordWrapWidth = 100; + + private readonly IBuilder builder; + protected readonly int commentWordWrapWidth; + + public JSBuilder(int commentWordWrapWidth = defaultCommentWordWrapWidth) + : this(new Builder(), commentWordWrapWidth) + { + } + + public JSBuilder(IBuilder builder, int commentWordWrapWidth = defaultCommentWordWrapWidth) + { + this.builder = builder; + this.commentWordWrapWidth = commentWordWrapWidth; + } + + /// + /// Whether or not a newline will be written before the next text. + /// + public bool WriteNewLineBeforeNextText { get => builder.WriteNewLineBeforeNextText; set => builder.WriteNewLineBeforeNextText = value; } + + /// + /// The word wrap width. A null wordWrapWidth indicates that no word wrapping should take place. + /// + public int? WordWrapWidth { get => builder.WordWrapWidth; set => builder.WordWrapWidth = value; } + + /// + /// Create a position object that will track a certain position within the JSBuilder's content. + /// + /// + public BuilderPosition CreatePosition() + { + return builder.CreatePosition(); + } + + /// + /// Get whether or not a new line character has been added to this JSBuilder since the provided character index. + /// + /// The character index to begin the search at. + /// + public bool HasChangedLinesSince(int index) + { + return builder.HasChangedLinesSince(index); + } + + public void Insert(int index, string text) + { + builder.Insert(index, text); + } + + public void InsertNewLine(int index) + { + builder.InsertNewLine(index); + } + + public void AddIndentToLinesAfter(int index) + { + builder.AddIndentToLinesAfter(index); + } + + /// + /// Add the provided value to end of the line prefix. + /// + /// The value to add to the line prefix. + public void AddToPrefix(string toAdd) + { + builder.AddToPrefix(toAdd); + } + + /// + /// Remove the provided value from the end of the line prefix. + /// + /// The value to remove from the end of the line prefix. + public void RemoveFromPrefix(string toRemove) + { + builder.RemoveFromPrefix(toRemove); + } + + /// + /// Invoke the provided action with the provided additional prefix. + /// + /// The additional text to add to the line prefix for the duration of the provided action. + /// The action to invoke with the provided additional line prefix text. + public void WithAddedPrefix(string toAdd, Action action) + { + builder.WithAddedPrefix(toAdd, action); + } + + /// + /// Add a single indentation for the context of the provided action. + /// + /// The action to invoke with an extra level of indentation. + public void Indent(Action action) + { + builder.Indent(action); + } + + /// + /// Add a new level of indentation to the line prefix. + /// + public void IncreaseIndent() + { + builder.IncreaseIndent(); + } + + /// + /// Remove a level of indentation from the line prefix. + /// + public void DecreaseIndent() + { + builder.DecreaseIndent(); + } + + /// + /// Add the provided text to this JSBuilder. + /// + /// The text to add. + public void Text(string text, params object[] formattedArguments) + { + builder.Text(text, formattedArguments); + } + + /// + /// Add the provided line of the text to this JSBuilder. + /// + /// The line of text to add to this JSBuilder. + /// Any optional formatted arguments that will be inserted into the text if provided. + public void Line(string text = "", params object[] formattedArguments) + { + builder.Line(text, formattedArguments); + } + + public override string ToString() + { + return builder.ToString(); + } + + public void Class(string className, Action classAction) + { + Block($"class {className}", block => + { + classAction?.Invoke(new JSClass(this)); + }); + } + + /// + /// Get whether or not the provided lines has any lines that are not null and not whitespace. + /// + /// The lines to check. + /// Whether or not the provided lines has any lines that are not null and not whitespace. + public bool AnyCommentLines(IEnumerable lines) + { + return lines != null && lines.Any((string line) => !string.IsNullOrWhiteSpace(line)); + } + + private void Comment(string commentHeader, IEnumerable lines) + { + if (AnyCommentLines(lines)) + { + Line(commentHeader); + WithAddedPrefix(" * ", () => + { + int? previousWordWrapWidth = WordWrapWidth; + WordWrapWidth = commentWordWrapWidth; + try + { + foreach (string line in lines) + { + if (line != null) + { + Line(line); + } + } + } + finally + { + WordWrapWidth = previousWordWrapWidth; + } + }); + Line(" */"); + } + } + + /// + /// Add a /* */ comment to this JSBuilder. If no non-null and non-empty lines are provided, then nothing will be added. + /// + /// The lines to add. Null lines will be ignored. + public void Comment(params string[] lines) + { + Comment("/*", lines); + } + + public void DocumentationComment(Action commentAction) + { + if (commentAction != null) + { + using (JSDocumentationComment comment = new JSDocumentationComment(this, commentWordWrapWidth)) + { + commentAction.Invoke(comment); + } + } + } + + /// + /// Add a // comment to this JSBuilder. + /// + /// + public void LineComment(string line) + { + Line($"// {line}"); + } + + /// + /// Add a /** */ comment to this JSBuilder. If no non-null and non-empty lines are provided, then nothing will be added. + /// + /// The lines to add. Null lines will be ignored. + public void DocumentationComment(params string[] lines) + { + Comment("/**", lines); + } + + /// + /// Add a /** */ comment to this JSBuilder. If no non-null and non-empty lines are provided, then nothing will be added. + /// + /// The lines to add. Null lines will be ignored. + public void DocumentationComment(IEnumerable lines) + { + Comment("/**", lines); + } + + /// + /// Add a JSON array to this JSBuilder that uses the provided action to add the array's elements. + /// + /// The action that will be invoked to add the array's elements. + public void Array(Action action = null) + { + Text("["); + Indent(() => + { + using (JSArray tsArray = new JSArray(this)) + { + action?.Invoke(tsArray); + } + }); + Text("]"); + } + + /// + /// Add a JSON object to this JSBuilder that uses the provided action to add the object's properties. + /// + /// The action that will be invoked to add the object's properties. + public void Object(Action action = null) + { + Text($"{{"); + Indent(() => + { + using (JSObject tsObject = new JSObject(this)) + { + action?.Invoke(tsObject); + } + }); + Text($"}}"); + } + + /// + /// Surround the provided text with double-quotes and add it to this JSBuilder. + /// + /// The text to double-quote and add to this JSBuilder. + public void QuotedString(string text) + { + Text($"\"{text}\""); + } + + /// + /// Add the provided boolean value to this JSBuilder. + /// + /// + public void Boolean(bool value) + { + Text(value ? "true" : "false"); + } + + /// + /// Add a null value to this JSBuilder. + /// + public void Null() + { + Text("null"); + } + + /// + /// Add an undefined value to this JSBuilder. + /// + public void Undefined() + { + Text("undefined"); + } + + public void Lambda(string paramName, Action lambdaBodyAction) + { + Line($"{paramName} => {{"); + Indent(() => + { + lambdaBodyAction.Invoke(new JSBlock(this)); + }); + Text($"}}"); + } + + /// + /// Invoke the provided action in order to produce a value in this JSBuilder. + /// + /// The action to invoke. + public void Value(Action valueAction) + { + valueAction?.Invoke(new JSValue(this)); + } + + /// + /// Add a function call with the provided functionName to this JSBuilder. The provided + /// action will be used to create the arguments for the function call. + /// + /// The name of the function to invoke. + /// The action to invoke to populate the arguments of the function. + public void FunctionCall(string functionName, Action argumentListAction) + { + Text($"{functionName}("); + using (JSArgumentList argumentList = new JSArgumentList(this)) + { + argumentListAction.Invoke(argumentList); + } + Text(")"); + } + + public void Method(string methodName, string parameterList, Action methodBodyAction) + { + Block($"{methodName}({parameterList})", methodBodyAction); + } + + public void Block(string beforeBlock, Action blockAction) + { + Block(beforeBlock, true, blockAction); + } + + public void Block(string beforeBlock, bool newLineAfterBlock, Action blockAction) + { + Line($"{beforeBlock} {{"); + Indent(() => + { + using (JSBlock block = new JSBlock(this)) + { + blockAction.Invoke(block); + } + }); + WriteNewLineBeforeNextText = true; + Text($"}}"); + WriteNewLineBeforeNextText = newLineAfterBlock; + } + + public JSIfBlock If(string condition, Action bodyAction) + { + Block($"if ({condition})", bodyAction); + return new JSIfBlock(this); + } + + public JSIfBlock ElseIf(string condition, Action bodyAction) + { + WriteNewLineBeforeNextText = false; + Block($" else if ({condition})", bodyAction); + return new JSIfBlock(this); + } + + public void Else(Action bodyAction) + { + WriteNewLineBeforeNextText = false; + Block($" else", bodyAction); + } + + public JSTryBlock Try(Action tryAction) + { + Block($"try", tryAction); + return new JSTryBlock(this); + } + + public void Catch(string errorName, Action catchAction) + { + WriteNewLineBeforeNextText = false; + Block($" catch ({errorName})", catchAction); + } + + public void Return(string result) + { + Return(value => value.Text(result)); + } + + public void Return(Action returnValueAction) + { + Text("return "); + Value(returnValueAction); + Line(";"); + } + + public void Throw(string valueToThrow) + { + Line($"throw {valueToThrow};"); + } + + public void ConstQuotedStringVariable(string variableName, string text) + { + ConstVariable(variableName, $"\"{text}\""); + } + + public void ConstVariable(string variableName, string variableValue) + { + Line($"const {variableName} = {variableValue};"); + } + + public void ConstObjectVariable(string variableName, Action valueAction) + { + Text($"const {variableName} = "); + Object(valueAction); + Line($";"); + } + + public void ConstObjectVariable(string variableName, string value) + { + Line($"const {variableName} = {value};"); + } + + public void ImportAllAs(string importAs, string importSource) + { + Line($"import * as {importAs} from \"{importSource}\";"); + } + + public void Import(IEnumerable importedTypeNames, string importSource) + { + Line($"import {{ {string.Join(", ", importedTypeNames)} }} from \"{importSource}\";"); + } + } +} \ No newline at end of file diff --git a/src/DSL/JSClass.cs b/src/DSL/JSClass.cs new file mode 100644 index 000000000000..93ab4be1786a --- /dev/null +++ b/src/DSL/JSClass.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; + +namespace AutoRest.TypeScript.DSL +{ + public class JSClass + { + private readonly JSBuilder builder; + private State currentState; + + private enum State + { + Start, + DocumentationComment, + PropertyDeclaration, + MethodOverload, + Method + } + + public JSClass(JSBuilder builder) + { + this.builder = builder; + currentState = State.Start; + } + + private void SetCurrentState(State newState) + { + switch (currentState) + { + case State.PropertyDeclaration: + builder.Line(); + break; + + case State.Method: + builder.Line(); + break; + } + currentState = newState; + } + + public void DocumentationComment(params string[] documentationCommentLines) + { + if (builder.AnyCommentLines(documentationCommentLines)) + { + SetCurrentState(State.DocumentationComment); + builder.DocumentationComment(documentationCommentLines); + } + } + + public void DocumentationComment(Action commentAction) + { + if (commentAction != null) + { + SetCurrentState(State.DocumentationComment); + builder.DocumentationComment(commentAction); + } + } + + public void Line(string text) + { + builder.Line(text); + } + + public void Method(string methodName, string parameterList, Action methodBodyAction) + { + SetCurrentState(State.Method); + builder.Method(methodName, parameterList, methodBodyAction); + } + + public void Method(string methodName, Action parameterListAction, Action methodBodyAction) + { + string parameterListString = ParameterListActionToString(parameterListAction); + Method(methodName, parameterListString, methodBodyAction); + } + + public void Method(string methodName, IEnumerable parameters, Action methodBodyAction) + { + Method(methodName, parameterList => parameterList.Parameters(parameters), methodBodyAction); + } + + private static string ParameterListActionToString(Action parameterListAction) + { + string parameterListString = null; + if (parameterListAction != null) + { + JSBuilder parameterListBuilder = new JSBuilder(); + JSParameterList parameterList = new JSParameterList(parameterListBuilder); + parameterListAction(parameterList); + parameterListString = parameterListBuilder.ToString(); + } + return parameterListString; + } + } +} diff --git a/src/DSL/JSDocumentationComment.cs b/src/DSL/JSDocumentationComment.cs new file mode 100644 index 000000000000..db01652055e5 --- /dev/null +++ b/src/DSL/JSDocumentationComment.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace AutoRest.TypeScript.DSL +{ + public class JSDocumentationComment : IDisposable + { + private readonly JSBuilder builder; + private readonly int? previousWordWrapWidth; + private State currentState; + + private enum State + { + Start, + Summary, + Description, + Parameters, + Deprecated, + Returns + } + + public JSDocumentationComment(JSBuilder builder, int commentWordWrapWidth) + { + this.builder = builder; + previousWordWrapWidth = builder.WordWrapWidth; + builder.WordWrapWidth = commentWordWrapWidth; + } + + private void SetCurrentState(State newState) + { + if (currentState == State.Start) + { + builder.Line("/**"); + builder.AddToPrefix(" * "); + } + currentState = newState; + } + + public void Dispose() + { + if (currentState != State.Start) + { + builder.WordWrapWidth = previousWordWrapWidth; + builder.RemoveFromPrefix(" * "); + builder.Line(" */"); + } + } + + public void Summary(string text) + { + if (!string.IsNullOrEmpty(text)) + { + SetCurrentState(State.Summary); + builder.Line($"@summary {text}"); + } + } + + public void Description(string text) + { + if (!string.IsNullOrEmpty(text)) + { + bool addJSDocTag = currentState != State.Start && currentState != State.Description; + SetCurrentState(State.Description); + builder.Line($"{(addJSDocTag ? "@description " : "")}{text}"); + } + } + + public void Parameter(string parameterName, string parameterType, string parameterDocumentation, bool isOptional = false) + { + SetCurrentState(State.Parameters); + builder.Line($"@param {{{parameterType}}} {(isOptional ? '[' + parameterName + ']' : parameterName)} {parameterDocumentation}"); + } + + public void Parameters(IEnumerable parameters) + { + if (parameters != null) + { + foreach (JSParameter parameter in parameters) + { + Parameter(parameter.Name, parameter.Type, parameter.Description, !parameter.Required); + } + } + } + + /// + /// Add an @deprecated tag to this comment. If the deprecatedMessage is null, then the tag + /// will not be added. If the deprecatedMessage is empty or whitespace, then just the + /// @deprecated tag will be added. Otherwise the @deprecated tag and the message will be + /// written. + /// + /// The message to accompany the @deprecated tag. + public void Deprecated(string deprecatedMessage) + { + if (deprecatedMessage != null) + { + SetCurrentState(State.Deprecated); + string text = "@deprecated"; + if (!string.IsNullOrWhiteSpace(deprecatedMessage)) + { + text += $" {deprecatedMessage}"; + } + builder.Line(text); + } + } + + public void Returns(string returnType, string returnDocumentation) + { + SetCurrentState(State.Returns); + builder.Line($"@returns {{{returnType}}} {returnDocumentation}"); + } + + public void ReadOnly() + { + builder.Line("@readonly"); + } + + public void Enum(string enumType) + { + builder.Line($"@enum {{{enumType}}}"); + } + } +} diff --git a/src/DSL/JSIfBlock.cs b/src/DSL/JSIfBlock.cs new file mode 100644 index 000000000000..e7aacdd1d2d0 --- /dev/null +++ b/src/DSL/JSIfBlock.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace AutoRest.TypeScript.DSL +{ + public class JSIfBlock : JSBlock + { + public JSIfBlock(JSBuilder builder) + : base(builder) + { + } + + public JSIfBlock ElseIf(string condition, Action bodyAction) + { + return builder.ElseIf(condition, bodyAction); + } + + public void Else(Action bodyAction) + { + builder.Else(bodyAction); + } + } +} diff --git a/src/DSL/JSObject.cs b/src/DSL/JSObject.cs new file mode 100644 index 000000000000..b0b9c6a34bc9 --- /dev/null +++ b/src/DSL/JSObject.cs @@ -0,0 +1,242 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// A TypeScript DSL representation for a JSON object. + /// + public class JSObject : IDisposable + { + private readonly JSBuilder builder; + private string propertyBeingConstructed; + private State currentState = State.Start; + private IList propertyNames = new List(); + + private enum State + { + Start, + LineComment, + BlockComment, + Property + } + + /// + /// Create a new JSON object. + /// + /// The JSBuilder that this JSObject will emit to. + public JSObject(JSBuilder builder) + { + this.builder = builder; + } + + /// + /// Set the current state of this JSObject. Changing the state may add "\n" or ",\n". + /// + /// + private void SetCurrentState(State value) + { + switch (currentState) + { + case State.Start: + builder.Line(); + break; + + case State.LineComment: + case State.BlockComment: + break; + + case State.Property: + builder.Line(","); + break; + + default: + throw new Exception($"Unrecognized current state: {currentState}"); + } + + currentState = value; + } + + /// + /// Get whether or not a property with the provided propertyName has already been added to this JSObject. + /// + /// The name of the property to check. + public bool ContainsProperty(string propertyName) + { + return propertyNames.Contains(propertyName); + } + + /// + /// Add a line comment to this JSObject. + /// + /// The line to add as a line comment to this JSObject. + public void LineComment(string line) + { + SetCurrentState(State.LineComment); + builder.LineComment(line); + } + + /// + /// Add a documentation comment to this JSObject. If no non-null lines are provided, then nothing will be added. + /// + /// The lines of text to add to the documentation comment. + public void DocumentationComment(params string[] lines) + { + if (builder.AnyCommentLines(lines)) + { + SetCurrentState(State.BlockComment); + builder.DocumentationComment(lines); + } + } + + /// + /// Mark the end of this JSObject. If the current state is not Start, then a newline will be added. + /// + public void Dispose() + { + if (currentState != State.Start) + { + builder.Line(); + } + } + + private bool PropertyNameNeedsToBeQuoted(string propertyName) + { + return propertyName.Contains("."); + } + + /// + /// Add a property to this JSObject with the provided name. The provided action will be invoked to populate the value of this property. + /// + /// The name of the new property. + /// The action to invoke to add the property's value. + public void Property(string propertyName, Action propertyValueAction) + { + if (!string.IsNullOrEmpty(propertyBeingConstructed)) + { + throw new InvalidOperationException($"Cannot add a property to a JSObject while constructing its child property (\"{propertyBeingConstructed}\")."); + } + + SetCurrentState(State.Property); + if (PropertyNameNeedsToBeQuoted(propertyName)) + { + builder.QuotedString(propertyName); + } + else + { + builder.Text(propertyName); + } + builder.Text(": "); + propertyBeingConstructed = propertyName; + try + { + builder.Value(propertyValueAction); + } + finally + { + propertyBeingConstructed = null; + } + + propertyNames.Add(propertyName); + } + + /// + /// Spreads the properties of the given object expression into this JSObject. + /// + /// The object expression to spread in this object. + public void Spread(string objectExpression) + { + SetCurrentState(State.Property); + builder.Text("..."); + builder.Text(objectExpression); + } + + /// + /// Add a property to this JSObject with the provided name and null value. + /// + /// The name of the new property. + public void NullProperty(string propertyName) + { + Property(propertyName, (JSValue tsValue) => tsValue.Null()); + } + + /// + /// Add a property to this JSObject with the provided name and boolean value. + /// + /// The name of the new property. + /// The boolean value of the new property. + public void BooleanProperty(string propertyName, bool propertyValue) + { + Property(propertyName, (JSValue tsValue) => tsValue.Boolean(propertyValue)); + } + + /// + /// Add a property to this JSObject with the provided name and text value. The text value will not be quoted. + /// + /// The name of the new property. + /// The text value of the new property. This value will not be quoted. + public void TextProperty(string propertyName, string propertyValue) + { + if (!PropertyNameNeedsToBeQuoted(propertyName) && propertyName == propertyValue) + { + SetCurrentState(State.Property); + builder.Text(propertyName); + propertyNames.Add(propertyName); + } + else + { + Property(propertyName, (JSValue tsValue) => tsValue.Text(propertyValue)); + } + } + + /// + /// Add a property to this JSObject with the provided name and text value. The text value will be quoted. + /// + /// The name of the new property. + /// The text value of the new property. This value will be quoted. + public void QuotedStringProperty(string propertyName, string propertyValue) + { + Property(propertyName, (JSValue tsValue) => tsValue.QuotedString(propertyValue)); + } + + /// + /// Add a property to this JSObject with the provided name and quoted string[] values. + /// + /// The name of the new property. + /// The quoted string[] values of the new property. + public void QuotedStringArrayProperty(string propertyName, string[] propertyValue) + { + ArrayProperty(propertyName, (JSArray tsArray) => + { + foreach (string propertyValueElement in propertyValue) + { + tsArray.QuotedString(propertyValueElement); + } + }); + } + + /// + /// Add a property to this JSObject with the provided name. The provided action will be invoked to populate the array value of this property. + /// + /// The name of the new property. + /// The action to invoke to add the property's array value. + public void ArrayProperty(string propertyName, Action propertyValueAction) + { + Property(propertyName, (JSValue tsValue) => tsValue.Array(propertyValueAction)); + } + + /// + /// Add a property to this JSObject with the provided name. The provided action will be invoked to populate the object value of this property. + /// + /// The name of the new property. + /// The action to invoke to add the property's object value. + public void ObjectProperty(string propertyName, Action propertyValueAction) + { + Property(propertyName, (JSValue tsValue) => tsValue.Object(propertyValueAction)); + } + } +} diff --git a/src/DSL/JSParameter.cs b/src/DSL/JSParameter.cs new file mode 100644 index 000000000000..16e3cb705db6 --- /dev/null +++ b/src/DSL/JSParameter.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +namespace AutoRest.TypeScript.DSL +{ + /// + /// A data class that contains the details needed to describe a TypeScript parameter. + /// + public class JSParameter + { + /// + /// Create a new JSParameter object. + /// + /// The name of the parameter. + /// The type of the parameter. + /// The description of the parameter. + /// Whether or not the parameter is required. + public JSParameter(string name, string type, string description, bool required = true) + { + Name = name; + Type = type; + Description = description; + Required = required; + } + + /// + /// The name of the parameter. + /// + public string Name { get; } + + /// + /// The type of the parameter. + /// + public string Type { get; } + + /// + /// The description of the parameter. + /// + public string Description { get; } + + /// + /// Whether or not the parameter is required. + /// + public bool Required { get; } + } +} diff --git a/src/DSL/JSParameterList.cs b/src/DSL/JSParameterList.cs new file mode 100644 index 000000000000..daf1f54fccac --- /dev/null +++ b/src/DSL/JSParameterList.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System.Collections.Generic; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// The TypeScript DSL representation for adding parameters to a function or method signature. + /// + public class JSParameterList + { + private readonly JSBuilder builder; + private bool hasParameters; + + /// + /// Create a new parameter list builder. + /// + /// The JSBuilder that will be used to collect the generated text. + public JSParameterList(JSBuilder builder) + { + this.builder = builder; + } + + /// + /// Add a parameter to this parameter list. + /// + /// The parameter to add. + public void Parameter(JSParameter parameter) + { + Parameter(parameter.Name, parameter.Type, !parameter.Required); + } + + /// + /// Add a new parameter to this parameter list. + /// + /// The name of the parameter. + /// The type of the parameter. + /// Whether or not the parameter is optional. + public void Parameter(string parameterName, string parameterType, bool optional = false) + { + if (hasParameters) + { + builder.Text(", "); + } + else + { + hasParameters = true; + } + + builder.Text(parameterName); + if (optional) + { + builder.Text("?"); + } + builder.Text($": {parameterType}"); + } + + /// + /// Add the provided parameters to this parameter list. + /// + /// The parameters to add to this parameter list. + public void Parameters(IEnumerable parameters) + { + if (parameters != null) + { + foreach (JSParameter parameter in parameters) + { + Parameter(parameter); + } + } + } + } +} diff --git a/src/DSL/JSTryBlock.cs b/src/DSL/JSTryBlock.cs new file mode 100644 index 000000000000..1bf407af49e9 --- /dev/null +++ b/src/DSL/JSTryBlock.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace AutoRest.TypeScript.DSL +{ + public class JSTryBlock : JSBlock + { + public JSTryBlock(JSBuilder builder) + : base(builder) + { + } + + public void Catch(string errorName, Action catchAction) + { + builder.Catch(errorName, catchAction); + } + } +} diff --git a/src/DSL/JSValue.cs b/src/DSL/JSValue.cs new file mode 100644 index 000000000000..62afa2da968e --- /dev/null +++ b/src/DSL/JSValue.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// A TypeScript DSL representation for a value. This could be a property value, an argument in + /// an argument list, or an element in an array. + /// + public class JSValue + { + private readonly JSBuilder builder; + + protected BuilderPosition CreatePosition() + { + return builder.CreatePosition(); + } + + protected bool HasChangedLinesSince(int index) + { + return builder.HasChangedLinesSince(index); + } + + protected void Insert(int index, string text) + { + builder.Insert(index, text); + } + + protected void InsertNewLine(int index) + { + builder.InsertNewLine(index); + } + + protected void AddIndentToLinesAfter(int index) + { + builder.AddIndentToLinesAfter(index); + } + + /// + /// Create a new TSValue that will emit to the provided JSBuilder. + /// + /// The JSBuilder this TSValue will emit to. + public JSValue(JSBuilder builder) + { + this.builder = builder; + } + + /// + /// Add a quoted-string to this TSValue. + /// + /// The text to quote and add. + public virtual void QuotedString(string text) + { + builder.QuotedString(text); + } + + /// + /// Add a function call to this TSValue. + /// + /// The name of the function. + /// An action that will be invoked to produce the arguments of the function call. + public virtual void FunctionCall(string functionName, Action argumentListAction) + { + builder.FunctionCall(functionName, argumentListAction); + } + + /// + /// Add text to this TSValue. + /// + /// The text to add to this TSValue. + public virtual void Text(string text) + { + builder.Text(text); + } + + /// + /// Add a JSON array to this TSValue. + /// + /// The action that will be invoked to produce the elements of the JSON array. + public virtual void Array(Action arrayAction = null) + { + builder.Array(arrayAction); + } + + /// + /// Add a JSON object to this TSValue. + /// + /// The action that will be invoked to produce the properties of the JSON object. + public virtual void Object(Action objectAction = null) + { + builder.Object(objectAction); + } + + /// + /// Add a boolean value to this TSValue. + /// + /// The boolean value to add to this TSValue. + public virtual void Boolean(bool value) + { + builder.Boolean(value); + } + + /// + /// Add a null value to this TSValue. + /// + public virtual void Null() + { + builder.Null(); + } + + /// + /// Add an undefined value to this TSValue. + /// + public virtual void Undefined() + { + builder.Undefined(); + } + + public virtual void Lambda(string paramName, Action lambdaBodyAction) + { + builder.Lambda(paramName, lambdaBodyAction); + } + } +} diff --git a/src/DSL/MarkdownBuilder.cs b/src/DSL/MarkdownBuilder.cs new file mode 100644 index 000000000000..c6e0070d3a60 --- /dev/null +++ b/src/DSL/MarkdownBuilder.cs @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AutoRest.TypeScript.DSL +{ + /// + /// A builder class that can be used to generate Markdown text. + /// + public class MarkdownBuilder : IBuilder + { + private readonly IBuilder builder; + private int currentHeaderLevel; + + public MarkdownBuilder() + : this(new Builder()) + { + } + + public MarkdownBuilder(IBuilder builder) + { + this.builder = builder; + this.currentHeaderLevel = 0; + } + + /// + /// Whether or not a newline will be written before the next text. + /// + public bool WriteNewLineBeforeNextText { get => builder.WriteNewLineBeforeNextText; set => builder.WriteNewLineBeforeNextText = value; } + + /// + /// The word wrap width. A null wordWrapWidth indicates that no word wrapping should take place. + /// + public int? WordWrapWidth { get => builder.WordWrapWidth; set => builder.WordWrapWidth = value; } + + /// + /// Create a position object that will track a certain position within the TSBuilder's content. + /// + /// + public BuilderPosition CreatePosition() + { + return builder.CreatePosition(); + } + + /// + /// Get whether or not a new line character has been added to this TSBuilder since the provided character index. + /// + /// The character index to begin the search at. + /// + public bool HasChangedLinesSince(int index) + { + return builder.HasChangedLinesSince(index); + } + + public void Insert(int index, string text) + { + builder.Insert(index, text); + } + + public void InsertNewLine(int index) + { + builder.InsertNewLine(index); + } + + public void AddIndentToLinesAfter(int index) + { + builder.AddIndentToLinesAfter(index); + } + + /// + /// Add the provided value to end of the line prefix. + /// + /// The value to add to the line prefix. + public void AddToPrefix(string toAdd) + { + builder.AddToPrefix(toAdd); + } + + /// + /// Remove the provided value from the end of the line prefix. + /// + /// The value to remove from the end of the line prefix. + public void RemoveFromPrefix(string toRemove) + { + builder.RemoveFromPrefix(toRemove); + } + + /// + /// Invoke the provided action with the provided additional prefix. + /// + /// The additional text to add to the line prefix for the duration of the provided action. + /// The action to invoke with the provided additional line prefix text. + public void WithAddedPrefix(string toAdd, Action action) + { + builder.WithAddedPrefix(toAdd, action); + } + + /// + /// Add a single indentation for the context of the provided action. + /// + /// The action to invoke with an extra level of indentation. + public void Indent(Action action) + { + builder.Indent(action); + } + + /// + /// Add a new level of indentation to the line prefix. + /// + public void IncreaseIndent() + { + builder.IncreaseIndent(); + } + + /// + /// Remove a level of indentation from the line prefix. + /// + public void DecreaseIndent() + { + builder.DecreaseIndent(); + } + + /// + /// Add the provided text to this TSBuilder. + /// + /// The text to add. + public void Text(string text, params object[] formattedArguments) + { + builder.Text(text, formattedArguments); + } + + /// + /// Add the provided line of the text to this TSBuilder. + /// + /// The line of text to add to this TSBuilder. + /// Any optional formatted arguments that will be inserted into the text if provided. + public void Line(string text = "", params object[] formattedArguments) + { + builder.Line(text, formattedArguments); + } + + public override string ToString() + { + return builder.ToString(); + } + + /// + /// Create a new header. + /// + /// The level of the header (the number of # sounds). + /// + public void Header(int level, string text) + { + if (level >= 1 && !string.IsNullOrEmpty(text)) + { + Line($"{Repeat("#", level)} {text}"); + } + } + + /// + /// Add a bullet point list. + /// + /// The items that will be added. + public void List(params string[] items) + { + List((IEnumerable)items); + } + + /// + /// Add a bullet point list. + /// + /// The items that will be added. + public void List(IEnumerable items) + { + if (items != null) + { + foreach (string item in items) + { + Line($"- {item}"); + } + } + } + + public void IncreaseCurrentHeaderLevel() + { + ++currentHeaderLevel; + } + + public void DecreaseCurrentHeaderLevel() + { + --currentHeaderLevel; + } + + /// + /// Create a new section with the provided section header. + /// + /// The text that will be put in the section's header. + /// The code that populates the section. + public void Section(string headerText, Action action = null) + { + IncreaseCurrentHeaderLevel(); + Header(currentHeaderLevel, headerText); + try + { + if (action != null) + { + Line(); + action.Invoke(); + } + } + finally + { + DecreaseCurrentHeaderLevel(); + } + } + + /// + /// Add a console section. + /// + /// The commands that will be output as individual lines within the console section. + public void Console(params string[] commands) + { + Console((IEnumerable)commands); + } + + /// + /// Add a console section. + /// + /// The commands that will be output as individual lines within the console section. + public void Console(IEnumerable commands) + { + Block(commands); + } + + public void HTML(params string[] lines) + { + HTML((IEnumerable)lines); + } + + public void HTML(IEnumerable lines) + { + Block("html", lines); + } + + public void HTML(Action action) + { + Line($"```html"); + action.Invoke(new HTMLBuilder(this)); + Line("```"); + } + + /// + /// Create a new TypeScript code block in the Markdown text. + /// + /// The action that will generate the TypeScript code. + public void TypeScript(Action action) + { + Line($"```ts"); + action.Invoke(new TSBuilder(this)); + Line("```"); + } + + private void Block(IEnumerable lines) + { + Block("", lines); + } + + private void Block(string blockLanguage, IEnumerable lines) + { + if (lines != null && lines.Any((string line) => !string.IsNullOrEmpty(line))) + { + if (blockLanguage == null) + { + blockLanguage = ""; + } + + Line($"```{blockLanguage}"); + foreach (string line in lines) + { + Line(line); + } + Line("```"); + } + } + + private static string Repeat(string text, int count) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < count; ++i) + { + builder.Append(text); + } + return builder.ToString(); + } + } +} diff --git a/src/DSL/TSArgumentList.cs b/src/DSL/TSArgumentList.cs index 8fb0fc998aa1..665fb41e20d6 100644 --- a/src/DSL/TSArgumentList.cs +++ b/src/DSL/TSArgumentList.cs @@ -13,7 +13,7 @@ namespace AutoRest.TypeScript.DSL /// public class TSArgumentList : TSValue, IDisposable { - private List argumentPositions; + private List argumentPositions; /// /// Create a new TSArgumentList that will emit text to the provided TSBuilder. @@ -22,7 +22,7 @@ public class TSArgumentList : TSValue, IDisposable public TSArgumentList(TSBuilder builder) : base(builder) { - argumentPositions = new List(); + argumentPositions = new List(); } public void Dispose() @@ -32,7 +32,7 @@ public void Dispose() int firstArgumentPositionIndex = argumentPositions.First().GetIndexInBuilder(); if (HasChangedLinesSince(firstArgumentPositionIndex)) { - foreach (TSPosition argumentPosition in argumentPositions) + foreach (BuilderPosition argumentPosition in argumentPositions) { InsertNewLine(argumentPosition.GetIndexInBuilder()); } @@ -40,7 +40,7 @@ public void Dispose() } else { - foreach (TSPosition argumentPosition in argumentPositions.Skip(1)) + foreach (BuilderPosition argumentPosition in argumentPositions.Skip(1)) { Insert(argumentPosition.GetIndexInBuilder(), " "); } diff --git a/src/DSL/TSBlock.cs b/src/DSL/TSBlock.cs index 62fd2f8c0bef..aa5a12bac0be 100644 --- a/src/DSL/TSBlock.cs +++ b/src/DSL/TSBlock.cs @@ -87,7 +87,7 @@ public void ConstObjectVariable(string variableName, string variableType, Action public void ConstObjectVariable(string variableName, Action valueAction) { SetCurrentState(State.Statement); - builder.ConstObjectVariable(variableName, valueAction); + builder.ConstObjectVariable(variableName, null, valueAction); } public void ConstObjectVariable(string variableName, string value) diff --git a/src/DSL/TSBuilder.cs b/src/DSL/TSBuilder.cs index a9e6b51df5a0..7d8ec170b0f3 100644 --- a/src/DSL/TSBuilder.cs +++ b/src/DSL/TSBuilder.cs @@ -2,313 +2,25 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // -using AutoRest.Core.Utilities; using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace AutoRest.TypeScript.DSL { /// /// A StringBuilder that has helper methods for building TypeScript code. /// - public class TSBuilder + public class TSBuilder : JSBuilder { - private const string newLine = "\n"; - private const string singleIndent = " "; - private const int defaultCommentWordWrapWidth = 100; - - private readonly int commentWordWrapWidth; - private readonly StringBuilder contents = new StringBuilder(); - private readonly StringBuilder linePrefix = new StringBuilder(); - private readonly List positions = new List(); - public TSBuilder(int commentWordWrapWidth = defaultCommentWordWrapWidth) + : base(commentWordWrapWidth) { - this.commentWordWrapWidth = commentWordWrapWidth; - } - - internal bool WriteNewLineBeforeNextText { get; private set; } - - /// - /// The word wrap width. A null wordWrapWidth indicates that no word wrapping should take place. - /// - public int? WordWrapWidth { get; set; } - - /// - /// Create a position object that will track a certain position within the TSBuilder's content. - /// - /// - public TSPosition CreatePosition() - { - TSPosition position; - int contentLength = contents.Length; - if (!positions.Any()) - { - position = new TSPosition(null, contentLength); - positions.Add(position); - } - else - { - position = positions.Last(); - int lastPositionIndexInBuilder = position.GetIndexInBuilder(); - if (lastPositionIndexInBuilder != contentLength) - { - position = new TSPosition(position, contentLength - lastPositionIndexInBuilder); - positions.Add(position); - } - } - return position; - } - - /// - /// Get whether or not a new line character has been added to this TSBuilder since the provided character index. - /// - /// The character index to begin the search at. - /// - public bool HasChangedLinesSince(int index) - { - bool result = false; - for (int i = index; i < contents.Length; ++i) - { - if (contents[i] == '\n') - { - result = true; - break; - } - } - return result; - } - - public void Insert(int index, string text) - { - if (positions.Any()) - { - int positionIndex = 0; - foreach (TSPosition position in positions) - { - if (index <= positionIndex + position.CharactersAfterPreviousPosition) - { - position.CharactersAfterPreviousPosition += text.Length; - break; - } - else - { - positionIndex += position.CharactersAfterPreviousPosition; - } - } - } - contents.Insert(index, text); - } - - public void InsertNewLine(int index) - { - Insert(index, newLine + linePrefix); - } - - public void AddIndentToLinesAfter(int index) - { - for (int i = index; i < contents.Length; ++i) - { - if (contents[i] == '\n' && i + 1 < contents.Length && contents[i + 1] != '\n') - { - contents.Insert(i + 1, singleIndent); - } - } - } - - /// - /// Get the text that has been added to this TSBuilder. - /// - /// The text that has been added to this TSBuilder. - public override string ToString() - { - return contents.ToString(); - } - - /// - /// Add the provided value to end of the line prefix. - /// - /// The value to add to the line prefix. - public void AddToPrefix(string toAdd) - { - linePrefix.Append(toAdd); - } - - /// - /// Remove the provided value from the end of the line prefix. - /// - /// The value to remove from the end of the line prefix. - public void RemoveFromPrefix(string toRemove) - { - int toRemoveLength = toRemove.Length; - if (linePrefix.Length <= toRemoveLength) - { - linePrefix.Clear(); - } - else - { - linePrefix.Remove(linePrefix.Length - toRemoveLength, toRemoveLength); - } - } - - /// - /// Invoke the provided action with the provided additional prefix. - /// - /// The additional text to add to the line prefix for the duration of the provided action. - /// The action to invoke with the provided additional line prefix text. - public void WithAddedPrefix(string toAdd, Action action) - { - AddToPrefix(toAdd); - try - { - action.Invoke(); - } - finally - { - RemoveFromPrefix(toAdd); - } - } - - /// - /// Add a single indentation for the context of the provided action. - /// - /// The action to invoke with an extra level of indentation. - public void Indent(Action action) - { - IncreaseIndent(); - try - { - action.Invoke(); - } - finally - { - DecreaseIndent(); - } - } - - /// - /// Add a new level of indentation to the line prefix. - /// - public void IncreaseIndent() - => AddToPrefix(singleIndent); - - /// - /// Remove a level of indentation from the line prefix. - /// - public void DecreaseIndent() - => RemoveFromPrefix(singleIndent); - - /// - /// Wrap the provided line using the existing wordWrapWidth. - /// - /// The line to wrap. - /// Whether or not to add the line prefix to the wrapped lines. - /// The wrapped lines. - internal IEnumerable WordWrap(string line, bool addPrefix) - { - List lines = new List(); - - if (!string.IsNullOrEmpty(line)) - { - if (WordWrapWidth == null) - { - lines.Add(line); - } - else - { - // Subtract an extra column from the word wrap width because columns generally are - // 1 -based instead of 0-based. - int wordWrapIndexMinusLinePrefixLength = WordWrapWidth.Value - (addPrefix ? linePrefix.Length : 0) - 1; - - IEnumerable wrappedLines = line.WordWrap(wordWrapIndexMinusLinePrefixLength); - foreach (string wrappedLine in wrappedLines.SkipLast(1)) - { - lines.Add(wrappedLine + newLine); - } - - string lastWrappedLine = wrappedLines.Last(); - if (!string.IsNullOrEmpty(lastWrappedLine)) - { - lines.Add(lastWrappedLine); - } - } - } - return lines; - } - - /// - /// Add the provided text to this TSBuilder. - /// - /// The text to add. - public void Text(string text, params object[] formattedArguments) - { - if (!string.IsNullOrEmpty(text) && formattedArguments != null && formattedArguments.Length > 0) - { - text = string.Format(text, formattedArguments); - } - - bool addPrefix = WriteNewLineBeforeNextText; - - List lines = new List(); - - if (WriteNewLineBeforeNextText) - { - WriteNewLineBeforeNextText = false; - contents.Append(newLine); - } - - if (string.IsNullOrEmpty(text)) - { - lines.Add(""); - } - else - { - int lineStartIndex = 0; - int textLength = text.Length; - while (lineStartIndex < textLength) - { - int newLineCharacterIndex = text.IndexOf(newLine, lineStartIndex); - if (newLineCharacterIndex == -1) - { - string line = text.Substring(lineStartIndex); - IEnumerable wrappedLines = WordWrap(line, addPrefix); - lines.AddRange(wrappedLines); - lineStartIndex = textLength; - } - else - { - int nextLineStartIndex = newLineCharacterIndex + 1; - string line = text.Substring(lineStartIndex, nextLineStartIndex - lineStartIndex); - IEnumerable wrappedLines = WordWrap(line, addPrefix); - lines.AddRange(wrappedLines); - lineStartIndex = nextLineStartIndex; - } - } - } - - string prefix = addPrefix ? linePrefix.ToString() : null; - foreach (string line in lines) - { - if (addPrefix && !string.IsNullOrWhiteSpace(prefix) || (!string.IsNullOrEmpty(prefix) && !string.IsNullOrWhiteSpace(line))) - { - contents.Append(prefix); - } - - contents.Append(line); - } } - /// - /// Add the provided line of the text to this TSBuilder. - /// - /// The line of text to add to this TSBuilder. - /// Any optional formatted arguments that will be inserted into the text if provided. - public void Line(string text = "", params object[] formattedArguments) + public TSBuilder(IBuilder builder, int commentWordWrapWidth = defaultCommentWordWrapWidth) + : base(builder, commentWordWrapWidth) { - Text(text, formattedArguments); - WriteNewLineBeforeNextText = true; } public void Class(string className, Action classAction) @@ -319,53 +31,6 @@ public void Class(string className, Action classAction) }); } - /// - /// Get whether or not the provided lines has any lines that are not null and not whitespace. - /// - /// The lines to check. - /// Whether or not the provided lines has any lines that are not null and not whitespace. - public bool AnyCommentLines(IEnumerable lines) - { - return lines != null && lines.Any((string line) => !string.IsNullOrWhiteSpace(line)); - } - - private void Comment(string commentHeader, IEnumerable lines) - { - if (AnyCommentLines(lines)) - { - Line(commentHeader); - WithAddedPrefix(" * ", () => - { - int? previousWordWrapWidth = WordWrapWidth; - WordWrapWidth = commentWordWrapWidth; - try - { - foreach (string line in lines) - { - if (line != null) - { - Line(line); - } - } - } - finally - { - WordWrapWidth = previousWordWrapWidth; - } - }); - Line(" */"); - } - } - - /// - /// Add a /* */ comment to this TSBuilder. If no non-null and non-empty lines are provided, then nothing will be added. - /// - /// The lines to add. Null lines will be ignored. - public void Comment(params string[] lines) - { - Comment("/*", lines); - } - public void DocumentationComment(Action commentAction) { if (commentAction != null) @@ -377,33 +42,6 @@ public void DocumentationComment(Action commentAction) } } - /// - /// Add a // comment to this TSBuilder. - /// - /// - public void LineComment(string line) - { - Line($"// {line}"); - } - - /// - /// Add a /** */ comment to this TSBuilder. If no non-null and non-empty lines are provided, then nothing will be added. - /// - /// The lines to add. Null lines will be ignored. - public void DocumentationComment(params string[] lines) - { - Comment("/**", lines); - } - - /// - /// Add a /** */ comment to this TSBuilder. If no non-null and non-empty lines are provided, then nothing will be added. - /// - /// The lines to add. Null lines will be ignored. - public void DocumentationComment(IEnumerable lines) - { - Comment("/**", lines); - } - /// /// Add a JSON array to this TSBuilder that uses the provided action to add the array's elements. /// @@ -438,40 +76,6 @@ public void Object(Action action = null) Text($"}}"); } - /// - /// Surround the provided text with double-quotes and add it to this TSBuilder. - /// - /// The text to double-quote and add to this TSBuilder. - public void QuotedString(string text) - { - Text($"\"{text}\""); - } - - /// - /// Add the provided boolean value to this TSBuilder. - /// - /// - public void Boolean(bool value) - { - Text(value ? "true" : "false"); - } - - /// - /// Add a null value to this TSBuilder. - /// - public void Null() - { - Text("null"); - } - - /// - /// Add an undefined value to this TSBuilder. - /// - public void Undefined() - { - Text("undefined"); - } - public void Lambda(string paramName, Action lambdaBodyAction) { Line($"{paramName} => {{"); @@ -545,7 +149,12 @@ public void Method(string methodName, string returnType, string parameterList, A Block($"{methodName}({parameterList}): {returnType}", methodBodyAction); } - private void Block(string beforeBlock, Action blockAction) + public void Block(string beforeBlock, Action blockAction) + { + Block(beforeBlock, true, blockAction); + } + + public void Block(string beforeBlock, bool newLineAfterBlock, Action blockAction) { Line($"{beforeBlock} {{"); Indent(() => @@ -557,7 +166,7 @@ private void Block(string beforeBlock, Action blockAction) }); WriteNewLineBeforeNextText = true; Text($"}}"); - WriteNewLineBeforeNextText = true; + WriteNewLineBeforeNextText = newLineAfterBlock; } public TSIfBlock If(string condition, Action bodyAction) @@ -591,11 +200,6 @@ public void Catch(string errorName, Action catchAction) Block($" catch ({errorName})", catchAction); } - public void Return(string result) - { - Return(value => value.Text(result)); - } - public void Return(Action returnValueAction) { Text("return "); @@ -603,21 +207,6 @@ public void Return(Action returnValueAction) Line(";"); } - public void Throw(string valueToThrow) - { - Line($"throw {valueToThrow};"); - } - - public void ConstQuotedStringVariable(string variableName, string text) - { - ConstVariable(variableName, $"\"{text}\""); - } - - public void ConstVariable(string variableName, string variableValue) - { - ConstVariable(variableName, null, variableValue); - } - public void ConstVariable(string variableName, string variableType, string variableValue) { Text($"const {variableName}"); @@ -652,16 +241,6 @@ public void ConstObjectVariable(string variableName, string variableType, string Line($" = {value};"); } - public void ConstObjectVariable(string variableName, Action valueAction) - { - ConstObjectVariable(variableName, null, valueAction); - } - - public void ConstObjectVariable(string variableName, string value) - { - ConstObjectVariable(variableName, null, value); - } - public void Property(string name, string type, bool required = true, string accessModifier = "") { string modifier = String.IsNullOrEmpty(accessModifier) ? "" : $"{accessModifier} "; @@ -669,16 +248,6 @@ public void Property(string name, string type, bool required = true, string acce Line($"{modifier}{name}{optionalSuffix}: {type};"); } - public void ImportAllAs(string importAs, string importSource) - { - Line($"import * as {importAs} from \"{importSource}\";"); - } - - public void Import(IEnumerable importedTypeNames, string importSource) - { - Line($"import {{ {string.Join(", ", importedTypeNames)} }} from \"{importSource}\";"); - } - public void Export(Action exportAction) { Line($"export {{"); diff --git a/src/DSL/TSValue.cs b/src/DSL/TSValue.cs index 35bb90eb8b4d..9dde39336b58 100644 --- a/src/DSL/TSValue.cs +++ b/src/DSL/TSValue.cs @@ -14,7 +14,7 @@ public class TSValue { private readonly TSBuilder builder; - protected TSPosition CreatePosition() + protected BuilderPosition CreatePosition() { return builder.CreatePosition(); } diff --git a/src/TemplateFactory.cs b/src/TemplateFactory.cs index 9a2238cce4b6..5f12615eab05 100644 --- a/src/TemplateFactory.cs +++ b/src/TemplateFactory.cs @@ -14,7 +14,5 @@ public abstract class TemplateFactory where TCodeModel : CodeModelTS public abstract Template CreateServiceClientContextTemplate(TCodeModel codeModel); public abstract Template CreateServiceClientTemplate(TCodeModel codeModel); - - public abstract Template CreateReadmeTemplate(TCodeModel codeModel); } } diff --git a/src/azure/AzureTemplateFactory.cs b/src/azure/AzureTemplateFactory.cs index b8408b19dc72..0457b15193ab 100644 --- a/src/azure/AzureTemplateFactory.cs +++ b/src/azure/AzureTemplateFactory.cs @@ -24,10 +24,5 @@ public override Template CreateModelsIndexTemplate(CodeModelTSa co { return new AzureModelIndexTemplate { Model = codeModel }; } - - public override Template CreateReadmeTemplate(CodeModelTSa codeModel) - { - return new AzureReadmeTemplate { Model = codeModel }; - } } } diff --git a/src/azure/Templates/AzureReadmeTemplate.cshtml b/src/azure/Templates/AzureReadmeTemplate.cshtml deleted file mode 100644 index 138a916122d2..000000000000 --- a/src/azure/Templates/AzureReadmeTemplate.cshtml +++ /dev/null @@ -1,84 +0,0 @@ -@using AutoRest.TypeScript.Azure.Model -@inherits AutoRest.Core.Template - -# Azure @(Model.Name) SDK for JavaScript -@if (Model.Settings.MultiapiLatest) -{ -@:This package provides the **latest API version (@(Model.Settings.ApiVersions[0]))** of @(Model.Name). -} -else if (Model.Settings.Multiapi) -{ -@:This package contains **API version @(Model.Settings.ApiVersion)** of @(Model.Name). -@EmptyLine - -@:For other API versions, see https://npmjs.com/@(Model.Settings.PackageName). -} -else -{ -@:This package contains an isomorphic SDK for @(Model.Name). -} -@EmptyLine - -## Currently supported environments -- Node.js version 6.x.x or higher -- Browser JavaScript -@EmptyLine - -## How to Install -``` -npm install @(Model.PackageName) -``` -@EmptyLine -@if (Model.Settings.MultiapiLatest) -{ -@:## Available API versions -@:| API version | NPM package | Latest | -@:| - | - | - | -bool first = true; -@foreach (string apiVersion in Model.Settings.ApiVersions) -{ -@:| @(apiVersion) | https://npmjs.com/@(Model.Settings.PackageName)-@(apiVersion) | @(first ? "✔️" : "") | -first = false; -} -} -@EmptyLine - -## How to use -@EmptyLine - -### nodejs - Authentication, client creation and @(Model.GetSampleMethod()?.Name) @(Model.GetSampleMethodGroupName()) as an example written in TypeScript. -@EmptyLine - -```ts -@(Model.GenerateReadmeMdNodeSampleCode(EmptyLine)) -``` -@EmptyLine - -### browser - Authentication, client creation and @(Model.GetSampleMethod().Name) @(Model.GetSampleMethodGroupName()) as an example written in JavaScript. -See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. -@EmptyLine - -- index.html -```html -@{ -@: -@: -@: -@: @(Model.PackageName) sample -@: -@: -@: -@: -@: -@: -@: -@: -@: -} -``` -@EmptyLine - -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/src/vanilla/CodeGeneratorTS.cs b/src/vanilla/CodeGeneratorTS.cs index 66827504240b..2bcb866153c3 100644 --- a/src/vanilla/CodeGeneratorTS.cs +++ b/src/vanilla/CodeGeneratorTS.cs @@ -92,7 +92,7 @@ protected async Task Generate(TemplateFactory templateFa if (ShouldWriteReadmeMdFile(codeModel)) { - await WriteReadmeMdFile(templateFactory.CreateReadmeTemplate(codeModel)); + await WriteReadmeMdFile(codeModel); } if (ShouldWriteLicenseFile(codeModel)) @@ -220,9 +220,9 @@ protected Task WritePackageJsonFile(CodeModelTS codeModel) return Write(new PackageJson { Model = codeModel }, "package.json"); } - protected Task WriteReadmeMdFile(Template readmeTemplate) where T : CodeModelTS + protected Task WriteReadmeMdFile(CodeModelTS codeModel) { - return Write(readmeTemplate, "README.md"); + return Write(codeModel.GenerateReadmeMd(), "README.md"); } protected Task WriteLicenseFile(CodeModelTS codeModel) diff --git a/src/vanilla/Model/CodeModelTS.cs b/src/vanilla/Model/CodeModelTS.cs index 4f57fb061557..f1b97ac691af 100644 --- a/src/vanilla/Model/CodeModelTS.cs +++ b/src/vanilla/Model/CodeModelTS.cs @@ -468,63 +468,43 @@ public virtual string GetSampleMethodGroupName() return GetSampleMethod()?.MethodGroup?.Name?.ToCamelCase(); } - public virtual string GenerateSampleMethod(bool isBrowser = false) + public void GenerateSampleMethod(JSBuilder builder, bool isBrowser = false) { - var method = GetSampleMethod(); - var methodGroup = GetSampleMethodGroupName(); - var requiredParameters = method.LogicalParameters.Where( + Method method = GetSampleMethod(); + string methodGroup = GetSampleMethodGroupName(); + List requiredParameters = method.LogicalParameters.Where( p => p != null && !p.IsClientProperty && !string.IsNullOrWhiteSpace(p.Name) && !p.IsConstant).OrderBy(item => !item.IsRequired).ToList(); - var builder = new IndentedStringBuilder(" "); - var paramInit = InitializeParametersForSampleMethod(requiredParameters, isBrowser); - builder.AppendLine(paramInit); - var declaration = new StringBuilder(); - bool first = true; - foreach (var param in requiredParameters) - { - if (!first) - declaration.Append(", "); - declaration.Append(param.Name); - first = false; - } - var clientRef = "client."; - if (!string.IsNullOrEmpty(methodGroup)) - { - clientRef = $"client.{methodGroup}."; - } - var methodRef = $"{clientRef}{method.Name.ToCamelCase()}({declaration.ToString()}).then((result) => {{"; - builder.AppendLine(methodRef) - .Indent() - .AppendLine("console.log(\"The result is:\");") - .AppendLine("console.log(result);") - .Outdent(); - if (isBrowser) - { - builder.Append("})"); - } - else + + foreach (Parameter param in requiredParameters) { - builder.AppendLine("});"); - } + string parameterName = param.Name; + if (param.ModelType is CompositeType && !isBrowser) + { + parameterName += $": {ClientPrefix}Models.{param.ModelTypeName}"; + } - return builder.ToString(); - } + string parameterValue = param.ModelType.InitializeType(param.Name, isBrowser); - public string InitializeParametersForSampleMethod(List requiredParameters, bool isBrowser = false) - { - var builder = new IndentedStringBuilder(" "); - foreach (var param in requiredParameters) + builder.ConstVariable(parameterName, parameterValue); + } + + builder.FunctionCall($"client.{(string.IsNullOrEmpty(methodGroup) ? "" : $"{methodGroup}.")}{method.Name.ToCamelCase()}", argumentList => { - var paramValue = "\"\""; - paramValue = param.ModelType.InitializeType(param.Name, isBrowser); - var paramDeclaration = $"const {param.Name}"; - if (param.ModelType is CompositeType && !isBrowser) + foreach (Parameter parameter in requiredParameters) { - paramDeclaration += $": {ClientPrefix}Models.{param.ModelTypeName}"; + argumentList.Text(parameter.Name); } - paramDeclaration += $" = {paramValue};"; - builder.AppendLine(paramDeclaration); + }); + builder.Block(".then((result) =>", false, block => + { + block.Line("console.log(\"The result is:\");"); + block.Line("console.log(result);"); + }); + builder.Text(")"); + if (!isBrowser) + { + builder.Line(";"); } - return builder.ToString(); } public string GenerateOperationSpecDefinitions(string emptyLine) @@ -761,19 +741,26 @@ protected virtual void GenerateNodeSampleImports(TSBuilder builder) GenerateNodeSampleClientImport(builder); } - public string GenerateReadmeMdNodeSampleCode(string emptyLine) + public string GenerateReadmeMdNodeSampleCode() { TSBuilder builder = new TSBuilder(); + GenerateReadmeMdNodeSampleCode(builder); + + return builder.ToString(); + } + + public void GenerateReadmeMdNodeSampleCode(TSBuilder builder) + { GenerateNodeSampleImports(builder); builder.ConstVariable("subscriptionId", "process.env[\"AZURE_SUBSCRIPTION_ID\"]"); - builder.Line(emptyLine); + builder.Line(); builder.Line($"msRestNodeAuth.interactiveLogin().then((creds) => {{"); builder.Indent(() => { builder.ConstVariable("client", $"new {Name}(creds, subscriptionId)"); - builder.Line(GenerateSampleMethod(false)); + GenerateSampleMethod(builder, false); }); builder.Line($"}}).catch((err) => {{"); builder.Indent(() => @@ -781,14 +768,20 @@ public string GenerateReadmeMdNodeSampleCode(string emptyLine) builder.Line("console.error(err);"); }); builder.Line($"}});"); + } + + public string GenerateReadmeMdBrowserSampleCode(string indentation = "") + { + JSBuilder builder = new JSBuilder(); + builder.AddToPrefix(indentation); + + GenerateReadmeMdBrowserSampleCode(builder); return builder.ToString(); } - public string GenerateReadmeMdBrowserSampleCode(string emptyLine) + public void GenerateReadmeMdBrowserSampleCode(JSBuilder builder) { - TSBuilder builder = new TSBuilder(); - builder.ConstQuotedStringVariable("subscriptionId", ""); builder.Text("const authManager = new msAuth.AuthManager("); builder.Object(tsObject => @@ -808,7 +801,8 @@ public string GenerateReadmeMdBrowserSampleCode(string emptyLine) }); builder.ConstVariable("client", $"new {BundleVarName}.{Name}(res.creds, subscriptionId)"); - builder.Line($"{GenerateSampleMethod(true)}.catch((err) => {{"); + GenerateSampleMethod(builder, true); + builder.Line($".catch((err) => {{"); builder.Indent(() => { builder.Line("console.log(\"An error occurred:\");"); @@ -817,8 +811,6 @@ public string GenerateReadmeMdBrowserSampleCode(string emptyLine) builder.Line($"}});"); }); builder.Line($"}});"); - - return builder.ToString(); } public string GenerateClassProperties(string emptyLine) @@ -838,5 +830,158 @@ protected virtual bool ShouldGenerateProperty(string propertyName) string camelCaseProperty = propertyName.ToCamelCase(); return !propertiesToIgnore.Contains(camelCaseProperty) && !serviceClientProperties.Contains(camelCaseProperty); } + + public string GenerateReadmeMd() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.IncreaseCurrentHeaderLevel(); + + string mainSectionHeader = IsAzure + ? $"Azure {Name} SDK for JavaScript" + : $"An isomorphic javascript sdk for - {Name}"; + + builder.Section(mainSectionHeader, () => + { + GeneratePackageContains(builder); + builder.Line(); + GenerateCurrentlySupportedEnvironments(builder); + builder.Line(); + GenerateHowToInstall(builder); + builder.Line(); + if (Settings.MultiapiLatest) + { + GenerateAvailableAPIVersions(builder); + builder.Line(); + } + builder.Section("How to use", () => + { + GenerateHowToUseInNodeJs(builder); + builder.Line(); + GenerateHowToUseInBrowser(builder); + }); + }); + builder.Line(); + GenerateRelatedProjects(builder); + + return builder.ToString(); + } + + private void GeneratePackageContains(MarkdownBuilder builder) + { + if (Settings.MultiapiLatest) + { + builder.Line($"This package contains the **latest API version ({Settings.ApiVersions[0]})** of {Name}."); + } + else if (Settings.Multiapi) + { + builder.Line($"This package contains **API version {Settings.ApiVersion}** of {Name}."); + builder.Line(); + builder.Line($"For other API versions, see https://npmjs.com/{Settings.PackageName}."); + } + else + { + builder.Line($"This package contains an isomorphic SDK for {Name}."); + } + } + + private void GenerateCurrentlySupportedEnvironments(MarkdownBuilder builder) + { + builder.Section("Currently supported environments", () => + { + builder.List(new[] + { + "Node.js version 6.x.x or higher", + "Browser JavaScript" + }); + }); + } + + private void GenerateHowToInstall(MarkdownBuilder builder) + { + builder.Section("How to Install", () => + { + builder.Console($"npm install {PackageName}"); + }); + } + + private void GenerateAvailableAPIVersions(MarkdownBuilder builder) + { + builder.Section("Available API versions", () => + { + builder.Line("| API version | NPM package | Latest |"); + builder.Line("| - | - | - |"); + bool first = true; + foreach (string apiVersion in Settings.ApiVersions) + { + builder.Line($"| {apiVersion} | https://npmjs.com/{Settings.PackageName}-{apiVersion} | {(first ? "✔️" : "")} |"); + first = false; + } + }); + } + + private void GenerateHowToUseInNodeJs(MarkdownBuilder builder) + { + builder.Section($"nodejs - Authentication, client creation and {GetSampleMethod()?.Name} {GetSampleMethodGroupName()} as an example written in TypeScript.", () => + { + builder.Section("Install ms-rest-nodeauth", () => + { + builder.Console("npm install ms-rest-nodeauth"); + }); + builder.Line(); + builder.Section("Sample code", () => + { + builder.TypeScript(tsBuilder => GenerateReadmeMdNodeSampleCode(tsBuilder)); + }); + }); + } + + private void GenerateHowToUseInBrowser(MarkdownBuilder builder) + { + builder.Section($"browser - Authentication, client creation and {GetSampleMethod().Name} {GetSampleMethodGroupName()} as an example written in JavaScript.", () => + { + builder.Section("Install ms-rest-browserauth", () => + { + builder.Console("npm install ms-rest-browserauth"); + }); + builder.Line(); + builder.Section("Sample code", () => + { + builder.Line("See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser."); + builder.Line(); + builder.List("index.html"); + builder.HTML(htmlBuilder => + { + htmlBuilder.DOCTYPE(); + htmlBuilder.Html(html => + { + html.Head(head => + { + head.Title($"{PackageName} sample"); + head.Script("node_modules/ms-rest-js/dist/msRest.browser.js"); + if (IsAzure) + { + head.Script("node_modules/ms-rest-azure-js/dist/msRestAzure.js"); + } + head.Script("node_modules/ms-rest-browserauth/dist/msAuth.js"); + head.Script($"node_modules/{PackageName}/dist/{BundleFilename}.js"); + head.Script(jsBuilder => + { + GenerateReadmeMdBrowserSampleCode(jsBuilder); + }); + }); + html.Body(); + }); + }); + }); + }); + } + + private void GenerateRelatedProjects(MarkdownBuilder builder) + { + builder.Section("Related projects", () => + { + builder.List("[Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js)"); + }); + } } } \ No newline at end of file diff --git a/src/vanilla/Templates/ReadmeTemplate.cshtml b/src/vanilla/Templates/ReadmeTemplate.cshtml deleted file mode 100644 index 49377e04a13f..000000000000 --- a/src/vanilla/Templates/ReadmeTemplate.cshtml +++ /dev/null @@ -1,53 +0,0 @@ -@using AutoRest.TypeScript.Model -@inherits AutoRest.Core.Template - -# An isomorphic javascript sdk for - @(Model.Name) -This project provides an isomorphic javascript package. Right now it supports: -- node.js version 6.x.x or higher -- browser javascript -@EmptyLine -## How to Install -@EmptyLine -- nodejs -``` -npm install @(Model.PackageName) -``` -- browser -```html -@{ -@: -} -``` -@EmptyLine -## How to use -@EmptyLine -### nodejs - Authentication, client creation and @(Model.GetSampleMethod()?.Name) @(Model.GetSampleMethodGroupName()) as an example written in TypeScript. -@EmptyLine -```ts -@(Model.GenerateReadmeMdNodeSampleCode(EmptyLine)) -``` -@EmptyLine -### browser - Authentication, client creation and @(Model.GetSampleMethod().Name) @(Model.GetSampleMethodGroupName()) as an example written in javascript. -@EmptyLine -- index.html -```html -@{ -@: -@: -@: -@: @(Model.PackageName) sample -@: -@: -@: -@: -@: -@: -@: -@: -} -``` -@EmptyLine -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/src/vanilla/VanillaTemplateFactory.cs b/src/vanilla/VanillaTemplateFactory.cs index fb7d427117c0..c40e87848b5f 100644 --- a/src/vanilla/VanillaTemplateFactory.cs +++ b/src/vanilla/VanillaTemplateFactory.cs @@ -24,10 +24,5 @@ public override Template CreateModelsIndexTemplate(CodeModelTS code { return new ModelIndexTemplate { Model = codeModel }; } - - public override Template CreateReadmeTemplate(CodeModelTS codeModel) - { - return new ReadmeTemplate { Model = codeModel }; - } } } diff --git a/test/azuremetadata/generated/Lro/README.md b/test/azuremetadata/generated/Lro/README.md index 89a147db0de4..4631c4243ee4 100644 --- a/test/azuremetadata/generated/Lro/README.md +++ b/test/azuremetadata/generated/Lro/README.md @@ -1,19 +1,29 @@ -# Azure AutoRestLongRunningOperationTestService SDK for JavaScript +## Azure AutoRestLongRunningOperationTestService SDK for JavaScript + This package contains an isomorphic SDK for AutoRestLongRunningOperationTestService. -## Currently supported environments +### Currently supported environments + - Node.js version 6.x.x or higher - Browser JavaScript -## How to Install +### How to Install + ``` npm install ``` +### How to use + +#### nodejs - Authentication, client creation and put200Succeeded lROs as an example written in TypeScript. -## How to use +##### Install ms-rest-nodeauth + +``` +npm install ms-rest-nodeauth +``` -### nodejs - Authentication, client creation and put200Succeeded lROs as an example written in TypeScript. +##### Sample code ```ts import * as msRest from "ms-rest-js"; @@ -38,7 +48,16 @@ msRestNodeAuth.interactiveLogin().then((creds) => { }); ``` -### browser - Authentication, client creation and put200Succeeded lROs as an example written in JavaScript. +#### browser - Authentication, client creation and put200Succeeded lROs as an example written in JavaScript. + +##### Install ms-rest-browserauth + +``` +npm install ms-rest-browserauth +``` + +##### Sample code + See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. - index.html @@ -51,7 +70,7 @@ See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to - - - + ``` -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) +## Related projects + +- [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/test/metadata/generated/BodyComplex/README.md b/test/metadata/generated/BodyComplex/README.md index 9d4edd99ef59..8d9ed74c2de5 100644 --- a/test/metadata/generated/BodyComplex/README.md +++ b/test/metadata/generated/BodyComplex/README.md @@ -1,22 +1,29 @@ -# An isomorphic javascript sdk for - AutoRestComplexTestService -This project provides an isomorphic javascript package. Right now it supports: -- node.js version 6.x.x or higher -- browser javascript +## An isomorphic javascript sdk for - AutoRestComplexTestService -## How to Install +This package contains an isomorphic SDK for AutoRestComplexTestService. + +### Currently supported environments + +- Node.js version 6.x.x or higher +- Browser JavaScript + +### How to Install -- nodejs ``` npm install ``` -- browser -```html - -``` -## How to use +### How to use -### nodejs - Authentication, client creation and getValid basic as an example written in TypeScript. +#### nodejs - Authentication, client creation and getValid basic as an example written in TypeScript. + +##### Install ms-rest-nodeauth + +``` +npm install ms-rest-nodeauth +``` + +##### Sample code ```ts import * as msRest from "ms-rest-js"; @@ -35,7 +42,17 @@ msRestNodeAuth.interactiveLogin().then((creds) => { }); ``` -### browser - Authentication, client creation and getValid basic as an example written in javascript. +#### browser - Authentication, client creation and getValid basic as an example written in JavaScript. + +##### Install ms-rest-browserauth + +``` +npm install ms-rest-browserauth +``` + +##### Sample code + +See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. - index.html ```html @@ -68,10 +85,10 @@ msRestNodeAuth.interactiveLogin().then((creds) => { }); - - + ``` -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) +## Related projects + +- [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/test/multiapi/packages/multiapi-test-2017-10-01/README.md b/test/multiapi/packages/multiapi-test-2017-10-01/README.md index d297094c1d18..ea3029136c64 100644 --- a/test/multiapi/packages/multiapi-test-2017-10-01/README.md +++ b/test/multiapi/packages/multiapi-test-2017-10-01/README.md @@ -1,21 +1,31 @@ -# Azure AutoRestParameterizedHostTestClient SDK for JavaScript +## Azure AutoRestParameterizedHostTestClient SDK for JavaScript + This package contains **API version 2017-10-01** of AutoRestParameterizedHostTestClient. For other API versions, see https://npmjs.com/@azure/multiapi-test. -## Currently supported environments +### Currently supported environments + - Node.js version 6.x.x or higher - Browser JavaScript -## How to Install +### How to Install + ``` npm install @azure/multiapi-test-2017-10-01 ``` +### How to use + +#### nodejs - Authentication, client creation and getEmpty paths as an example written in TypeScript. -## How to use +##### Install ms-rest-nodeauth + +``` +npm install ms-rest-nodeauth +``` -### nodejs - Authentication, client creation and getEmpty paths as an example written in TypeScript. +##### Sample code ```ts import * as msRest from "ms-rest-js"; @@ -36,7 +46,16 @@ msRestNodeAuth.interactiveLogin().then((creds) => { }); ``` -### browser - Authentication, client creation and getEmpty paths as an example written in JavaScript. +#### browser - Authentication, client creation and getEmpty paths as an example written in JavaScript. + +##### Install ms-rest-browserauth + +``` +npm install ms-rest-browserauth +``` + +##### Sample code + See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. - index.html @@ -49,7 +68,7 @@ See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to - - - + ``` -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) +## Related projects + +- [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/test/multiapi/packages/multiapi-test-2018-02-01/README.md b/test/multiapi/packages/multiapi-test-2018-02-01/README.md index d4737524976a..c0300aaaf303 100644 --- a/test/multiapi/packages/multiapi-test-2018-02-01/README.md +++ b/test/multiapi/packages/multiapi-test-2018-02-01/README.md @@ -1,21 +1,31 @@ -# Azure AutoRestParameterizedCustomHostTestClient SDK for JavaScript +## Azure AutoRestParameterizedCustomHostTestClient SDK for JavaScript + This package contains **API version 2018-02-01** of AutoRestParameterizedCustomHostTestClient. For other API versions, see https://npmjs.com/@azure/multiapi-test. -## Currently supported environments +### Currently supported environments + - Node.js version 6.x.x or higher - Browser JavaScript -## How to Install +### How to Install + ``` npm install @azure/multiapi-test-2018-02-01 ``` +### How to use + +#### nodejs - Authentication, client creation and getEmpty paths as an example written in TypeScript. -## How to use +##### Install ms-rest-nodeauth + +``` +npm install ms-rest-nodeauth +``` -### nodejs - Authentication, client creation and getEmpty paths as an example written in TypeScript. +##### Sample code ```ts import * as msRest from "ms-rest-js"; @@ -39,7 +49,16 @@ msRestNodeAuth.interactiveLogin().then((creds) => { }); ``` -### browser - Authentication, client creation and getEmpty paths as an example written in JavaScript. +#### browser - Authentication, client creation and getEmpty paths as an example written in JavaScript. + +##### Install ms-rest-browserauth + +``` +npm install ms-rest-browserauth +``` + +##### Sample code + See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. - index.html @@ -52,7 +71,7 @@ See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to - - - + ``` -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) +## Related projects + +- [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/test/multiapi/packages/multiapi-test/README.md b/test/multiapi/packages/multiapi-test/README.md index e1c729b7b151..1f7a550bcc2a 100644 --- a/test/multiapi/packages/multiapi-test/README.md +++ b/test/multiapi/packages/multiapi-test/README.md @@ -1,24 +1,36 @@ -# Azure AutoRestParameterizedCustomHostTestClient SDK for JavaScript -This package provides the **latest API version (2018-02-01)** of AutoRestParameterizedCustomHostTestClient. +## Azure AutoRestParameterizedCustomHostTestClient SDK for JavaScript + +This package contains the **latest API version (2018-02-01)** of AutoRestParameterizedCustomHostTestClient. + +### Currently supported environments -## Currently supported environments - Node.js version 6.x.x or higher - Browser JavaScript -## How to Install +### How to Install + ``` npm install @azure/multiapi-test ``` -## Available API versions +### Available API versions + | API version | NPM package | Latest | | - | - | - | | 2018-02-01 | https://npmjs.com/@azure/multiapi-test-2018-02-01 | ✔️ | | 2017-10-01 | https://npmjs.com/@azure/multiapi-test-2017-10-01 | | -## How to use +### How to use + +#### nodejs - Authentication, client creation and getEmpty paths as an example written in TypeScript. + +##### Install ms-rest-nodeauth + +``` +npm install ms-rest-nodeauth +``` -### nodejs - Authentication, client creation and getEmpty paths as an example written in TypeScript. +##### Sample code ```ts import * as msRest from "ms-rest-js"; @@ -42,7 +54,16 @@ msRestNodeAuth.interactiveLogin().then((creds) => { }); ``` -### browser - Authentication, client creation and getEmpty paths as an example written in JavaScript. +#### browser - Authentication, client creation and getEmpty paths as an example written in JavaScript. + +##### Install ms-rest-browserauth + +``` +npm install ms-rest-browserauth +``` + +##### Sample code + See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. - index.html @@ -55,7 +76,7 @@ See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to - - - + ``` -# Related projects - - [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) +## Related projects + +- [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) diff --git a/unittests/AssertEx.cs b/unittests/AssertEx.cs index fb66a6fbf128..33cf5d4036d5 100644 --- a/unittests/AssertEx.cs +++ b/unittests/AssertEx.cs @@ -22,7 +22,7 @@ public static void EqualLines(string expected, string actual) EqualLines(Lines(expected), Lines(actual)); } - public static void EqualLines(string expected, TSBuilder actual) + public static void EqualLines(string expected, IBuilder actual) { EqualLines(Lines(expected), actual?.ToString()); } @@ -37,7 +37,7 @@ public static void EqualLines(IEnumerable expected, string actual) EqualLines(expected, Lines(actual)); } - public static void EqualLines(IEnumerable expected, TSBuilder actual) + public static void EqualLines(IEnumerable expected, IBuilder actual) { EqualLines(expected, actual?.ToString()); } diff --git a/unittests/DSL/BuilderTests.cs b/unittests/DSL/BuilderTests.cs new file mode 100644 index 000000000000..d05ee518e257 --- /dev/null +++ b/unittests/DSL/BuilderTests.cs @@ -0,0 +1,397 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AutoRest.TypeScript.DSL +{ + [TestClass] + public class BuilderTests + { + [TestMethod] + public void Constructor() + { + Builder builder = new Builder(); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void CreatePositionWithEmptyBuilder() + { + Builder builder = new Builder(); + BuilderPosition p = builder.CreatePosition(); + Assert.AreEqual(0, p.CharactersAfterPreviousPosition); + Assert.AreEqual(0, p.GetIndexInBuilder()); + Assert.AreSame(p, builder.CreatePosition()); + } + + [TestMethod] + public void CreatePositionWithNonEmptyBuilder() + { + Builder builder = new Builder(); + builder.Text("abcd"); + BuilderPosition p = builder.CreatePosition(); + Assert.AreEqual(4, p.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p.GetIndexInBuilder()); + Assert.AreSame(p, builder.CreatePosition()); + } + + [TestMethod] + public void CreatePositionWhenAnotherPositionAlreadyExists() + { + Builder builder = new Builder(); + BuilderPosition p1 = builder.CreatePosition(); + builder.Text("abcd"); + BuilderPosition p2 = builder.CreatePosition(); + Assert.AreEqual(0, p1.CharactersAfterPreviousPosition); + Assert.AreEqual(0, p1.GetIndexInBuilder()); + Assert.AreEqual(4, p2.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p2.GetIndexInBuilder()); + } + + [TestMethod] + public void HasChangedLinesSinceWithNegativeIndexAndEmptyBuilder() + { + Builder builder = new Builder(); + Assert.ThrowsException(() => builder.HasChangedLinesSince(-1)); + } + + [TestMethod] + public void HasChangedLinesSinceWithNegativeIndexAndSingleLine() + { + Builder builder = new Builder(); + builder.Text("abc"); + Assert.ThrowsException(() => builder.HasChangedLinesSince(-1)); + } + + [TestMethod] + public void HasChangedLinesSinceWithNegativeIndexAndMultipleLines() + { + Builder builder = new Builder(); + builder.Text("a\nb\nc"); + Assert.ThrowsException(() => builder.HasChangedLinesSince(-1)); + } + + [TestMethod] + public void HasChangedLinesSinceWithZeroIndexAndEmptyBuilder() + { + Builder builder = new Builder(); + Assert.IsFalse(builder.HasChangedLinesSince(0)); + } + + [TestMethod] + public void HasChangedLinesSinceWithZeroIndexAndSingleLine() + { + Builder builder = new Builder(); + builder.Text("abc"); + Assert.IsFalse(builder.HasChangedLinesSince(0)); + } + + [TestMethod] + public void HasChangedLinesSinceWithZeroIndexAndMultipleLines() + { + Builder builder = new Builder(); + builder.Text("a\nb\nc"); + Assert.IsTrue(builder.HasChangedLinesSince(0)); + } + + [TestMethod] + public void InsertWithNoPositions() + { + Builder builder = new Builder(); + builder.Insert(0, "abcd"); + Assert.AreEqual("abcd", builder.ToString()); + } + + [TestMethod] + public void InsertAtAnExistingPosition() + { + Builder builder = new Builder(); + BuilderPosition p = builder.CreatePosition(); + builder.Insert(0, "abcd"); + Assert.AreEqual("abcd", builder.ToString()); + Assert.AreEqual(4, p.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertBeforeAnExistingPosition() + { + Builder builder = new Builder(); + builder.Text("abd"); + BuilderPosition p = builder.CreatePosition(); + builder.Text("ef"); + + Assert.AreEqual(3, p.CharactersAfterPreviousPosition); + Assert.AreEqual(3, p.GetIndexInBuilder()); + + builder.Insert(2, "c"); + + Assert.AreEqual(4, p.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertAfterAnExistingPosition() + { + Builder builder = new Builder(); + builder.Text("abc"); + BuilderPosition p = builder.CreatePosition(); + builder.Text("df"); + + Assert.AreEqual(3, p.CharactersAfterPreviousPosition); + Assert.AreEqual(3, p.GetIndexInBuilder()); + + builder.Insert(4, "e"); + + Assert.AreEqual(3, p.CharactersAfterPreviousPosition); + Assert.AreEqual(3, p.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertBetweenExistingPositions() + { + Builder builder = new Builder(); + builder.Text("ab"); + BuilderPosition p1 = builder.CreatePosition(); + builder.Text("cd"); + BuilderPosition p2 = builder.CreatePosition(); + builder.Text("ef"); + + Assert.AreEqual(2, p1.CharactersAfterPreviousPosition); + Assert.AreEqual(2, p1.GetIndexInBuilder()); + Assert.AreEqual(2, p2.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p2.GetIndexInBuilder()); + + builder.Insert(3, "Z"); + Assert.AreEqual("abcZdef", builder.ToString()); + + Assert.AreEqual(2, p1.CharactersAfterPreviousPosition); + Assert.AreEqual(2, p1.GetIndexInBuilder()); + Assert.AreEqual(3, p2.CharactersAfterPreviousPosition); + Assert.AreEqual(5, p2.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertNewLine() + { + Builder builder = new Builder(); + builder.InsertNewLine(0); + Assert.AreEqual("\n", builder.ToString()); + } + + [TestMethod] + public void LineWithNoArguments() + { + Builder builder = new Builder(); + builder.Line(); + Assert.AreEqual("", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithNoArgumentsTwice() + { + Builder builder = new Builder(); + builder.Line(); + builder.Line(); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithNull() + { + Builder builder = new Builder(); + builder.Line(null); + Assert.AreEqual("", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithNullTwice() + { + Builder builder = new Builder(); + builder.Line(null); + builder.Line(null); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithEmpty() + { + Builder builder = new Builder(); + builder.Line(""); + Assert.AreEqual("", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithEmptyTwice() + { + Builder builder = new Builder(); + builder.Line(""); + builder.Line(""); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithText() + { + Builder builder = new Builder(); + builder.Line("Hello"); + Assert.AreEqual("Hello", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithTextTwice() + { + Builder builder = new Builder(); + builder.Line("Hello"); + builder.Line("World"); + Assert.AreEqual("Hello\nWorld", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNull() + { + Builder builder = new Builder(); + builder.Text(null); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNullTwice() + { + Builder builder = new Builder(); + builder.Text(null); + builder.Text(null); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithEmpty() + { + Builder builder = new Builder(); + builder.Text(""); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithEmptyTwice() + { + Builder builder = new Builder(); + builder.Text(""); + builder.Text(""); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithText() + { + Builder builder = new Builder(); + builder.Text("a"); + Assert.AreEqual("a", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithTextTwice() + { + Builder builder = new Builder(); + builder.Text("a"); + builder.Text("b"); + Assert.AreEqual("ab", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNewLine() + { + Builder builder = new Builder(); + builder.Text("\n"); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNewLineTwice() + { + Builder builder = new Builder(); + builder.Text("\n"); + builder.Text("\n"); + Assert.AreEqual("\n\n", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void WordWrapWithNullLine() + { + Builder builder = new Builder(); + IEnumerable wrappedLines = builder.WordWrap(null, false); + Assert.IsFalse(wrappedLines.Any()); + } + + [TestMethod] + public void WordWrapWithEmptyLine() + { + Builder builder = new Builder(); + IEnumerable wrappedLines = builder.WordWrap("", false); + Assert.IsFalse(wrappedLines.Any()); + } + + [TestMethod] + public void WordWrapWithNonEmptyLineShorterThanWordWrapWidth() + { + Builder builder = new Builder(); + builder.WordWrapWidth = 20; + IEnumerable wrappedLines = builder.WordWrap("abcd", false); + AssertEx.EqualLines( + "abcd", + wrappedLines); + } + + [TestMethod] + public void WordWrapWithNonEmptyLineWithNoWhitespaceLongerThanWordWrapWidth() + { + Builder builder = new Builder(); + builder.WordWrapWidth = 5; + IEnumerable wrappedLines = builder.WordWrap("abcdefghij", false); + AssertEx.EqualLines( + new[] + { + "abcdefghij" + }, + wrappedLines); + } + + [TestMethod] + public void WordWrapWithNonEmptyLineWithWhitespaceLongerThanWordWrapWidth() + { + Builder builder = new Builder(); + builder.WordWrapWidth = 5; + IEnumerable wrappedLines = builder.WordWrap("abc def ghij", false); + AssertEx.EqualLines( + new[] + { + "abc\n", + "def\n", + "ghij" + }, + wrappedLines); + } + } +} diff --git a/unittests/DSL/HTMLBuilderTests.cs b/unittests/DSL/HTMLBuilderTests.cs new file mode 100644 index 000000000000..cb1f6c333cac --- /dev/null +++ b/unittests/DSL/HTMLBuilderTests.cs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AutoRest.TypeScript.DSL +{ + [TestClass] + public class HTMLBuilderTests + { + [TestMethod] + public void Constructor() + { + HTMLBuilder builder = new HTMLBuilder(); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void DOCTYPE() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.DOCTYPE(); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithNameAndCanBeEmptyElementSetToFalse() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", false); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithNameAndCanBeEmptyElementSetToTrue() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", true); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithNameAndNullValue() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", (string)null); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithNameAndEmptyValue() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", ""); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithNameAndNonEmptyValue() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", "bcd"); + AssertEx.EqualLines("bcd", builder); + } + + [TestMethod] + public void ElementWithOneAttributeAndNoChildren() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", a => + { + a.Attribute("b", "c"); + }); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithTwoAttributesAndNoChildren() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", a => + { + a.Attribute("b", "c"); + a.Attribute("d", "e"); + }); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ElementWithNoAttributesAndOneChildText() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", a => + { + a.Text("b"); + }); + AssertEx.EqualLines("b", builder); + } + + [TestMethod] + public void ElementWithNoAttributesAndTwoChildTexts() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", a => + { + a.Text("b"); + a.Text("c"); + }); + AssertEx.EqualLines("bc", builder); + } + + [TestMethod] + public void ElementWithNoAttributesAndOneChildElement() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Element("a", a => + { + a.ChildElement("b"); + }); + AssertEx.EqualLines(new[] + { + "", + " ", + "" + }, + builder); + } + + [TestMethod] + public void DoctypeHtmlAndHeadElements() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.DOCTYPE(); + builder.Html(html => + { + html.Head(); + }); + AssertEx.EqualLines(new[] + { + "", + "", + " ", + "" + }, + builder); + } + + [TestMethod] + public void ScriptWithTSBuilderAction() + { + HTMLBuilder builder = new HTMLBuilder(); + builder.Script(tsBuilder => + { + tsBuilder.ConstQuotedStringVariable("x", "my special text"); + }); + AssertEx.EqualLines(new[] + { + "" + }, + builder); + } + } +} diff --git a/unittests/DSL/JSBuilderTests.cs b/unittests/DSL/JSBuilderTests.cs new file mode 100644 index 000000000000..a6e45ccf3752 --- /dev/null +++ b/unittests/DSL/JSBuilderTests.cs @@ -0,0 +1,804 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AutoRest.TypeScript.DSL +{ + [TestClass] + public class JSBuilderTests + { + [TestMethod] + public void Constructor() + { + JSBuilder builder = new JSBuilder(); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void CreatePositionWithEmptyBuilder() + { + JSBuilder builder = new JSBuilder(); + BuilderPosition p = builder.CreatePosition(); + Assert.AreEqual(0, p.CharactersAfterPreviousPosition); + Assert.AreEqual(0, p.GetIndexInBuilder()); + Assert.AreSame(p, builder.CreatePosition()); + } + + [TestMethod] + public void CreatePositionWithNonEmptyBuilder() + { + JSBuilder builder = new JSBuilder(); + builder.Text("abcd"); + BuilderPosition p = builder.CreatePosition(); + Assert.AreEqual(4, p.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p.GetIndexInBuilder()); + Assert.AreSame(p, builder.CreatePosition()); + } + + [TestMethod] + public void CreatePositionWhenAnotherPositionAlreadyExists() + { + JSBuilder builder = new JSBuilder(); + BuilderPosition p1 = builder.CreatePosition(); + builder.Text("abcd"); + BuilderPosition p2 = builder.CreatePosition(); + Assert.AreEqual(0, p1.CharactersAfterPreviousPosition); + Assert.AreEqual(0, p1.GetIndexInBuilder()); + Assert.AreEqual(4, p2.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p2.GetIndexInBuilder()); + } + + [TestMethod] + public void HasChangedLinesSinceWithNegativeIndexAndEmptyBuilder() + { + JSBuilder builder = new JSBuilder(); + Assert.ThrowsException(() => builder.HasChangedLinesSince(-1)); + } + + [TestMethod] + public void HasChangedLinesSinceWithNegativeIndexAndSingleLine() + { + JSBuilder builder = new JSBuilder(); + builder.Text("abc"); + Assert.ThrowsException(() => builder.HasChangedLinesSince(-1)); + } + + [TestMethod] + public void HasChangedLinesSinceWithNegativeIndexAndMultipleLines() + { + JSBuilder builder = new JSBuilder(); + builder.Text("a\nb\nc"); + Assert.ThrowsException(() => builder.HasChangedLinesSince(-1)); + } + + [TestMethod] + public void HasChangedLinesSinceWithZeroIndexAndEmptyBuilder() + { + JSBuilder builder = new JSBuilder(); + Assert.IsFalse(builder.HasChangedLinesSince(0)); + } + + [TestMethod] + public void HasChangedLinesSinceWithZeroIndexAndSingleLine() + { + JSBuilder builder = new JSBuilder(); + builder.Text("abc"); + Assert.IsFalse(builder.HasChangedLinesSince(0)); + } + + [TestMethod] + public void HasChangedLinesSinceWithZeroIndexAndMultipleLines() + { + JSBuilder builder = new JSBuilder(); + builder.Text("a\nb\nc"); + Assert.IsTrue(builder.HasChangedLinesSince(0)); + } + + [TestMethod] + public void InsertWithNoPositions() + { + JSBuilder builder = new JSBuilder(); + builder.Insert(0, "abcd"); + Assert.AreEqual("abcd", builder.ToString()); + } + + [TestMethod] + public void InsertAtAnExistingPosition() + { + JSBuilder builder = new JSBuilder(); + BuilderPosition p = builder.CreatePosition(); + builder.Insert(0, "abcd"); + Assert.AreEqual("abcd", builder.ToString()); + Assert.AreEqual(4, p.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertBeforeAnExistingPosition() + { + JSBuilder builder = new JSBuilder(); + builder.Text("abd"); + BuilderPosition p = builder.CreatePosition(); + builder.Text("ef"); + + Assert.AreEqual(3, p.CharactersAfterPreviousPosition); + Assert.AreEqual(3, p.GetIndexInBuilder()); + + builder.Insert(2, "c"); + + Assert.AreEqual(4, p.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertAfterAnExistingPosition() + { + JSBuilder builder = new JSBuilder(); + builder.Text("abc"); + BuilderPosition p = builder.CreatePosition(); + builder.Text("df"); + + Assert.AreEqual(3, p.CharactersAfterPreviousPosition); + Assert.AreEqual(3, p.GetIndexInBuilder()); + + builder.Insert(4, "e"); + + Assert.AreEqual(3, p.CharactersAfterPreviousPosition); + Assert.AreEqual(3, p.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertBetweenExistingPositions() + { + JSBuilder builder = new JSBuilder(); + builder.Text("ab"); + BuilderPosition p1 = builder.CreatePosition(); + builder.Text("cd"); + BuilderPosition p2 = builder.CreatePosition(); + builder.Text("ef"); + + Assert.AreEqual(2, p1.CharactersAfterPreviousPosition); + Assert.AreEqual(2, p1.GetIndexInBuilder()); + Assert.AreEqual(2, p2.CharactersAfterPreviousPosition); + Assert.AreEqual(4, p2.GetIndexInBuilder()); + + builder.Insert(3, "Z"); + Assert.AreEqual("abcZdef", builder.ToString()); + + Assert.AreEqual(2, p1.CharactersAfterPreviousPosition); + Assert.AreEqual(2, p1.GetIndexInBuilder()); + Assert.AreEqual(3, p2.CharactersAfterPreviousPosition); + Assert.AreEqual(5, p2.GetIndexInBuilder()); + } + + [TestMethod] + public void InsertNewLine() + { + JSBuilder builder = new JSBuilder(); + builder.InsertNewLine(0); + Assert.AreEqual("\n", builder.ToString()); + } + + [TestMethod] + public void AddPropertyToParentObjectWhileBuildingChildObject() + { + JSBuilder builder = new JSBuilder(); + InvalidOperationException exception = Assert.ThrowsException(() => + { + builder.Object((JSObject parentObject) => + { + parentObject.ObjectProperty("child", (JSObject childObject) => + { + childObject.TextProperty("a", "A"); + parentObject.TextProperty("b", "B"); + }); + }); + }); + Assert.AreEqual("Cannot add a property to a JSObject while constructing its child property (\"child\").", exception.Message); + } + + [TestMethod] + public void LineWithNoArguments() + { + JSBuilder builder = new JSBuilder(); + builder.Line(); + Assert.AreEqual("", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithNoArgumentsTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Line(); + builder.Line(); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithNull() + { + JSBuilder builder = new JSBuilder(); + builder.Line(null); + Assert.AreEqual("", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithNullTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Line(null); + builder.Line(null); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithEmpty() + { + JSBuilder builder = new JSBuilder(); + builder.Line(""); + Assert.AreEqual("", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithEmptyTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Line(""); + builder.Line(""); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithText() + { + JSBuilder builder = new JSBuilder(); + builder.Line("Hello"); + Assert.AreEqual("Hello", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void LineWithTextTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Line("Hello"); + builder.Line("World"); + Assert.AreEqual("Hello\nWorld", builder.ToString()); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNull() + { + JSBuilder builder = new JSBuilder(); + builder.Text(null); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNullTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Text(null); + builder.Text(null); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithEmpty() + { + JSBuilder builder = new JSBuilder(); + builder.Text(""); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithEmptyTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Text(""); + builder.Text(""); + Assert.AreEqual("", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithText() + { + JSBuilder builder = new JSBuilder(); + builder.Text("a"); + Assert.AreEqual("a", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithTextTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Text("a"); + builder.Text("b"); + Assert.AreEqual("ab", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNewLine() + { + JSBuilder builder = new JSBuilder(); + builder.Text("\n"); + Assert.AreEqual("\n", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TextWithNewLineTwice() + { + JSBuilder builder = new JSBuilder(); + builder.Text("\n"); + builder.Text("\n"); + Assert.AreEqual("\n\n", builder.ToString()); + Assert.IsFalse(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void TryWithEmptyBlock() + { + JSBuilder builder = new JSBuilder(); + builder.Try(tryBlock => { }); + AssertEx.EqualLines( + new[] + { + "try {", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void NestedTryBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.Try(tryBlock1 => + { + tryBlock1.Try(tryBlock2 => + { + }) + .Catch("error1", catchBlock2 => + { + }); + }) + .Catch("error2", catchBlock1 => + { + }); + AssertEx.EqualLines( + new[] + { + "try {", + " try {", + " } catch (error1) {", + " }", + "} catch (error2) {", + "}" + }, + builder); + } + + [TestMethod] + public void IfWithEmptyBlock() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => { }); + AssertEx.EqualLines( + new[] + { + "if (true) {", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void IfWithTextBlock() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => + { + ifBlock.Text("Hello"); + }); + AssertEx.EqualLines( + new[] + { + "if (true) {", + " Hello", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void IfWithLineBlock() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => + { + ifBlock.Line("Hello"); + }); + AssertEx.EqualLines( + new[] + { + "if (true) {", + " Hello", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void IfElseWithEmptyBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => { }) + .Else(elseBlock => { }); + AssertEx.EqualLines( + new[] + { + "if (true) {", + "} else {", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void IfElseIfWithEmptyBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => { }) + .ElseIf("false", elseBlock => { }); + AssertEx.EqualLines( + new[] + { + "if (true) {", + "} else if (false) {", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void IfElseIfWithTextBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => + { + ifBlock.Text("Hello"); + }) + .ElseIf("false", elseBlock => + { + elseBlock.Text("World"); + }); + AssertEx.EqualLines( + new[] + { + "if (true) {", + " Hello", + "} else if (false) {", + " World", + "}" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void IfWithLineAfterwards() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => { }); + builder.Line("Test"); + AssertEx.EqualLines( + new[] + { + "if (true) {", + "}", + "Test" + }, + builder); + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + } + + [TestMethod] + public void NestedIfBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.If("a", ifBlock1 => + { + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + AssertEx.EqualLines( + "if (a) {", + builder); + + ifBlock1.If("b", ifBlock2 => + { + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + AssertEx.EqualLines( + new[] + { + "if (a) {", + " if (b) {" + }, + builder); + }); + + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + AssertEx.EqualLines( + new[] + { + "if (a) {", + " if (b) {", + " }" + }, + builder); + }); + + Assert.IsTrue(builder.WriteNewLineBeforeNextText); + AssertEx.EqualLines( + new[] + { + "if (a) {", + " if (b) {", + " }", + "}" + }, + builder); + } + + [TestMethod] + public void NestedElseBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.If("a", ifBlock1 => + { + ifBlock1.If("b", ifBlock2 => + { + }) + .Else(elseBlock2 => + { + }); + }) + .Else(elseBlock1 => + { + }); ; + AssertEx.EqualLines( + new[] + { + "if (a) {", + " if (b) {", + " } else {", + " }", + "} else {", + "}", + }, + builder.ToString()); + } + + [TestMethod] + public void NestedElseIfBlocks() + { + JSBuilder builder = new JSBuilder(); + builder.If("a1", ifBlock1 => + { + ifBlock1.If("b1", ifBlock2 => + { + }) + .ElseIf("b2", elseIfBlock2 => + { + }); + }) + .ElseIf("a2", elseIfBlock1 => + { + }); ; + AssertEx.EqualLines( + new[] + { + "if (a1) {", + " if (b1) {", + " } else if (b2) {", + " }", + "} else if (a2) {", + "}", + }, + builder.ToString()); + } + + [TestMethod] + public void TryBlockInIfBlock() + { + JSBuilder builder = new JSBuilder(); + builder.If("true", ifBlock => + { + ifBlock.Try(tryBlock => + { + }) + .Catch("error", catchBlock2 => + { + }); + }); + AssertEx.EqualLines( + "if (true) {" + Environment.NewLine + + " try {" + Environment.NewLine + + " } catch (error) {" + Environment.NewLine + + " }" + Environment.NewLine + + "}", + builder.ToString()); + } + + [TestMethod] + public void IfBlockInTryBlock() + { + JSBuilder builder = new JSBuilder(); + builder.Try(tryBlock => + { + tryBlock.If("true", ifBlock => + { + }); + }) + .Catch("error", catchBlock1 => + { + }); + AssertEx.EqualLines( + "try {" + Environment.NewLine + + " if (true) {" + Environment.NewLine + + " }" + Environment.NewLine + + "} catch (error) {" + Environment.NewLine + + "}", + builder.ToString()); + } + + [TestMethod] + public void IfBlockWithSurroundingLines() + { + JSBuilder builder = new JSBuilder(); + builder.Line("const x = 5;"); + builder.If("true", ifBlock => + { + }); + builder.Line("const y = 6;"); + AssertEx.EqualLines( + new[] + { + "const x = 5;", + "if (true) {", + "}", + "const y = 6;" + }, + builder.ToString()); + } + + [TestMethod] + public void IfBlockWithSurroundingEmptyLines() + { + JSBuilder builder = new JSBuilder(); + builder.Line(); + builder.If("true", ifBlock => + { + }); + builder.Line(); + AssertEx.EqualLines( + new[] + { + "", + "if (true) {", + "}", + "" + }, + builder.ToString()); + } + + [TestMethod] + public void DocumentCommentWithNoContents() + { + JSBuilder builder = new JSBuilder(); + builder.DocumentationComment(comment => { }); + Assert.AreEqual("", builder.ToString()); + } + + [TestMethod] + public void DocumentationCommentWithDescription() + { + JSBuilder builder = new JSBuilder(); + builder.DocumentationComment(comment => + { + comment.Description("Hello"); + }); + AssertEx.EqualLines( + "/**\n" + + " * Hello\n" + + " */", + builder); + } + + [TestMethod] + public void DocumentationCommentWithDescriptionAndParameter() + { + JSBuilder builder = new JSBuilder(); + builder.DocumentationComment(comment => + { + comment.Description("This is my description."); + comment.Parameter("parameterName", "parameterType", "This is my parameter description."); + }); + AssertEx.EqualLines( + "/**\n" + + " * This is my description.\n" + + " * @param {parameterType} parameterName This is my parameter description.\n" + + " */", + builder); + } + + [TestMethod] + public void DocumentationCommentWithLeadingWhitespaceInDescription() + { + JSBuilder builder = new JSBuilder(); + builder.DocumentationComment(comment => + { + comment.Description("This\n is\n\tmy\ndescription."); + }); + // The leading whitespace in the description should be preserved, but the AutoRest + // WordWrap() trims away leading whitespace. + AssertEx.EqualLines( + "/**\n" + + " * This\n" + + " * is\n" + + " * my\n" + + " * description.\n" + + " */", + builder); + } + + [TestMethod] + public void DocumentationCommentWithWrappedDescription() + { + JSBuilder builder = new JSBuilder(10); + builder.DocumentationComment(comment => + { + comment.Description("This is my long description that will get wrapped."); + }); + // The AutoRest WordWrap() function seems to include the trailing newline character in + // its wordwrap algorithm. " * This is" is 10 characters long, so it shouldn't get + // wrapped, but AutoRest WordWrap() wraps it. + AssertEx.EqualLines( + "/**\n" + + " * This\n" + + " * is my\n" + + " * long\n" + + " * description\n" + + " * that\n" + + " * will\n" + + " * get\n" + + " * wrapped.\n" + + " */", + builder); + } + } +} diff --git a/unittests/DSL/MarkdownBuilderTests.cs b/unittests/DSL/MarkdownBuilderTests.cs new file mode 100644 index 000000000000..fe20527d18fa --- /dev/null +++ b/unittests/DSL/MarkdownBuilderTests.cs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AutoRest.TypeScript.DSL +{ + [TestClass] + public class MarkdownBuilderTests + { + [TestMethod] + public void Constructor() + { + MarkdownBuilder builder = new MarkdownBuilder(); + AssertEx.EqualLines("", builder.ToString()); + } + + [TestMethod] + public void HeaderWithNegativeLevel() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.Header(-1, "Nope"); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void HeaderWithZeroLevel() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.Header(0, "Nope"); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void HeaderWithNullText() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.Header(1, null); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void HeaderWithEmptyText() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.Header(1, null); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void HeaderWithOneLevel() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.Header(1, "ABC"); + AssertEx.EqualLines("# ABC", builder); + } + + [TestMethod] + public void HeaderWithTwoLevel() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.Header(2, "DEFG"); + AssertEx.EqualLines("## DEFG", builder); + } + + [TestMethod] + public void ListWithNull() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.List(null); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ListWithNoItems() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.List(new string[0]); + AssertEx.EqualLines("", builder); + } + + [TestMethod] + public void ListWithOneItem() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.List(new[] { "a" }); + AssertEx.EqualLines("- a", builder); + } + + [TestMethod] + public void ListWithTwoItems() + { + MarkdownBuilder builder = new MarkdownBuilder(); + builder.List(new[] { "a", "b" }); + AssertEx.EqualLines(new[] + { + "- a", + "- b", + }, + builder); + } + } +} diff --git a/unittests/DSL/TSBuilderTests.cs b/unittests/DSL/TSBuilderTests.cs index c3f7777e1b20..2d9359cf4d26 100644 --- a/unittests/DSL/TSBuilderTests.cs +++ b/unittests/DSL/TSBuilderTests.cs @@ -24,7 +24,7 @@ public void Constructor() public void CreatePositionWithEmptyBuilder() { TSBuilder builder = new TSBuilder(); - TSPosition p = builder.CreatePosition(); + BuilderPosition p = builder.CreatePosition(); Assert.AreEqual(0, p.CharactersAfterPreviousPosition); Assert.AreEqual(0, p.GetIndexInBuilder()); Assert.AreSame(p, builder.CreatePosition()); @@ -35,7 +35,7 @@ public void CreatePositionWithNonEmptyBuilder() { TSBuilder builder = new TSBuilder(); builder.Text("abcd"); - TSPosition p = builder.CreatePosition(); + BuilderPosition p = builder.CreatePosition(); Assert.AreEqual(4, p.CharactersAfterPreviousPosition); Assert.AreEqual(4, p.GetIndexInBuilder()); Assert.AreSame(p, builder.CreatePosition()); @@ -45,9 +45,9 @@ public void CreatePositionWithNonEmptyBuilder() public void CreatePositionWhenAnotherPositionAlreadyExists() { TSBuilder builder = new TSBuilder(); - TSPosition p1 = builder.CreatePosition(); + BuilderPosition p1 = builder.CreatePosition(); builder.Text("abcd"); - TSPosition p2 = builder.CreatePosition(); + BuilderPosition p2 = builder.CreatePosition(); Assert.AreEqual(0, p1.CharactersAfterPreviousPosition); Assert.AreEqual(0, p1.GetIndexInBuilder()); Assert.AreEqual(4, p2.CharactersAfterPreviousPosition); @@ -112,7 +112,7 @@ public void InsertWithNoPositions() public void InsertAtAnExistingPosition() { TSBuilder builder = new TSBuilder(); - TSPosition p = builder.CreatePosition(); + BuilderPosition p = builder.CreatePosition(); builder.Insert(0, "abcd"); Assert.AreEqual("abcd", builder.ToString()); Assert.AreEqual(4, p.CharactersAfterPreviousPosition); @@ -124,7 +124,7 @@ public void InsertBeforeAnExistingPosition() { TSBuilder builder = new TSBuilder(); builder.Text("abd"); - TSPosition p = builder.CreatePosition(); + BuilderPosition p = builder.CreatePosition(); builder.Text("ef"); Assert.AreEqual(3, p.CharactersAfterPreviousPosition); @@ -141,7 +141,7 @@ public void InsertAfterAnExistingPosition() { TSBuilder builder = new TSBuilder(); builder.Text("abc"); - TSPosition p = builder.CreatePosition(); + BuilderPosition p = builder.CreatePosition(); builder.Text("df"); Assert.AreEqual(3, p.CharactersAfterPreviousPosition); @@ -158,9 +158,9 @@ public void InsertBetweenExistingPositions() { TSBuilder builder = new TSBuilder(); builder.Text("ab"); - TSPosition p1 = builder.CreatePosition(); + BuilderPosition p1 = builder.CreatePosition(); builder.Text("cd"); - TSPosition p2 = builder.CreatePosition(); + BuilderPosition p2 = builder.CreatePosition(); builder.Text("ef"); Assert.AreEqual(2, p1.CharactersAfterPreviousPosition); @@ -716,63 +716,6 @@ public void IfBlockWithSurroundingEmptyLines() builder.ToString()); } - [TestMethod] - public void WordWrapWithNullLine() - { - TSBuilder builder = new TSBuilder(); - IEnumerable wrappedLines = builder.WordWrap(null, false); - Assert.IsFalse(wrappedLines.Any()); - } - - [TestMethod] - public void WordWrapWithEmptyLine() - { - TSBuilder builder = new TSBuilder(); - IEnumerable wrappedLines = builder.WordWrap("", false); - Assert.IsFalse(wrappedLines.Any()); - } - - [TestMethod] - public void WordWrapWithNonEmptyLineShorterThanWordWrapWidth() - { - TSBuilder builder = new TSBuilder(); - builder.WordWrapWidth = 20; - IEnumerable wrappedLines = builder.WordWrap("abcd", false); - AssertEx.EqualLines( - "abcd", - wrappedLines); - } - - [TestMethod] - public void WordWrapWithNonEmptyLineWithNoWhitespaceLongerThanWordWrapWidth() - { - TSBuilder builder = new TSBuilder(); - builder.WordWrapWidth = 5; - IEnumerable wrappedLines = builder.WordWrap("abcdefghij", false); - AssertEx.EqualLines( - new[] - { - "abcdefghij" - }, - wrappedLines); - } - - [TestMethod] - public void WordWrapWithNonEmptyLineWithWhitespaceLongerThanWordWrapWidth() - { - TSBuilder builder = new TSBuilder(); - builder.WordWrapWidth = 5; - IEnumerable wrappedLines = builder.WordWrap("abc def ghij", false); - AssertEx.EqualLines( - new[] - { - "abc\n", - "def\n", - "ghij" - }, - wrappedLines); - } - [TestMethod] public void DocumentCommentWithNoContents() {