mirror of
https://github.com/ralsina/tartrazine.git
synced 2025-05-23 16:40:08 -03:00
Added modeline strategy
This commit is contained in:
parent
45314b4903
commit
3d867abac3
2
alias.go
2
alias.go
@ -2,7 +2,7 @@ package slinguist
|
||||
|
||||
import "strings"
|
||||
|
||||
// GetLanguageByAlias returns the language related to the given alias
|
||||
// GetLanguageByAlias returns the language related to the given alias or Otherlanguage otherwise.
|
||||
func GetLanguageByAlias(alias string) (lang string) {
|
||||
a := strings.Split(alias, `,`)[0]
|
||||
a = strings.ToLower(a)
|
||||
|
@ -36,6 +36,10 @@ func GetLanguageExtensions(language string) []string {
|
||||
|
||||
// GetLanguage return the Language for a given filename and file content.
|
||||
func GetLanguage(filename string, content []byte) string {
|
||||
if lang, safe := GetLanguageByModeline(content); safe {
|
||||
return lang
|
||||
}
|
||||
|
||||
if lang, safe := GetLanguageByFilename(filename); safe {
|
||||
return lang
|
||||
}
|
||||
|
105
modeline.go
Normal file
105
modeline.go
Normal file
@ -0,0 +1,105 @@
|
||||
package slinguist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// GetLanguageByModeline returns the language of the given content looking for the modeline,
|
||||
// and safe to indicate the sureness of returned language.
|
||||
func GetLanguageByModeline(content []byte) (lang string, safe bool) {
|
||||
headFoot := getHeaderAndFooter(content)
|
||||
for _, getLang := range modelinesFunc {
|
||||
lang = getLang(headFoot)
|
||||
safe = lang != OtherLanguage
|
||||
if safe {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getHeaderAndFooter(content []byte) []byte {
|
||||
const (
|
||||
searchScope = 5
|
||||
eol = `\n`
|
||||
)
|
||||
|
||||
if bytes.Count(content, []byte(eol)) < 2*searchScope {
|
||||
return content
|
||||
}
|
||||
|
||||
splitted := bytes.Split(content, []byte(eol))
|
||||
header := splitted[:searchScope]
|
||||
footer := splitted[len(splitted)-searchScope:]
|
||||
headerAndFooter := append(header, footer...)
|
||||
return bytes.Join(headerAndFooter, []byte(eol))
|
||||
}
|
||||
|
||||
var modelinesFunc = []func(content []byte) string{
|
||||
GetLanguageByEmacsModeline,
|
||||
GetLanguageByVimModeline,
|
||||
}
|
||||
|
||||
var (
|
||||
reEmacsModeline = regexp.MustCompile(`.*-\*-\s*(.+?)\s*-\*-.*(?m:$)`)
|
||||
reEmacsLang = regexp.MustCompile(`.*(?i:mode)\s*:\s*([^\s;]+)\s*;*.*`)
|
||||
reVimModeline = regexp.MustCompile(`(?:(?m:\s|^)vi(?:m[<=>]?\d+|m)?|[\t\x20]*ex)\s*[:]\s*(.*)(?m:$)`)
|
||||
reVimLang = regexp.MustCompile(`(?i:filetype|ft|syntax)\s*=(\w+)(?:\s|:|$)`)
|
||||
)
|
||||
|
||||
// GetLanguageByEmacsModeline detecs if the content has a emacs modeline and try to get a
|
||||
// language basing on alias. If couldn't retrieve a valid language, it returns OtherLanguage.
|
||||
func GetLanguageByEmacsModeline(content []byte) (lang string) {
|
||||
matched := reEmacsModeline.FindAllSubmatch(content, -1)
|
||||
if matched == nil {
|
||||
return OtherLanguage
|
||||
}
|
||||
|
||||
// only take the last matched line, discard previous lines
|
||||
lastLineMatched := matched[len(matched)-1][1]
|
||||
matchedAlias := reEmacsLang.FindSubmatch(lastLineMatched)
|
||||
var alias string
|
||||
if matchedAlias != nil {
|
||||
alias = string(matchedAlias[1])
|
||||
} else {
|
||||
alias = string(lastLineMatched)
|
||||
}
|
||||
|
||||
lang = GetLanguageByAlias(alias)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLanguageByVimModeline detecs if the content has a vim modeline and try to get a
|
||||
// language basing on alias. If couldn't retrieve a valid language, it returns OtherLanguage.
|
||||
func GetLanguageByVimModeline(content []byte) (lang string) {
|
||||
matched := reVimModeline.FindAllSubmatch(content, -1)
|
||||
if matched == nil {
|
||||
return OtherLanguage
|
||||
}
|
||||
|
||||
// only take the last matched line, discard previous lines
|
||||
lastLineMatched := matched[len(matched)-1][1]
|
||||
matchedAlias := reVimLang.FindAllSubmatch(lastLineMatched, -1)
|
||||
if matchedAlias == nil {
|
||||
return OtherLanguage
|
||||
}
|
||||
|
||||
alias := string(matchedAlias[0][1])
|
||||
if len(matchedAlias) > 1 {
|
||||
// cases:
|
||||
// matchedAlias = [["syntax=ruby " "ruby"] ["ft=python " "python"] ["filetype=perl " "perl"]] returns OtherLanguage;
|
||||
// matchedAlias = [["syntax=python " "python"] ["ft=python " "python"] ["filetype=python " "python"]] returns "Python";
|
||||
for _, match := range matchedAlias {
|
||||
otherAlias := string(match[1])
|
||||
if otherAlias != alias {
|
||||
alias = OtherLanguage
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lang = GetLanguageByAlias(alias)
|
||||
return
|
||||
}
|
83
modeline_test.go
Normal file
83
modeline_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
package slinguist
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
modelinesDir = ".linguist/test/fixtures/Data/Modelines"
|
||||
)
|
||||
|
||||
func (s *TSuite) TestGetLanguageByModeline(c *C) {
|
||||
linguistTests := []struct {
|
||||
filename string
|
||||
expectedLang string
|
||||
expectedSafe bool
|
||||
}{
|
||||
// Emacs
|
||||
{filename: "example_smalltalk.md", expectedLang: "Smalltalk", expectedSafe: true},
|
||||
{filename: "fundamentalEmacs.c", expectedLang: "Text", expectedSafe: true},
|
||||
{filename: "iamphp.inc", expectedLang: "PHP", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs1", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs2", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs3", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs4", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs5", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs6", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs7", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs9", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs10", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs11", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "seeplusplusEmacs12", expectedLang: "C++", expectedSafe: true},
|
||||
|
||||
// Vim
|
||||
{filename: "seeplusplus", expectedLang: "C++", expectedSafe: true},
|
||||
{filename: "iamjs.pl", expectedLang: "JavaScript", expectedSafe: true},
|
||||
{filename: "iamjs2.pl", expectedLang: "JavaScript", expectedSafe: true},
|
||||
{filename: "not_perl.pl", expectedLang: "Prolog", expectedSafe: true},
|
||||
{filename: "ruby", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby2", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby3", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby4", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby5", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby6", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby7", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby8", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby9", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby10", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby11", expectedLang: "Ruby", expectedSafe: true},
|
||||
{filename: "ruby12", expectedLang: "Ruby", expectedSafe: true},
|
||||
}
|
||||
|
||||
for _, test := range linguistTests {
|
||||
content, err := ioutil.ReadFile(filepath.Join(modelinesDir, test.filename))
|
||||
c.Assert(err, Equals, nil)
|
||||
|
||||
lang, safe := GetLanguageByModeline(content)
|
||||
c.Assert(lang, Equals, test.expectedLang)
|
||||
c.Assert(safe, Equals, test.expectedSafe)
|
||||
}
|
||||
|
||||
const (
|
||||
wrongVim = `# vim: set syntax=ruby ft =python filetype=perl :`
|
||||
rightVim = `/* vim: set syntax=python ft =python filetype=python */`
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
content []byte
|
||||
expectedLang string
|
||||
expectedSafe bool
|
||||
}{
|
||||
{content: []byte(wrongVim), expectedLang: OtherLanguage, expectedSafe: false},
|
||||
{content: []byte(rightVim), expectedLang: "Python", expectedSafe: true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
lang, safe := GetLanguageByModeline(test.content)
|
||||
c.Assert(lang, Equals, test.expectedLang)
|
||||
c.Assert(safe, Equals, test.expectedSafe)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user