Exemplo n.º 1
0
        public DocTopic ParseProperty(ObjectProperty property, DocTopic parentClassTopic)
        {
            var topic = new DocTopic(_project)
            {
                Title = parentClassTopic.ClassInfo.Classname + "." + property.Name,
                //ListTitle = property.Name,
                DisplayType = "classproperty",

                ClassInfo = new ClassInfo
                {
                    MemberName = property.Name,
                    Signature  = property.Signature,
                    Scope      = property.Scope,
                    IsStatic   = property.Static,
                    Syntax     = property.Syntax
                },

                Remarks = property.Remarks,
                Example = property.Example,
                SeeAlso = property.SeeAlso,

                Parent   = parentClassTopic,
                ParentId = parentClassTopic?.Id
            };

            topic.Parent = parentClassTopic;
            topic.CreateRelativeSlugAndLink(topic);
            topic.Body = property.HelpText;

            return(topic);
        }
        public void BasicRazorStringTemplate()
        {
            var razor = new RazorStringTemplates();

            razor.StartRazorHost();

            var topic = new DocTopic(null)
            {
                Title = "New Topic",
                Body  = "Getting to the **point** of it all"
            };

            string template = @"@Helpers.TopicLink(""displaytext"",""test"")";

            string error;
            string output = razor.RenderTemplate(template, topic, out error);

            if (error != null)
            {
                Console.WriteLine(error);
            }
            Assert.NotNull(output);

            Console.WriteLine(output);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Saves a topic in the tree and then saves the project to
        /// disk.
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="project"></param>
        /// <param name="async"></param>
        /// <returns>false if data wasn't written (could be because there's nothing that's changed)</returns>
        public bool SaveProjectFileForTopic(DocTopic topic, DocProject project = null, bool async = false)
        {
            if (topic == null)
            {
                return(false);
            }

            if (!topic.TopicState.IsDirty)
            {
                return(false);
            }

            if (project == null)
            {
                project = kavaUi.AddinModel.ActiveProject;
            }


            if (async)
            {
                project.SaveProjectAsync();
                topic.TopicState.IsDirty = false;
                return(true);
            }

            bool result = project.SaveProject();

            if (result)
            {
                topic.TopicState.IsDirty = false;
            }

            return(result);
        }
        public void SelectTopic(DocTopic topic)
        {
            var found = FindTopic(null, topic);

            if (found != null)
            {
                found.TopicState.IsSelected = true;
            }
        }
Exemplo n.º 5
0
        public DocTopic ParseClass(DotnetObject obj, DocTopic parentTopic)
        {
            var topic = new DocTopic(_project)
            {
                Title = $"{obj.FormattedName} Class",
                //ListTitle = obj.FormattedName,
                DisplayType = "classheader",

                ClassInfo = new ClassInfo
                {
                    MemberName      = obj.Name,
                    Signature       = obj.Signature,
                    RawSignature    = obj.RawTypeName,
                    Namespace       = obj.Namespace,
                    Scope           = obj.Scope,
                    Syntax          = obj.Syntax,
                    Implements      = obj.Implements,
                    Inherits        = obj.InheritsFrom,
                    InheritanceTree = obj.InheritanceTree,
                    Classname       = obj.FormattedName,
                    Assembly        = obj.Assembly,
                },

                Remarks = obj.Remarks,
                Example = obj.Example,
                SeeAlso = obj.SeeAlso,
            };

            topic.Parent = parentTopic;
            topic.CreateRelativeSlugAndLink(topic);
            topic.Body = obj.HelpText;

            parentTopic?.Topics.Add(topic);

            foreach (var meth in obj.Methods.Where(m => m.IsConstructor))
            {
                var childTopic = ParseMethod(meth, topic);
                parentTopic.Topics.Add(childTopic);
            }
            foreach (var meth in obj.Methods.Where(m => !m.IsConstructor).OrderBy(m => m.Name))
            {
                var childTopic = ParseMethod(meth, topic);
                parentTopic.Topics.Add(childTopic);
            }
            foreach (var prop in obj.Properties.OrderBy(p => p.Name))
            {
                var childTopic = ParseProperty(prop, topic);
                parentTopic.Topics.Add(childTopic);
            }
            //foreach (var ev in obj.Events)
            //{
            //    var childTopic = ParseEvent(ev, parentTopic);
            //    parentTopic.Topics.Add(childTopic);
            //}
            return(topic);
        }
Exemplo n.º 6
0
        private void TreeViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var selected = TreeTopicBrowser.SelectedItem as DocTopic;

            if (selected != null)
            {
                SelectedTopic = selected;
                TopicSelected?.Invoke(selected);
            }
        }
        IEnumerable <DocTopic> GetParentTopics(DocTopic topic)
        {
            List <DocTopic> topicList = new List <DocTopic>();

            while (!string.IsNullOrEmpty(topic.ParentId))
            {
                var parentTopic = KavaDocsModel.ActiveProject.Topics.First(tp => tp.Id == topic.ParentId);
                topicList.Add(parentTopic);
                topic = parentTopic;
            }

            return(topicList);
        }
Exemplo n.º 8
0
        public NewTopicDialog(MainWindow window)
        {
            InitializeComponent();
            mmApp.SetThemeWindowOverride(this);

            Owner    = window;
            Window   = window;
            AppModel = kavaUi.AddinModel;

            Topic = new DocTopic(AppModel.ActiveProject);

            Loaded += NewTopicDialog_Loaded;
        }
        /// <summary>
        /// Called after a topic file (the MD file linked to a topic)
        /// has been saved on disk. This allows updating of topic
        /// info from the saved content.
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="doc"></param>
        public void OnTopicFilesSaved(DocTopic topic, MarkdownDocument doc)
        {
            if (topic == null)
            {
                return;
            }

            var projectFilename = topic?.Project?.Filename;
            var filename        = doc.Filename.ToLower();


            if (topic.Body == "NoContent")
            {
                topic.Body = null;
            }

            // Save Project File
            if (projectFilename != null && filename.Equals(projectFilename, StringComparison.InvariantCultureIgnoreCase))
            {
                // reload the project
                KavaDocsModel.LoadProject(KavaDocsModel.ActiveProject.Filename);
                return;
            }

            // Check for explicit KavaDocs Documents
            if (string.IsNullOrEmpty(doc.CurrentText))
            {
                return;
            }

            // Save the underlying topic file
            if (filename.Equals(topic.GetTopicFileName(), StringComparison.InvariantCultureIgnoreCase))
            {
                KavaDocsModel.ActiveProject.UpdateTopicFromMarkdown(doc, topic);
                KavaDocsModel.ActiveProject.SaveProject();
            }
            // READ-ONLY this shouldn't really happen any more
            // Saving the KavaDocs.md file - assume we're on the active topic
            else if (filename.Equals(topic.GetKavaDocsEditorFilePath(), StringComparison.InvariantCultureIgnoreCase))
            {
                topic.Body = doc.CurrentText;
                KavaDocsModel.ActiveProject.UpdateTopicFromMarkdown(doc, topic);
                KavaDocsModel.ActiveProject.SaveProject();
            }
            // Any previously activated document file
            else if (KavaDocsModel.ActiveMarkdownEditor.Properties.TryGetValue(Constants.EditorPropertyNames.KavaDocsTopic, out object objTopic))
            {
                KavaDocsModel.ActiveProject.UpdateTopicFromMarkdown(doc, objTopic as DocTopic);
                KavaDocsModel.ActiveProject.SaveProject();
            }
        }
Exemplo n.º 10
0
        private void TreeTopicBrowser_Selected(object sender, RoutedEventArgs e)
        {
            e.Handled = true; // don't bubble up through parents

            var topic = TreeTopicBrowser.SelectedItem as DocTopic;

            if (topic != null)
            {
                SelectedTopic = topic;

                TreeViewItem tvi = e.OriginalSource as TreeViewItem;
                tvi?.BringIntoView();
            }
        }
Exemplo n.º 11
0
        DocProject CreateTopics()
        {
            var project = new DocProject();

            var topic = new DocTopic(null)
            {
                Title    = "Markdown Monster",
                Body     = "Markdown Monster is an easy to use Makrdown Editor and Web Publishing tool.",
                Keywords = "markdown, editor"
            };

            topic.Properties.Add("Custom", "Custom Field");
            topic.Properties.Add("Custom2", "Custom Field 2");

            var rootParentId = topic.Id;

            Assert.True(project.SaveTopic(topic), project.ErrorMessage);

            topic = new DocTopic(project)
            {
                Title    = "Markdown Monster 2",
                Body     = "Markdown Monster is an easy to use Makrdown Editor and Web Publishing tool.",
                Keywords = "markdown, editor"
            };
            topic.Properties.Add("Custom", "Custom Field");
            topic.Properties.Add("Custom2", "Custom Field 2");

            Assert.True(project.SaveTopic(topic), project.ErrorMessage);


            topic = new DocTopic(project)
            {
                ParentId = rootParentId,
                Title    = "Markdown Monster 3",
                Body     = "Markdown Monster is an easy to use Makrdown Editor and Web Publishing tool.",
                Keywords = "markdown, editor"
            };
            topic.Properties.Add("Custom", "Custom Field");
            topic.Properties.Add("Custom3", "Custom Field 3");

            Assert.True(project.SaveTopic(topic), project.ErrorMessage);

            return(project);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Attaches a KavaDocs Topic to the current document and sets the Identifier to
        /// `KavaDocsDocument' so that you can easily retrieve the topic associated with
        /// an open document.
        ///
        /// Used to allow for syncing back topics to the underlying topic.
        /// </summary>
        /// <param name="editor"></param>
        /// <param name="topic"></param>
        public static void SetEditorWithTopic(MarkdownDocumentEditor editor, DocTopic topic, bool isUnEdited = true)
        {
            if (editor == null)
            {
                return;
            }

            if (topic == null)
            {
                editor.Identifier = null;
                editor.Properties.Remove(Constants.EditorPropertyNames.KavaDocsTopic);
                editor.Properties.Remove(Constants.EditorPropertyNames.KavaDocsUnedited);
            }
            else
            {
                editor.Identifier = "KavaDocsDocument";
                editor.Properties[Constants.EditorPropertyNames.KavaDocsTopic]    = topic;
                editor.Properties[Constants.EditorPropertyNames.KavaDocsUnedited] = isUnEdited;
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Parses an entire assembly
        /// </summary>
        /// <returns></returns>
        public DocTopic ParseAssembly(string assemblyFile, DocTopic parentTopic, bool parseXmlDocs = true)
        {
            var parser = new Westwind.TypeImporter.TypeParser()
            {
                ParseXmlDocumentation = parseXmlDocs,
                NoInheritedMembers    = NoInheritedMembers,
                ClassesToImport       = ClassesToImport
            };

            var topics = new List <DocTopic>();

            var types = parser.GetAllTypes(assemblyFile);

            if (types == null || types.Count < 1)
            {
                return(null);
            }

            try
            {
                foreach (var type in types)
                {
                    var topic = ParseClass(type, parentTopic);
                    topic.Parent = parentTopic;
                    topics.Add(topic);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            if (parentTopic == null)
            {
                parentTopic = new DocTopic();
            }

            parentTopic.Topics = new System.Collections.ObjectModel.ObservableCollection <DocTopic>(topics);

            return(parentTopic);
        }
Exemplo n.º 14
0
        public void SlugTests()
        {
            var topicName = "This is a simple topic name";

            var topic = new DocTopic();

            topic.Title = topicName;
            var slug = topic.CreateSlug();

            Console.WriteLine(slug);
            Assert.AreEqual(slug, "This-is-a-simple-topic-name");

            slug = topic.CreateSlug("This is Rick's Test topic");
            Console.WriteLine(slug);
            Assert.AreEqual(slug, "This-is-Ricks-Test-topic");


            slug = topic.CreateSlug("This is an off-center topic");
            Console.WriteLine(slug);
            Assert.AreEqual(slug, "This-is-an-off--center-topic");
        }
Exemplo n.º 15
0
        public DocTopic ParseMethod(ObjectMethod method, DocTopic parentClassTopic)
        {
            var topic = new DocTopic(_project)
            {
                Title = parentClassTopic.ClassInfo.Classname + "." + method.Name,
                //ListTitle = method.Name,
                DisplayType = method.IsConstructor ? "classconstructor" : "classmethod",

                ClassInfo = new ClassInfo
                {
                    MemberName    = method.Name,
                    Signature     = method.Signature,
                    Exceptions    = method.Exceptions,
                    Scope         = method.Scope,
                    IsStatic      = method.Static,
                    Syntax        = method.Syntax,
                    Parameters    = method.Parameters,
                    IsConstructor = method.IsConstructor,
                    IsInherited   = method.IsInherited,
                },

                Remarks = method.Remarks,
                Example = method.Example,
                SeeAlso = method.SeeAlso,

                ParentId = parentClassTopic?.Id
            };

            topic.Parent = parentClassTopic;
            topic.CreateRelativeSlugAndLink(topic);
            topic.Body = method.HelpText;

            parentClassTopic?.Topics.Add(topic);



            return(topic);
        }
        public void BasicRazorTemplate()
        {
            string folder = PathUtility.GetDeployPath();
            var    razor  = new RazorTemplates();

            razor.StartRazorHost("C:\\projects2010\\DocumentationMonster\\SampleProject");

            var topic = new DocTopic(null)
            {
                Title = "New Topic",
                Body  = "Getting to the **point** of it all"
            };
            string error;
            string output = razor.RenderTemplate("~/topic.cshtml", topic, out error);

            if (error != null)
            {
                Console.WriteLine(error);
            }
            Assert.NotNull(output);

            Console.WriteLine(output);
        }
        /// <summary>
        /// Searches the tree for a specific item
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="fullName"></param>
        /// <returns></returns>
        public DocTopic FindTopic(DocTopic parent, DocTopic topic)
        {
            var topics = parent?.Topics;

            if (topics == null)
            {
                topics = Project.Topics;
            }

            if (parent != null)
            {
                // check for root folder match
                if (parent.Link == topic.Link)
                {
                    return(parent);
                }
            }

            foreach (var ttopic in topics)
            {
                if (ttopic.Link == topic.Link)
                {
                    return(ttopic);
                }

                if (ttopic.Topics != null && ttopic.Topics.Count > 0)
                {
                    var ftopic = FindTopic(ttopic, topic);
                    if (ftopic != null)
                    {
                        return(ftopic);
                    }
                }
            }

            return(null);
        }
        public bool SaveProjectFileForTopic(DocTopic topic, DocProject project = null)
        {
            if (topic == null)
            {
                return(false);
            }

            if (!topic.TopicState.IsDirty)
            {
                return(false);
            }

            if (!string.IsNullOrEmpty(topic.TopicState.OldLink) && topic.TopicState.OldLink != topic.Link)
            {
                if (MessageBox.Show(
                        $@"Link has changed from {topic.TopicState.OldLink} to {
                                topic.Link
                            }.\r\n\rnDo you want to fix up the link and file?",
                        "Topic Link Changed",
                        MessageBoxButton.YesNo,
                        MessageBoxImage.Question) == MessageBoxResult.Yes)
                {
                    var oldFile = topic.GetTopicFileName(topic.TopicState.OldLink);
                    topic.SaveTopicFile(); // save new file
                    File.Delete(oldFile);
                }
            }


            if (project == null)
            {
                project = kavaUi.AddinModel.ActiveProject;
            }

            project.SaveProjectAsync();
            return(true);
        }
Exemplo n.º 19
0
 public void SelectTopic(DocTopic topic)
 {
     Model.SelectTopic(topic);
 }
 public void LoadTopic(DocTopic topic)
 {
     Model.KavaDocsModel.ActiveTopic = topic;
     topic.TopicState.IsSelected     = true;
 }
 void TopicSelected(DocTopic topic)
 {
     Model.ParentTopic = topic;
 }
        void ParseFolder(string folderName, DocProject project, DocTopic parentTopic, string inputRootFolder, string outputRootFolder)
        {
            foreach (var folder in Directory.GetDirectories(folderName).OrderBy(f => f.ToLower()))
            {
                var justFolder = Path.GetFileName(folder);
                if (justFolder == ".git" || justFolder == "node_modules")
                {
                    continue;
                }

                var topic = new DocTopic(project)
                {
                    Title       = Path.GetFileName(folder),
                    DisplayType = "header",
                    Type        = TopicBodyFormats.Markdown,
                    Project     = project,
                    ParentId    = parentTopic?.Id,
                    Topics      = new ObservableCollection <DocTopic>()
                };

                if (parentTopic != null)
                {
                    parentTopic.Topics.Add(topic);
                }
                else
                {
                    project.Topics.Add(topic);
                }

                var newFolder    = Path.Combine(outputRootFolder, justFolder);
                var newFolderWWW = Path.Combine(outputRootFolder, "wwwroot", justFolder);

                //if (!Directory.Exists(newFolder))
                //    Directory.CreateDirectory(newFolder);
                if (!Directory.Exists(newFolderWWW))
                {
                    Directory.CreateDirectory(newFolderWWW);
                }

                ParseFolder(folder, project, topic, inputRootFolder, outputRootFolder);
            }

            foreach (var file in Directory.GetFiles(folderName))
            {
                var title = Path.GetFileNameWithoutExtension(file);
                var ext   = Path.GetExtension(file).Replace(".", "").ToLower();

                if (!KavaDocsConfiguration.AllowedTopicFileExtensions.Contains(ext))
                {
                    // copy all non-MD files
                    var targetFile = FileUtils.GetRelativePath(file, inputRootFolder);
                    targetFile = System.Net.WebUtility.UrlDecode(targetFile);
                    var newFile = Path.Combine(outputRootFolder, "wwwroot", targetFile);
                    if (newFile.ToLower() != project.Filename.ToLower())
                    {
                        File.Copy(file, newFile);
                    }
                    continue;
                }

                var topic = new DocTopic(project)
                {
                    Title       = title,
                    DisplayType = "topic",
                    Type        = TopicBodyFormats.Markdown,
                    Project     = project,
                    ParentId    = parentTopic?.Id
                };

                if (ext == ".htm" || ext == ".html")
                {
                    topic.Type = TopicBodyFormats.Html;
                }

                topic.Body = File.ReadAllText(file);

                if (parentTopic != null)
                {
                    parentTopic.Topics.Add(topic);
                }
                else
                {
                    project.Topics.Add(topic);
                }
            }
        }
Exemplo n.º 23
0
 public TemplateHelpers(DocTopic topic, RazorTemplateBase template)
 {
     Topic    = topic;
     Project  = Topic?.Project;
     Template = template as RazorTemplateFolderHost <DocTopic>;
 }
Exemplo n.º 24
0
 public DocTopic ParseNamespace(DocTopic parentTopic)
 {
     return(null);
 }
Exemplo n.º 25
0
 public TypeTopicParser(DocProject project, DocTopic parentTopic)
 {
     _project     = project;
     _parentTopic = parentTopic;
 }
Exemplo n.º 26
0
        private void TreeViewItem_Drop(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(typeof(DocTopic)))
            {
                return;
            }

            DocTopic sourceTopic = (DocTopic)e.Data.GetData(typeof(DocTopic));

            if (sourceTopic == null)
            {
                return;
            }

            var tvItem = WindowUtilities.FindAnchestor <TreeViewItem>((DependencyObject)e.OriginalSource);

            if (tvItem == null)
            {
                return;
            }
            var targetTopic = tvItem.DataContext as DocTopic;

            if (targetTopic == null)
            {
                return;
            }

            if (sourceTopic == targetTopic)
            {
                return;
            }

            if (MoveTopicCommand == null)
            {
                CreateMoveTopicCommand();
            }

            _dragContextMenu = new ContextMenu()
            {
                DataContext = DragMoveResult
            };

            var mi = new MenuItem
            {
                Header           = "Move underneath this item",
                Tag              = _dragContextMenu,
                Command          = MoveTopicCommand,
                CommandParameter = new DragMoveResult
                {
                    SourceTopic  = sourceTopic,
                    TargetTopic  = targetTopic,
                    DropLocation = DropLocations.Below
                }
            };

            _dragContextMenu.Items.Add(mi);
            _dragContextMenu.Items.Add(new Separator());
            _dragContextMenu.Items.Add(new MenuItem
            {
                Header           = "Move before this item",
                Tag              = _dragContextMenu,
                Command          = MoveTopicCommand,
                CommandParameter = new DragMoveResult
                {
                    SourceTopic  = sourceTopic,
                    TargetTopic  = targetTopic,
                    DropLocation = DropLocations.Before
                }
            });
            _dragContextMenu.Items.Add(new MenuItem
            {
                Header           = "Move after this item",
                Tag              = _dragContextMenu,
                Command          = MoveTopicCommand,
                CommandParameter = new DragMoveResult
                {
                    SourceTopic  = sourceTopic,
                    TargetTopic  = targetTopic,
                    DropLocation = DropLocations.After
                }
            });
            _dragContextMenu.Items.Add(new Separator());
            _dragContextMenu.Items.Add(new MenuItem
            {
                Header           = "Cancel topic move",
                Tag              = _dragContextMenu,
                Command          = MoveTopicCommand,
                CommandParameter = null
            });

            WindowUtilities.DoEvents();
            _dragContextMenu.Placement       = System.Windows.Controls.Primitives.PlacementMode.Mouse;
            _dragContextMenu.PlacementTarget = tvItem;
            _dragContextMenu.Visibility      = Visibility.Visible;
            _dragContextMenu.IsOpen          = true;
            WindowUtilities.DoEvents();
        }
Exemplo n.º 27
0
 public TreeViewItem GetTreeviewItem(DocTopic item)
 {
     return((TreeViewItem)TreeTopicBrowser
            .ItemContainerGenerator
            .ContainerFromItem(item));
 }
Exemplo n.º 28
0
        public bool HandleSelection(DocTopic topic = null, bool forceFocus = false)
        {
            bool selectTopic = false;

            if (topic == null)
            {
                topic = TreeTopicBrowser.SelectedItem as DocTopic;
            }
            else
            {
                selectTopic = true;
            }

            if (topic == null)
            {
                return(false);
            }

            var lastTopic = kavaUi.AddinModel.ActiveTopic;

            if (lastTopic != null)
            {
                lastTopic.TopicState.IsSelected = false;
            }

            kavaUi.AddinModel.LastTopic = lastTopic;

            bool result = SaveProjectFileForTopic(kavaUi.AddinModel.LastTopic);

            if (result)
            {
                kavaUi.AddinModel.Window.ShowStatus("Topic saved.", 3000);
            }

            kavaUi.AddinModel.ActiveTopic = topic;

            // TODO: Move to function
            if (kavaUi.AddinModel.RecentTopics.Contains(topic))
            {
                kavaUi.AddinModel.RecentTopics.Remove(topic);
            }
            kavaUi.AddinModel.RecentTopics.Insert(0, topic);

            if (kavaUi.AddinModel.RecentTopics.Count > 15)
            {
                kavaUi.AddinModel.RecentTopics =
                    new ObservableCollection <DocTopic>(kavaUi.AddinModel.RecentTopics.Take(14));
            }

            //OpenTopicInEditor();
            Dispatcher.InvokeAsync(() => OpenTopicInEditor(), DispatcherPriority.ApplicationIdle);

            var file = topic.GetTopicFileName();
            var doc  = new MarkdownDocument();

            doc.Load(file);

            // set topic state to selected and unchanged
            if (selectTopic)
            {
                topic.TopicState.IsSelected = true;
            }
            topic.TopicState.IsDirty = false;

            return(true);
        }
Exemplo n.º 29
0
 /// <summary>
 /// Makes a topic the active topic
 /// </summary>
 /// <param name="topic"></param>
 public void LoadTopic(DocTopic topic)
 {
     ActiveTopic = topic;
 }
Exemplo n.º 30
0
        /// <summary>
        /// Opens read
        /// </summary>
        /// <returns></returns>
        public TabItem OpenTopicInEditor(bool setFocus = false)
        {
            Debug.WriteLine("OpenTopicInEditor");

            DocTopic topic = TreeTopicBrowser.SelectedItem as DocTopic;

            if (topic == null)
            {
                return(null);
            }

            var window = Model.KavaDocsModel.Window;

            TabItem tab;

            if (topic != null && topic.Body != null && (topic.IsLink || topic.Body.StartsWith("http")))
            {
                tab = Model.MarkdownMonsterModel.Window.OpenBrowserTab(topic.Link ?? topic.Body);
                return(tab);
            }

            var file = topic.GetTopicFileName(force: true);

            MarkdownDocumentEditor editor = null;

            // is tab open already as a file? If so use that
            tab = window.GetTabFromFilename(file);
            if (tab != null)
            {
                editor = tab?.Tag as MarkdownDocumentEditor;
                if (editor == null)
                {
                    return(null);
                }
            }
            else
            {
                // Will also open the tab if not open yet
                // EXPLICITLY NOT SELECTING THE TAB SO THAT IT'S NOT RENDERED YET
                // Assign topic first then explicitly select
                //tab = Model.KavaDocsModel.Window.RefreshTabFromFile(file, noFocus: !setFocus, isPreview: true, noSelectTab:true);

                tab = Model.KavaDocsModel.Window.ActivateTab(file,
                                                             noFocus: !setFocus,
                                                             isPreview: true,
                                                             noSelectTab: true);

                //RefreshTabFromFile(file, noFocus: !setFocus, isPreview: true, noSelectTab: true);

                editor = tab?.Tag as MarkdownDocumentEditor;
                if (editor == null)
                {
                    return(null);
                }
            }

            if (tab == null)
            {
                return(null);
            }

            // make sure topic is associated with editor
            SetEditorWithTopic(editor, topic, isUnEdited: true); // kavaUi.AddinModel.ActiveTopic);

            // Explicitly read in the current text from an open tab and save to body
            var body = editor.GetMarkdown();

            if (!string.IsNullOrEmpty(body))
            {
                topic.Body = topic.StripYaml(body);
            }

            if (string.IsNullOrEmpty(topic.Link))
            {
                var relative = FileUtils.GetRelativePath(topic.GetTopicFileName(), topic.Project.ProjectDirectory);
                if (!string.IsNullOrEmpty(relative))
                {
                    topic.Link = FileUtils.NormalizePath(relative);
                    if (string.IsNullOrEmpty(topic.Link))
                    {
                        topic.Link = topic.Link.Replace("\\", "/");
                    }
                }
            }

            if (body != topic.Body)
            {
                editor.SetMarkdown(topic.Body);
            }

            window.TabControl.SelectedItem = tab;


            return(tab);
        }