<commit_before>package main

import (
	"fmt"
	"github.com/pranavraja/refactor/patch"
	"github.com/vrischmann/termcolor"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
)

func walk(root string, suffix string, filePaths chan<- string) {
	defer close(filePaths)
	entries, err := ioutil.ReadDir(root)
	if err != nil {
		return
	}
	for _, file := range entries {
		if strings.HasPrefix(file.Name(), ".") {
			continue // Ignore hidden files
		}
		fullPath := path.Join(root, file.Name())
		if file.IsDir() {
			nestedFilePaths := make(chan string)
			go walk(fullPath, suffix, nestedFilePaths)
			for f := range nestedFilePaths {
				filePaths <- f
			}
		} else {
			if strings.HasSuffix(fullPath, suffix) {
				filePaths <- fullPath
			}
		}
	}
}

func patchAll(filenames <-chan string, find *regexp.Regexp, replace string, patches chan<- patch.Patch, proceed <-chan bool) {
	defer close(patches)
	for filename := range filenames {
		f, err := os.Open(filename)
		if err != nil {
			continue
		}
		contents, err := ioutil.ReadAll(f)
		f.Close()
		if err != nil {
			continue
		}
		filePatches := make(chan patch.Patch)
		patcherCanProceed := make(chan bool)
		fileResult := make(chan string)
		go patch.Patcher(string(contents), find, replace, filePatches, patcherCanProceed, fileResult)
		for patch := range filePatches {
			patch.Filename = filename
			patches <- patch
			patcherCanProceed <- <-proceed
		}
		r, changed := <-fileResult
		if changed {
			ioutil.WriteFile(filename, []byte(r), 0) // Assume file already exists
		}
	}
}

func prettyPrint(patch patch.Patch, out io.Writer) {
	fmt.Fprintf(out, "%s\n %s\n %s\n", termcolor.Colored(patch.Filename, termcolor.Cyan), termcolor.Colored("-"+patch.Before, termcolor.Red), termcolor.Colored("+"+patch.After, termcolor.Green))
}

func main() {
	if len(os.Args) <= 3 {
		println("Example: refactor .rb require import\n  Replaces 'require' with 'import' in all .rb files")
		return
	}
	suffix := os.Args[1]
	find := regexp.MustCompile(os.Args[2])
	replace := os.Args[3]
	patches := make(chan patch.Patch)
	proceed := make(chan bool)

	paths := make(chan string)
	go walk(".", suffix, paths)
	go patchAll(paths, find, replace, patches, proceed)
	var canProceed bool
	for p := range patches {
		prettyPrint(p, os.Stdout)
		if !canProceed {
			fmt.Printf("Continue? ([a]ll/[y]es/[n]o (default no): ")
			var input rune
			_, err := fmt.Scanf("%c\n", &input)
			if err != nil {
				fmt.Printf("%v", err)
				return
			}
			switch input {
			case 'a':
				canProceed = true
			case 'y':
			default:
				return
			}
		}
		proceed <- canProceed
	}
}<commit_msg>Read a string from stdin for better cross-platform<commit_after>package main

import (
	"fmt"
	"github.com/pranavraja/refactor/patch"
	"github.com/vrischmann/termcolor"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
)

func walk(root string, suffix string, filePaths chan<- string) {
	defer close(filePaths)
	entries, err := ioutil.ReadDir(root)
	if err != nil {
		return
	}
	for _, file := range entries {
		if strings.HasPrefix(file.Name(), ".") {
			continue // Ignore hidden files
		}
		fullPath := path.Join(root, file.Name())
		if file.IsDir() {
			nestedFilePaths := make(chan string)
			go walk(fullPath, suffix, nestedFilePaths)
			for f := range nestedFilePaths {
				filePaths <- f
			}
		} else {
			if strings.HasSuffix(fullPath, suffix) {
				filePaths <- fullPath
			}
		}
	}
}

func patchAll(filenames <-chan string, find *regexp.Regexp, replace string, patches chan<- patch.Patch, proceed <-chan bool) {
	defer close(patches)
	for filename := range filenames {
		f, err := os.Open(filename)
		if err != nil {
			continue
		}
		contents, err := ioutil.ReadAll(f)
		f.Close()
		if err != nil {
			continue
		}
		filePatches := make(chan patch.Patch)
		patcherCanProceed := make(chan bool)
		fileResult := make(chan string)
		go patch.Patcher(string(contents), find, replace, filePatches, patcherCanProceed, fileResult)
		for patch := range filePatches {
			patch.Filename = filename
			patches <- patch
			patcherCanProceed <- <-proceed
		}
		r, changed := <-fileResult
		if changed {
			ioutil.WriteFile(filename, []byte(r), 0) // Assume file already exists
		}
	}
}

func prettyPrint(patch patch.Patch, out io.Writer) {
	fmt.Fprintf(out, "%s\n %s\n %s\n", termcolor.Colored(patch.Filename, termcolor.Cyan), termcolor.Colored("-"+patch.Before, termcolor.Red), termcolor.Colored("+"+patch.After, termcolor.Green))
}

func main() {
	if len(os.Args) <= 3 {
		println("Example: refactor .rb require import\n  Replaces 'require' with 'import' in all .rb files")
		return
	}
	suffix := os.Args[1]
	find := regexp.MustCompile(os.Args[2])
	replace := os.Args[3]
	patches := make(chan patch.Patch)
	proceed := make(chan bool)

	paths := make(chan string)
	go walk(".", suffix, paths)
	go patchAll(paths, find, replace, patches, proceed)
	var canProceed bool
	for p := range patches {
		prettyPrint(p, os.Stdout)
		if !canProceed {
			fmt.Printf("Continue? ([a]ll/[y]es/[n]o (default no): ")
			var input string
			_, err := fmt.Scanf("%s", &input)
			if err != nil {
				return
			}
			switch input {
			case "a":
				canProceed = true
			case "y":
			default:
				return
			}
		}
		proceed <- canProceed
	}
}
{'fraction_non_alphanumeric': 0.14613180515759314, 'fraction_numerical': 0.0019102196752626551, 'mean_word_length': 3.5176876617773942, 'pattern_counts': {'":': 2, '<': 27, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '576463', 'n_tokens_mistral': 2103, 'n_tokens_neox': 1873, 'n_words': 473}