package emdbed import ( "fmt" "regexp" "strconv" "strings" ) var endline = regexp.MustCompile(`(?m)$`) type emdbed struct { name string contents string language string start string end string } func (e emdbed) line(line int, end bool) (int, error) { if line == 1 && !end { return 0, nil } indexes := endline.FindAllStringIndex(e.contents, -1) if len(indexes) < line { return 0, fmt.Errorf("file %q has no line %d", e.name, line) } return indexes[line-1][0], nil } func (e emdbed) regex(pattern string, end bool) (int, error) { re, err := regexp.Compile(pattern) if err != nil { return 0, err } match := re.FindStringIndex(e.contents) if match == nil { return 0, fmt.Errorf("file %q has no match for pattern %q", e.name, pattern) } var inc int if end { inc++ } return match[0] + inc, nil } func (e emdbed) selector(selector string, end bool) (int, error) { switch { case strings.HasPrefix(selector, "L"): line, err := strconv.Atoi(selector[1:]) if err != nil { return 0, err } return e.line(line, end) case strings.HasPrefix(selector, "/"): pattern := strings.Trim(selector, "/") return e.regex(pattern, end) default: return 0, fmt.Errorf("unknown selector %q", selector) } } func (e emdbed) Start() (int, error) { if e.start == "" { return 0, nil } return e.selector(e.start, false) } func (e emdbed) End() (int, error) { if e.end == "" { return len(e.contents), nil } return e.selector(e.end, true) } func (e emdbed) Selection() (string, error) { start, err := e.Start() if err != nil { return "", err } end, err := e.End() if err != nil { return "", err } return e.contents[start:end], nil }