<commit_before>package protocol

import (
	"errors"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strings"

	"golang-refactoring.org/go-doctor/doctor"
)

type XRun struct {
	Transformation string                 `json:"transformation"`
	Fileselection  []string               `json:"fileselection"`
	Textselection  map[string]interface{} `json:"textselection"`
	Arguments      []interface{}          `json:"arguments"`
	Limit          int                    `json:"limit"`
	Mode           string                 `json:"mode" chk:"text|patch"`
}

// TODO implement
func (x *XRun) Run(state *State, input map[string]interface{}) (Reply, error) {
	if valid, err := x.Validate(state, input); !valid {
		return Reply{map[string]interface{}{"reply": "Error", "message": err.Error()}}, err
	}
	// setup TextSelection
	textselection := input["textselection"].(map[string]interface{})
	ts := doctor.TextSelection{
		Filename:  filepath.Join(state.Dir, textselection["filename"].(string)),
		StartLine: int(textselection["startline"].(float64)),
		StartCol:  int(textselection["startcol"].(float64)),
		EndLine:   int(textselection["endline"].(float64)),
		EndCol:    int(textselection["endcol"].(float64)),
	}

	// get refactoring
	refactoring := doctor.GetRefactoring(input["transformation"].(string))

	config := &doctor.Config{
		FileSystem: state.Filesystem,
		Scope:      nil,
		Selection:  ts,
		Args:       input["arguments"].([]interface{}),
	}

	// run
	result := refactoring.Run(config)

	// grab logs
	logs := make([]map[string]interface{}, 0)
	for _, entry := range result.Log.Entries {
		var severity string
		switch entry.Severity {
		case doctor.INFO:
			// No prefix
		case doctor.WARNING:
			severity = "warning"
		case doctor.ERROR:
			severity = "error"
		case doctor.FATAL_ERROR:
			severity = "fatal"
		}
		log := map[string]interface{}{"severity": severity, "message": entry.Message}
		logs = append(logs, log)
	}
	changes := make([]map[string]string, 0)
	if input["mode"].(string) == "patch" {
		for f, e := range result.Edits {
			var p *doctor.Patch
			var err error
			p, err = doctor.CreatePatchForFile(e, f)
			if err != nil {
				return Reply{map[string]interface{}{"reply": "Error", "message": err.Error()}}, err
			}
			diffFile, err := os.Create(strings.Join([]string{f, ".diff"}, ""))
			p.Write(f, f, diffFile)
			//fmt.Println(f)
			//fmt.Println(diffFile.Name())
			changes = append(changes, map[string]string{"filename": f, "patchFile": diffFile.Name()})
			diffFile.Close()
		}
	} else {
		for f, e := range result.Edits {
			content, err := doctor.ApplyToFile(e, f)
			if err != nil {
				return Reply{map[string]interface{}{"reply": "Error", "message": err.Error()}}, err
			}
			changes = append(changes, map[string]string{"filename": f, "content": string(content)})
		}
	}

	return Reply{map[string]interface{}{"reply": "OK", "description": refactoring.Description().Name, "log": logs, "files": changes}}, nil
}

// TODO validate TextSelection, FileSelection, arguments
func (x *XRun) Validate(state *State, input map[string]interface{}) (bool, error) {
	if state.State < 2 {
		return false, errors.New("State of 2 (file system configured) is required")
	}

	// check transformation is valid
	var valid bool
	for shortName, _ := range doctor.AllRefactorings() {
		if shortName == input["transformation"].(string) {<commit_msg>added file system changes to the return from xrun<commit_after>package protocol

import (
	"errors"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strings"

	"golang-refactoring.org/go-doctor/doctor"
)

type XRun struct {
	Transformation string                 `json:"transformation"`
	Fileselection  []string               `json:"fileselection"`
	Textselection  map[string]interface{} `json:"textselection"`
	Arguments      []interface{}          `json:"arguments"`
	Limit          int                    `json:"limit"`
	Mode           string                 `json:"mode" chk:"text|patch"`
}

// TODO implement
func (x *XRun) Run(state *State, input map[string]interface{}) (Reply, error) {
	if valid, err := x.Validate(state, input); !valid {
		return Reply{map[string]interface{}{"reply": "Error", "message": err.Error()}}, err
	}
	// setup TextSelection
	textselection := input["textselection"].(map[string]interface{})
	ts := doctor.TextSelection{
		Filename:  filepath.Join(state.Dir, textselection["filename"].(string)),
		StartLine: int(textselection["startline"].(float64)),
		StartCol:  int(textselection["startcol"].(float64)),
		EndLine:   int(textselection["endline"].(float64)),
		EndCol:    int(textselection["endcol"].(float64)),
	}

	// get refactoring
	refactoring := doctor.GetRefactoring(input["transformation"].(string))

	config := &doctor.Config{
		FileSystem: state.Filesystem,
		Scope:      nil,
		Selection:  ts,
		Args:       input["arguments"].([]interface{}),
	}

	// run
	result := refactoring.Run(config)

	// grab logs
	logs := make([]map[string]interface{}, 0)
	fatalError := false
	for _, entry := range result.Log.Entries {
		var severity string
		switch entry.Severity {
		case doctor.INFO:
			// No prefix
		case doctor.WARNING:
			severity = "warning"
		case doctor.ERROR:
			severity = "error"
		case doctor.FATAL_ERROR:
			severity = "fatal"
			fatalError = true
		}
		log := map[string]interface{}{"severity": severity, "message": entry.Message}
		logs = append(logs, log)
	}
	// any fatal errors? return without giving changes
	if fatalError {
		return Reply{map[string]interface{}{"reply": "OK", "description": refactoring.Description().Name, "log": logs}}, nil
	}

	changes := make([]map[string]string, 0)

	// if mode == patch or no mode was given
	if mode, found := input["mode"]; !found || mode.(string) == "patch" {
		for f, e := range result.Edits {
			var p *doctor.Patch
			var err error
			p, err = doctor.CreatePatchForFile(e, f)
			if err != nil {
				return Reply{map[string]interface{}{"reply": "Error", "message": err.Error()}}, err
			}
			diffFile, err := os.Create(strings.Join([]string{f, ".diff"}, ""))
			p.Write(f, f, diffFile)
			//fmt.Println(f)
			//fmt.Println(diffFile.Name())
			changes = append(changes, map[string]string{"filename": f, "patchFile": diffFile.Name()})
			diffFile.Close()
		}
	} else {
		for f, e := range result.Edits {
			content, err := doctor.ApplyToFile(e, f)
			if err != nil {
				return Reply{map[string]interface{}{"reply": "Error", "message": err.Error()}}, err
			}
			changes = append(changes, map[string]string{"filename": f, "content": string(content)})
		}
	}

	// filesystem changes
	var fschanges []map[string]string
	if len(result.FSChanges) > 0 {
		fschanges = make([]map[string]string, len(result.FSChanges))
		for i, change := range result.FSChanges {
			switch change := change.(type) {
			case *doctor.FSCreateFile:
				fschanges[i] = map[string]string{"change": "create", "file": change.Path, "content": change.Contents}
			case *doctor.FSRemove:
				fschanges[i] = map[string]string{"change": "delete", "path": change.Path}
			case *doctor.FSRename:
				fschanges[i] = map[string]string{"change": "rename", "from": change.Path, "to": change.NewName}
			}
		}
		// return with filesystem changes
		return Reply{map[string]interface{}{"reply": "OK", "description": refactoring.Description().Name, "log": logs, "files": changes, "fsChanges": fschanges}}, nil
	}

	// return without filesystem changes
	return Reply{map[string]interface{}{"reply": "OK", "description": refactoring.Description().Name, "log": logs, "files": changes}}, nil
}

// TODO validate TextSelection, FileSelection, arguments
func (x *XRun) Validate(state *State, input map[string]interface{}) (bool, error) {
	if state.State < 2 {
		return false, errors.New("State of 2 (file system configured) is required")
	}

	// check transformation is valid
	var valid bool
	for shortName, _ := range doctor.AllRefactorings() {
		if shortName == input["transformation"].(string) {
{'fraction_non_alphanumeric': 0.16681421525230808, 'fraction_numerical': 0.0031617554066017454, 'mean_word_length': 4.062740076824584, 'pattern_counts': {'":': 48, '<': 5, '<?xml version=': 0, '>': 4, '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': '7343823', 'n_tokens_mistral': 2753, 'n_tokens_neox': 2508, 'n_words': 603}