func ExampleIsLetter() { fmt.Println(com.IsLetter('1')) fmt.Println(com.IsLetter('[')) fmt.Println(com.IsLetter('a')) fmt.Println(com.IsLetter('Z')) // Output: // false // false // true // true }
func (r *Render) findType(name string) (*Link, bool) { if !com.IsLetter(name[0]) { return nil, false } // We cannot deal with struct field now. if name[len(name)-1] == '[' || name[len(name)-1] == ']' || name[len(name)-1] == ' ' || name[len(name)-1] == ':' { return nil, false } // We cannot deal with chain operation. if name[0] == '.' { return nil, false } name = name[:len(name)-1] // This is for functions and types from imported packages. i := strings.Index(name, ".") // We cannot deal with struct field or chain operation now. if i != strings.LastIndex(name, ".") { return nil, false } if filte := r.FilteList[name]; filte { return nil, false } var left, right string if i > -1 { left = name[:i+1] right = name[i+1:] } for _, l := range r.Links { if i == -1 { // Exported types and functions in current package. if l.Name == name { return l, true } } else { // Functions and types from imported packages. if l.Name == left { if len(l.Path) > 0 { return &Link{Name: name, Path: "/" + l.Path + "#" + right}, true } else { return &Link{Name: name, Path: "#" + right}, true } } else if r.recv.name == left[:len(left)-1] { // fmt.Println(r.recv.tp + "." + right) // fmt.Println(r.findType(r.recv.tp + "." + right)) return nil, false } } } r.FilteList[name] = true return nil, false }
// FormatCode highlights keywords and adds HTML links to them. func FormatCode(w io.Writer, code *string, links []*Link) { length := len(*code) // Length of whole code. if length == 0 { return } *code = strings.Replace(*code, """, `"`, -1) *code = strings.Replace(*code, "'", `'`, -1) length = len(*code) strTag := uint8(0) // Indicates what kind of string is chekcing. isString := false // Indicates if right now is checking string. isComment := false // Indicates if right now is checking comments. isBlockComment := false // Indicates if right now is checking block comments. last := 0 // Start index of the word. pos := 0 // Current index. for { // Cut words. CutWords: for { curChar := (*code)[pos] // Current check character. if !com.IsLetter(curChar) { if !isComment { switch { case curChar == '\'' || curChar == '"' || curChar == '`': // String. if !isString { // Set string tag. strTag = curChar isString = true } else { // CHeck if it is end of string or escaped character. if ((*code)[pos-1] == '\\' && (*code)[pos-2] == '\\') || (*code)[pos-1] != '\\' { // Check string tag. if curChar == strTag { // Handle string highlight. break CutWords } } } case !isString && curChar == '/' && ((*code)[pos+1] == '/' || (*code)[pos+1] == '*'): isComment = true case !isString && curChar > 47 && curChar < 58: // Ends with number. case !isString && curChar == '_' && (*code)[pos-1] != ' ': // Underline: _. case !isString && (curChar != '.' || curChar == '\n'): break CutWords } } else { if isBlockComment { // End of block comments. if curChar == '/' && (*code)[pos-1] == '*' { break CutWords } } else { switch { case curChar == '*' && (*code)[pos-1] == '/': // Start of block comments. isBlockComment = true case curChar == '\n': break CutWords } } } } if pos == length-1 { break CutWords } pos++ } seg := (*code)[last : pos+1] CheckLink: switch { case isComment: isComment = false isBlockComment = false fmt.Fprintf(w, `<span class="com">%s</span>`, seg) case isString: isString = false fmt.Fprintf(w, `<span class="str">%s</span>`, template.HTMLEscapeString(seg)) case seg == "\t": fmt.Fprintf(w, `%s`, " ") case pos-last > 1: // Check if the last word of the paragraphy. l := len(seg) keyword := seg if !com.IsLetter(seg[l-1]) { keyword = seg[:l-1] } else { l++ } // Check keywords. switch keyword { case "return", "break": fmt.Fprintf(w, `<span class="ret">%s</span>%s`, keyword, seg[l-1:]) break CheckLink case "func", "range", "for", "if", "else", "type", "struct", "select", "case", "var", "const", "switch", "default", "continue": fmt.Fprintf(w, `<span class="key">%s</span>%s`, keyword, seg[l-1:]) break CheckLink case "true", "false", "nil": fmt.Fprintf(w, `<span class="boo">%s</span>%s`, keyword, seg[l-1:]) break CheckLink case "new", "append", "make", "panic", "recover", "len", "cap", "copy", "close", "delete", "defer": fmt.Fprintf(w, `<span class="bui">%s</span>%s`, keyword, seg[l-1:]) break CheckLink } // Check links. link, ok := findType(seg[:l-1], links) if ok { switch { case len(link.Path) == 0 && len(link.Name) > 0: // Exported types in current package. fmt.Fprintf(w, `<a class="int" title="%s" href="#%s">%s</a>%s`, link.Comment, link.Name, link.Name, seg[l-1:]) case len(link.Path) > 0 && len(link.Name) > 0: if strings.HasPrefix(link.Path, "#") { fmt.Fprintf(w, `<a class="ext" title="%s" href="%s">%s</a>%s`, link.Comment, link.Path, link.Name, seg[l-1:]) } else { fmt.Fprintf(w, `<a class="ext" title="%s" target="_blank" href="%s">%s</a>%s`, link.Comment, link.Path, link.Name, seg[l-1:]) } } } else if seg[len(seg)-1] == ' ' { fmt.Fprintf(w, "<span id=\"%s\">%s</span> ", seg[:len(seg)-1], seg[:len(seg)-1]) } else { fmt.Fprintf(w, "%s", seg) } default: fmt.Fprintf(w, "%s", seg) } last = pos + 1 pos++ // End of code. if pos == length { fmt.Fprintf(w, "%s", (*code)[last:]) return } } }
// Render highlights code. func (r *Render) Render(name string, data []byte) []byte { if len(data) == 0 { return nil } code := string(data) l := len(code) buf := new(bytes.Buffer) //buf.WriteString("<pre>") strTag := uint8(0) isComment := false isBlockComment := false isString := false isFuncDecl := false isFuncBlock := false isHasRecv := false isTypeDecl := false last := 0 pos := 0 for { CutWords: for { curChar := code[pos] if !com.IsLetter(curChar) { if isComment { // Comment. if isBlockComment { // Check if in end of block comment. if curChar == '/' && code[pos-1] == '*' { break CutWords } } else { // Check if in start of block comment. if curChar == '*' && code[pos-1] == '/' { isBlockComment = true } else if curChar == '\n' { break CutWords } } } else { // String. if curChar == '\'' || curChar == '"' || curChar == '`' { if !isString { // Set string tag. strTag = curChar isString = true } else { // Check if it is end of string or escaped character. if (code[pos-1] == '\\' && code[pos-2] == '\\') || code[pos-1] != '\\' { // Check string tag. if curChar == strTag { // Handle string highlight. break CutWords } } } } if !isString { switch { case curChar == '/' && (code[pos+1] == '/' || code[pos+1] == '*'): isComment = true case curChar > 47 && curChar < 58: // Ends with number. case curChar == '_' && code[pos-1] != ' ': // Underline: _. case (curChar != '.' || curChar == '\n'): break CutWords } } } } if pos == l-1 { break CutWords } pos++ } seg := code[last : pos+1] CheckLink: switch { case isComment: isComment = false isBlockComment = false fmt.Fprintf(buf, `<span class="com">%s</span>`, seg) case isString: isString = false fmt.Fprintf(buf, `<span class="str">%s</span>`, template.HTMLEscapeString(seg)) case seg == "\t": fmt.Fprintf(buf, `%s`, " ") case seg == "{": if isFuncDecl { isFuncDecl = false isFuncBlock = true } if isFuncBlock { r.blockLevel++ } fmt.Fprintf(buf, "%s", seg) case seg == "}": if isFuncBlock { r.blockLevel-- } if r.blockLevel == 0 { isFuncBlock = false r.recv.name = "" } fmt.Fprintf(buf, "%s", seg) case isFuncDecl: if isHasRecv { if seg != "(" && seg != " " && seg != "*" { if len(r.recv.name) == 0 { r.recv.name = seg[:len(seg)-1] } else { r.recv.tp = seg[:len(seg)-1] isHasRecv = false } } } else if len(seg) > 1 && code[pos] == '(' { if len(r.recv.name) > 0 { fmt.Fprintf(buf, "<span id=\"%s_%s\">%s</span>(", r.recv.tp, seg[:len(seg)-1], seg[:len(seg)-1]) } else { fmt.Fprintf(buf, "<span id=\"%s\">%s</span>(", seg[:len(seg)-1], seg[:len(seg)-1]) } break CheckLink } fallthrough case pos-last > 1: // Check if the last word of the paragraphy. l := len(seg) keyword := seg if !com.IsLetter(seg[l-1]) { keyword = seg[:l-1] } else { l++ } // Check keywords. switch keyword { case "return", "break": fmt.Fprintf(buf, `<span class="ret">%s</span>%s`, keyword, seg[l-1:]) break CheckLink case "func": isFuncDecl = true if code[pos+1] == '(' { isHasRecv = true } fallthrough case "package", "import", "range", "for", "if", "else", "type", "struct", "select", "case", "var", "const", "switch", "default", "continue": if keyword == "type" { isTypeDecl = true } fmt.Fprintf(buf, `<span class="key">%s</span>%s`, keyword, seg[l-1:]) break CheckLink case "new", "append", "make", "panic", "recover", "len", "cap", "copy", "close", "delete", "defer": fmt.Fprintf(buf, `<span class="bui">%s</span>%s`, keyword, seg[l-1:]) break CheckLink } if isPredeclared(keyword) { fmt.Fprintf(buf, `<span class="boo">%s</span>%s`, keyword, seg[l-1:]) break CheckLink } // Check links. link, ok := r.findType(seg) if ok { switch { case strings.HasSuffix(link.Path, name) && len(link.Name) > 0: // Current file. fmt.Fprintf(buf, `<a class="int" title="%s" href="#%s">%s</a>%s`, link.Comment, link.Name, link.Name, seg[l-1:]) case len(link.Path) > 0 && len(link.Name) > 0: if strings.HasPrefix(link.Path, "#") { fmt.Fprintf(buf, `<a class="ext" title="%s" href="%s">%s</a>%s`, link.Comment, link.Path, link.Name, seg[l-1:]) } else { if strings.Index(link.Path, "#") > -1 { fmt.Fprintf(buf, `<a class="ext" title="%s" target="_blank" href="%s">%s</a>%s`, link.Comment, link.Path, link.Name, seg[l-1:]) } else { fmt.Fprintf(buf, `<a class="ext" title="%s" target="_blank" href="/%s#%s">%s</a>%s`, link.Comment, link.Path, link.Name, link.Name, seg[l-1:]) } } } } else if seg[len(seg)-1] == ' ' || seg[len(seg)-1] == '\n' { if isFuncDecl || isTypeDecl { isTypeDecl = false fmt.Fprintf(buf, "<span id=\"%s\">%s</span>%s", seg[:len(seg)-1], seg[:len(seg)-1], seg[l-1:]) } else { fmt.Fprintf(buf, "%s", seg) } } else { fmt.Fprintf(buf, "%s", seg) } default: fmt.Fprintf(buf, "%s", seg) } last = pos + 1 pos++ // End of code. if pos == l { fmt.Fprintf(buf, "%s", code[last:]) break } } return buf.Bytes() }