Esempio n. 1
0
        public void OnDeleteFile(File file)
        {
            if (file.Type == FileType.Image)
            {
                // We need to get the image links affected by this

                CodeDB.Accessor accessor = EngineInstance.CodeDB.GetAccessor();

                try
                {
                    accessor.GetReadOnlyLock();
                    IDObjects.NumberSet linksAffected = accessor.GetImageLinkIDsByTarget(file.ID, Delegates.NeverCancel);
                    accessor.ReleaseLock();

                    unprocessedChanges.DeleteImageFile((ImageFile)file, linksAffected);
                }
                finally
                {
                    if (accessor.HasLock)
                    {
                        accessor.ReleaseLock();
                    }

                    accessor.Dispose();
                }
            }
        }
Esempio n. 2
0
        public void OnFileChanged(File file)
        {
            if (file.Type == FileType.Style)
            {
                // Add it to the build list.  The build function will check if it's part of a style we're using.
                unprocessedChanges.AddStyleFile(file.ID);
            }

            else if (file.Type == FileType.Image)
            {
                // Add it to the build list.  The build function will check if it's used or unused.
                unprocessedChanges.AddImageFile(file.ID);

                // Also rebuild the HTML files containing links to it in case the dimensions changed.

                // Creating and destroying an accessor on every event might be a heavy operation.  This event shouldn't
                // happen too often though so it's probably not worth caching one, plus it would be difficult to know when
                // to release it.
                CodeDB.Accessor accessor = EngineInstance.CodeDB.GetAccessor();

                try
                {
                    accessor.GetReadOnlyLock();

                    List <ImageLink> imageLinks = accessor.GetImageLinksByTarget(file.ID, Delegates.NeverCancel,
                                                                                 CodeDB.Accessor.GetImageLinkFlags.DontLookupClasses);

                    foreach (var imageLink in imageLinks)
                    {
                        unprocessedChanges.AddSourceFile(imageLink.FileID);

                        if (imageLink.ClassID != 0)
                        {
                            unprocessedChanges.AddClass(imageLink.ClassID);
                        }
                    }

                    accessor.ReleaseLock();
                }
                finally
                {
                    if (accessor.HasLock)
                    {
                        accessor.ReleaseLock();
                    }

                    accessor.Dispose();
                }
            }

            // We don't care about source files here.  They'll be handled by functions like OnUpdateTopic().
        }
Esempio n. 3
0
        /* Function: ProcessDeletedFile
         * Takes a deleted <File> and updates <CodeDB.Manager>.  The <CodeDB.Accessor> should NOT already hold a lock.
         */
        protected ProcessFileResult ProcessDeletedFile(File file, CodeDB.Accessor codeDBAccessor, CancelDelegate cancelDelegate)
        {
            if (file.Type == FileType.Source)
            {
                return(ProcessDeletedSourceFile(file, codeDBAccessor, cancelDelegate));
            }

            // Style and image files are only processed by output targets.  They're not in CodeDB so we don't need to do anything here.
            else
            {
                return(ProcessFileResult.Success);
            }
        }
Esempio n. 4
0
        // Group: Functions
        // __________________________________________________________________________

        /* Function: BuildSourceFile
         * Builds an output file based on a source file.  The accessor should NOT hold a lock on the database.  This will also
         * build the metadata files.
         */
        protected void BuildSourceFile(int fileID, CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
                        #if DEBUG
            if (accessor.LockHeld != CodeDB.Accessor.LockType.None)
            {
                throw new Exception("Shouldn't call BuildSourceFile() when the accessor already holds a database lock.");
            }
                        #endif

            Components.HTMLTopicPages.File page = new Components.HTMLTopicPages.File(this, fileID);

            bool hasTopics = page.Build(accessor, cancelDelegate);

            if (cancelDelegate())
            {
                return;
            }


            if (hasTopics)
            {
                lock (accessLock)
                {
                    if (buildState.SourceFilesWithContent.Add(fileID) == true)
                    {
                        buildState.NeedToBuildMenu = true;;
                    }
                }
            }
            else
            {
                DeleteOutputFileIfExists(page.OutputFile);
                DeleteOutputFileIfExists(page.ToolTipsFile);
                DeleteOutputFileIfExists(page.SummaryFile);
                DeleteOutputFileIfExists(page.SummaryToolTipsFile);

                lock (accessLock)
                {
                    if (buildState.SourceFilesWithContent.Remove(fileID) == true)
                    {
                        buildState.NeedToBuildMenu = true;
                    }
                }
            }
        }
Esempio n. 5
0
        /* Function: GetLinks
         *
         * Retrieves the <Links> appearing in the file.
         *
         * If the <CodeDB.Accessor> doesn't have a lock this function will acquire and release a read-only lock.
         * If it already has a lock it will use it and not release it.
         */
        public override List <Link> GetLinks(CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
            bool releaseLock = false;

            if (accessor.LockHeld == CodeDB.Accessor.LockType.None)
            {
                accessor.GetReadOnlyLock();
                releaseLock = true;
            }

            try
            { return(accessor.GetLinksInFile(fileID, cancelDelegate)); }
            finally
            {
                if (releaseLock)
                {
                    accessor.ReleaseLock();
                }
            }
        }
Esempio n. 6
0
        /* Function: ProcessDeletedSourceFile
         * Takes a deleted <File> and updates <CodeDB.Manager>.  The <CodeDB.Accessor> should NOT already hold a lock.
         */
        protected ProcessFileResult ProcessDeletedSourceFile(File file, CodeDB.Accessor codeDBAccessor,
                                                             CancelDelegate cancelDelegate)
        {
            codeDBAccessor.GetReadPossibleWriteLock();

            try
            {
                codeDBAccessor.DeleteTopicsInFile(file.ID, cancelDelegate);
                codeDBAccessor.DeleteLinksInFile(file.ID, cancelDelegate);
                codeDBAccessor.DeleteImageLinksInFile(file.ID, cancelDelegate);
            }
            finally
            { codeDBAccessor.ReleaseLock(); }

            // Need this check in case CodeDB quit early because of the cancel delegate.
            if (cancelDelegate())
            {
                return(ProcessFileResult.Cancelled);
            }

            return(ProcessFileResult.Success);
        }
Esempio n. 7
0
        // Group: Prefix Data File Functions
        // __________________________________________________________________________


        /* Function: BuildPrefixDataFile
         */
        public void BuildPrefixDataFile(string prefix, CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
            var keywordEntries = GetPrefixKeywords(prefix, accessor, cancelDelegate);

            if (keywordEntries == null || keywordEntries.Count == 0)
            {
                HTMLBuilder.DeleteOutputFileIfExists(HTMLBuilder.SearchIndex_PrefixDataFile(prefix));
                return;
            }

            SortKeywordEntries(keywordEntries);

            foreach (var keywordEntry in keywordEntries)
            {
                SortTopicEntries(keywordEntry);
                RemoveDuplicateTopics(keywordEntry);
            }

            BuildPrefixDataFileJS(prefix, keywordEntries);

            Path path = HTMLBuilder.SearchIndex_PrefixDataFile(prefix);

            try
            {
                // This will create multiple subdirectories if needed, and will not throw an exception if it already exists.
                // We can't use SearchIndex_DataFolder because we may need a subfolder of it.
                System.IO.Directory.CreateDirectory(path.ParentFolder);
            }
            catch (Exception e)
            {
                throw new Exceptions.UserFriendly(
                          Locale.Get("NaturalDocs.Engine", "Error.CouldNotCreateOutputFolder(name, exception)",
                                     path.ParentFolder, e.Message)
                          );
            }

            System.IO.File.WriteAllText(path, output.ToString());
        }
Esempio n. 8
0
        /* Function: ProcessDeletedFile
         * Takes a deleted <File> retrieved using <ClaimDeletedFile()> and updates <CodeDB.Manager>.  Returns the result code that
         * should be passed to <ReleaseClaimedFile()> if it was retrieved by <ClaimDeletedFile()>.  The <CodeDB.Accessor> should NOT
         * already hold a lock.
         */
        public ReleaseClaimedFileReason ProcessDeletedFile(File file, CodeDB.Accessor codeDBAccessor, CancelDelegate cancelDelegate)
        {
            // Source files

            if (file.Type == FileType.Source)
            {
                codeDBAccessor.GetReadPossibleWriteLock();

                try
                {
                    codeDBAccessor.DeleteTopicsInFile(file.ID, cancelDelegate);
                    codeDBAccessor.DeleteLinksInFile(file.ID, cancelDelegate);
                    codeDBAccessor.DeleteImageLinksInFile(file.ID, cancelDelegate);
                }
                finally
                { codeDBAccessor.ReleaseLock(); }

                // Need this check in case CodeDB quit early because of the cancel delegate.
                if (cancelDelegate())
                {
                    return(ReleaseClaimedFileReason.CancelledProcessing);
                }
                else
                {
                    return(ReleaseClaimedFileReason.SuccessfullyProcessed);
                }
            }


            // Style and image files

            else
            {
                // These are only processed by output builders.  They're not in CodeDB so we don't need to do anything here.
                return(ReleaseClaimedFileReason.SuccessfullyProcessed);
            }
        }
Esempio n. 9
0
        // Group: Abstract Functions
        // __________________________________________________________________________


        /* Function: GetTopics
         *
         * Retrieves the <Topics> for the page's location.
         *
         * When implementing this function note that the <CodeDB.Accessor> may or may not already have a lock.
         */
        public abstract List <Topic> GetTopics(CodeDB.Accessor accessor, CancelDelegate cancelDelegate);
Esempio n. 10
0
        // Group: Functions
        // __________________________________________________________________________


        /* Function: BuildMenu
         */
        protected void BuildMenu(CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
            JSMenuData jsMenuData = new JSMenuData(this);


            // Build file menu

            lock (accessLock)
            {
                foreach (int fileID in buildState.SourceFilesWithContent)
                {
                    if (cancelDelegate())
                    {
                        return;
                    }

                    jsMenuData.AddFile(EngineInstance.Files.FromID(fileID));
                }
            }


            // Build class and database menu

            List <KeyValuePair <int, ClassString> > classes = null;

            accessor.GetReadOnlyLock();
            try
            { classes = accessor.GetClassesByID(buildState.ClassFilesWithContent, cancelDelegate); }
            finally
            { accessor.ReleaseLock(); }

            foreach (KeyValuePair <int, ClassString> classEntry in classes)
            {
                if (cancelDelegate())
                {
                    return;
                }

                jsMenuData.AddClass(classEntry.Value);
            }


            // Condense, sort, and build

            jsMenuData.Condense();

            if (cancelDelegate())
            {
                return;
            }

            jsMenuData.Sort();

            if (cancelDelegate())
            {
                return;
            }

            StringTable <IDObjects.NumberSet> newMenuDataFiles = jsMenuData.Build();

            if (cancelDelegate())
            {
                return;
            }


            // Clear out any old menu files that are no longer in use.

            lock (accessLock)
            {
                foreach (var usedMenuDataFileInfo in buildState.UsedMenuDataFiles)
                {
                    string type = usedMenuDataFileInfo.Key;
                    IDObjects.NumberSet oldNumbers = usedMenuDataFileInfo.Value;
                    IDObjects.NumberSet newNumbers = newMenuDataFiles[type];

                    if (newNumbers != null)
                    {
                        // It's okay that we're altering the original NumberSet, we're throwing it out later.
                        oldNumbers.Remove(newNumbers);
                    }

                    foreach (int oldNumber in oldNumbers)
                    {
                        try
                        { System.IO.File.Delete(Menu_DataFile(type, oldNumber)); }
                        catch (Exception e)
                        {
                            if (!(e is System.IO.IOException || e is System.IO.DirectoryNotFoundException))
                            {
                                throw;
                            }
                        }
                    }
                }

                buildState.UsedMenuDataFiles = newMenuDataFiles;
            }
        }
Esempio n. 11
0
 /* Function: BuildSearchPrefixDataFile
  */
 protected void BuildSearchPrefixDataFile(string prefix, CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
 {
     Components.JSSearchData searchData = new Components.JSSearchData(this);
     searchData.BuildPrefixDataFile(prefix, accessor, cancelDelegate);
 }
Esempio n. 12
0
        // Group: Functions
        // __________________________________________________________________________


        /* Function: BuildSearchPrefixIndex
         */
        protected void BuildSearchPrefixIndex(CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
            Components.JSSearchData searchData = new Components.JSSearchData(this);
            searchData.BuildPrefixIndex();
        }
Esempio n. 13
0
        /* Function: BuildDataFiles
         *
         * Builds the content HTML file for the passed <Context's> <PageLocation> and its supporting <JSONSummary> and
         * <JSONToolTips>.  Returns whether there was any content.  It will also return false if it was interrupted by the
         * <CancelDelegate>.
         *
         * If the <CodeDB.Accessor> doesn't have a lock, this function will automatically acquire and release a read-only
         * lock.  This is the preferred way of using this function as the lock will only be held during the data querying stage
         * and will be  released before writing output to disk.
         *
         * If it already had a lock it will use it and not release it unless you set releaseExistingLocks.
         */
        public bool BuildDataFiles(Context context, CodeDB.Accessor accessor, CancelDelegate cancelDelegate,
                                   bool releaseExistingLocks = false)
        {
            this.Context = context;
            var location = context.Page;

            List <Engine.Topics.Topic>    topics;
            List <Engine.Links.Link>      links;
            List <Engine.Links.ImageLink> imageLinks;

            bool releaseDBLock = false;

            if (accessor.LockHeld == CodeDB.Accessor.LockType.None)
            {
                accessor.GetReadOnlyLock();
                releaseDBLock = true;
            }
            else if (releaseExistingLocks)
            {
                releaseDBLock = true;
            }

            try
            {
                // Get the topics from the database.

                if (location.IsSourceFile)
                {
                    topics = accessor.GetTopicsInFile(location.FileID, cancelDelegate);
                }
                else if (location.InHierarchy)
                {
                    topics = accessor.GetTopicsInClass(location.ClassID, cancelDelegate);
                }
                else
                {
                    throw new NotImplementedException();
                }

                if (topics == null || topics.Count == 0 || cancelDelegate())
                {
                    return(false);
                }


                // Create the class view if appropriate

                if (location.InHierarchy)
                {
                    ClassView.Merge(ref topics, EngineInstance);

                    // It's possible for ClassView to reduce the number of topics to zero, so check for that and treat it as if the
                    // class page has no content.
                    if (topics.Count == 0)
                    {
                        return(false);
                    }
                }


                // Get the links from the database.

                if (location.IsSourceFile)
                {
                    links      = accessor.GetLinksInFile(location.FileID, cancelDelegate) ?? new List <Engine.Links.Link>();
                    imageLinks = accessor.GetImageLinksInFile(location.FileID, cancelDelegate) ?? new List <Engine.Links.ImageLink>();
                }
                else if (location.InHierarchy)
                {
                    links      = accessor.GetLinksInClass(location.ClassID, cancelDelegate) ?? new List <Engine.Links.Link>();
                    imageLinks = accessor.GetImageLinksInClass(location.ClassID, cancelDelegate) ?? new List <Engine.Links.ImageLink>();
                }
                else
                {
                    throw new NotImplementedException();
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // Find all the classes that are defined in this page, since we have to do additional lookups for class prototypes.

                IDObjects.NumberSet classIDsDefined = new IDObjects.NumberSet();

                foreach (var topic in topics)
                {
                    if (topic.DefinesClass)
                    {
                        classIDsDefined.Add(topic.ClassID);
                    }
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // We need the class parent links of all the classes defined on this page so the class prototypes can show the parents.
                // If this is a class page then we can skip this step since all the links should already be included.  However, for any
                // other type of page this may not be the case.  A source file page would return all the links in that file, but the class
                // may be defined across multiple files and we need the class parent links in all of them.  In this case we need to look
                // up the class parent links separately by class ID.

                if (location.InHierarchy == false && classIDsDefined.IsEmpty == false)
                {
                    List <Engine.Links.Link> classParentLinks = accessor.GetClassParentLinksInClasses(classIDsDefined, cancelDelegate);

                    if (classParentLinks != null && classParentLinks.Count > 0)
                    {
                        links.AddRange(classParentLinks);
                    }
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // Now we need to find the children of all the classes defined on this page.  Get the class parent links that resolve to
                // any of the defined classes, but keep them separate for now.

                List <Engine.Links.Link> childLinks = null;

                if (classIDsDefined.IsEmpty == false)
                {
                    childLinks = accessor.GetClassParentLinksToClasses(classIDsDefined, cancelDelegate);
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // Get link targets for everything but the children, since they would just resolve to classes already in this file.

                IDObjects.NumberSet linkTargetIDs = new IDObjects.NumberSet();

                foreach (var link in links)
                {
                    if (link.IsResolved)
                    {
                        linkTargetIDs.Add(link.TargetTopicID);
                    }
                }

                List <Engine.Topics.Topic> linkTargets = accessor.GetTopicsByID(linkTargetIDs, cancelDelegate) ??
                                                         new List <Engine.Topics.Topic>();

                if (cancelDelegate())
                {
                    return(false);
                }


                // Now get targets for the children.

                List <Engine.Topics.Topic> childTargets = null;

                if (childLinks != null && childLinks.Count > 0)
                {
                    IDObjects.NumberSet childClassIDs = new IDObjects.NumberSet();

                    foreach (var childLink in childLinks)
                    {
                        childClassIDs.Add(childLink.ClassID);
                    }

                    childTargets = accessor.GetBestClassDefinitionTopics(childClassIDs, cancelDelegate);
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // We can merge the child links and targets into the main lists now.

                if (childLinks != null)
                {
                    links.AddRange(childLinks);
                }

                if (childTargets != null)
                {
                    linkTargets.AddRange(childTargets);
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // Now we need to find any Natural Docs and image links appearing inside the summaries of link targets.
                // The tooltips that will be generated for them include their summaries, and even though we don't generate
                // HTML links inside tooltips, how and if they're resolved affects their appearance.  We need to know whether
                // to include the original text with angle brackets, the text without angle brackets if it's resolved, or only part
                // of the text if it's a resolved named link.

                // Links don't store which topic they appear in but they do store the file, so gather the file IDs of the link
                // targets that have Natural Docs or image links in the summaries and get all the links in those files.

                // Links also store which class they appear in, so why not do this by class instead of by file?  Because a
                // link could be to something global, and the global scope could potentially have a whole hell of a lot of
                // content, depending on the project and language.  While there can also be some really long files, the
                // chances of that are smaller so we stick with doing this by file.

                IDObjects.NumberSet summaryLinkFileIDs = new IDObjects.NumberSet();

                foreach (var linkTarget in linkTargets)
                {
                    if (linkTarget.Summary != null &&
                        (linkTarget.Summary.IndexOf("<link type=\"naturaldocs\"") != -1 || linkTarget.Summary.IndexOf("<image ") != -1))
                    {
                        summaryLinkFileIDs.Add(linkTarget.FileID);
                    }
                }

                List <Engine.Links.Link>      summaryLinks      = null;
                List <Engine.Links.ImageLink> summaryImageLinks = null;

                if (!summaryLinkFileIDs.IsEmpty)
                {
                    summaryLinks      = accessor.GetNaturalDocsLinksInFiles(summaryLinkFileIDs, cancelDelegate);
                    summaryImageLinks = accessor.GetImageLinksInFiles(summaryLinkFileIDs, cancelDelegate);
                }

                if (cancelDelegate())
                {
                    return(false);
                }


                // Finally done with the database.

                if (releaseDBLock)
                {
                    accessor.ReleaseLock();
                    releaseDBLock = false;
                }


                // Determine the page title

                string pageTitle;

                if (context.Page.IsSourceFile)
                {
                    pageTitle = EngineInstance.Files.FromID(context.Page.FileID).FileName.NameWithoutPath;
                }
                else if (context.Page.InHierarchy)
                {
                    pageTitle = context.Page.ClassString.Symbol.LastSegment;
                }
                else
                {
                    throw new NotImplementedException();
                }


                // Build the HTML for the list of topics

                StringBuilder html = new StringBuilder("\r\n\r\n");

                HTML.Components.Topic   topicBuilder   = new HTML.Components.Topic(context);
                HTML.Components.Tooltip tooltipBuilder = new HTML.Components.Tooltip(context);

                // We don't put embedded topics in the output, so we need to find the last non-embedded one to make
                // sure that the "last" CSS tag is correctly applied.
                int lastNonEmbeddedTopic = topics.Count - 1;
                while (lastNonEmbeddedTopic > 0 && topics[lastNonEmbeddedTopic].IsEmbedded == true)
                {
                    lastNonEmbeddedTopic--;
                }

                for (int i = 0; i <= lastNonEmbeddedTopic; i++)
                {
                    string extraClass = null;

                    if (i == 0)
                    {
                        extraClass = "first";
                    }
                    else if (i == lastNonEmbeddedTopic)
                    {
                        extraClass = "last";
                    }

                    if (topics[i].IsEmbedded == false)
                    {
                        topicBuilder.AppendTopic(topics[i], context, links, linkTargets, imageLinks, html, topics, i + 1, extraClass);
                        html.Append("\r\n\r\n");
                    }
                }


                // Build the full HTML file

                Build(context.OutputFile, pageTitle, html.ToString(), PageType.Content);


                // Build summary and tooltips files

                JSONSummary summaryBuilder = new JSONSummary(context);
                summaryBuilder.ConvertToJSON(topics, context);
                summaryBuilder.BuildDataFile(pageTitle);

                JSONToolTips toolTipsBuilder = new JSONToolTips(context);
                toolTipsBuilder.ConvertToJSON(topics, links, imageLinks, context);
                toolTipsBuilder.BuildDataFileForSummary();

                toolTipsBuilder.ConvertToJSON(linkTargets, summaryLinks, summaryImageLinks, context);
                toolTipsBuilder.BuildDataFileForContent();

                return(true);
            }

            catch (Exception e)
            {
                try
                { e.AddNaturalDocsTask("Building HTML file " + context.OutputFile); }
                catch
                {  }

                throw;
            }

            finally
            {
                if (releaseDBLock)
                {
                    accessor.ReleaseLock();
                }
            }
        }
Esempio n. 14
0
 /* Function: GetPrefixKeywords
  */
 protected List <SearchIndex.KeywordEntry> GetPrefixKeywords(string prefix, CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
 {
     return(EngineInstance.SearchIndex.GetKeywordEntries(prefix, accessor, cancelDelegate));
 }
Esempio n. 15
0
        /* Function: GetKeywordEntries
         * Returns a list of all the <KeywordEntries> for a prefix, complete with all their <TopicEntries>.  If there are none it will return
         * null.  The returned list will not be in any particular order, it is up to the calling code to sort them as desired.
         */
        public List <KeywordEntry> GetKeywordEntries(string prefix, CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
            // Retrieve the topics from the database

            IDObjects.NumberSet topicIDs = PrefixTopicIDs(prefix);

            if (topicIDs == null || topicIDs.IsEmpty)
            {
                return(null);
            }

            List <Topic> topics        = null;
            bool         releaseDBLock = false;

            if (accessor.LockHeld == CodeDB.Accessor.LockType.None)
            {
                accessor.GetReadOnlyLock();
                releaseDBLock = true;
            }

            try
            {
                // Need to lookup class strings to be able to build hash paths
                topics = accessor.GetTopicsByID(topicIDs, cancelDelegate, CodeDB.Accessor.GetTopicFlags.BodyLengthOnly |
                                                CodeDB.Accessor.GetTopicFlags.DontLookupContexts |
                                                CodeDB.Accessor.GetTopicFlags.DontIncludeSummary |
                                                CodeDB.Accessor.GetTopicFlags.DontIncludePrototype);
            }
            finally
            {
                if (releaseDBLock)
                {
                    accessor.ReleaseLock();
                }
            }

            if (cancelDelegate())
            {
                return(null);
            }


            // Convert the topics into entries

            StringTable <KeywordEntry> keywordEntryTable = new StringTable <KeywordEntry>(KeySettings.IgnoreCase);

            foreach (var topic in topics)
            {
                TopicEntry topicEntry = new TopicEntry(topic, this);

                foreach (var keyword in topicEntry.Keywords)
                {
                    if (KeywordMatchesPrefix(keyword, prefix))
                    {
                        var keywordEntry = keywordEntryTable[keyword];

                        if (keywordEntry == null)
                        {
                            keywordEntry = new KeywordEntry(keyword);
                            keywordEntryTable[keyword] = keywordEntry;
                        }
                        else if (keywordEntry.Keyword != keyword)
                        {
                            // If they differ in case we still want to combine them, but we have to choose which case will be in the
                            // results.  Prioritize in this order: mixed case ('m'), all lowercase ('l'), all uppercase ('u').

                            char keywordCase, keywordEntryCase;

                            if (keyword == keyword.ToLower())
                            {
                                keywordCase = 'l';
                            }
                            else if (keyword == keyword.ToUpper())
                            {
                                keywordCase = 'u';
                            }
                            else
                            {
                                keywordCase = 'm';
                            }

                            if (keywordEntry.Keyword == keywordEntry.Keyword.ToLower())
                            {
                                keywordEntryCase = 'l';
                            }
                            else if (keywordEntry.Keyword == keywordEntry.Keyword.ToUpper())
                            {
                                keywordEntryCase = 'u';
                            }
                            else
                            {
                                keywordEntryCase = 'm';
                            }

                            if ((keywordCase == 'm' && keywordEntryCase != 'm') ||
                                (keywordCase == 'l' && keywordEntryCase == 'u'))
                            {
                                keywordEntry.Keyword = keyword;
                            }
                            else if (keywordCase == 'm' && keywordEntryCase == 'm')
                            {
                                // If they're both mixed, use the sort order.  This lets SomeValue be used instead of someValue.
                                if (string.Compare(keyword, keywordEntry.Keyword) > 0)
                                {
                                    keywordEntry.Keyword = keyword;
                                }
                            }
                        }

                        keywordEntry.TopicEntries.Add(topicEntry);
                    }
                }
            }

            List <KeywordEntry> keywordEntries = new List <KeywordEntry>(keywordEntryTable.Count);

            foreach (var keywordEntryTablePair in keywordEntryTable)
            {
                keywordEntries.Add(keywordEntryTablePair.Value);
            }


            return(keywordEntries);
        }
Esempio n. 16
0
        /* Function: BuildPrefixDataFile
         *
         * Creates a data file for a single prefix as described in <JavaScript Search Data>.  It requires a <CodeDB.Accessor> to
         * be able to get information about each search result, such as what type it is and the hash path needed to get t oit.
         *
         * Pass a <CancelDelegate> if you need to be able to interrupt the process, or <Delegates.NeverCancel> if not.
         */
        public void BuildPrefixDataFile(string prefix, CodeDB.Accessor accessor, CancelDelegate cancelDelegate)
        {
            Path path = Paths.SearchIndex.PrefixOutputFile(context.Target.OutputFolder, prefix);


            // Get and sort the keywords and topics

            var keywordEntries = GetPrefixKeywords(prefix, accessor, cancelDelegate);

            if (keywordEntries == null || keywordEntries.Count == 0)
            {
                if (System.IO.File.Exists(path))
                {
                    System.IO.File.Delete(path);
                }

                return;
            }

            SortKeywordEntries(keywordEntries);

            foreach (var keywordEntry in keywordEntries)
            {
                SortTopicEntries(keywordEntry);
                RemoveDuplicateTopics(keywordEntry);
            }


            // Build the list of all used comment types

            if (usedCommentTypes == null)
            {
                usedCommentTypes = new List <CommentType>();
            }
            else
            {
                usedCommentTypes.Clear();
            }

            foreach (var keywordEntry in keywordEntries)
            {
                foreach (var topicEntry in keywordEntry.TopicEntries)
                {
                    int commentTypeID = topicEntry.WrappedTopic.CommentTypeID;

                    if (UsedCommentTypeIndex(commentTypeID) == -1)
                    {
                        usedCommentTypes.Add(EngineInstance.CommentTypes.FromID(commentTypeID));
                    }
                }
            }


            // Build the output

            StringBuilder output        = new StringBuilder();
            bool          addWhitespace = !EngineInstance.Config.ShrinkFiles;

            output.Append("NDSearch.OnPrefixDataLoaded(\"");
            output.StringEscapeAndAppend(prefix);
            output.Append("\",");

            if (addWhitespace)
            {
                output.Append("\n   ");
            }

            AppendUsedCommentTypes(output);
            output.Append(',');

            if (addWhitespace)
            {
                output.Append("\n   ");
            }

            output.Append('[');

            bool isFirstKeywordEntry = true;

            foreach (var keywordEntry in keywordEntries)
            {
                if (isFirstKeywordEntry)
                {
                    isFirstKeywordEntry = false;
                }
                else
                {
                    output.Append(',');
                }

                AppendKeyword(keywordEntry, output);
            }

            if (addWhitespace)
            {
                output.Append("\n\n");
            }

            output.Append("]);");


            // Write it to the file

            WriteTextFile(path, output.ToString());
        }