Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Go] Refactor embedding package #53

Merged
merged 20 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions embed-code-go/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ issues:
- revive
text: "dot-imports: should not use dot imports"
path: _test\.go
# We may have repeating lines in tests and it should be valid.
- linters:
- goconst
path: _test\.go
# Excluding test folder as tests are in progress of moving to the source files.
exclude-dirs:
- test/
Expand Down
2 changes: 1 addition & 1 deletion embed-code-go/analyzing/analyzing.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func extractAnalyticsForDocs(
var changedEmbeddingsLines, problemEmbeddingsLines []string

for _, docFile := range docFiles {
processor := embedding.NewEmbeddingProcessor(docFile, config)
processor := embedding.NewProcessor(docFile, config)
changedEmbeddings, err := processor.FindChangedEmbeddings()

// If there is an error during embedding, it is written to the analytics file.
Expand Down
Binary file modified embed-code-go/bin/embed-code-macos
Binary file not shown.
Binary file modified embed-code-go/bin/embed-code-ubuntu
Binary file not shown.
Binary file modified embed-code-go/bin/embed-code-win.exe
Binary file not shown.
106 changes: 106 additions & 0 deletions embed-code-go/embedding/embedding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2020, TeamDev. All rights reserved.
//
// Redistribution and use in source and/or binary forms, with or without
// modification, must retain the above copyright notice and the following
// disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package embedding_test

import (
"fmt"
"os"
"testing"

"embed-code/embed-code-go/configuration"
"embed-code/embed-code-go/embedding"
"embed-code/embed-code-go/embedding/parsing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestEmbedding(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Data Suite")
}

var _ = Describe("Embedding", func() {
var config configuration.Configuration

BeforeEach(func() {
currentDir, err := os.Getwd()
if err != nil {
Fail("unexpected error during the test setup: " + err.Error())
}
err = os.Chdir(currentDir)
if err != nil {
Fail("unexpected error during the test setup: " + err.Error())
}
config = buildConfigWithPreparedFragments()
})

It("should be up to date", func() {
docPath := fmt.Sprintf("%s/whole-file-fragment.md", config.DocumentationRoot)
processor := embedding.NewProcessor(docPath, config)

Expect(processor.Embed()).ShouldNot(HaveOccurred())
Expect(processor.IsUpToDate()).Should(BeTrue())
})

It("should be up to date as there is nothing to update", func() {
docPath := fmt.Sprintf("%s/no-embedding-doc.md", config.DocumentationRoot)
processor := embedding.NewProcessor(docPath, config)

Expect(processor.Embed()).ShouldNot(HaveOccurred())
Expect(processor.IsUpToDate()).Should(BeTrue())
})

It("should have error as it has invalid transition map", func() {
docPath := fmt.Sprintf("%s/split-lines.md", config.DocumentationRoot)

falseTransitions := parsing.TransitionMap{
parsing.Start: {parsing.RegularLine, parsing.Finish,
parsing.EmbedInstruction},
parsing.RegularLine: {parsing.Finish, parsing.EmbedInstruction,
parsing.RegularLine},
parsing.EmbedInstruction: {parsing.CodeFenceStart, parsing.BlankLine},
parsing.BlankLine: {parsing.CodeFenceStart, parsing.BlankLine},
parsing.CodeFenceStart: {parsing.CodeFenceEnd, parsing.CodeSampleLine},
parsing.CodeSampleLine: {parsing.CodeFenceEnd, parsing.CodeSampleLine},
parsing.CodeFenceEnd: {parsing.Finish, parsing.EmbedInstruction,
parsing.RegularLine},
}

falseProcessor := embedding.NewProcessorWithTransitions(docPath, config, falseTransitions)
Expect(falseProcessor.Embed()).Error().Should(HaveOccurred())
})

It("should successfully embed with multi lined tag", func() {
docPath := fmt.Sprintf("%s/multi-lined-tag.md", config.DocumentationRoot)
processor := embedding.NewProcessor(docPath, config)
Expect(processor.Embed()).Error().ShouldNot(HaveOccurred())

Expect(processor.IsUpToDate()).Should(BeTrue())
})
})

func buildConfigWithPreparedFragments() configuration.Configuration {
var config = configuration.NewConfiguration()
config.DocumentationRoot = "../test/resources/docs"
config.CodeRoot = "../test/resources/code"
config.FragmentsDir = "../test/resources/prepared-fragments"

return config
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,28 @@ import (
"embed-code/embed-code-go/embedding/parsing"
)

// Describes an error which occurs if something goes wrong during embedding.
type EmbeddingError struct {
Context parsing.ParsingContext
// UnexpectedDiffError describes an error which occurs if outdated files are found during
// the checking.
type UnexpectedDiffError struct {
changedFiles []string
}

func (err EmbeddingError) Error() string {
errorString := fmt.Sprintf("embedding error for file `%s`.", err.Context.MarkdownFile)
func (e *UnexpectedDiffError) Error() string {
return fmt.Sprintf("unexpected diff: %v", e.changedFiles)
}

// UnexpectedProcessingError describes an error which occurs if something goes wrong during
// embedding.
type UnexpectedProcessingError struct {
Context parsing.Context
}

func (e UnexpectedProcessingError) Error() string {
errorString := fmt.Sprintf("embedding error for file `%s`.", e.Context.MarkdownFilePath)

if len(err.Context.EmbeddingsNotFound) > 0 {
if len(e.Context.EmbeddingsNotFound) > 0 {
embeddingsNotFoundStr := "\nMissing embeddings: \n"
for _, emb := range err.Context.EmbeddingsNotFound {
for _, emb := range e.Context.EmbeddingsNotFound {
embeddingsNotFoundStr += fmt.Sprintf(
"%s — %s\n",
emb.CodeFile,
Expand All @@ -43,15 +54,15 @@ func (err EmbeddingError) Error() string {
errorString += embeddingsNotFoundStr
}

if len(err.Context.UnacceptedEmbeddings) > 0 {
unacceptedEmbbeddingsStr := "\nUnaccepted embeddings: \n"
for _, emb := range err.Context.UnacceptedEmbeddings {
unacceptedEmbbeddingsStr += fmt.Sprintf(
if len(e.Context.UnacceptedEmbeddings) > 0 {
unacceptedEmbeddingStr := "\nUnaccepted embeddings: \n"
for _, emb := range e.Context.UnacceptedEmbeddings {
unacceptedEmbeddingStr += fmt.Sprintf(
"%s — %s\n",
emb.CodeFile,
emb.Fragment)
}
errorString += unacceptedEmbbeddingsStr
errorString += unacceptedEmbeddingStr
}

return errorString
Expand Down
24 changes: 10 additions & 14 deletions embed-code-go/embedding/parsing/blank_line.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,24 @@ import (
"embed-code/embed-code-go/configuration"
)

// Represents a blank line of a markdown.
type BlankLine struct{}
// BlankLineState represents a blank line of a markdown.
type BlankLineState struct{}

// Reports whether the current line is a blank line.
// Recognize reports whether the current line is blank.
//
// Checks if the current line is empty and not part of a code fence,
// and if there is an embedding. If these conditions are met, it returns true.
// Otherwise, it returns false.
func (b BlankLine) Recognize(context ParsingContext) bool {
if !context.ReachedEOF() && strings.TrimSpace(context.CurrentLine()) == "" {
// Checks if the current line is empty and not part of a code fence, and if there is an embedding.
// If these conditions are met, it returns true. Otherwise, it returns false.
func (b BlankLineState) Recognize(context Context) bool {
isEmptyString := strings.TrimSpace(context.CurrentLine()) == ""
if !context.ReachedEOF() && isEmptyString {
return !context.CodeFenceStarted && context.Embedding != nil
}

return false
}

// Processes a blank line of a markdown.
//
// Appends the current line of the context to the result, and moves to the next line.
//
// This implementation never returns an error.
func (b BlankLine) Accept(context *ParsingContext, _ configuration.Configuration) error {
// Accept appends the current line of the context to the result, and moves to the next line.
func (b BlankLineState) Accept(context *Context, _ configuration.Configuration) error {
line := context.CurrentLine()
context.Result = append(context.Result, line)
context.ToNextLine()
Expand Down
39 changes: 13 additions & 26 deletions embed-code-go/embedding/parsing/code_fence_end.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,31 @@ import (
"embed-code/embed-code-go/configuration"
)

//
// Public methods
//

// Represents the end of a code fence.
type CodeFenceEnd struct{}
// CodeFenceEndState represents the end of a code fence.
type CodeFenceEndState struct{}

// Reports whether the current line is the end of a code fence.
//
// The line is a code fence end if:
// - the end is not reached;
// Recognize reports whether the current line meets this conditions:
// - the end of file is not reached;
// - the code fence has started;
// - the current line starts with the appropriate indentation and "```"
//
// context — a context of the parsing process.
func (c CodeFenceEnd) Recognize(context ParsingContext) bool {
if !context.ReachedEOF() {
indentation := strings.Repeat(" ", context.CodeFenceIndentation)

return context.CodeFenceStarted && strings.HasPrefix(context.CurrentLine(), indentation+"```")
func (c CodeFenceEndState) Recognize(context Context) bool {
if context.ReachedEOF() {
return false
}
indentation := strings.Repeat(" ", context.CodeFenceIndentation)

return false
return context.CodeFenceStarted && strings.HasPrefix(context.CurrentLine(), indentation+"```")
}

// Processes the end of a code fence by adding the current line to the result,
// resetting certain context variables, and moving to the next line.
// Accept adds the current line to the result, resets certain context variables, and moves to
// the next line.
//
// context — a context of the parsing process.
//
// config — a configuration of the embedding.
//
// Returns an error if the rendering was not successful.
func (c CodeFenceEnd) Accept(context *ParsingContext, _ configuration.Configuration) error {
func (c CodeFenceEndState) Accept(context *Context, _ configuration.Configuration) error {
line := context.CurrentLine()
err := renderSample(context)
context.SetEmbedding(nil)
Expand All @@ -73,16 +64,12 @@ func (c CodeFenceEnd) Accept(context *ParsingContext, _ configuration.Configurat
return err
}

//
// Private methods
//

// Renders the sample content of the embedding.
//
// context — a context of the parsing process.
//
// Returns an error if the reading of the embedding's content was not successful.
func renderSample(context *ParsingContext) error {
func renderSample(context *Context) error {
content, err := context.Embedding.Content()
if err != nil {
return err
Expand Down
28 changes: 8 additions & 20 deletions embed-code-go/embedding/parsing/code_fence_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,26 @@ import (
"embed-code/embed-code-go/configuration"
)

// Represents the start of a code fence.
type CodeFenceStart struct{}
// CodeFenceStartState represents the StartState of a code fence.
type CodeFenceStartState struct{}

//
// Public methods
//

// Reports whether the current line is the start of a code fence.
//
// The line is a code fence start if the end is not reached and the current line starts with "```".
// Recognize reports whether the current line is not reached the end and starts with "```".
//
// context — a context of the parsing process.
func (c CodeFenceStart) Recognize(context ParsingContext) bool {
func (c CodeFenceStartState) Recognize(context Context) bool {
if !context.ReachedEOF() {
return strings.HasPrefix(strings.TrimSpace(context.CurrentLine()), "```")
}

return false
}

// Processes the start of a code fence.
//
// Appends the current line from the parsing context to the result,
// sets a flag to indicate that a code fence has started,
// calculates the indentation level of the code fence, and moves to the next line in the context.
// Accept appends the current line from the parsing context to the result, sets a flag to indicate
// that a code fence has started, calculates the indentation level of the code fence, and moves
// to the next line in the context.
//
// context — a context of the parsing process.
//
// config — a configuration of the embedding.
//
// This implementation never returns an error.
func (c CodeFenceStart) Accept(context *ParsingContext, _ configuration.Configuration) error {
func (c CodeFenceStartState) Accept(context *Context, _ configuration.Configuration) error {
line := context.CurrentLine()
context.Result = append(context.Result, line)
context.CodeFenceStarted = true
Expand Down
28 changes: 8 additions & 20 deletions embed-code-go/embedding/parsing/code_sample_line.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,23 @@

package parsing

import (
"embed-code/embed-code-go/configuration"
)
import "embed-code/embed-code-go/configuration"

// Represents a line of a code sample.
type CodeSampleLine struct{}
// CodeSampleLineState represents a line of a code sample.
type CodeSampleLineState struct{}

//
// Public methods
//

// Reports whether the current line is a code sample line.
//
// If codeFenceStarted is true and it's not the end of the file,
// the line is a code sample line.
// Recognize reports whether the current line is a code sample line: the code fence is started, and
// it is not the end of a file.
//
// context — a context of the parsing process.
func (c CodeSampleLine) Recognize(context ParsingContext) bool {
func (c CodeSampleLineState) Recognize(context Context) bool {
return !context.ReachedEOF() && context.CodeFenceStarted
}

// Moves to the next line.
// Accept moves to the next line.
//
// context — a context of the parsing process.
//
// config — a configuration of the embedding.
//
// This implementation never returns an error.
func (c CodeSampleLine) Accept(context *ParsingContext, _ configuration.Configuration) error {
func (c CodeSampleLineState) Accept(context *Context, _ configuration.Configuration) error {
context.ToNextLine()

return nil
Expand Down
Loading