diff --git a/src/Jupyter/ConfigurationSource/IConfigurationSource.cs b/src/Jupyter/ConfigurationSource/IConfigurationSource.cs
index 89c854d53c..acb9885bd3 100644
--- a/src/Jupyter/ConfigurationSource/IConfigurationSource.cs
+++ b/src/Jupyter/ConfigurationSource/IConfigurationSource.cs
@@ -232,18 +232,28 @@ public TEnum GetOptionOrDefault<TEnum>(string optionName, TEnum defaultValue) wh
         public string WorkspaceName =>
             GetOptionOrDefault("azure.quantum.workspace.name", string.Empty);
 
-        /// <summary>
-        ///      If set to <c>true</c>, shows additional performance breakdowns
-        ///      from within the notebook.
-        /// </summary>
-        public bool InternalShowPerf =>
-            GetOptionOrDefault("internal.showPerf", false);
-
-        /// <summary>
-        ///      If set to <c>true</c>, shows additional performance breakdowns
-        ///      forwarded from the Q# compiler.
-        /// </summary>
-        public bool InternalShowCompilerPerf =>
-            GetOptionOrDefault("internal.showCompilerPerf", false);
+        // We also want to support some internal-only options that are disabled
+        // in release builds, making it easier to diagnose some internals
+        // during local development. When in release mode, we'll disable setting
+        // these options and always use defaults.
+
+        #if DEBUG
+            public TEnum GetInternalOptionOrDefault<TEnum>(string optionName, TEnum defaultValue) where TEnum : struct =>
+                GetOptionOrDefault("internal." + optionName, defaultValue, Enum.Parse<TEnum>);
+            public string GetInternalOptionOrDefault(string optionName, string defaultValue) =>
+                GetOptionOrDefault("internal." + optionName, defaultValue, e => e);
+            public bool GetInternalOptionOrDefault(string optionName, bool defaultValue) =>
+                GetOptionOrDefault("internal." + optionName, defaultValue, bool.Parse);
+        #else
+            public string GetInternalOptionOrDefault(string optionName, string defaultValue) =>
+                defaultValue;
+            public TEnum GetInternalOptionOrDefault<TEnum>(string optionName, TEnum defaultValue) where TEnum : struct =>
+                defaultValue;
+            public bool GetInternalOptionOrDefault(string optionName, bool defaultValue) =>
+                defaultValue;
+        #endif
+
+        public bool InternalHelpShowAllAttributes =>
+            GetInternalOptionOrDefault("help.showAllAttributes", false);
     }
 }
diff --git a/src/Jupyter/SymbolResolver.cs b/src/Jupyter/SymbolResolver.cs
index 9e36434e07..a666d28bec 100644
--- a/src/Jupyter/SymbolResolver.cs
+++ b/src/Jupyter/SymbolResolver.cs
@@ -72,7 +72,16 @@ public class IQSharpSymbol : ISymbol
         /// <summary>
         /// </summary>
         [JsonProperty("inputs", NullValueHandling=NullValueHandling.Ignore)]
-        public ImmutableDictionary<string?, string?>? Inputs { get; private set; } = null;
+        public ImmutableDictionary<string?, string?> Inputs { get; private set; }
+
+        /// <summary>
+        /// </summary>
+        [JsonProperty("examples", NullValueHandling=NullValueHandling.Ignore)]
+        public ImmutableList<string?> Examples { get; private set; }
+
+        
+        [JsonProperty("type_parameters", NullValueHandling=NullValueHandling.Ignore)]
+        public ImmutableDictionary<string?, string?> TypeParameters { get; private set; }
 
         // TODO: continue exposing documentation here.
 
@@ -94,12 +103,20 @@ public IQSharpSymbol(OperationInfo op)
                 .Operation
                 .GetStringAttributes("Description")
                 .SingleOrDefault();
-            var inputs = this
+            this.Inputs = this
+                .Operation
+                .GetDictionaryAttributes("Input")
+                .ToImmutableDictionary();
+            this.TypeParameters = this
+                .Operation
+                .GetDictionaryAttributes("TypeParameter")
+                .ToImmutableDictionary();
+            this.Examples = this
                 .Operation
-                .GetDictionaryAttributes("Input");
-            this.Inputs = inputs.Count >= 0
-                ? inputs.ToImmutableDictionary()
-                : null;
+                .GetStringAttributes("Example")
+                .Where(ex => ex != null)
+                .ToImmutableList();
+            
         }
     }
 
diff --git a/src/Kernel/IQSharpEngine.cs b/src/Kernel/IQSharpEngine.cs
index 0fb6588b50..901f2079a4 100644
--- a/src/Kernel/IQSharpEngine.cs
+++ b/src/Kernel/IQSharpEngine.cs
@@ -157,6 +157,11 @@ private void AttachCommsListeners()
             };
         }
 
+        private void RegisterDisplayEncoder<T>()
+        where T: IResultEncoder =>
+            RegisterDisplayEncoder(ActivatorUtilities.CreateInstance<T>(services));
+
+
         private async Task StartAsync()
         {
             base.Start();
@@ -214,11 +219,11 @@ private async Task StartAsync()
 
 
             logger.LogDebug("Registering IQ# display and JSON encoders.");
-            RegisterDisplayEncoder(new IQSharpSymbolToHtmlResultEncoder());
-            RegisterDisplayEncoder(new IQSharpSymbolToTextResultEncoder());
+            RegisterDisplayEncoder<IQSharpSymbolToHtmlResultEncoder>();
+            RegisterDisplayEncoder<IQSharpSymbolToTextResultEncoder>();
             RegisterDisplayEncoder(new TaskStatusToTextEncoder());
-            RegisterDisplayEncoder(new StateVectorToHtmlResultEncoder(configurationSource));
-            RegisterDisplayEncoder(new StateVectorToTextResultEncoder(configurationSource));
+            RegisterDisplayEncoder<StateVectorToHtmlResultEncoder>();
+            RegisterDisplayEncoder<StateVectorToTextResultEncoder>();
             RegisterDisplayEncoder(new DataTableToHtmlEncoder());
             RegisterDisplayEncoder(new DataTableToTextEncoder());
             RegisterDisplayEncoder(new DisplayableExceptionToHtmlEncoder());
diff --git a/src/Kernel/SymbolEncoders.cs b/src/Kernel/SymbolEncoders.cs
index a491315bbd..01ef0e856c 100644
--- a/src/Kernel/SymbolEncoders.cs
+++ b/src/Kernel/SymbolEncoders.cs
@@ -6,9 +6,125 @@
 using Microsoft.Jupyter.Core;
 using Markdig;
 using Microsoft.Quantum.IQSharp.Jupyter;
+using System.Linq;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+using System;
+using Microsoft.Quantum.QsCompiler.SyntaxTokens;
+using System.Threading.Tasks;
+using System.Net.Http;
+using System.Diagnostics.CodeAnalysis;
+using Newtonsoft.Json.Linq;
 
 namespace Microsoft.Quantum.IQSharp.Kernel
 {
+    using ResolvedTypeKind = QsTypeKind<ResolvedType, UserDefinedType, QsTypeParameter, CallableInformation>;
+
+    // NB: These are defined in the documentation generation tool in the
+    //     compiler, and should not be duplicated here. These should be removed
+    //     before merging to main.
+    internal static class SyntaxExtensions
+    {
+        internal static List<(string, ResolvedType)> InputDeclarations(this QsTuple<LocalVariableDeclaration<QsLocalSymbol>> items) => items switch
+            {
+                QsTuple<LocalVariableDeclaration<QsLocalSymbol>>.QsTuple tuple =>
+                    tuple.Item.SelectMany(
+                        item => item.InputDeclarations())
+                    .ToList(),
+                QsTuple<LocalVariableDeclaration<QsLocalSymbol>>.QsTupleItem item =>
+                    new List<(string, ResolvedType)>
+                    {
+                        (
+                            item.Item.VariableName switch
+                            {
+                                QsLocalSymbol.ValidName name => name.Item,
+                                _ => "__invalid__",
+                            },
+                            item.Item.Type),
+                    },
+                _ => throw new Exception(),
+            };
+
+        internal static string ToSyntax(this ResolvedCharacteristics characteristics) =>
+            characteristics.SupportedFunctors switch
+            {
+                { IsNull: true } => "",
+                { Item: { Count: 0 } } => "",
+
+                // Be sure to add the leading space before is!
+                { Item: var functors } => $" is {string.Join(" + ", functors.Select(functor => functor.ToSyntax()))}",
+            };
+
+        internal static string ToSyntax(this QsFunctor functor) =>
+            functor.Tag switch
+            {
+                QsFunctor.Tags.Adjoint => "Adj",
+                QsFunctor.Tags.Controlled => "Ctl",
+                _ => "__invalid__",
+            };
+
+        // TODO: memoize
+        internal async static Task<string?> TryResolveXref(string xref)
+        {
+            var client = new HttpClient();
+            try
+            {
+                var response = await client.GetStringAsync($"https://xref.docs.microsoft.com/query?uid={xref}");
+                var json = JToken.Parse(response);
+                return json.Value<string>("href");
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        internal async static Task<string> ToLink(string text, string xref, string? fragment = null)
+        {
+            var href = await TryResolveXref(xref);
+            if (href == null)
+            {
+                return text;
+            }
+            else
+            {
+                return $"<a href=\"{href}{(fragment == null ? "" : $"#{fragment}")}</a>";
+            }
+        }
+
+        internal static async Task<string> ToHtml(this ResolvedType type) => type.Resolution switch
+            {
+                ResolvedTypeKind.ArrayType array => $"{await array.Item.ToHtml()}[]",
+                ResolvedTypeKind.Function function =>
+                    $"{await function.Item1.ToHtml()} -> {await function.Item2.ToHtml()}",
+                ResolvedTypeKind.Operation operation =>
+                    $"{await operation.Item1.Item1.ToHtml()} => {await operation.Item1.Item2.ToHtml()} "
+                    + operation.Item2.Characteristics.ToSyntax(),
+                ResolvedTypeKind.TupleType tuple => "(" + string.Join(
+                    ",", tuple.Item.Select(async type => await type.ToHtml())) + ")",
+                ResolvedTypeKind.UserDefinedType udt => await udt.Item.ToHtml(),
+                ResolvedTypeKind.TypeParameter typeParam =>
+                    $"'{typeParam.Item.TypeName}",
+                _ => type.Resolution.Tag switch
+                    {
+                        ResolvedTypeKind.Tags.BigInt => await ToLink("BigInt", "xref:microsoft.quantum.qsharp.valueliterals", "bigint-literals"),
+                        ResolvedTypeKind.Tags.Bool => await ToLink("Bool", "xref:microsoft.quantum.qsharp.valueliterals", "bool-literals"),
+                        ResolvedTypeKind.Tags.Double => await ToLink("Double", "xref:microsoft.quantum.qsharp.valueliterals", "double-literals"),
+                        ResolvedTypeKind.Tags.Int => await ToLink("Int", "xref:microsoft.quantum.qsharp.valueliterals", "int-literals"),
+                        ResolvedTypeKind.Tags.Pauli => await ToLink("Pauli", "xref:microsoft.quantum.qsharp.valueliterals", "pauli-literals"),
+                        ResolvedTypeKind.Tags.Qubit => await ToLink("Qubit", "xref:microsoft.quantum.qsharp.valueliterals", "qubit-literals"),
+                        ResolvedTypeKind.Tags.Range => await ToLink("Range", "xref:microsoft.quantum.qsharp.valueliterals", "range-literals"),
+                        ResolvedTypeKind.Tags.String => await ToLink("String", "xref:microsoft.quantum.qsharp.valueliterals", "string-literals"),
+                        ResolvedTypeKind.Tags.UnitType => await ToLink("Unit", "xref:microsoft.quantum.qsharp.valueliterals", "unit-literal"),
+                        ResolvedTypeKind.Tags.Result => await ToLink("Result", "xref:microsoft.quantum.qsharp.valueliterals", "result-literal"),
+                        ResolvedTypeKind.Tags.InvalidType => "__invalid__",
+                        _ => $"__invalid<{type.Resolution.ToString()}>__",
+                    },
+            };
+
+        internal static async Task<string> ToHtml(this UserDefinedType type) =>
+            await ToLink($"{type.Namespace}.{type.Name}", $"{type.Namespace}.{type.Name}");
+    }
+
     /// <summary>
     ///     Encodes Q# symbols into plain text, e.g. for printing to the console.
     /// </summary>
@@ -39,15 +155,24 @@ public class IQSharpSymbolToTextResultEncoder : IResultEncoder
     /// </summary>
     public class IQSharpSymbolToHtmlResultEncoder : IResultEncoder
     {
+        private readonly IConfigurationSource ConfigurationSource;
+
         /// <inheritdoc />
         public string MimeType => MimeTypes.Html;
 
+        public IQSharpSymbolToHtmlResultEncoder(IConfigurationSource configurationSource)
+        {
+            this.ConfigurationSource = configurationSource;
+        }
+
         /// <summary>
         ///     Checks if a displayable object is an IQ# symbol, and if so,
         ///     returns an encoding of that symbol into HTML.
         /// </summary>
         public EncodedData? Encode(object displayable)
         {
+            var tableEncoder = new TableToHtmlDisplayEncoder();
+
             if (displayable is IQSharpSymbol symbol)
             {
                 var codeLink =
@@ -58,10 +183,63 @@ public class IQSharpSymbolToHtmlResultEncoder : IResultEncoder
                 var description = symbol.Description != null
                     ? "<h5>Description</h5>" + Markdown.ToHtml(symbol.Description)
                     : string.Empty;
+                // TODO: Make sure to list
+                //       type parameters even if they're not documented.
+                var typeParams = symbol.TypeParameters.Count > 0
+                    ? "<h5>Type Parameters</h5>\n" +
+                      tableEncoder.Encode(new Table<KeyValuePair<string?, string?>>
+                      {
+                          Columns = new List<(string, Func<KeyValuePair<string?, string?>, string>)>
+                          {
+                              ("", input => $"<code>{input.Key}</code>"),
+                              ("", input => Markdown.ToHtml(input.Value))
+                          },
+                          Rows = symbol.TypeParameters.ToList()
+                      })!.Value.Data
+                    : string.Empty;
+
+                // TODO: Check if Inputs is empty before formatting, make sure
+                //       to list even if they're not documented.
+                var inputDecls = symbol.Operation.Header.ArgumentTuple.InputDeclarations().ToDictionary(item => item.Item1, item => item.Item2);
+                var inputs = symbol.Inputs.Count > 0
+                    ? "<h5>Inputs</h5>\n" + tableEncoder.Encode(new Table<KeyValuePair<string?, string?>>
+                    {
+                        Columns = new List<(string, Func<KeyValuePair<string?, string?>, string>)>
+                        {
+                            ("", input => $"<code>{input.Key}</code>"),
+                            ("", input => $"<code>{inputDecls[input.Key].ToHtml().Result}</code>"),
+                            ("", input => Markdown.ToHtml(input.Value))
+                        },
+                        Rows = symbol.Inputs.ToList()
+                    })!.Value.Data
+                    : string.Empty;
+                var examples = string.Join("\n",
+                    symbol.Examples.Select(example => $"<h5>Example</h5>\n{Markdown.ToHtml(example)}")
+                );
+
+                var attributes = ConfigurationSource.InternalHelpShowAllAttributes
+                    ? tableEncoder.Encode(new Table<QsDeclarationAttribute>
+                      {
+                          Columns = new List<(string, Func<QsDeclarationAttribute, string>)>
+                          {
+                              ("Name", attr => attr.TypeId switch
+                              {
+                                  { Item: UserDefinedType udt } => $"{udt.Namespace}.{udt.Name}",
+                                  _ => "<unknown>"
+                              }),
+                              ("Value", attr => attr.Argument.ToString())
+                          },
+                          Rows = symbol.Operation.Header.Attributes.ToList()
+                      })!.Value.Data
+                    : "";
                 return $@"
                     <h4><i class=""fa fas fa-terminal""></i> {symbol.Name} {codeLink}</h4>
                     {summary}
                     {description}
+                    {typeParams}
+                    {inputs}
+                    {examples}
+                    {attributes}
                 ".ToEncodedData();
 
             }
diff --git a/src/Tests/TelemetryTests.cs b/src/Tests/TelemetryTests.cs
index 8ba45b8621..2808630016 100644
--- a/src/Tests/TelemetryTests.cs
+++ b/src/Tests/TelemetryTests.cs
@@ -259,6 +259,7 @@ public async Task CompileCode()
             var logger = GetAppLogger(services);
 
             var snippets = services.GetService<ISnippets>();
+
             // Filter out device capabilities, since they may only be sent
             // well after we initialize the workspace.
             Func<EventProperties, bool> filter = 
@@ -377,6 +378,7 @@ public async Task LoadProjects()
 
             var ws = services.GetService<IWorkspace>();
 
+
             // Filter out device capabilities, since they may only be sent
             // well after we initialize the workspace.
             Func<EventProperties, bool> filter =