diff --git a/.gomarkdoc.yml b/.gomarkdoc.yml index fccf645..03233e3 100644 --- a/.gomarkdoc.yml +++ b/.gomarkdoc.yml @@ -6,4 +6,4 @@ header: |+ repository: url: https://github.com/princjef/gomarkdoc defaultBranch: master - directory: . + path: / diff --git a/cmd/gomarkdoc/command.go b/cmd/gomarkdoc/command.go index baf53ce..c47ae5f 100644 --- a/cmd/gomarkdoc/command.go +++ b/cmd/gomarkdoc/command.go @@ -91,7 +91,7 @@ func buildCommand() *cobra.Command { opts.footerFile = viper.GetString("footerFile") opts.repository.Remote = viper.GetString("repository.url") opts.repository.DefaultBranch = viper.GetString("repository.defaultBranch") - opts.repository.RootDir = viper.GetString("repository.directory") + opts.repository.PathFromRoot = viper.GetString("repository.path") if opts.check && opts.output == "" { log.Fatal("check mode cannot be run without an output set") @@ -198,10 +198,10 @@ func buildCommand() *cobra.Command { "Manual override for the git repository URL used in place of automatic detection.", ) command.Flags().StringVar( - &opts.repository.RootDir, - "repository.directory", + &opts.repository.PathFromRoot, + "repository.path", "", - "Manual override for the root directory of the git repository use in place of automatic detection.", + "Manual override for the path from the root of the git repository used in place of automatic detection.", ) command.Flags().BoolVar( &opts.version, @@ -223,7 +223,7 @@ func buildCommand() *cobra.Command { _ = viper.BindPFlag("footerFile", command.Flags().Lookup("footer-file")) _ = viper.BindPFlag("repository.url", command.Flags().Lookup("repository.url")) _ = viper.BindPFlag("repository.defaultBranch", command.Flags().Lookup("repository.default-branch")) - _ = viper.BindPFlag("repository.directory", command.Flags().Lookup("repository.directory")) + _ = viper.BindPFlag("repository.path", command.Flags().Lookup("repository.path")) return command } diff --git a/format/README.md b/format/README.md index b8748c1..5d9ca80 100755 --- a/format/README.md +++ b/format/README.md @@ -65,7 +65,7 @@ AzureDevOpsMarkdown provides a Format which is compatible with Azure DevOps's sy type AzureDevOpsMarkdown struct{} ``` -### func \(\*AzureDevOpsMarkdown\) [Accordion]() +### func \(\*AzureDevOpsMarkdown\) [Accordion]() ```go func (f *AzureDevOpsMarkdown) Accordion(title, body string) (string, error) @@ -73,7 +73,7 @@ func (f *AzureDevOpsMarkdown) Accordion(title, body string) (string, error) Accordion generates a collapsible content\. The accordion's visible title while collapsed is the provided title and the expanded content is the body\. -### func \(\*AzureDevOpsMarkdown\) [AccordionHeader]() +### func \(\*AzureDevOpsMarkdown\) [AccordionHeader]() ```go func (f *AzureDevOpsMarkdown) AccordionHeader(title string) (string, error) @@ -87,7 +87,7 @@ The AccordionHeader is expected to be used in conjunction with AccordionTerminat accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator() ``` -### func \(\*AzureDevOpsMarkdown\) [AccordionTerminator]() +### func \(\*AzureDevOpsMarkdown\) [AccordionTerminator]() ```go func (f *AzureDevOpsMarkdown) AccordionTerminator() (string, error) @@ -119,7 +119,7 @@ func (f *AzureDevOpsMarkdown) CodeHref(loc lang.Location) (string, error) CodeHref generates an href to the provided code entry\. -### func \(\*AzureDevOpsMarkdown\) [Escape]() +### func \(\*AzureDevOpsMarkdown\) [Escape]() ```go func (f *AzureDevOpsMarkdown) Escape(text string) string @@ -135,7 +135,7 @@ func (f *AzureDevOpsMarkdown) Header(level int, text string) (string, error) Header converts the provided text into a header of the provided level\. The level is expected to be at least 1\. -### func \(\*AzureDevOpsMarkdown\) [Link]() +### func \(\*AzureDevOpsMarkdown\) [Link]() ```go func (f *AzureDevOpsMarkdown) Link(text, href string) (string, error) @@ -143,7 +143,7 @@ func (f *AzureDevOpsMarkdown) Link(text, href string) (string, error) Link generates a link with the given text and href values\. -### func \(\*AzureDevOpsMarkdown\) [ListEntry]() +### func \(\*AzureDevOpsMarkdown\) [ListEntry]() ```go func (f *AzureDevOpsMarkdown) ListEntry(depth int, text string) (string, error) @@ -159,7 +159,7 @@ func (f *AzureDevOpsMarkdown) LocalHref(headerText string) (string, error) LocalHref generates an href for navigating to a header with the given headerText located within the same document as the href itself\. Link generation follows the guidelines here: https://docs\.microsoft\.com/en\-us/azure/devops/project/wiki/markdown\-guidance?view=azure\-devops\#anchor\-links -### func \(\*AzureDevOpsMarkdown\) [Paragraph]() +### func \(\*AzureDevOpsMarkdown\) [Paragraph]() ```go func (f *AzureDevOpsMarkdown) Paragraph(text string) (string, error) @@ -248,7 +248,7 @@ GitHubFlavoredMarkdown provides a Format which is compatible with GitHub Flavore type GitHubFlavoredMarkdown struct{} ``` -### func \(\*GitHubFlavoredMarkdown\) [Accordion]() +### func \(\*GitHubFlavoredMarkdown\) [Accordion]() ```go func (f *GitHubFlavoredMarkdown) Accordion(title, body string) (string, error) @@ -256,7 +256,7 @@ func (f *GitHubFlavoredMarkdown) Accordion(title, body string) (string, error) Accordion generates a collapsible content\. The accordion's visible title while collapsed is the provided title and the expanded content is the body\. -### func \(\*GitHubFlavoredMarkdown\) [AccordionHeader]() +### func \(\*GitHubFlavoredMarkdown\) [AccordionHeader]() ```go func (f *GitHubFlavoredMarkdown) AccordionHeader(title string) (string, error) @@ -270,7 +270,7 @@ The AccordionHeader is expected to be used in conjunction with AccordionTerminat accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator() ``` -### func \(\*GitHubFlavoredMarkdown\) [AccordionTerminator]() +### func \(\*GitHubFlavoredMarkdown\) [AccordionTerminator]() ```go func (f *GitHubFlavoredMarkdown) AccordionTerminator() (string, error) @@ -302,7 +302,7 @@ func (f *GitHubFlavoredMarkdown) CodeHref(loc lang.Location) (string, error) CodeHref generates an href to the provided code entry\. -### func \(\*GitHubFlavoredMarkdown\) [Escape]() +### func \(\*GitHubFlavoredMarkdown\) [Escape]() ```go func (f *GitHubFlavoredMarkdown) Escape(text string) string @@ -326,7 +326,7 @@ func (f *GitHubFlavoredMarkdown) Link(text, href string) (string, error) Link generates a link with the given text and href values\. -### func \(\*GitHubFlavoredMarkdown\) [ListEntry]() +### func \(\*GitHubFlavoredMarkdown\) [ListEntry]() ```go func (f *GitHubFlavoredMarkdown) ListEntry(depth int, text string) (string, error) @@ -342,7 +342,7 @@ func (f *GitHubFlavoredMarkdown) LocalHref(headerText string) (string, error) LocalHref generates an href for navigating to a header with the given headerText located within the same document as the href itself\. -### func \(\*GitHubFlavoredMarkdown\) [Paragraph]() +### func \(\*GitHubFlavoredMarkdown\) [Paragraph]() ```go func (f *GitHubFlavoredMarkdown) Paragraph(text string) (string, error) diff --git a/format/devops.go b/format/devops.go index e14dbe8..b34b1f3 100644 --- a/format/devops.go +++ b/format/devops.go @@ -63,12 +63,21 @@ func (f *AzureDevOpsMarkdown) CodeHref(loc lang.Location) (string, error) { return "", nil } - fullPath, err := filepath.Abs(loc.Filepath) - if err != nil { - return "", err + var ( + relative string + err error + ) + if filepath.IsAbs(loc.Filepath) { + relative, err = filepath.Rel(loc.WorkDir, loc.Filepath) + if err != nil { + return "", err + } + } else { + relative = loc.Filepath } - p, err := filepath.Rel(loc.Repo.RootDir, fullPath) + full := filepath.Join(loc.Repo.PathFromRoot, relative) + p, err := filepath.Rel(string(filepath.Separator), full) if err != nil { return "", err } diff --git a/format/devops_test.go b/format/devops_test.go index 7f86358..ec21702 100644 --- a/format/devops_test.go +++ b/format/devops_test.go @@ -119,22 +119,20 @@ func TestLocalHref(t *testing.T) { func TestCodeHref(t *testing.T) { is := is.New(t) - repoDir, err := filepath.Abs(".") + wd, err := filepath.Abs(".") is.NoErr(err) - - locPath, err := filepath.Abs(".") - is.NoErr(err) - locPath = filepath.Join(locPath, "subdir", "file.go") + locPath := filepath.Join(wd, "subdir", "file.go") var f format.AzureDevOpsMarkdown res, err := f.CodeHref(lang.Location{ Start: lang.Position{Line: 12, Col: 1}, End: lang.Position{Line: 14, Col: 43}, Filepath: locPath, + WorkDir: wd, Repo: &lang.Repo{ Remote: "https://dev.azure.com/org/project/_git/repo", DefaultBranch: "master", - RootDir: repoDir, + PathFromRoot: "/", }, }) is.NoErr(err) @@ -144,15 +142,16 @@ func TestCodeHref(t *testing.T) { func TestCodeHref_noRepo(t *testing.T) { is := is.New(t) - locPath, err := filepath.Abs(".") + wd, err := filepath.Abs(".") is.NoErr(err) - locPath = filepath.Join(locPath, "subdir", "file.go") + locPath := filepath.Join(wd, "subdir", "file.go") var f format.AzureDevOpsMarkdown res, err := f.CodeHref(lang.Location{ Start: lang.Position{Line: 12, Col: 1}, End: lang.Position{Line: 14, Col: 43}, Filepath: locPath, + WorkDir: wd, Repo: nil, }) is.NoErr(err) diff --git a/format/github.go b/format/github.go index ea53c31..a016d0f 100644 --- a/format/github.go +++ b/format/github.go @@ -67,12 +67,21 @@ func (f *GitHubFlavoredMarkdown) CodeHref(loc lang.Location) (string, error) { return "", nil } - fullPath, err := filepath.Abs(loc.Filepath) - if err != nil { - return "", err + var ( + relative string + err error + ) + if filepath.IsAbs(loc.Filepath) { + relative, err = filepath.Rel(loc.WorkDir, loc.Filepath) + if err != nil { + return "", err + } + } else { + relative = loc.Filepath } - p, err := filepath.Rel(loc.Repo.RootDir, fullPath) + full := filepath.Join(loc.Repo.PathFromRoot, relative) + p, err := filepath.Rel(string(filepath.Separator), full) if err != nil { return "", err } diff --git a/lang/README.md b/lang/README.md index 12718ca..458a039 100644 --- a/lang/README.md +++ b/lang/README.md @@ -17,7 +17,7 @@ Package lang provides constructs for defining golang language constructs and ext - [func (b *Block) Text() string](<#func-block-text>) - [type BlockKind](<#type-blockkind>) - [type Config](<#type-config>) - - [func NewConfig(log logger.Logger, pkgDir string, opts ...ConfigOption) (*Config, error)](<#func-newconfig>) + - [func NewConfig(log logger.Logger, workDir string, pkgDir string, opts ...ConfigOption) (*Config, error)](<#func-newconfig>) - [func (c *Config) Inc(step int) *Config](<#func-config-inc>) - [type ConfigOption](<#type-configoption>) - [func ConfigWithRepoOverrides(overrides *Repo) ConfigOption](<#func-configwithrepooverrides>) @@ -141,7 +141,7 @@ BlockKind identifies the type of block element represented by the corresponding type BlockKind string ``` -## type [Config]() +## type [Config]() Config defines contextual information used to resolve documentation for a construct\. @@ -151,19 +151,20 @@ type Config struct { Level int Repo *Repo PkgDir string + WorkDir string Log logger.Logger } ``` -### func [NewConfig]() +### func [NewConfig]() ```go -func NewConfig(log logger.Logger, pkgDir string, opts ...ConfigOption) (*Config, error) +func NewConfig(log logger.Logger, workDir string, pkgDir string, opts ...ConfigOption) (*Config, error) ``` NewConfig generates a Config for the provided package directory\. It will resolve the filepath and attempt to determine the repository containing the directory\. If no repository is found\, the Repo field will be set to nil\. An error is returned if the provided directory is invalid\. -### func \(\*Config\) [Inc]() +### func \(\*Config\) [Inc]() ```go func (c *Config) Inc(step int) *Config @@ -171,7 +172,7 @@ func (c *Config) Inc(step int) *Config Inc copies the Config and increments the level by the provided step\. -## type [ConfigOption]() +## type [ConfigOption]() ConfigOption modifies the Config generated by NewConfig\. @@ -179,7 +180,7 @@ ConfigOption modifies the Config generated by NewConfig\. type ConfigOption func(c *Config) error ``` -### func [ConfigWithRepoOverrides]() +### func [ConfigWithRepoOverrides]() ```go func ConfigWithRepoOverrides(overrides *Repo) ConfigOption @@ -405,7 +406,7 @@ func (fn *Func) Title() string Title provides the formatted name of the func\. It is primarily designed for generating headers\. -## type [Location]() +## type [Location]() Location holds information for identifying a position within a file and repository\, if present\. @@ -414,11 +415,12 @@ type Location struct { Start Position End Position Filepath string + WorkDir string Repo *Repo } ``` -### func [NewLocation]() +### func [NewLocation]() ```go func NewLocation(cfg *Config, node ast.Node) Location @@ -452,7 +454,7 @@ func NewPackageFromBuild(log logger.Logger, pkg *build.Package, opts ...PackageO NewPackageFromBuild creates a representation of a package's documentation from the build metadata for that package\. It can be configured using the provided options\. -### func \(\*Package\) [Consts]() +### func \(\*Package\) [Consts]() ```go func (pkg *Package) Consts() (consts []*Value) @@ -460,7 +462,7 @@ func (pkg *Package) Consts() (consts []*Value) Consts lists the top\-level constants provided by the package\. -### func \(\*Package\) [Dir]() +### func \(\*Package\) [Dir]() ```go func (pkg *Package) Dir() string @@ -468,7 +470,7 @@ func (pkg *Package) Dir() string Dir provides the name of the full directory in which the package is located\. -### func \(\*Package\) [Dirname]() +### func \(\*Package\) [Dirname]() ```go func (pkg *Package) Dirname() string @@ -476,7 +478,7 @@ func (pkg *Package) Dirname() string Dirname provides the name of the leaf directory in which the package is located\. -### func \(\*Package\) [Doc]() +### func \(\*Package\) [Doc]() ```go func (pkg *Package) Doc() *Doc @@ -484,7 +486,7 @@ func (pkg *Package) Doc() *Doc Doc provides the structured contents of the documentation comment for the package\. -### func \(\*Package\) [Examples]() +### func \(\*Package\) [Examples]() ```go func (pkg *Package) Examples() (examples []*Example) @@ -492,7 +494,7 @@ func (pkg *Package) Examples() (examples []*Example) Examples provides the package\-level examples that have been defined\. This does not include examples that are associated with symbols contained within the package\. -### func \(\*Package\) [Funcs]() +### func \(\*Package\) [Funcs]() ```go func (pkg *Package) Funcs() (funcs []*Func) @@ -500,7 +502,7 @@ func (pkg *Package) Funcs() (funcs []*Func) Funcs lists the top\-level functions provided by the package\. -### func \(\*Package\) [Import]() +### func \(\*Package\) [Import]() ```go func (pkg *Package) Import() string @@ -508,7 +510,7 @@ func (pkg *Package) Import() string Import provides the raw text for the import declaration that is used to import code from the package\. If your package's documentation is generated from a local path and does not use Go Modules\, this will typically print \`import "\."\`\. -### func \(\*Package\) [Level]() +### func \(\*Package\) [Level]() ```go func (pkg *Package) Level() int @@ -516,7 +518,7 @@ func (pkg *Package) Level() int Level provides the default level that headers for the package's root documentation should be rendered\. -### func \(\*Package\) [Name]() +### func \(\*Package\) [Name]() ```go func (pkg *Package) Name() string @@ -524,7 +526,7 @@ func (pkg *Package) Name() string Name provides the name of the package as it would be seen from another package importing it\. -### func \(\*Package\) [Summary]() +### func \(\*Package\) [Summary]() ```go func (pkg *Package) Summary() string @@ -532,7 +534,7 @@ func (pkg *Package) Summary() string Summary provides the one\-sentence summary of the package's documentation comment\. -### func \(\*Package\) [Types]() +### func \(\*Package\) [Types]() ```go func (pkg *Package) Types() (types []*Type) @@ -540,7 +542,7 @@ func (pkg *Package) Types() (types []*Type) Types lists the top\-level types provided by the package\. -### func \(\*Package\) [Vars]() +### func \(\*Package\) [Vars]() ```go func (pkg *Package) Vars() (vars []*Value) @@ -556,7 +558,7 @@ PackageOption configures one or more options for the package\. type PackageOption func(opts *PackageOptions) error ``` -### func [PackageWithRepositoryOverrides]() +### func [PackageWithRepositoryOverrides]() ```go func PackageWithRepositoryOverrides(repo *Repo) PackageOption @@ -564,7 +566,7 @@ func PackageWithRepositoryOverrides(repo *Repo) PackageOption PackageWithRepositoryOverrides can be used along with the NewPackageFromBuild function to define manual overrides to the automatic repository detection logic\. -### func [PackageWithUnexportedIncluded]() +### func [PackageWithUnexportedIncluded]() ```go func PackageWithUnexportedIncluded() PackageOption @@ -582,7 +584,7 @@ type PackageOptions struct { } ``` -## type [Position]() +## type [Position]() Position represents a line and column number within a file\. @@ -593,7 +595,7 @@ type Position struct { } ``` -## type [Repo]() +## type [Repo]() Repo represents information about a repository relevant to documentation generation\. @@ -601,7 +603,7 @@ Repo represents information about a repository relevant to documentation generat type Repo struct { Remote string DefaultBranch string - RootDir string + PathFromRoot string } ``` diff --git a/lang/config.go b/lang/config.go index e7a1589..2500c06 100644 --- a/lang/config.go +++ b/lang/config.go @@ -22,6 +22,7 @@ type ( Level int Repo *Repo PkgDir string + WorkDir string Log logger.Logger } @@ -30,7 +31,7 @@ type ( Repo struct { Remote string DefaultBranch string - RootDir string + PathFromRoot string } // Location holds information for identifying a position within a file and @@ -39,6 +40,7 @@ type ( Start Position End Position Filepath string + WorkDir string Repo *Repo } @@ -56,7 +58,7 @@ type ( // resolve the filepath and attempt to determine the repository containing the // directory. If no repository is found, the Repo field will be set to nil. An // error is returned if the provided directory is invalid. -func NewConfig(log logger.Logger, pkgDir string, opts ...ConfigOption) (*Config, error) { +func NewConfig(log logger.Logger, workDir string, pkgDir string, opts ...ConfigOption) (*Config, error) { cfg := &Config{ FileSet: token.NewFileSet(), Level: 1, @@ -69,15 +71,20 @@ func NewConfig(log logger.Logger, pkgDir string, opts ...ConfigOption) (*Config, } } - dir, err := filepath.Abs(pkgDir) + var err error + + cfg.PkgDir, err = filepath.Abs(pkgDir) if err != nil { return nil, err } - cfg.PkgDir = dir + cfg.WorkDir, err = filepath.Abs(workDir) + if err != nil { + return nil, err + } - if cfg.Repo == nil || cfg.Repo.Remote == "" || cfg.Repo.DefaultBranch == "" || cfg.Repo.RootDir == "" { - repo, err := getRepoForDir(log, dir, cfg.Repo) + if cfg.Repo == nil || cfg.Repo.Remote == "" || cfg.Repo.DefaultBranch == "" || cfg.Repo.PathFromRoot == "" { + repo, err := getRepoForDir(log, cfg.WorkDir, cfg.PkgDir, cfg.Repo) if err != nil { log.Infof("unable to resolve repository due to error: %s", err) cfg.Repo = nil @@ -85,10 +92,10 @@ func NewConfig(log logger.Logger, pkgDir string, opts ...ConfigOption) (*Config, } log.Debugf( - "resolved repository with remote %s, default branch %s, root directory %s", + "resolved repository with remote %s, default branch %s, path from root %s", repo.Remote, repo.DefaultBranch, - repo.RootDir, + repo.PathFromRoot, ) cfg.Repo = repo } else { @@ -103,7 +110,10 @@ func (c *Config) Inc(step int) *Config { return &Config{ FileSet: c.FileSet, Level: c.Level + step, + PkgDir: c.PkgDir, + WorkDir: c.WorkDir, Repo: c.Repo, + Log: c.Log, } } @@ -111,14 +121,19 @@ func (c *Config) Inc(step int) *Config { // information to be used in place of automatic repository detection. func ConfigWithRepoOverrides(overrides *Repo) ConfigOption { return func(c *Config) error { - // The root dir must be absolute. Make sure it's normalized - if overrides != nil && overrides.RootDir != "" { - dir, err := filepath.Abs(overrides.RootDir) - if err != nil { - return fmt.Errorf("lang: unable to convert repository root directory into an absolute path: %w", err) + if overrides == nil { + return nil + } + + if overrides.PathFromRoot != "" { + // Convert it to the right pathing system + unslashed := filepath.FromSlash(overrides.PathFromRoot) + + if len(unslashed) == 0 || unslashed[0] != filepath.Separator { + return fmt.Errorf("provided repository path %s must be absolute", overrides.PathFromRoot) } - overrides.RootDir = dir + overrides.PathFromRoot = unslashed } c.Repo = overrides @@ -126,7 +141,7 @@ func ConfigWithRepoOverrides(overrides *Repo) ConfigOption { } } -func getRepoForDir(log logger.Logger, dir string, ri *Repo) (*Repo, error) { +func getRepoForDir(log logger.Logger, wd string, dir string, ri *Repo) (*Repo, error) { if ri == nil { ri = &Repo{} } @@ -138,14 +153,21 @@ func getRepoForDir(log logger.Logger, dir string, ri *Repo) (*Repo, error) { return nil, err } - // Set the root dir if there wasn't one - if ri.RootDir == "" { + // Set the path from root if there wasn't one + if ri.PathFromRoot == "" { t, err := repo.Worktree() if err != nil { return nil, err } - ri.RootDir = t.Filesystem.Root() + // Get the path from the root of the repo to the working dir, then make + // it absolute (i.e. prefix with /). + p, err := filepath.Rel(t.Filesystem.Root(), wd) + if err != nil { + return nil, err + } + + ri.PathFromRoot = filepath.Join(string(filepath.Separator), p) } // No need to check remotes if we already have a url and a default branch @@ -326,6 +348,7 @@ func NewLocation(cfg *Config, node ast.Node) Location { Start: Position{start.Line, start.Column}, End: Position{end.Line, end.Column}, Filepath: start.Filename, + WorkDir: cfg.WorkDir, Repo: cfg.Repo, } } diff --git a/lang/package.go b/lang/package.go index 64b0b35..aea9ee7 100644 --- a/lang/package.go +++ b/lang/package.go @@ -56,7 +56,12 @@ func NewPackageFromBuild(log logger.Logger, pkg *build.Package, opts ...PackageO } } - cfg, err := NewConfig(log, pkg.Dir, ConfigWithRepoOverrides(options.repositoryOverrides)) + wd, err := os.Getwd() + if err != nil { + return nil, err + } + + cfg, err := NewConfig(log, wd, pkg.Dir, ConfigWithRepoOverrides(options.repositoryOverrides)) if err != nil { return nil, err }