feat(processor): add git-aware comment removal with line-based processing
- Add processGitChanges() for git-aware operation routing - Implement line-based comment removal with precise targeting - Add GitTotals tracking for comprehensive dry-run reporting - Create showGitPreviewWithTotals() with enhanced preview display - Fix spacing preservation (only trim when comments are removed) - Include comprehensive test suite for comment removal logic
This commit is contained in:
@@ -0,0 +1,326 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/carlosarraes/shush/internal/git"
|
||||||
|
"github.com/carlosarraes/shush/internal/types"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type GitTotals struct {
|
||||||
|
FilesProcessed int
|
||||||
|
TotalChanged int
|
||||||
|
TotalKept int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *Processor) processGitChanges() error {
|
||||||
|
|
||||||
|
gitStatus, err := git.DetectRepo()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect git repository: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gitStatus.IsRepo {
|
||||||
|
return fmt.Errorf("not in a git repository. Use without git flags to process files normally")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("Git repository detected: %s\n", gitStatus.RootDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var changes []git.FileChange
|
||||||
|
switch {
|
||||||
|
case p.cli.ChangesOnly:
|
||||||
|
changes, err = git.GetChangesOnly()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get git changes: %v", err)
|
||||||
|
}
|
||||||
|
case p.cli.Staged:
|
||||||
|
changes, err = git.GetStagedChanges()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get staged changes: %v", err)
|
||||||
|
}
|
||||||
|
case p.cli.Unstaged:
|
||||||
|
changes, err = git.GetUnstagedChanges()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get unstaged changes: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) == 0 {
|
||||||
|
fmt.Println("No changes found to process")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("Found %d files with changes to process\n", len(changes))
|
||||||
|
}
|
||||||
|
|
||||||
|
totals := &GitTotals{}
|
||||||
|
|
||||||
|
|
||||||
|
for _, change := range changes {
|
||||||
|
|
||||||
|
if !IsSupportedFile(change.Path) {
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("Skipping unsupported file: %s\n", change.Path)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("Processing: %s\n", change.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cli.DryRun {
|
||||||
|
if err := p.showGitPreviewWithTotals(change.Path, change.LineRanges, totals); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error processing %s: %v\n", change.Path, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := p.processFileWithLineRanges(change.Path, change.LineRanges); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error processing %s: %v\n", change.Path, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totals.FilesProcessed++
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if p.cli.DryRun && totals.FilesProcessed > 0 {
|
||||||
|
p.showGitTotals(totals)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *Processor) processFileWithLineRanges(filename string, lineRanges []git.LineRange) error {
|
||||||
|
language, err := DetectLanguage(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("Processing %s (language: %s)\n", filename, GetLanguageName(filename))
|
||||||
|
if len(lineRanges) == 0 {
|
||||||
|
fmt.Printf("Processing entire file (untracked)\n")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Processing %d line ranges\n", len(lineRanges))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
lines = append(lines, scanner.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if p.cli.Backup {
|
||||||
|
if err := p.createBackup(filename); err != nil {
|
||||||
|
return fmt.Errorf("failed to create backup: %v", err)
|
||||||
|
}
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("✓ Backup created: %s.bak\n", filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
processEntireFile := len(lineRanges) == 0
|
||||||
|
modified := false
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
lineNum := i + 1
|
||||||
|
shouldProcess := processEntireFile || git.IsInLineRanges(lineNum, lineRanges)
|
||||||
|
|
||||||
|
if shouldProcess {
|
||||||
|
newLine := p.removeCommentsFromLine(line, language)
|
||||||
|
if newLine != line {
|
||||||
|
lines[i] = newLine
|
||||||
|
modified = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if modified {
|
||||||
|
outFile, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if _, err := fmt.Fprintln(outFile, line); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cli.Verbose {
|
||||||
|
fmt.Printf("✓ Comments removed from %s\n", filename)
|
||||||
|
}
|
||||||
|
} else if p.cli.Verbose {
|
||||||
|
fmt.Printf("✓ No changes made to %s\n", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *Processor) removeCommentsFromLine(line string, language types.Language) string {
|
||||||
|
result := line
|
||||||
|
hasChanges := false
|
||||||
|
|
||||||
|
|
||||||
|
if !p.cli.Block && language.LineComment != "" {
|
||||||
|
|
||||||
|
if idx := strings.Index(result, language.LineComment); idx != -1 {
|
||||||
|
result = result[:idx]
|
||||||
|
hasChanges = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if !p.cli.Inline && language.BlockComment != nil {
|
||||||
|
|
||||||
|
|
||||||
|
startComment := language.BlockComment.Start
|
||||||
|
endComment := language.BlockComment.End
|
||||||
|
|
||||||
|
for {
|
||||||
|
startIdx := strings.Index(result, startComment)
|
||||||
|
if startIdx == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
endIdx := strings.Index(result[startIdx:], endComment)
|
||||||
|
if endIdx == -1 {
|
||||||
|
|
||||||
|
result = result[:startIdx]
|
||||||
|
hasChanges = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
endIdx += startIdx + len(endComment)
|
||||||
|
result = result[:startIdx] + result[endIdx:]
|
||||||
|
hasChanges = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if hasChanges {
|
||||||
|
result = strings.TrimSpace(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *Processor) showGitPreviewWithTotals(filename string, lineRanges []git.LineRange, totals *GitTotals) error {
|
||||||
|
language, err := DetectLanguage(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
red := color.New(color.FgRed, color.CrossedOut)
|
||||||
|
green := color.New(color.FgGreen)
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
gray := color.New(color.FgHiBlack)
|
||||||
|
yellow := color.New(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Printf("\n%s %s\n", yellow.Sprint("Git Preview:"), filename)
|
||||||
|
if len(lineRanges) == 0 {
|
||||||
|
fmt.Printf("%s\n", blue.Sprint("Processing entire file (untracked)"))
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s %d line ranges\n", blue.Sprint("Processing"), len(lineRanges))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
lineNum := 0
|
||||||
|
keptCount := 0
|
||||||
|
changedCount := 0
|
||||||
|
|
||||||
|
processEntireFile := len(lineRanges) == 0
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
lineNum++
|
||||||
|
line := scanner.Text()
|
||||||
|
shouldProcess := processEntireFile || git.IsInLineRanges(lineNum, lineRanges)
|
||||||
|
|
||||||
|
lineNumStr := gray.Sprintf("%4d", lineNum)
|
||||||
|
|
||||||
|
if shouldProcess {
|
||||||
|
newLine := p.removeCommentsFromLine(line, language)
|
||||||
|
if newLine != line {
|
||||||
|
changedCount++
|
||||||
|
fmt.Printf("%s %s %s\n", lineNumStr, red.Sprint("~"), red.Sprint(line))
|
||||||
|
if strings.TrimSpace(newLine) != "" {
|
||||||
|
fmt.Printf("%s %s %s\n", lineNumStr, green.Sprint("+"), green.Sprint(newLine))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keptCount++
|
||||||
|
fmt.Printf("%s %s %s\n", lineNumStr, green.Sprint(" "), line)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keptCount++
|
||||||
|
fmt.Printf("%s %s %s\n", lineNumStr, gray.Sprint(" "), gray.Sprint(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n%s\n", strings.Repeat("-", 50))
|
||||||
|
fmt.Printf("%s %d lines would be changed\n", yellow.Sprint("~"), changedCount)
|
||||||
|
fmt.Printf("%s %d lines would be kept\n\n", green.Sprint("✓"), keptCount)
|
||||||
|
|
||||||
|
|
||||||
|
totals.TotalChanged += changedCount
|
||||||
|
totals.TotalKept += keptCount
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *Processor) showGitTotals(totals *GitTotals) {
|
||||||
|
yellow := color.New(color.FgYellow)
|
||||||
|
green := color.New(color.FgGreen)
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
|
||||||
|
fmt.Printf("\n%s\n", strings.Repeat("=", 60))
|
||||||
|
fmt.Printf("%s\n", blue.Sprint("GIT PROCESSING TOTALS"))
|
||||||
|
fmt.Printf("%s\n", strings.Repeat("=", 60))
|
||||||
|
fmt.Printf("%s %d files processed\n", blue.Sprint("📁"), totals.FilesProcessed)
|
||||||
|
fmt.Printf("%s %d lines would be changed\n", yellow.Sprint("~"), totals.TotalChanged)
|
||||||
|
fmt.Printf("%s %d lines would be kept\n", green.Sprint("✓"), totals.TotalKept)
|
||||||
|
fmt.Printf("%s\n", strings.Repeat("=", 60))
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/carlosarraes/shush/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRemoveCommentsFromLine(t *testing.T) {
|
||||||
|
|
||||||
|
cli := types.CLI{}
|
||||||
|
p := &Processor{cli: cli}
|
||||||
|
|
||||||
|
|
||||||
|
jsLanguage := types.Language{
|
||||||
|
LineComment: "
|
||||||
|
BlockComment: &types.BlockComment{
|
||||||
|
Start: "
|
||||||
|
End: "*/",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pyLanguage := types.Language{
|
||||||
|
LineComment: "#",
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
line string
|
||||||
|
language types.Language
|
||||||
|
cli types.CLI
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "line comment removal - JavaScript",
|
||||||
|
line: "console.log('hello');
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{},
|
||||||
|
expected: "console.log('hello');",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "line comment removal - Python",
|
||||||
|
line: "print('hello') # This is a comment",
|
||||||
|
language: pyLanguage,
|
||||||
|
cli: types.CLI{},
|
||||||
|
expected: "print('hello')",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "block comment removal - single line",
|
||||||
|
line: "var x = 5; var y = 10;",
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{},
|
||||||
|
expected: "var x = 5; var y = 10;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only line comment when inline flag set",
|
||||||
|
line: "code();
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{Inline: true},
|
||||||
|
expected: "code();",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only block comment when block flag set",
|
||||||
|
line: "code();
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{Block: true},
|
||||||
|
expected: "code();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no comments to remove",
|
||||||
|
line: "var x = 5;",
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{},
|
||||||
|
expected: "var x = 5;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comment-only line",
|
||||||
|
line: "
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple block comments",
|
||||||
|
line: " code ",
|
||||||
|
language: jsLanguage,
|
||||||
|
cli: types.CLI{},
|
||||||
|
expected: "code",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
p.cli = tt.cli
|
||||||
|
result := p.removeCommentsFromLine(tt.line, tt.language)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("removeCommentsFromLine() = %q, want %q", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveCommentsFromLineEdgeCases(t *testing.T) {
|
||||||
|
cli := types.CLI{}
|
||||||
|
p := &Processor{cli: cli}
|
||||||
|
|
||||||
|
jsLanguage := types.Language{
|
||||||
|
LineComment: "
|
||||||
|
BlockComment: &types.BlockComment{
|
||||||
|
Start: "
|
||||||
|
End: "*/",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
line string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty line",
|
||||||
|
line: "",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "whitespace only",
|
||||||
|
line: " \t ",
|
||||||
|
expected: " \t ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comment at start",
|
||||||
|
line: "
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comment with special characters",
|
||||||
|
line: "code();
|
||||||
|
expected: "code();",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comment after code",
|
||||||
|
line: `console.log("hello");
|
||||||
|
expected: `console.log("hello");`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := p.removeCommentsFromLine(tt.line, jsLanguage)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("removeCommentsFromLine() = %q, want %q", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,12 @@ func New(cli types.CLI) *Processor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) Process() error {
|
func (p *Processor) Process() error {
|
||||||
|
|
||||||
|
if p.cli.ChangesOnly || p.cli.Staged || p.cli.Unstaged {
|
||||||
|
return p.processGitChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
info, err := os.Stat(p.cli.Path)
|
info, err := os.Stat(p.cli.Path)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return fmt.Errorf("path not found: %s", p.cli.Path)
|
return fmt.Errorf("path not found: %s", p.cli.Path)
|
||||||
@@ -34,13 +40,13 @@ func (p *Processor) Process() error {
|
|||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
return p.processDirectory(p.cli.Path)
|
return p.processDirectory(p.cli.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.processFile(p.cli.Path)
|
return p.processFile(p.cli.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) processDirectory(dirPath string) error {
|
func (p *Processor) processDirectory(dirPath string) error {
|
||||||
var files []string
|
var files []string
|
||||||
|
|
||||||
if p.cli.Recursive {
|
if p.cli.Recursive {
|
||||||
err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error {
|
err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,7 +65,7 @@ func (p *Processor) processDirectory(dirPath string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if !entry.IsDir() {
|
if !entry.IsDir() {
|
||||||
fullPath := filepath.Join(dirPath, entry.Name())
|
fullPath := filepath.Join(dirPath, entry.Name())
|
||||||
@@ -82,7 +88,7 @@ func (p *Processor) processDirectory(dirPath string) error {
|
|||||||
if p.cli.Verbose {
|
if p.cli.Verbose {
|
||||||
fmt.Printf("Processing: %s\n", file)
|
fmt.Printf("Processing: %s\n", file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.processFile(file); err != nil {
|
if err := p.processFile(file); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error processing %s: %v\n", file, err)
|
fmt.Fprintf(os.Stderr, "Error processing %s: %v\n", file, err)
|
||||||
continue
|
continue
|
||||||
@@ -102,7 +108,7 @@ func (p *Processor) processFile(filename string) error {
|
|||||||
fmt.Printf("Processing %s...\n", filename)
|
fmt.Printf("Processing %s...\n", filename)
|
||||||
fmt.Printf("Detected language: %s\n", GetLanguageName(filename))
|
fmt.Printf("Detected language: %s\n", GetLanguageName(filename))
|
||||||
if language.BlockComment != nil {
|
if language.BlockComment != nil {
|
||||||
fmt.Printf("Comment types: line (%s), block (%s %s)\n",
|
fmt.Printf("Comment types: line (%s), block (%s %s)\n",
|
||||||
language.LineComment, language.BlockComment.Start, language.BlockComment.End)
|
language.LineComment, language.BlockComment.Start, language.BlockComment.End)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Comment types: line (%s)\n", language.LineComment)
|
fmt.Printf("Comment types: line (%s)\n", language.LineComment)
|
||||||
@@ -112,7 +118,7 @@ func (p *Processor) processFile(filename string) error {
|
|||||||
if p.cli.DryRun {
|
if p.cli.DryRun {
|
||||||
return p.showPreview(filename, language)
|
return p.showPreview(filename, language)
|
||||||
}
|
}
|
||||||
|
|
||||||
sedCmd := p.buildSedCommand(language)
|
sedCmd := p.buildSedCommand(language)
|
||||||
|
|
||||||
if p.cli.Backup {
|
if p.cli.Backup {
|
||||||
@@ -142,7 +148,7 @@ func (p *Processor) processFile(filename string) error {
|
|||||||
|
|
||||||
func (p *Processor) buildSedCommand(language types.Language) string {
|
func (p *Processor) buildSedCommand(language types.Language) string {
|
||||||
var commands []string
|
var commands []string
|
||||||
|
|
||||||
if !p.cli.Block && language.LineComment != "" {
|
if !p.cli.Block && language.LineComment != "" {
|
||||||
escaped := escapeForSed(language.LineComment)
|
escaped := escapeForSed(language.LineComment)
|
||||||
// Delete lines that are only comments (optionally with whitespace)
|
// Delete lines that are only comments (optionally with whitespace)
|
||||||
@@ -150,7 +156,7 @@ func (p *Processor) buildSedCommand(language types.Language) string {
|
|||||||
// Remove inline comments but keep the line
|
// Remove inline comments but keep the line
|
||||||
commands = append(commands, fmt.Sprintf("s/%s.*//g", escaped))
|
commands = append(commands, fmt.Sprintf("s/%s.*//g", escaped))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.cli.Inline && language.BlockComment != nil {
|
if !p.cli.Inline && language.BlockComment != nil {
|
||||||
startEscaped := escapeForSed(language.BlockComment.Start)
|
startEscaped := escapeForSed(language.BlockComment.Start)
|
||||||
endEscaped := escapeForSed(language.BlockComment.End)
|
endEscaped := escapeForSed(language.BlockComment.End)
|
||||||
@@ -159,9 +165,9 @@ func (p *Processor) buildSedCommand(language types.Language) string {
|
|||||||
// Remove multi-line block comments
|
// Remove multi-line block comments
|
||||||
commands = append(commands, fmt.Sprintf("/%s/,/%s/d", startEscaped, endEscaped))
|
commands = append(commands, fmt.Sprintf("/%s/,/%s/d", startEscaped, endEscaped))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't remove empty lines - preserve original file structure
|
// Don't remove empty lines - preserve original file structure
|
||||||
|
|
||||||
return strings.Join(commands, "; ")
|
return strings.Join(commands, "; ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,19 +187,19 @@ func escapeForSed(pattern string) string {
|
|||||||
|
|
||||||
func (p *Processor) createBackup(filename string) error {
|
func (p *Processor) createBackup(filename string) error {
|
||||||
backupName := filename + ".bak"
|
backupName := filename + ".bak"
|
||||||
|
|
||||||
srcFile, err := os.Open(filename)
|
srcFile, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer srcFile.Close()
|
defer srcFile.Close()
|
||||||
|
|
||||||
dstFile, err := os.Create(backupName)
|
dstFile, err := os.Create(backupName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer dstFile.Close()
|
defer dstFile.Close()
|
||||||
|
|
||||||
buffer := make([]byte, 1024)
|
buffer := make([]byte, 1024)
|
||||||
for {
|
for {
|
||||||
n, err := srcFile.Read(buffer)
|
n, err := srcFile.Read(buffer)
|
||||||
@@ -203,12 +209,12 @@ func (p *Processor) createBackup(filename string) error {
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := dstFile.Write(buffer[:n]); err != nil {
|
if _, err := dstFile.Write(buffer[:n]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,4 +290,4 @@ func (p *Processor) showPreview(filename string, language types.Language) error
|
|||||||
fmt.Printf("%s %d lines would be kept\n\n", green.Sprint("✓"), keptCount)
|
fmt.Printf("%s %d lines would be kept\n\n", green.Sprint("✓"), keptCount)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user