ProcessResult Apply(IEnumerable<SnippetGroup> availableSnippets, TextWriter writer, IndexReader reader)
        {
            var snippets = availableSnippets.ToList();
            var missingSnippets = new List<MissingSnippet>();
            var usedSnippets = new List<SnippetGroup>();
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string key;
                if (!keyReader(line, out key))
                {
                    writer.WriteLine(line);
                    continue;
                }
                writer.WriteLine("<!-- snippet: {0} -->", key);

                var snippetGroup = snippets.FirstOrDefault(x => x.Key == key);
                if (snippetGroup == null)
                {
                    var missingSnippet = new MissingSnippet(key: key, line: reader.Index);
                    missingSnippets.Add(missingSnippet);
                    var message = $"** Could not find key '{key}' **";
                    writer.WriteLine(message);
                    continue;
                }

                AppendGroup(snippetGroup, writer);
                if (usedSnippets.All(x => x.Key != snippetGroup.Key))
                {
                    usedSnippets.Add(snippetGroup);
                }
            }
            return new ProcessResult(missingSnippets: missingSnippets, usedSnippets: usedSnippets);
        }
        ReadSnippets GetSnippetsFromFile(IndexReader stringReader, string file)
        {
            var errors = new List<ReadSnippetError>();
            var snippets = new List<ReadSnippet>();
            var language = GetLanguageFromFile(file);
            var loopState = new LoopState();
            while (true)
            {
                var line = stringReader.ReadLine();
                if (line == null)
                {
                    if (loopState.IsInSnippet)
                    {
                        errors.Add(new ReadSnippetError(
                            message: "Snippet was not closed",
                            file: file,
                            line: loopState.StartLine.Value + 1,
                            key: loopState.CurrentKey, 
                            version: null));
                    }
                    break;
                }

                var trimmedLine = line.Trim().Replace("  ", " ").ToLowerInvariant();
                if (loopState.IsInSnippet)
                {
                    if (!loopState.EndFunc(trimmedLine))
                    {
                        loopState.SnippetLines.Add(line);
                        continue;
                    }

                    TryAddSnippet(stringReader, file, loopState, language, errors, snippets);
                    loopState.Reset();
                    continue;
                }
                IsStart(stringReader, trimmedLine, loopState);
            }
            return new ReadSnippets(snippets, errors);
        }