<commit_before>// Released under an MIT-style license. See LICENSE.

package main

import (
	"bufio"
	"fmt"
	"github.com/peterh/liner"
	"math/big"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"unicode"
)

type Liner struct {
	*liner.State
}

func (cli *Liner) ReadString(delim byte) (line string, err error) {
	SetForegroundGroup(pgid)

	uncooked.ApplyMode()
	defer cooked.ApplyMode()

	if line, err = cli.State.Prompt("> "); err == nil {
		cli.AppendHistory(line)
		if task0.Job.command == "" {
			task0.Job.command = line
		}
		line += "\n"
	}
	return
}

type Conduit interface {
	Context

	Close()
	ReaderClose()
	Read(t *Task) Cell
	ReadLine(t *Task) Cell
	WriterClose()
	Write(c Cell)
}

type Notification struct {
	pid    int
	status syscall.WaitStatus
}

type Registration struct {
	pid int
	cb  chan Notification
}

const (
	SaveCarCode = 1 << iota
	SaveCdrCode
	SaveDynamic
	SaveLexical
	SaveScratch
	SaveMax
)

const (
	psChangeContext = SaveMax + iota

	psEvalArguments
	psEvalArgumentsBuiltin
	psEvalBlock
	psEvalCommand
	psEvalElement
	psEvalElementBuiltin
	psEvalMember

	psExecBuiltin
	psExecCommand
	psExecDefine
	psExecDynamic
	psExecIf
	psExecMethod
	psExecPublic
	psExecSet
	psExecSetenv
	psExecSplice
	psExecSyntax
	psExecWhileBody
	psExecWhileTest
	psReturn

	psMax
	SaveCode = SaveCarCode | SaveCdrCode
)

var (
	cli         *Liner
	cooked      liner.ModeApplier
	env0        *Env
	envc        *Env
	envs        *Env
	external    Cell
	incoming    chan os.Signal
	interactive bool
	pgid        int
	pid         int
	register    chan Registration
	runnable    chan bool
	scope0      *Scope
	task0       *Task
	uncooked    liner.ModeApplier
)

var next = map[int64][]int64{
	psEvalArguments:        {SaveCdrCode, psEvalElement},
	psEvalArgumentsBuiltin: {SaveCdrCode, psEvalElementBuiltin},
	psExecIf:               {psEvalBlock},
	psExecWhileBody:        {psExecWhileTest, SaveCode, psEvalBlock},
}

/* Convert Context into a Conduit. (Return nil if not possible). */
func as_conduit(o Context) Conduit {
	if c, ok := o.(Conduit); ok {
		return c
	}

	return nil
}

func complete(line string, pos int) (string, []string, string) {
	head := line[:pos]
	tail := line[pos:]

	fields := strings.Fields(head)

	if len(fields) == 0 {
		return head, []string{"    "}, tail
	}

	word := fields[len(fields)-1]
	if !strings.HasSuffix(head, word) {
		return head, []string{}, tail
	}

	head = head[0 : len(head)-len(word)]

	completions := files(word)
	completions = append(completions, task0.Complete(word)...)

	if len(completions) == 0 {
		return head, []string{word}, tail
	}

	return head, completions, tail
}

func expand(t *Task, args Cell) Cell {
	list := Null

	for ; args != Null; args = Cdr(args) {
		c := Car(args)

		s := raw(c)
		if _, ok := c.(*Symbol); !ok {
			list = AppendTo(list, NewSymbol(s))
			continue
		}

		if s[:1] == "~" {
			s = filepath.Join(os.Getenv("HOME"), s[1:])
		}
<commit_msg>Check path when suggesting completions.<commit_after>// Released under an MIT-style license. See LICENSE.

package main

import (
	"bufio"
	"fmt"
	"github.com/peterh/liner"
	"math/big"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"unicode"
)

type Liner struct {
	*liner.State
}

func (cli *Liner) ReadString(delim byte) (line string, err error) {
	SetForegroundGroup(pgid)

	uncooked.ApplyMode()
	defer cooked.ApplyMode()

	if line, err = cli.State.Prompt("> "); err == nil {
		cli.AppendHistory(line)
		if task0.Job.command == "" {
			task0.Job.command = line
		}
		line += "\n"
	}
	return
}

type Conduit interface {
	Context

	Close()
	ReaderClose()
	Read(t *Task) Cell
	ReadLine(t *Task) Cell
	WriterClose()
	Write(c Cell)
}

type Notification struct {
	pid    int
	status syscall.WaitStatus
}

type Registration struct {
	pid int
	cb  chan Notification
}

const (
	SaveCarCode = 1 << iota
	SaveCdrCode
	SaveDynamic
	SaveLexical
	SaveScratch
	SaveMax
)

const (
	psChangeContext = SaveMax + iota

	psEvalArguments
	psEvalArgumentsBuiltin
	psEvalBlock
	psEvalCommand
	psEvalElement
	psEvalElementBuiltin
	psEvalMember

	psExecBuiltin
	psExecCommand
	psExecDefine
	psExecDynamic
	psExecIf
	psExecMethod
	psExecPublic
	psExecSet
	psExecSetenv
	psExecSplice
	psExecSyntax
	psExecWhileBody
	psExecWhileTest
	psReturn

	psMax
	SaveCode = SaveCarCode | SaveCdrCode
)

var (
	cli         *Liner
	cooked      liner.ModeApplier
	env0        *Env
	envc        *Env
	envs        *Env
	external    Cell
	incoming    chan os.Signal
	interactive bool
	pgid        int
	pid         int
	register    chan Registration
	runnable    chan bool
	scope0      *Scope
	task0       *Task
	uncooked    liner.ModeApplier
)

var next = map[int64][]int64{
	psEvalArguments:        {SaveCdrCode, psEvalElement},
	psEvalArgumentsBuiltin: {SaveCdrCode, psEvalElementBuiltin},
	psExecIf:               {psEvalBlock},
	psExecWhileBody:        {psExecWhileTest, SaveCode, psEvalBlock},
}

/* Convert Context into a Conduit. (Return nil if not possible). */
func as_conduit(o Context) Conduit {
	if c, ok := o.(Conduit); ok {
		return c
	}

	return nil
}

func complete(line string, pos int) (string, []string, string) {
	head := line[:pos]
	tail := line[pos:]

	fields := strings.Fields(head)

	if len(fields) == 0 {
		return head, []string{"    "}, tail
	}

	word := fields[len(fields)-1]
	if !strings.HasSuffix(head, word) {
		return head, []string{}, tail
	}

	head = head[0 : len(head)-len(word)]

	completions := task0.Complete(word)
	completions = append(completions, files(word)...)
        if len(fields) == 1 {
		completions = append(completions, executables(word)...)
	}

	if len(completions) == 0 {
		return head, []string{word}, tail
	}

	unique := make(map[string]bool)
	for _, completion := range completions {
		unique[completion] = true
	}

	completions = make([]string, 0, len(unique))
	for completion := range unique {
		completions = append(completions, completion)
	}

	return head, completions, tail
}

func executables(word string) []string {
        completions := []string{}

	if strings.Contains(word, string(os.PathSeparator)) {
		return completions
	}

	pathenv := os.Getenv("PATH")
	for _, dir := range strings.Split(pathenv, string(os.PathListSeparator)) {
		if dir == "" {
			dir = "."
		} else {
			dir = path.Clean(dir)
		}

		stat, err := os.Stat(dir)
		if err != nil || !stat.IsDir() {
			continue
		}
	
		max := strings.Count(dir, "/") + 1
		filepath.Walk(dir, func(p string, i os.FileInfo, err error) error {
			depth := strings.Count(p, "/")
			if depth > max {
				if i.IsDir() {
					return filepath.SkipDir
				}
				return nil
			} else if depth < max {
				return nil
			}

			_, basename := filepath.Split(p)

			if strings.HasPrefix(basename, word) {
				completions = append(completions, basename)
			}

			return nil
		})
	}

	return completions
}

func expand(t *Task, args Cell) Cell {
	list := Null

	for ; args != Null; args = Cdr(args) {
		c := Car(args)

		s := raw(c)
		if _, ok := c.(*Symbol); !ok {
			list = AppendTo(list, NewSymbol(s))
			continue
		}

		if s[:1] == "~" {
			s = filepath.Join(os.Getenv("HOME"), s[1:])
		}

{'fraction_non_alphanumeric': 0.12283617227530813, 'fraction_numerical': 0.005123944052070351, 'mean_word_length': 3.306499701848539, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 6, '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': '2661715', 'n_tokens_mistral': 2859, 'n_tokens_neox': 2640, 'n_words': 655}