diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/analyzing/analyzing.go b/analyzing/analyzing.go index 80fb652..904fe10 100644 --- a/analyzing/analyzing.go +++ b/analyzing/analyzing.go @@ -3,7 +3,7 @@ package analyzing import ( "fmt" "os" - "strings" + "path/filepath" "embed-code/embed-code-go/configuration" "embed-code/embed-code-go/embedding" @@ -38,7 +38,7 @@ func AnalyzeAll(config configuration.Configuration) { // Generates a path to a given file in the analytics directory. func pathToFile(fileName string) string { - return fmt.Sprintf("%s/%s", analyticsDir, fileName) + return filepath.Join(analyticsDir, fileName) } // Finds all documentation files for given config. @@ -47,12 +47,14 @@ func findDocumentationFiles(config configuration.Configuration) []string { docPatterns := config.DocIncludes var documentationFiles []string for _, pattern := range docPatterns { - globString := strings.Join([]string{documentationRoot, pattern}, "/") + globString := filepath.Join(documentationRoot, filepath.FromSlash(pattern)) matches, err := doublestar.FilepathGlob(globString) if err != nil { panic(err) } - documentationFiles = append(documentationFiles, matches...) + for _, match := range matches { + documentationFiles = append(documentationFiles, filepath.ToSlash(match)) + } } return documentationFiles diff --git a/embedding/embedding_test.go b/embedding/embedding_test.go index 7262c4e..0463ef7 100644 --- a/embedding/embedding_test.go +++ b/embedding/embedding_test.go @@ -107,6 +107,42 @@ var _ = Describe("Embedding", func() { Expect(processor.IsUpToDate()).Should(BeTrue()) }) + It("should embed with multi lined tag attributes", func() { + docPath := fmt.Sprintf("%s/multi-lined-valid-tag-attributes.md", config.DocumentationRoot) + processor := embedding.NewProcessor(docPath, config) + Expect(processor.Embed()).Error().ShouldNot(HaveOccurred()) + + Expect(processor.IsUpToDate()).Should(BeTrue()) + }) + + It("should report a missing closing tag", func() { + docPath := fmt.Sprintf("%s/missing-closing-tag.md", config.DocumentationRoot) + processor := embedding.NewProcessor(docPath, config) + + _, err := processor.Embed() + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).Should(ContainSubstring( + "missing-closing-tag.md:3`: " + + "failed to parse an embedding instruction: " + + "the `` tag is not closed", + )) + }) + + It("should report the XML parser error", func() { + docPath := fmt.Sprintf("%s/unclosed-nested-tag.md", config.DocumentationRoot) + processor := embedding.NewProcessor(docPath, config) + + _, err := processor.Embed() + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).Should(ContainSubstring( + "unclosed-nested-tag.md:3`: " + + "failed to parse an embedding instruction: " + + "element closed by ", + )) + }) + // TODO:olena-zmiiova:https://github.com/SpineEventEngine/embed-code/issues/65 It("should successfully embed to a file in a nested dir", func() { Skip( diff --git a/embedding/parsing/constants.go b/embedding/parsing/constants.go index e1f9110..325ad86 100644 --- a/embedding/parsing/constants.go +++ b/embedding/parsing/constants.go @@ -18,8 +18,8 @@ package parsing -// EmbeddingTag is a StartState of a tag where it requires to embed the code. -const EmbeddingTag = "") && + !strings.Contains(instruction, "") { + return fmt.Sprintf("the `<%s>` tag is not closed", + EmbeddingTag, + ) + } + if parseErr != nil { + var syntaxErr *xml.SyntaxError + if errors.As(parseErr, &syntaxErr) { + return syntaxErr.Msg + } + return parseErr.Error() + } + + return "invalid embedding instruction" +} diff --git a/embedding/parsing/xml_parse.go b/embedding/parsing/xml_parse.go index 63840cc..4fe51b1 100644 --- a/embedding/parsing/xml_parse.go +++ b/embedding/parsing/xml_parse.go @@ -24,8 +24,6 @@ import ( "fmt" ) -const xmlStringHeader string = "embed-code" - // Item needed for xml.Unmarshal parsing. The fields are filling up during the parsing. // // XMLName — a name of the tag in XML line. @@ -74,9 +72,9 @@ func ParseXMLLine(xmlLine string) (map[string]string, error) { return map[string]string{}, err } - if root.XMLName.Local != xmlStringHeader { + if root.XMLName.Local != EmbeddingTag { return map[string]string{}, - fmt.Errorf("the provided line's header is not 'embed-code':\n%s", xmlLine) + fmt.Errorf("the provided line's header is not `%s`:\n%s", EmbeddingTag, xmlLine) } attributes := make(map[string]string) diff --git a/embedding/processor.go b/embedding/processor.go index 839fda0..b612947 100644 --- a/embedding/processor.go +++ b/embedding/processor.go @@ -217,13 +217,13 @@ func (p Processor) fillEmbeddingContext() (parsing.Context, error) { for currentState != finishState { accepted, newState, err := p.moveToNextState(¤tState, &context) if err != nil { - return context, fmt.Errorf(errorStr, absDocPath, context.CurrentEmbedding().SourceStartIndex-1, err) + return context, fmt.Errorf(errorStr, absDocPath, errorLine(context, err), err) } if !accepted { currentState = &parsing.RegularLineState{} context.ResolveUnacceptedEmbedding() - return context, fmt.Errorf(errorStr, absDocPath, context.CurrentEmbedding().SourceStartIndex-1, err) + return context, fmt.Errorf(errorStr, absDocPath, errorLine(context, err), err) } currentState = *newState } @@ -231,6 +231,19 @@ func (p Processor) fillEmbeddingContext() (parsing.Context, error) { return context, nil } +// errorLine returns the source line that should be used in the embedding error location. +func errorLine(context parsing.Context, err error) int { + var parseErr parsing.InstructionParseError + if errors.As(err, &parseErr) { + return parseErr.Line + } + if context.EmbeddingsCount() > 0 { + return context.CurrentEmbedding().SourceStartIndex - 1 + } + + return context.CurrentIndex() +} + // Moves to the next state accordingly to a transition map from the current state. Reports whether // it successfully moved to the next state and returns the new state. func (p Processor) moveToNextState(state *parsing.State, context *parsing.Context) ( @@ -289,12 +302,14 @@ func requiredDocs(config configuration.Configuration) []string { func getFilesByPatterns(root string, patterns []string) ([]string, error) { var result []string for _, pattern := range patterns { - globString := strings.Join([]string{root, pattern}, "/") + globString := filepath.Join(root, filepath.FromSlash(pattern)) matches, err := doublestar.FilepathGlob(globString) if err != nil { return nil, err } - result = append(result, matches...) + for _, match := range matches { + result = append(result, filepath.ToSlash(match)) + } } return result, nil diff --git a/files/files.go b/files/files.go index 78b9ec8..0464acd 100644 --- a/files/files.go +++ b/files/files.go @@ -90,7 +90,7 @@ func BuildDocRelativePath(absolutePath string, config configuration.Configuratio panic(err) } - return relativePath + return filepath.ToSlash(relativePath) } // EnsureDirExists creates dir at given path (relative or absolute) if it doesn't exist. diff --git a/fragmentation/fragment_file.go b/fragmentation/fragment_file.go index c3b465e..9794ca3 100644 --- a/fragmentation/fragment_file.go +++ b/fragmentation/fragment_file.go @@ -20,15 +20,16 @@ package fragmentation import ( "crypto/sha256" - _type "embed-code/embed-code-go/type" "encoding/hex" "fmt" "os" + "path" "path/filepath" "strings" config "embed-code/embed-code-go/configuration" "embed-code/embed-code-go/files" + _type "embed-code/embed-code-go/type" ) // FragmentFile is a file storing a single fragment from the file. @@ -73,7 +74,9 @@ func NewFragmentFileFromAbsolute( } if strings.TrimSpace(codeRoot.Name) != "" { - relativePath = filepath.Join(NamedPathPrefix+codeRoot.Name, relativePath) + relativePath = path.Join(NamedPathPrefix+codeRoot.Name, filepath.ToSlash(relativePath)) + } else { + relativePath = filepath.ToSlash(relativePath) } return FragmentFile{ @@ -143,10 +146,10 @@ func (f FragmentFile) absolutePath() string { } if f.FragmentName == DefaultFragmentName { - return filepath.Join(fragmentsAbsDir, f.CodePath) + return filepath.Join(fragmentsAbsDir, filepath.FromSlash(f.CodePath)) } - withoutExtension := strings.TrimSuffix(f.CodePath, fileExtension) + withoutExtension := filepath.FromSlash(strings.TrimSuffix(f.CodePath, fileExtension)) filename := fmt.Sprintf("%s-%s", withoutExtension, f.fragmentHash()) return filepath.Join(fragmentsAbsDir, filename+fileExtension) diff --git a/main.go b/main.go index d78aabf..f40913a 100644 --- a/main.go +++ b/main.go @@ -28,7 +28,7 @@ import ( ) // Version of the embed-code application. -const Version = "1.1.0" +const Version = "1.1.1" // The entry point for embed-code. // diff --git a/test/resources/docs/missing-closing-tag.md b/test/resources/docs/missing-closing-tag.md new file mode 100644 index 0000000..50d9df6 --- /dev/null +++ b/test/resources/docs/missing-closing-tag.md @@ -0,0 +1,5 @@ +# Test missing closing tag + + +```java +``` diff --git a/test/resources/docs/multi-lined-valid-tag-attributes.md b/test/resources/docs/multi-lined-valid-tag-attributes.md new file mode 100644 index 0000000..772e954 --- /dev/null +++ b/test/resources/docs/multi-lined-valid-tag-attributes.md @@ -0,0 +1,8 @@ +# Test multiline attributes + + +```java +``` diff --git a/test/resources/docs/unclosed-nested-tag.md b/test/resources/docs/unclosed-nested-tag.md new file mode 100644 index 0000000..cbc1ff1 --- /dev/null +++ b/test/resources/docs/unclosed-nested-tag.md @@ -0,0 +1,5 @@ +# Test unclosed nested tag + + +```java +```