From 9e2db6a2e70494e349153091b1a4168b0e5e88a8 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 8 Aug 2017 11:49:45 +0200 Subject: Vendor --- vendor/golang.org/x/tools/present/code.go | 267 ++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 vendor/golang.org/x/tools/present/code.go (limited to 'vendor/golang.org/x/tools/present/code.go') diff --git a/vendor/golang.org/x/tools/present/code.go b/vendor/golang.org/x/tools/present/code.go new file mode 100644 index 0000000..b47a72a --- /dev/null +++ b/vendor/golang.org/x/tools/present/code.go @@ -0,0 +1,267 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package present + +import ( + "bufio" + "bytes" + "fmt" + "html/template" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +// PlayEnabled specifies whether runnable playground snippets should be +// displayed in the present user interface. +var PlayEnabled = false + +// TODO(adg): replace the PlayEnabled flag with something less spaghetti-like. +// Instead this will probably be determined by a template execution Context +// value that contains various global metadata required when rendering +// templates. + +// NotesEnabled specifies whether presenter notes should be displayed in the +// present user interface. +var NotesEnabled = false + +func init() { + Register("code", parseCode) + Register("play", parseCode) +} + +type Code struct { + Text template.HTML + Play bool // runnable code + Edit bool // editable code + FileName string // file name + Ext string // file extension + Raw []byte // content of the file +} + +func (c Code) TemplateName() string { return "code" } + +// The input line is a .code or .play entry with a file name and an optional HLfoo marker on the end. +// Anything between the file and HL (if any) is an address expression, which we treat as a string here. +// We pick off the HL first, for easy parsing. +var ( + highlightRE = regexp.MustCompile(`\s+HL([a-zA-Z0-9_]+)?$`) + hlCommentRE = regexp.MustCompile(`(.+) // HL(.*)$`) + codeRE = regexp.MustCompile(`\.(code|play)\s+((?:(?:-edit|-numbers)\s+)*)([^\s]+)(?:\s+(.*))?$`) +) + +// parseCode parses a code present directive. Its syntax: +// .code [-numbers] [-edit] [address] [highlight] +// The directive may also be ".play" if the snippet is executable. +func parseCode(ctx *Context, sourceFile string, sourceLine int, cmd string) (Elem, error) { + cmd = strings.TrimSpace(cmd) + + // Pull off the HL, if any, from the end of the input line. + highlight := "" + if hl := highlightRE.FindStringSubmatchIndex(cmd); len(hl) == 4 { + if hl[2] < 0 || hl[3] < 0 { + return nil, fmt.Errorf("%s:%d invalid highlight syntax", sourceFile, sourceLine) + } + highlight = cmd[hl[2]:hl[3]] + cmd = cmd[:hl[2]-2] + } + + // Parse the remaining command line. + // Arguments: + // args[0]: whole match + // args[1]: .code/.play + // args[2]: flags ("-edit -numbers") + // args[3]: file name + // args[4]: optional address + args := codeRE.FindStringSubmatch(cmd) + if len(args) != 5 { + return nil, fmt.Errorf("%s:%d: syntax error for .code/.play invocation", sourceFile, sourceLine) + } + command, flags, file, addr := args[1], args[2], args[3], strings.TrimSpace(args[4]) + play := command == "play" && PlayEnabled + + // Read in code file and (optionally) match address. + filename := filepath.Join(filepath.Dir(sourceFile), file) + textBytes, err := ctx.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err) + } + lo, hi, err := addrToByteRange(addr, 0, textBytes) + if err != nil { + return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err) + } + if lo > hi { + // The search in addrToByteRange can wrap around so we might + // end up with the range ending before its starting point + hi, lo = lo, hi + } + + // Acme pattern matches can stop mid-line, + // so run to end of line in both directions if not at line start/end. + for lo > 0 && textBytes[lo-1] != '\n' { + lo-- + } + if hi > 0 { + for hi < len(textBytes) && textBytes[hi-1] != '\n' { + hi++ + } + } + + lines := codeLines(textBytes, lo, hi) + + data := &codeTemplateData{ + Lines: formatLines(lines, highlight), + Edit: strings.Contains(flags, "-edit"), + Numbers: strings.Contains(flags, "-numbers"), + } + + // Include before and after in a hidden span for playground code. + if play { + data.Prefix = textBytes[:lo] + data.Suffix = textBytes[hi:] + } + + var buf bytes.Buffer + if err := codeTemplate.Execute(&buf, data); err != nil { + return nil, err + } + return Code{ + Text: template.HTML(buf.String()), + Play: play, + Edit: data.Edit, + FileName: filepath.Base(filename), + Ext: filepath.Ext(filename), + Raw: rawCode(lines), + }, nil +} + +// formatLines returns a new slice of codeLine with the given lines +// replacing tabs with spaces and adding highlighting where needed. +func formatLines(lines []codeLine, highlight string) []codeLine { + formatted := make([]codeLine, len(lines)) + for i, line := range lines { + // Replace tabs with spaces, which work better in HTML. + line.L = strings.Replace(line.L, "\t", " ", -1) + + // Highlight lines that end with "// HL[highlight]" + // and strip the magic comment. + if m := hlCommentRE.FindStringSubmatch(line.L); m != nil { + line.L = m[1] + line.HL = m[2] == highlight + } + + formatted[i] = line + } + return formatted +} + +// rawCode returns the code represented by the given codeLines without any kind +// of formatting. +func rawCode(lines []codeLine) []byte { + b := new(bytes.Buffer) + for _, line := range lines { + b.WriteString(line.L) + b.WriteByte('\n') + } + return b.Bytes() +} + +type codeTemplateData struct { + Lines []codeLine + Prefix, Suffix []byte + Edit, Numbers bool +} + +var leadingSpaceRE = regexp.MustCompile(`^[ \t]*`) + +var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{ + "trimSpace": strings.TrimSpace, + "leadingSpace": leadingSpaceRE.FindString, +}).Parse(codeTemplateHTML)) + +const codeTemplateHTML = ` +{{with .Prefix}}
{{printf "%s" .}}
{{end}} + +{{/* + */}}{{range .Lines}}{{/* + */}}{{if .HL}}{{leadingSpace .L}}{{trimSpace .L}}{{/* + */}}{{else}}{{.L}}{{end}}{{/* +*/}} +{{end}} + +{{with .Suffix}}
{{printf "%s" .}}
{{end}} +` + +// codeLine represents a line of code extracted from a source file. +type codeLine struct { + L string // The line of code. + N int // The line number from the source file. + HL bool // Whether the line should be highlighted. +} + +// codeLines takes a source file and returns the lines that +// span the byte range specified by start and end. +// It discards lines that end in "OMIT". +func codeLines(src []byte, start, end int) (lines []codeLine) { + startLine := 1 + for i, b := range src { + if i == start { + break + } + if b == '\n' { + startLine++ + } + } + s := bufio.NewScanner(bytes.NewReader(src[start:end])) + for n := startLine; s.Scan(); n++ { + l := s.Text() + if strings.HasSuffix(l, "OMIT") { + continue + } + lines = append(lines, codeLine{L: l, N: n}) + } + // Trim leading and trailing blank lines. + for len(lines) > 0 && len(lines[0].L) == 0 { + lines = lines[1:] + } + for len(lines) > 0 && len(lines[len(lines)-1].L) == 0 { + lines = lines[:len(lines)-1] + } + return +} + +func parseArgs(name string, line int, args []string) (res []interface{}, err error) { + res = make([]interface{}, len(args)) + for i, v := range args { + if len(v) == 0 { + return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) + } + switch v[0] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + n, err := strconv.Atoi(v) + if err != nil { + return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) + } + res[i] = n + case '/': + if len(v) < 2 || v[len(v)-1] != '/' { + return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) + } + res[i] = v + case '$': + res[i] = "$" + case '_': + if len(v) == 1 { + // Do nothing; "_" indicates an intentionally empty parameter. + break + } + fallthrough + default: + return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) + } + } + return +} -- cgit v1.2.3