Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 156 additions & 108 deletions internal/buffer/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,30 +682,22 @@ func calcHash(b *Buffer, out *[md5.Size]byte) error {
return nil
}

// UpdateRules updates the syntax rules and filetype for this buffer
// This is called when the colorscheme changes
func (b *Buffer) UpdateRules() {
if !b.Type.Syntax {
return
}
ft := b.Settings["filetype"].(string)
if ft == "off" {
return
}

// syntaxFileBuffer is a helper structure
// to store properties of one single syntax file
type syntaxFileBuffer struct {
header *highlight.Header
fileName string
syntaxDef *highlight.Def
}
// syntaxFileBuffer is a helper structure
// to store properties of one single syntax file
type syntaxFileBuffer struct {
header *highlight.Header
fileName string
syntaxDef *highlight.Def
}

syntaxFiles := []syntaxFileBuffer{}
// searchUserSyntax searches for the syntax file
// in the user's custom syntax files
func (b *Buffer) searchUserSyntax(ft string, syntaxFiles *[]syntaxFileBuffer, useHeader bool) (string, bool, *highlight.Header) {
syntaxFile := ""
foundDef := false
match := false
var header *highlight.Header
// search for the syntax file in the user's custom syntax files

for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
data, err := f.Data()
if err != nil {
Expand All @@ -724,7 +716,13 @@ func (b *Buffer) UpdateRules() {
continue
}

if ((ft == "unknown" || ft == "") && header.MatchFileName(b.Path)) || header.FileType == ft {
if !useHeader {
match = header.MatchFileName(b.Path)
} else {
match = header.MatchFileHeader(b.lines[0].data)
}

if ((ft == "unknown" || ft == "") && match) || header.FileType == ft {
syndef, err := highlight.ParseDef(file, header)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
Expand All @@ -736,130 +734,180 @@ func (b *Buffer) UpdateRules() {
b.SyntaxDef = syndef
syntaxFile = f.Name()
break
}
if syntaxFiles != nil {
*syntaxFiles = append(*syntaxFiles, syntaxFileBuffer{header, f.Name(), syndef})
}
}
}

return syntaxFile, foundDef, header
}

// searchRuntimeHeader search in the runtime hdr files
func (b *Buffer) searchRuntimeHeader(ft string, syntaxFiles *[]syntaxFileBuffer, useHeader bool) (string, *highlight.Header) {
syntaxFile := ""
var header *highlight.Header

for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error())
continue
}

header, err = highlight.MakeHeader(data)
if err != nil {
screen.TermMessage("Error reading syntax header file", f.Name(), err)
continue
}

if ft == "unknown" || ft == "" {
if !useHeader {
if header.MatchFileName(b.Path) && syntaxFiles != nil {
*syntaxFiles = append(*syntaxFiles, syntaxFileBuffer{header, f.Name(), nil})
}
} else {
syntaxFiles = append(syntaxFiles, syntaxFileBuffer{header, f.Name(), syndef})
if header.MatchFileHeader(b.lines[0].data) && syntaxFiles != nil {
*syntaxFiles = append(*syntaxFiles, syntaxFileBuffer{header, f.Name(), nil})
}
}
} else if header.FileType == ft {
syntaxFile = f.Name()
break
}
}

if !foundDef {
// search in the default syntax files
for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
return syntaxFile, header
}

// searchRuntimeSyntax search in the runtime syntax files
func (b *Buffer) searchRuntimeSyntax(name string, header *highlight.Header) {
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
if f.Name() == name {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error())
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
continue
}

header, err = highlight.MakeHeader(data)
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error reading syntax header file", f.Name(), err)
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}

if ft == "unknown" || ft == "" {
if header.MatchFileName(b.Path) {
syntaxFiles = append(syntaxFiles, syntaxFileBuffer{header, f.Name(), nil})
}
} else if header.FileType == ft {
syntaxFile = f.Name()
break
syndef, err := highlight.ParseDef(file, header)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
b.SyntaxDef = syndef
break
}
}
}

if syntaxFile == "" {
length := len(syntaxFiles)
if length > 0 {
signatureMatch := false
if length > 1 {
detectlimit := util.IntOpt(b.Settings["detectlimit"])
lineCount := len(b.lines)
limit := lineCount
if detectlimit > 0 && lineCount > detectlimit {
limit = detectlimit
}
for i := 0; i < length && !signatureMatch; i++ {
if syntaxFiles[i].header.HasFileSignature() {
for j := 0; j < limit && !signatureMatch; j++ {
if syntaxFiles[i].header.MatchFileSignature(b.lines[j].data) {
syntaxFile = syntaxFiles[i].fileName
b.SyntaxDef = syntaxFiles[i].syntaxDef
header = syntaxFiles[i].header
signatureMatch = true
}
}
}
}
}
if length == 1 || !signatureMatch {
syntaxFile = syntaxFiles[0].fileName
b.SyntaxDef = syntaxFiles[0].syntaxDef
header = syntaxFiles[0].header
}
}
}
// solveSyntaxIncludes search includes in the runtime syntax files
func (b *Buffer) solveSyntaxIncludes(*highlight.Def) {
includes := highlight.GetIncludes(b.SyntaxDef)

if syntaxFile != "" && !foundDef {
// we found a syntax file using a syntax header file
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
if f.Name() == syntaxFile {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
continue
}
var files []*highlight.File
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
header, err := highlight.MakeHeaderYaml(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}

for _, i := range includes {
if header.FileType == i {
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}

syndef, err := highlight.ParseDef(file, header)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
b.SyntaxDef = syndef
files = append(files, file)
break
}
}
if len(files) >= len(includes) {
break
}
}

if b.SyntaxDef != nil && highlight.HasIncludes(b.SyntaxDef) {
includes := highlight.GetIncludes(b.SyntaxDef)
highlight.ResolveIncludes(b.SyntaxDef, files)
}

var files []*highlight.File
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
header, err := highlight.MakeHeaderYaml(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
// UpdateRules updates the syntax rules and filetype for this buffer
// This is called when the colorscheme changes
func (b *Buffer) UpdateRules() {
if !b.Type.Syntax {
return
}
ft := b.Settings["filetype"].(string)
if ft == "off" {
return
}

syntaxFiles := []syntaxFileBuffer{}

// check for filenames first...
syntaxFile, foundDef, header := b.searchUserSyntax(ft, &syntaxFiles, false)
if !foundDef {
syntaxFile, header = b.searchRuntimeHeader(ft, &syntaxFiles, false)
}

for _, i := range includes {
if header.FileType == i {
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
// ...try the file header definition afterwards
if syntaxFile == "" {
syntaxFile, foundDef, header = b.searchUserSyntax(ft, &syntaxFiles, true)
if !foundDef {
syntaxFile, header = b.searchRuntimeHeader(ft, &syntaxFiles, true)
}
}

length := len(syntaxFiles)
if length > 0 {
signatureMatch := false
if length > 1 {
detectlimit := util.IntOpt(b.Settings["detectlimit"])
lineCount := len(b.lines)
limit := lineCount
if detectlimit > 0 && lineCount > detectlimit {
limit = detectlimit
}
for i := 0; i < length && !signatureMatch; i++ {
if syntaxFiles[i].header.HasFileSignature() {
for j := 0; j < limit && !signatureMatch; j++ {
if syntaxFiles[i].header.MatchFileSignature(b.lines[j].data) {
syntaxFile = syntaxFiles[i].fileName
b.SyntaxDef = syntaxFiles[i].syntaxDef
header = syntaxFiles[i].header
signatureMatch = true
}
}
files = append(files, file)
break
}
}
if len(files) >= len(includes) {
break
}
}
if length == 1 || !signatureMatch {
syntaxFile = syntaxFiles[0].fileName
b.SyntaxDef = syntaxFiles[0].syntaxDef
header = syntaxFiles[0].header
}
}

highlight.ResolveIncludes(b.SyntaxDef, files)
if syntaxFile != "" && !foundDef {
// we found a syntax file using a syntax header file
b.searchRuntimeSyntax(syntaxFile, header)
}

if b.SyntaxDef != nil && highlight.HasIncludes(b.SyntaxDef) {
b.solveSyntaxIncludes(b.SyntaxDef)
}

if b.Highlighter == nil || syntaxFile != "" {
Expand Down
27 changes: 25 additions & 2 deletions pkg/highlight/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ type Def struct {
type Header struct {
FileType string
FileNameRegex *regexp.Regexp
HeaderRegex *regexp.Regexp
SignatureRegex *regexp.Regexp
}

type HeaderYaml struct {
FileType string `yaml:"filetype"`
Detect struct {
FNameRegexStr string `yaml:"filename"`
HeaderRegexStr string `yaml:"header"`
SignatureRegexStr string `yaml:"signature"`
} `yaml:"detect"`
}
Expand Down Expand Up @@ -103,11 +105,15 @@ func MakeHeader(data []byte) (*Header, error) {
var err error
header.FileType = string(lines[0])
fnameRegexStr := string(lines[1])
signatureRegexStr := string(lines[2])
headerRegexStr := string(lines[2])
signatureRegexStr := string(lines[3])

if fnameRegexStr != "" {
header.FileNameRegex, err = regexp.Compile(fnameRegexStr)
}
if err == nil && headerRegexStr != "" {
header.HeaderRegex, err = regexp.Compile(headerRegexStr)
}
if err == nil && signatureRegexStr != "" {
header.SignatureRegex, err = regexp.Compile(signatureRegexStr)
}
Expand All @@ -134,6 +140,9 @@ func MakeHeaderYaml(data []byte) (*Header, error) {
if hdrYaml.Detect.FNameRegexStr != "" {
header.FileNameRegex, err = regexp.Compile(hdrYaml.Detect.FNameRegexStr)
}
if err == nil && hdrYaml.Detect.HeaderRegexStr != "" {
header.HeaderRegex, err = regexp.Compile(hdrYaml.Detect.HeaderRegexStr)
}
if err == nil && hdrYaml.Detect.SignatureRegexStr != "" {
header.SignatureRegex, err = regexp.Compile(hdrYaml.Detect.SignatureRegexStr)
}
Expand All @@ -154,14 +163,28 @@ func (header *Header) MatchFileName(filename string) bool {
return false
}

// HasFileHeader checks the presence of a stored header
func (header *Header) HasFileHeader() bool {
return header.HeaderRegex != nil
}

// MatchFileHeader will check the given line with the stored regex
func (header *Header) MatchFileHeader(line []byte) bool {
if header.HasFileHeader() {
return header.HeaderRegex.Match(line)
}

return false
}

// HasFileSignature checks the presence of a stored signature
func (header *Header) HasFileSignature() bool {
return header.SignatureRegex != nil
}

// MatchFileSignature will check the given line with the stored regex
func (header *Header) MatchFileSignature(line []byte) bool {
if header.SignatureRegex != nil {
if header.HasFileSignature() {
return header.SignatureRegex.Match(line)
}

Expand Down
Loading