diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-08 23:30:47 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-08 23:30:47 +0300 |
| commit | df623bfdcecad9fd4c0f5229ce9af5e8416a9558 (patch) | |
| tree | 31392b853685d7ead9540ecc2cf6cbca48cac421 | |
| parent | 22c5df936434d172f46e9a24cd4582d3658636eb (diff) | |
feat: add syntax highlighting labels and strip comments from code snippets
- Add language labels to code blocks for syntax highlighting (e.g., ```go, ```sh)
- Map languages to source-highlight compatible identifiers
- Strip comments from code snippets to show more actual code
- Comments no longer count towards the ~10 line target
- Support for multi-line and single-line comment removal
- Remove inline comments where appropriate
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
| -rw-r--r-- | internal/showcase/code_extractor.go | 80 | ||||
| -rw-r--r-- | internal/showcase/showcase.go | 59 |
2 files changed, 134 insertions, 5 deletions
diff --git a/internal/showcase/code_extractor.go b/internal/showcase/code_extractor.go index e8ba0d3..b51761c 100644 --- a/internal/showcase/code_extractor.go +++ b/internal/showcase/code_extractor.go @@ -165,13 +165,14 @@ func extractSnippetFromFile(filePath string, minLines, maxLines int) (string, er // Try to find the smallest complete function bestFunction := findSmallestCompleteFunction(lines) if bestFunction != "" { - return bestFunction, nil + return stripComments(bestFunction), nil } // If no complete function found, try to find a complete function/method functionStart, functionEnd := findCompleteFunctionOrMethod(lines, minLines, maxLines*2) // Allow larger functions if functionStart >= 0 && functionEnd >= 0 { - return strings.Join(lines[functionStart:functionEnd+1], "\n"), nil + snippet := strings.Join(lines[functionStart:functionEnd+1], "\n") + return stripComments(snippet), nil } // Fallback to finding an interesting start with at least minLines @@ -181,7 +182,8 @@ func extractSnippetFromFile(filePath string, minLines, maxLines int) (string, er if endLine > totalLines { endLine = totalLines } - return strings.Join(lines[interestingStart:endLine], "\n"), nil + snippet := strings.Join(lines[interestingStart:endLine], "\n") + return stripComments(snippet), nil } // Last resort: return first minLines (skip imports if possible) @@ -201,7 +203,8 @@ func extractSnippetFromFile(filePath string, minLines, maxLines int) (string, er endLine = totalLines } - return strings.Join(lines[skipLines:endLine], "\n"), nil + snippet := strings.Join(lines[skipLines:endLine], "\n") + return stripComments(snippet), nil } // findSmallestCompleteFunction finds the smallest complete function in the file @@ -392,4 +395,73 @@ func findInterestingStart(lines []string, snippetSize int) int { // No interesting start found return -1 +} + +// stripComments removes comment lines from code snippets to make them more concise +func stripComments(code string) string { + lines := strings.Split(code, "\n") + var result []string + inMultilineComment := false + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + + // Handle multi-line comments for C-style languages + if strings.Contains(line, "/*") { + inMultilineComment = true + // If comment ends on same line, process the rest + if strings.Contains(line, "*/") { + inMultilineComment = false + // Skip this line entirely if it's just a comment + if strings.TrimSpace(strings.Split(line, "/*")[0]) == "" { + continue + } + } else { + continue + } + } + + if inMultilineComment { + if strings.Contains(line, "*/") { + inMultilineComment = false + } + continue + } + + // Skip single-line comments + if trimmed == "" { + // Keep empty lines for readability + result = append(result, line) + } else if strings.HasPrefix(trimmed, "//") || + strings.HasPrefix(trimmed, "#") && !strings.HasPrefix(trimmed, "#include") && !strings.HasPrefix(trimmed, "#define") || + strings.HasPrefix(trimmed, "<!--") || + strings.HasPrefix(trimmed, "*") && len(trimmed) > 1 && trimmed[1] == ' ' { + // Skip comment lines + continue + } else if strings.HasPrefix(trimmed, "\"\"\"") || strings.HasPrefix(trimmed, "'''") { + // Skip Python docstrings (simplified - doesn't handle all cases) + continue + } else { + // Keep the line but remove inline comments for some languages + // This is a simple approach - doesn't handle all edge cases + if idx := strings.Index(line, " //"); idx > 0 { + // Check if it's not inside a string (very basic check) + beforeComment := line[:idx] + if strings.Count(beforeComment, "\"")%2 == 0 && strings.Count(beforeComment, "'")%2 == 0 { + line = strings.TrimRight(line[:idx], " \t") + } + } + result = append(result, line) + } + } + + // Remove leading and trailing empty lines + for len(result) > 0 && strings.TrimSpace(result[0]) == "" { + result = result[1:] + } + for len(result) > 0 && strings.TrimSpace(result[len(result)-1]) == "" { + result = result[:len(result)-1] + } + + return strings.Join(result, "\n") }
\ No newline at end of file diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go index bb71b0e..884f81a 100644 --- a/internal/showcase/showcase.go +++ b/internal/showcase/showcase.go @@ -492,7 +492,9 @@ func (g *Generator) formatGemtext(summaries []ProjectSummary) string { // Add code snippet at the end for all projects if summary.CodeSnippet != "" { - builder.WriteString(fmt.Sprintf("\n%s:\n\n```\n%s\n```\n", summary.CodeLanguage, summary.CodeSnippet)) + // Extract the language name for syntax highlighting + languageName := extractLanguageForHighlighting(summary.CodeLanguage) + builder.WriteString(fmt.Sprintf("\n%s:\n\n```%s\n%s\n```\n", summary.CodeLanguage, languageName, summary.CodeSnippet)) } } @@ -714,4 +716,59 @@ func detectAIUsage(repoPath string) bool { } return false +} + +// extractLanguageForHighlighting extracts the language name from the CodeLanguage string +// for syntax highlighting in code blocks +func extractLanguageForHighlighting(codeLanguage string) string { + // codeLanguage format: "Language from `path/to/file`" + // Extract just the language name + parts := strings.Split(codeLanguage, " ") + if len(parts) > 0 { + lang := strings.ToLower(parts[0]) + + // Map language names to syntax highlighting identifiers + // Based on source-highlight --lang-list output + languageMap := map[string]string{ + "go": "go", + "python": "python", + "javascript": "javascript", + "typescript": "javascript", // Use javascript for TypeScript + "java": "java", + "c": "c", + "c++": "cpp", + "c/c++": "cpp", + "c#": "csharp", + "ruby": "ruby", + "rust": "rust", + "shell": "sh", // source-highlight uses sh.lang + "bash": "bash", + "perl": "perl", + "php": "php", + "swift": "swift", + "kotlin": "java", // Use java highlighting for Kotlin + "haskell": "haskell", + "lua": "lua", + "html": "html", + "css": "css", + "sql": "sql", + "make": "makefile", + "docker": "dockerfile", + "yaml": "yaml", + "json": "json", + "xml": "xml", + "toml": "toml", + "hcl": "hcl", + "vim": "vim", + } + + if mapped, ok := languageMap[lang]; ok { + return mapped + } + + // Return the original language name if no mapping exists + return lang + } + + return "" }
\ No newline at end of file |
