123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- package source
- import (
- "bufio"
- "fmt"
- "sort"
- "strings"
- "unicode"
- )
- type Edit struct {
- Location int
- Old string
- New string
- OldFunc func(string) int
- }
- type CommentSyntax struct {
- Dash bool
- Hash bool
- SlashStar bool
- }
- func LineNumber(source string, head int) (int, int) {
- // Calculate the true line and column number for a query, ignoring spaces
- var comment bool
- var loc, line, col int
- for i, char := range source {
- loc += 1
- col += 1
- // TODO: Check bounds
- if char == '-' && source[i+1] == '-' {
- comment = true
- }
- if char == '\n' {
- comment = false
- line += 1
- col = 0
- }
- if loc <= head {
- continue
- }
- if unicode.IsSpace(char) {
- continue
- }
- if comment {
- continue
- }
- break
- }
- return line + 1, col
- }
- func Pluck(source string, location, length int) (string, error) {
- head := location
- tail := location + length
- return source[head:tail], nil
- }
- func Mutate(raw string, a []Edit) (string, error) {
- if len(a) == 0 {
- return raw, nil
- }
- sort.Slice(a, func(i, j int) bool { return a[i].Location > a[j].Location })
- s := raw
- for idx, edit := range a {
- start := edit.Location
- if start > len(s) || start < 0 {
- return "", fmt.Errorf("edit start location is out of bounds")
- }
- var oldLen int
- if edit.OldFunc != nil {
- oldLen = edit.OldFunc(s[start:])
- } else {
- oldLen = len(edit.Old)
- }
- stop := edit.Location + oldLen
- if stop > len(s) {
- return "", fmt.Errorf("edit stop location is out of bounds")
- }
- // If this is not the first edit, (applied backwards), check if
- // this edit overlaps the previous one (and is therefore a developer error)
- if idx != 0 {
- prevEdit := a[idx-1]
- if prevEdit.Location < edit.Location+oldLen {
- return "", fmt.Errorf("2 edits overlap")
- }
- }
- s = s[:start] + edit.New + s[stop:]
- }
- return s, nil
- }
- func StripComments(sql string) (string, []string, error) {
- s := bufio.NewScanner(strings.NewReader(strings.TrimSpace(sql)))
- var lines, comments []string
- for s.Scan() {
- t := s.Text()
- if strings.HasPrefix(t, "-- name:") {
- continue
- }
- if strings.HasPrefix(t, "/* name:") && strings.HasSuffix(t, "*/") {
- continue
- }
- if strings.HasPrefix(t, "# name:") {
- continue
- }
- if strings.HasPrefix(t, "--") {
- comments = append(comments, strings.TrimPrefix(t, "--"))
- continue
- }
- if strings.HasPrefix(t, "/*") && strings.HasSuffix(t, "*/") {
- t = strings.TrimPrefix(t, "/*")
- t = strings.TrimSuffix(t, "*/")
- comments = append(comments, t)
- continue
- }
- if strings.HasPrefix(t, "#") {
- comments = append(comments, strings.TrimPrefix(t, "#"))
- continue
- }
- lines = append(lines, t)
- }
- return strings.Join(lines, "\n"), comments, s.Err()
- }
- func CleanedComments(rawSQL string, cs CommentSyntax) ([]string, error) {
- s := bufio.NewScanner(strings.NewReader(strings.TrimSpace(rawSQL)))
- var comments []string
- for s.Scan() {
- line := s.Text()
- var prefix string
- if strings.HasPrefix(line, "--") {
- if !cs.Dash {
- continue
- }
- prefix = "--"
- }
- if strings.HasPrefix(line, "/*") {
- if !cs.SlashStar {
- continue
- }
- prefix = "/*"
- }
- if strings.HasPrefix(line, "#") {
- if !cs.Hash {
- continue
- }
- prefix = "#"
- }
- if prefix == "" {
- continue
- }
- rest := line[len(prefix):]
- rest = strings.TrimSuffix(rest, "*/")
- comments = append(comments, rest)
- }
- return comments, s.Err()
- }
|