diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4883409 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Highlight is licensed under the MIT "Expat" License: + +Copyright (c) 2016: Zachary Yedidia. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..fd18478 --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Highlight + +This is a package for syntax highlighting a large number of different languages. To see the list of +languages currently supported, see the [`syntax_files`](./syntax_files) directory. + +Highlight allows you to pass in a string and get back all the information you need to syntax highlight +that string well. + +# Installation + +``` +go get github.com/zyedidia/highlight +``` + +# Usage + +Here is how to use this package to highlight a string: + +```go +package main + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/fatih/color" + "github.com/zyedidia/highlight" +) + +func main() { + // Here is going to be the go code we highlight + inputString := ` +package main + +import "fmt" + +// A hello world program +func main() { + fmt.Println("Hello world") +} +` + + // Load the go syntax file + // Make sure that the syntax_files directory is in the current directory + syntaxFile, _ := ioutil.ReadFile("syntax_files/go.yaml") + + // Parse it into a `*highlight.Def` + syntaxDef, err := highlight.ParseDef(syntaxFile) + if err != nil { + fmt.Println(err) + return + } + + // Make a new highlighter from the definition + h := highlight.NewHighlighter(syntaxDef) + // Highlight the string + // Matches is an array of maps which point to groups + // matches[lineNum][colNum] will give you the change in group at that line and column number + // Note that there is only a group at a line and column number if the syntax highlighting changed at that position + matches := h.Highlight(inputString) + + // We split the string into a bunch of lines + // Now we will print the string + lines := strings.Split(inputString, "\n") + for lineN, l := range lines { + for colN, c := range l { + // Check if the group changed at the current position + if group, ok := matches[lineN][colN]; ok { + // Check the group name and set the color accordingly (the colors chosen are arbitrary) + if group == "statement" { + color.Set(color.FgGreen) + } else if group == "identifier" { + color.Set(color.FgBlue) + } else if group == "preproc" { + color.Set(color.FgHiRed) + } else if group == "special" { + color.Set(color.FgRed) + } else if group == "constant.string" { + color.Set(color.FgCyan) + } else if group == "constant" { + color.Set(color.FgCyan) + } else if group == "constant.specialChar" { + color.Set(color.FgHiMagenta) + } else if group == "type" { + color.Set(color.FgYellow) + } else if group == "constant.number" { + color.Set(color.FgCyan) + } else if group == "comment" { + color.Set(color.FgHiGreen) + } else { + color.Unset() + } + } + // Print the character + fmt.Print(string(c)) + } + // This is at a newline, but highlighting might have been turned off at the very end of the line so we should check that. + if group, ok := matches[lineN][len(l)]; ok { + if group == "" { + color.Unset() + } + } + + fmt.Print("\n") + } +} +``` + +If you would like to automatically detect the filetype of a file based on the filename, and have the appropriate definition returned, +you can use the `DetectFiletype`: + +```go +// Name of the file +filename := ... +// The first line of the file (needed to check the filetype by header: e.g. `#!/bin/bash` means shell) +firstLine := ... + +// Parse all the syntax files in an array with type []*highlight.Def +var defs []*highlight.Def +... + +def := highlight.DetectFiletype(defs, filename, firstLine) +fmt.Println("Filetype is", def.ft) +``` diff --git a/ftdetect.go b/ftdetect.go index 9651205..ad8f879 100644 --- a/ftdetect.go +++ b/ftdetect.go @@ -1,10 +1,6 @@ package highlight -import "bytes" - -func DetectFiletype(defs []*Def, filename string, fileSrc []byte) *Def { - firstLine := bytes.Split(fileSrc, []byte("\n"))[0] - +func DetectFiletype(defs []*Def, filename string, firstLine []byte) *Def { for _, d := range defs { if d.ftdetect[0].Match([]byte(filename)) { return d diff --git a/syntax b/syntax new file mode 100755 index 0000000..0bf94ca Binary files /dev/null and b/syntax differ diff --git a/syntax_files/README.md b/syntax_files/README.md new file mode 100644 index 0000000..515e6ce --- /dev/null +++ b/syntax_files/README.md @@ -0,0 +1,5 @@ +# Syntax Files + +Here are highlight's syntax files. If you would like to make a new syntax file, you should first check it +with the `syntax_checker.go` program. Just place it in this directory and run the program (`go run syntax_checker.go`) +and it will let you know if there are issues with any of the files in the directory. diff --git a/syntax_files/c.yaml b/syntax_files/c.yaml index 6f0d1d0..5f4bfae 100644 --- a/syntax_files/c.yaml +++ b/syntax_files/c.yaml @@ -1,7 +1,7 @@ filetype: c detect: - filename: "\\.(c|C)$" + filename: "(\\.(c|C)$|\\.(h|H)$|\\.ii?$|\\.(def)$)" rules: - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" diff --git a/syntax_files/yaml.yaml b/syntax_files/yaml.yaml new file mode 100644 index 0000000..2ca334f --- /dev/null +++ b/syntax_files/yaml.yaml @@ -0,0 +1,32 @@ +filetype: yaml + +detect: + filename: "\\.ya?ml$" + header: "%YAML" + +rules: + - type: "(^| )!!(binary|bool|float|int|map|null|omap|seq|set|str) " + - constant: "\\b(YES|yes|Y|y|ON|on|NO|no|N|n|OFF|off)\\b" + - constant: "\\b(true|false)\\b" + - statement: "(:[[:space:]]|\\[|\\]|:[[:space:]]+[|>]|^[[:space:]]*- )" + - identifier: "[[:space:]][\\*&][A-Za-z0-9]+" + - type: "([-\\w]+:\\s+)|([-\\w]+:$)" + - special: "(^---|^\\.\\.\\.|^%YAML|^%TAG)" + + - constant.string: + start: "\"" + end: "\"" + rules: + - constant.specialChar: "\\\\." + + - constant.string: + start: "'" + end: "'" + rules: + - constant.specialChar: "\\\\." + + - comment: + start: "#" + end: "$" + rules: [] +