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

feat(gnoweb): add hyperlinks to import p/*and r/* in source code viewer #3759

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f03b438
feat: add processing import
mous1985 Feb 5, 2025
2f8b529
feat: add hiperlink for source code of gno import
mous1985 Feb 15, 2025
27f7ac8
feat: make gno.land imports link to their source code
mous1985 Feb 15, 2025
bc14e9c
doc: re-add comments deleted by mistake
mous1985 Feb 15, 2025
76be49a
ref: move style to input.css
mous1985 Feb 15, 2025
94e4d74
fix: typo
mous1985 Feb 17, 2025
207cde4
fix: remove temp file from base32 package
mous1985 Feb 17, 2025
efdbef4
Merge branch 'master' into linkimport
mous1985 Feb 17, 2025
a32fbf8
fix: ci
mous1985 Feb 17, 2025
ae6382b
fix : ci update generated files
mous1985 Feb 17, 2025
fffb7f4
ref: use Tailwaind config theme instead to new class
mous1985 Feb 19, 2025
cf94c68
Merge branch 'master' into linkimport
mous1985 Feb 19, 2025
97e8ae5
fix: CI
mous1985 Feb 19, 2025
24991fa
Merge branch 'linkimport' of https://github.com/mous1985/gno into lin…
mous1985 Feb 19, 2025
e9130a0
ref: remove logger
mous1985 Feb 20, 2025
ede8448
fix: CI
mous1985 Feb 20, 2025
d17d363
test: add test cases for FormatSource
mous1985 Feb 20, 2025
be86fba
Merge remote-tracking branch 'upstream/master' into linkimport
mous1985 Feb 21, 2025
b409a55
Merge branch 'master' into linkimport
Feb 25, 2025
2346a61
Update gno.land/pkg/gnoweb/webclient_html.go
mous1985 Feb 26, 2025
30f26a5
fix: CI update test
Feb 26, 2025
558b00d
Merge branch 'master' into linkimport
Feb 26, 2025
c57b3de
Merge branch 'linkimport' of https://github.com/mous1985/gno into lin…
Feb 26, 2025
d9f5794
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 3, 2025
4887d09
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 4, 2025
f471bda
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 6, 2025
9fbfd2f
ref: remove contains gno.land
Mar 6, 2025
5d43cc6
ref: add const to chroma span formatting
Mar 6, 2025
a3ce1be
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 6, 2025
3952b3d
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 7, 2025
3d25c38
ref: use regex
Mar 7, 2025
89c0e2b
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 8, 2025
b0e05ba
Merge branch 'master' into linkimport
mous1985 Mar 9, 2025
4a18050
Merge branch 'master' of https://github.com/mous1985/gno into linkimport
Mar 10, 2025
62e8feb
Merge branch 'master' into linkimport
thehowl Mar 11, 2025
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
32 changes: 30 additions & 2 deletions gno.land/pkg/gnoweb/webclient_html.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package gnoweb

import (
"bytes"
"errors"
"fmt"
"io"
"log/slog"
gopath "path"
"regexp"
"strings"

"github.com/alecthomas/chroma/v2"
Expand Down Expand Up @@ -252,6 +254,8 @@
func (s *HTMLWebClient) FormatSource(w io.Writer, fileName string, src []byte) error {
var lexer chroma.Lexer

const gnolandPrefix = "gno.land/"

// Determine the lexer to be used based on the file extension.
switch strings.ToLower(gopath.Ext(fileName)) {
case ".gno":
Expand All @@ -268,16 +272,40 @@
return fmt.Errorf("unsupported lexer for file %q", fileName)
}

// Format with Chroma
var buf bytes.Buffer
iterator, err := lexer.Tokenise(nil, string(src))
if err != nil {
return fmt.Errorf("unable to tokenise %q: %w", fileName, err)
}

if err := s.Formatter.Format(w, s.chromaStyle, iterator); err != nil {
if err := s.Formatter.Format(&buf, s.chromaStyle, iterator); err != nil {
return fmt.Errorf("unable to format source file %q: %w", fileName, err)
}

return nil
formatted := buf.String()

// Process .gno files to add links
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hack

We have an extensible markdown engine, we should implement this as an extension there, unless you can show it's impossible

if strings.HasSuffix(fileName, ".gno") {
pattern := `(<span class="chroma-s">&#34;)(gno\.land/[^&#34;]+)(&#34;</span>)`
re := regexp.MustCompile(pattern)

formatted = re.ReplaceAllStringFunc(formatted, func(match string) string {
submatch := re.FindStringSubmatch(match)
if len(submatch) < 4 {
return match
}

Check warning on line 297 in gno.land/pkg/gnoweb/webclient_html.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoweb/webclient_html.go#L296-L297

Added lines #L296 - L297 were not covered by tests

importPath := submatch[2]
urlPath := strings.TrimPrefix(importPath, gnolandPrefix)

return fmt.Sprintf(`<span class="chroma-s"><a href="/%s$source" class="hover:underline">&#34;%s&#34;</a></span>`,
urlPath, importPath)
})
}

_, err = w.Write([]byte(formatted))
return err
}

func (s *HTMLWebClient) WriteFormatterCSS(w io.Writer) error {
Expand Down
109 changes: 109 additions & 0 deletions gno.land/pkg/gnoweb/webclient_html_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package gnoweb

import (
"bytes"
"fmt"
"log/slog"
"testing"

chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/stretchr/testify/assert"
)

func TestFormatSource(t *testing.T) {
// Setup test client with a no-op logger
logger := slog.New(slog.NewTextHandler(bytes.NewBuffer(nil), nil))

cfg := &HTMLWebClientConfig{
Domain: "gno.land",
ChromaStyle: chromaDefaultStyle,
ChromaHTMLOptions: []chromahtml.Option{
chromahtml.WithClasses(true),
chromahtml.ClassPrefix("chroma-"),
},
}
client := NewHTMLClient(logger, cfg)

cases := []struct {
name string
fileName string
input string
expectedPaths []string
linkable bool
}{
{
name: "single import",
fileName: "test.gno",
input: `import "gno.land/a/b/abc"`,
expectedPaths: []string{"a/b/abc"},
linkable: true,
},
{
name: "multiple imports",
fileName: "test.gno",
input: `import (
"gno.land/a/b/abc"
"gno.land/d/e/def"
)`,
expectedPaths: []string{"a/b/abc", "d/e/def"},
linkable: true,
},
{
name: "named import",
fileName: "test.gno",
input: `import name "gno.land/x/y/xyz"`,
expectedPaths: []string{"x/y/xyz"},
linkable: true,
},
{
name: "empty name import",
fileName: "test.gno",
input: `import _ "gno.land/a/b/abc"`,
expectedPaths: []string{"a/b/abc"},
linkable: true,
},
{
name: "multiple imports with name and empty name",
fileName: "test.gno",
input: `import (
name "gno.land/x/y/xyz"
_ "gno.land/a/b/abc"
)`,
expectedPaths: []string{"x/y/xyz", "a/b/abc"},
linkable: true,
},
{
name: "non gno file",
fileName: "test.go",
input: `import "gno.land/a/b/abc"`,
expectedPaths: []string{},
linkable: false,
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
err := client.FormatSource(&buf, tt.fileName, []byte(tt.input))
assert.NoError(t, err)

result := buf.String()

if tt.linkable {
// Check each expected path has a corresponding link
for _, path := range tt.expectedPaths {
expectedLink := fmt.Sprintf(`<a href="/%s$source"`, path)
assert.Contains(t, result, expectedLink,
"Should contain link to source for path %s", path)
assert.Contains(t, result, `class="hover:underline"`)
}
} else {
assert.NotContains(t, result, `<a href=`,
"Should not contain any links")
}

// Check syntax highlighting is present
assert.Contains(t, result, `<span class="chroma-`)
})
}
}
Loading