mirror of https://gitea.com/gitea/tea
Update Vendors (#337)
* update & migrate gitea sdk (Fix Delete Tag Issue) * upgraded github.com/AlecAivazis/survey v2.2.7 => v2.2.8 * upgraded github.com/adrg/xdg v0.2.3 => v0.3.1 * upgraded github.com/araddon/dateparse * upgraded github.com/olekukonko/tablewriter v0.0.4 => v0.0.5 * upgraded gopkg.in/yaml.v2 v2.3.0 => v2.4.0 Reviewed-on: https://gitea.com/gitea/tea/pulls/337 Reviewed-by: Norwin <noerw@noreply.gitea.io> Reviewed-by: khmarbaise <khmarbaise@noreply.gitea.io> Co-authored-by: 6543 <6543@obermui.de> Co-committed-by: 6543 <6543@obermui.de>pull/334/head^2
parent
15c4edba1a
commit
0d98cbd657
@ -0,0 +1,81 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ListStargazersOptions options for listing a repository's stargazers
|
||||
type ListStargazersOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListRepoStargazers list a repository's stargazers
|
||||
func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions) ([]*User, *Response, error) {
|
||||
opt.setDefaults()
|
||||
stargazers := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/stargazers?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &stargazers)
|
||||
return stargazers, resp, err
|
||||
}
|
||||
|
||||
// GetStarredRepos returns the repos that the given user has starred
|
||||
func (c *Client) GetStarredRepos(user string) ([]*Repository, *Response, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/starred", user), jsonHeader, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// GetMyStarredRepos returns the repos that the authenticated user has starred
|
||||
func (c *Client) GetMyStarredRepos() ([]*Repository, *Response, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", "/user/starred", jsonHeader, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// IsRepoStarring returns whether the authenticated user has starred the repo or not
|
||||
func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) {
|
||||
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return false, resp, nil
|
||||
case http.StatusNoContent:
|
||||
return true, resp, nil
|
||||
default:
|
||||
return false, resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// StarRepo star specified repo as the authenticated user
|
||||
func (c *Client) StarRepo(user, repo string) (*Response, error) {
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNoContent:
|
||||
return resp, nil
|
||||
default:
|
||||
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// UnStarRepo remove star to specified repo as the authenticated user
|
||||
func (c *Client) UnStarRepo(user, repo string) (*Response, error) {
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNoContent:
|
||||
return resp, nil
|
||||
default:
|
||||
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
package c
|
||||
|
||||
import (
|
||||
. "github.com/alecthomas/chroma" // nolint
|
||||
"github.com/alecthomas/chroma/lexers/internal"
|
||||
)
|
||||
|
||||
// caddyfileCommon are the rules common to both of the lexer variants
|
||||
var caddyfileCommon = Rules{
|
||||
"site_block_common": {
|
||||
// Import keyword
|
||||
{`(import)(\s+)([^\s]+)`, ByGroups(Keyword, Text, NameVariableMagic), nil},
|
||||
// Matcher definition
|
||||
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
|
||||
// Matcher token stub for docs
|
||||
{`\[\<matcher\>\]`, NameDecorator, Push("matcher")},
|
||||
// These cannot have matchers but may have things that look like
|
||||
// matchers in their arguments, so we just parse as a subdirective.
|
||||
{`try_files`, Keyword, Push("subdirective")},
|
||||
// These are special, they can nest more directives
|
||||
{`handle_errors|handle|route|handle_path|not`, Keyword, Push("nested_directive")},
|
||||
// Any other directive
|
||||
{`[^\s#]+`, Keyword, Push("directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"matcher": {
|
||||
{`\{`, Punctuation, Push("block")},
|
||||
// Not can be one-liner
|
||||
{`not`, Keyword, Push("deep_not_matcher")},
|
||||
// Any other same-line matcher
|
||||
{`[^\s#]+`, Keyword, Push("arguments")},
|
||||
// Terminators
|
||||
{`\n`, Text, Pop(1)},
|
||||
{`\}`, Punctuation, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"block": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
// Not can be one-liner
|
||||
{`not`, Keyword, Push("not_matcher")},
|
||||
// Any other subdirective
|
||||
{`[^\s#]+`, Keyword, Push("subdirective")},
|
||||
Include("base"),
|
||||
},
|
||||
"nested_block": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
// Matcher definition
|
||||
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
|
||||
// Something that starts with literally < is probably a docs stub
|
||||
{`\<[^#]+\>`, Keyword, Push("nested_directive")},
|
||||
// Any other directive
|
||||
{`[^\s#]+`, Keyword, Push("nested_directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"not_matcher": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{`[^\s#]+`, Keyword, Push("arguments")},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
"deep_not_matcher": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{`[^\s#]+`, Keyword, Push("deep_subdirective")},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
"directive": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
Include("matcher_token"),
|
||||
Include("comments_pop_1"),
|
||||
{`\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"nested_directive": {
|
||||
{`\{(?=\s)`, Punctuation, Push("nested_block")},
|
||||
Include("matcher_token"),
|
||||
Include("comments_pop_1"),
|
||||
{`\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"subdirective": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
Include("comments_pop_1"),
|
||||
{`\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"arguments": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
Include("comments_pop_2"),
|
||||
{`\\\n`, Text, nil}, // Skip escaped newlines
|
||||
{`\n`, Text, Pop(2)},
|
||||
Include("base"),
|
||||
},
|
||||
"deep_subdirective": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
Include("comments_pop_3"),
|
||||
{`\n`, Text, Pop(3)},
|
||||
Include("base"),
|
||||
},
|
||||
"matcher_token": {
|
||||
{`@[^\s]+`, NameDecorator, Push("arguments")}, // Named matcher
|
||||
{`/[^\s]+`, NameDecorator, Push("arguments")}, // Path matcher
|
||||
{`\*`, NameDecorator, Push("arguments")}, // Wildcard path matcher
|
||||
{`\[\<matcher\>\]`, NameDecorator, Push("arguments")}, // Matcher token stub for docs
|
||||
},
|
||||
"comments": {
|
||||
{`^#.*\n`, CommentSingle, nil}, // Comment at start of line
|
||||
{`\s+#.*\n`, CommentSingle, nil}, // Comment preceded by whitespace
|
||||
},
|
||||
"comments_pop_1": {
|
||||
{`^#.*\n`, CommentSingle, Pop(1)}, // Comment at start of line
|
||||
{`\s+#.*\n`, CommentSingle, Pop(1)}, // Comment preceded by whitespace
|
||||
},
|
||||
"comments_pop_2": {
|
||||
{`^#.*\n`, CommentSingle, Pop(2)}, // Comment at start of line
|
||||
{`\s+#.*\n`, CommentSingle, Pop(2)}, // Comment preceded by whitespace
|
||||
},
|
||||
"comments_pop_3": {
|
||||
{`^#.*\n`, CommentSingle, Pop(3)}, // Comment at start of line
|
||||
{`\s+#.*\n`, CommentSingle, Pop(3)}, // Comment preceded by whitespace
|
||||
},
|
||||
"base": {
|
||||
Include("comments"),
|
||||
{`(on|off|first|last|before|after|internal|strip_prefix|strip_suffix|replace)\b`, NameConstant, nil},
|
||||
{`(https?://)?([a-z0-9.-]+)(:)([0-9]+)`, ByGroups(Name, Name, Punctuation, LiteralNumberInteger), nil},
|
||||
{`[a-z-]+/[a-z-+]+`, LiteralString, nil},
|
||||
{`[0-9]+[km]?\b`, LiteralNumberInteger, nil},
|
||||
{`\{[\w+.\$-]+\}`, LiteralStringEscape, nil}, // Placeholder
|
||||
{`\[(?=[^#{}$]+\])`, Punctuation, nil},
|
||||
{`\]|\|`, Punctuation, nil},
|
||||
{`[^\s#{}$\]]+`, LiteralString, nil},
|
||||
{`/[^\s#]*`, Name, nil},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
}
|
||||
|
||||
// Caddyfile lexer.
|
||||
var Caddyfile = internal.Register(MustNewLexer(
|
||||
&Config{
|
||||
Name: "Caddyfile",
|
||||
Aliases: []string{"caddyfile", "caddy"},
|
||||
Filenames: []string{"Caddyfile*"},
|
||||
MimeTypes: []string{},
|
||||
},
|
||||
Rules{
|
||||
"root": {
|
||||
Include("comments"),
|
||||
// Global options block
|
||||
{`^\s*(\{)\s*$`, ByGroups(Punctuation), Push("globals")},
|
||||
// Snippets
|
||||
{`(\([^\s#]+\))(\s*)(\{)`, ByGroups(NameVariableAnonymous, Text, Punctuation), Push("snippet")},
|
||||
// Site label
|
||||
{`[^#{(\s,]+`, GenericHeading, Push("label")},
|
||||
// Site label with placeholder
|
||||
{`\{[\w+.\$-]+\}`, LiteralStringEscape, Push("label")},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
"globals": {
|
||||
{`\}`, Punctuation, Pop(1)},
|
||||
{`[^\s#]+`, Keyword, Push("directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"snippet": {
|
||||
{`\}`, Punctuation, Pop(1)},
|
||||
// Matcher definition
|
||||
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
|
||||
// Any directive
|
||||
{`[^\s#]+`, Keyword, Push("directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"label": {
|
||||
// Allow multiple labels, comma separated, newlines after
|
||||
// a comma means another label is coming
|
||||
{`,\s*\n?`, Text, nil},
|
||||
{` `, Text, nil},
|
||||
// Site label with placeholder
|
||||
{`\{[\w+.\$-]+\}`, LiteralStringEscape, nil},
|
||||
// Site label
|
||||
{`[^#{(\s,]+`, GenericHeading, nil},
|
||||
// Comment after non-block label (hack because comments end in \n)
|
||||
{`#.*\n`, CommentSingle, Push("site_block")},
|
||||
// Note: if \n, we'll never pop out of the site_block, it's valid
|
||||
{`\{(?=\s)|\n`, Punctuation, Push("site_block")},
|
||||
},
|
||||
"site_block": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
Include("site_block_common"),
|
||||
},
|
||||
}.Merge(caddyfileCommon),
|
||||
))
|
||||
|
||||
// Caddyfile directive-only lexer.
|
||||
var CaddyfileDirectives = internal.Register(MustNewLexer(
|
||||
&Config{
|
||||
Name: "Caddyfile Directives",
|
||||
Aliases: []string{"caddyfile-directives", "caddyfile-d", "caddy-d"},
|
||||
Filenames: []string{},
|
||||
MimeTypes: []string{},
|
||||
},
|
||||
Rules{
|
||||
// Same as "site_block" in Caddyfile
|
||||
"root": {
|
||||
Include("site_block_common"),
|
||||
},
|
||||
}.Merge(caddyfileCommon),
|
||||
))
|
@ -1,34 +0,0 @@
|
||||
package circular
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/alecthomas/chroma" // nolint
|
||||
"github.com/alecthomas/chroma/lexers/h"
|
||||
"github.com/alecthomas/chroma/lexers/internal"
|
||||
)
|
||||
|
||||
// PHTML lexer is PHP in HTML.
|
||||
var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
|
||||
&Config{
|
||||
Name: "PHTML",
|
||||
Aliases: []string{"phtml"},
|
||||
Filenames: []string{"*.phtml"},
|
||||
MimeTypes: []string{"application/x-php", "application/x-httpd-php", "application/x-httpd-php3", "application/x-httpd-php4", "application/x-httpd-php5"},
|
||||
DotAll: true,
|
||||
CaseInsensitive: true,
|
||||
EnsureNL: true,
|
||||
},
|
||||
Rules{
|
||||
"root": {
|
||||
{`<\?(php)?`, CommentPreproc, Push("php")},
|
||||
{`[^<]+`, Other, nil},
|
||||
{`<`, Other, nil},
|
||||
},
|
||||
}.Merge(phpCommonRules),
|
||||
).SetAnalyser(func(text string) float32 {
|
||||
if strings.Contains(text, "<?php") {
|
||||
return 0.5
|
||||
}
|
||||
return 0.0
|
||||
})))
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,59 +0,0 @@
|
||||
package p
|
||||
|
||||
import (
|
||||
. "github.com/alecthomas/chroma" // nolint
|
||||
"github.com/alecthomas/chroma/lexers/internal"
|
||||
)
|
||||
|
||||
// Pony lexer.
|
||||
var Pony = internal.Register(MustNewLexer(
|
||||
&Config{
|
||||
Name: "Pony",
|
||||
Aliases: []string{"pony"},
|
||||
Filenames: []string{"*.pony"},
|
||||
MimeTypes: []string{},
|
||||
},
|
||||
Rules{
|
||||
"root": {
|
||||
{`\n`, Text, nil},
|
||||
{`[^\S\n]+`, Text, nil},
|
||||
{`//.*\n`, CommentSingle, nil},
|
||||
{`/\*`, CommentMultiline, Push("nested_comment")},
|
||||
{`"""(?:.|\n)*?"""`, LiteralStringDoc, nil},
|
||||
{`"`, LiteralString, Push("string")},
|
||||
{`\'.*\'`, LiteralStringChar, nil},
|
||||
{`=>|[]{}:().~;,|&!^?[]`, Punctuation, nil},
|
||||
{Words(``, `\b`, `addressof`, `and`, `as`, `consume`, `digestof`, `is`, `isnt`, `not`, `or`), OperatorWord, nil},
|
||||
{`!=|==|<<|>>|[-+/*%=<>]`, Operator, nil},
|
||||
{Words(``, `\b`, `box`, `break`, `compile_error`, `compile_intrinsic`, `continue`, `do`, `else`, `elseif`, `embed`, `end`, `error`, `for`, `if`, `ifdef`, `in`, `iso`, `lambda`, `let`, `match`, `object`, `recover`, `ref`, `repeat`, `return`, `tag`, `then`, `this`, `trn`, `try`, `until`, `use`, `var`, `val`, `where`, `while`, `with`, `#any`, `#read`, `#send`, `#share`), Keyword, nil},
|
||||
{`(actor|class|struct|primitive|interface|trait|type)((?:\s)+)`, ByGroups(Keyword, Text), Push("typename")},
|
||||
{`(new|fun|be)((?:\s)+)`, ByGroups(Keyword, Text), Push("methodname")},
|
||||
{Words(``, `\b`, `U8`, `U16`, `U32`, `U64`, `ULong`, `USize`, `U128`, `Unsigned`, `Stringable`, `String`, `StringBytes`, `StringRunes`, `InputNotify`, `InputStream`, `Stdin`, `ByteSeq`, `ByteSeqIter`, `OutStream`, `StdStream`, `SourceLoc`, `I8`, `I16`, `I32`, `I64`, `ILong`, `ISize`, `I128`, `Signed`, `Seq`, `RuntimeOptions`, `Real`, `Integer`, `SignedInteger`, `UnsignedInteger`, `FloatingPoint`, `Number`, `Int`, `ReadSeq`, `ReadElement`, `Pointer`, `Platform`, `NullablePointer`, `None`, `Iterator`, `F32`, `F64`, `Float`, `Env`, `DoNotOptimise`, `DisposableActor`, `Less`, `Equal`, `Greater`, `Compare`, `HasEq`, `Equatable`, `Comparable`, `Bool`, `AsioEventID`, `AsioEventNotify`, `AsioEvent`, `Array`, `ArrayKeys`, `ArrayValues`, `ArrayPairs`, `Any`, `AmbientAuth`), KeywordType, nil},
|
||||
{`_?[A-Z]\w*`, NameClass, nil},
|
||||
{`string\(\)`, NameOther, nil},
|
||||
{`(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+`, LiteralNumberFloat, nil},
|
||||
{`0x[0-9a-fA-F]+`, LiteralNumberHex, nil},
|
||||
{`\d+`, LiteralNumberInteger, nil},
|
||||
{`(true|false)\b`, Keyword, nil},
|
||||
{`_\d*`, Name, nil},
|
||||
{`_?[a-z][\w\'_]*`, Name, nil},
|
||||
},
|
||||
"typename": {
|
||||
{`(iso|trn|ref|val|box|tag)?((?:\s)*)(_?[A-Z]\w*)`, ByGroups(Keyword, Text, NameClass), Pop(1)},
|
||||
},
|
||||
"methodname": {
|
||||
{`(iso|trn|ref|val|box|tag)?((?:\s)*)(_?[a-z]\w*)`, ByGroups(Keyword, Text, NameFunction), Pop(1)},
|
||||
},
|
||||
"nested_comment": {
|
||||
{`[^*/]+`, CommentMultiline, nil},
|
||||
{`/\*`, CommentMultiline, Push()},
|
||||
{`\*/`, CommentMultiline, Pop(1)},
|
||||
{`[*/]`, CommentMultiline, nil},
|
||||
},
|
||||
"string": {
|
||||
{`"`, LiteralString, Pop(1)},
|
||||
{`\\"`, LiteralString, nil},
|
||||
{`[^\\"]+`, LiteralString, nil},
|
||||
},
|
||||
},
|
||||
))
|
@ -1,54 +0,0 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
. "github.com/alecthomas/chroma" // nolint
|
||||
"github.com/alecthomas/chroma/lexers/internal"
|
||||
)
|
||||
|
||||
// Zig lexer.
|
||||
var Zig = internal.Register(MustNewLexer(
|
||||
&Config{
|
||||
Name: "Zig",
|
||||
Aliases: []string{"zig"},
|
||||
Filenames: []string{"*.zig"},
|
||||
MimeTypes: []string{"text/zig"},
|
||||
},
|
||||
Rules{
|
||||
"root": {
|
||||
{`\n`, TextWhitespace, nil},
|
||||
{`\s+`, TextWhitespace, nil},
|
||||
{`//.*?\n`, CommentSingle, nil},
|
||||
{Words(``, `\b`, `break`, `return`, `continue`, `asm`, `defer`, `errdefer`, `unreachable`, `try`, `catch`, `async`, `await`, `suspend`, `resume`, `cancel`), Keyword, nil},
|
||||
{Words(``, `\b`, `const`, `var`, `extern`, `packed`, `export`, `pub`, `noalias`, `inline`, `comptime`, `nakedcc`, `stdcallcc`, `volatile`, `allowzero`, `align`, `linksection`, `threadlocal`), KeywordReserved, nil},
|
||||
{Words(``, `\b`, `struct`, `enum`, `union`, `error`), Keyword, nil},
|
||||
{Words(``, `\b`, `while`, `for`), Keyword, nil},
|
||||
{Words(``, `\b`, `bool`, `f16`, `f32`, `f64`, `f128`, `void`, `noreturn`, `type`, `anyerror`, `promise`, `i0`, `u0`, `isize`, `usize`, `comptime_int`, `comptime_float`, `c_short`, `c_ushort`, `c_int`, `c_uint`, `c_long`, `c_ulong`, `c_longlong`, `c_ulonglong`, `c_longdouble`, `c_voidi8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`), KeywordType, nil},
|
||||
{Words(``, `\b`, `true`, `false`, `null`, `undefined`), KeywordConstant, nil},
|
||||
{Words(``, `\b`, `if`, `else`, `switch`, `and`, `or`, `orelse`), Keyword, nil},
|
||||
{Words(``, `\b`, `fn`, `usingnamespace`, `test`), Keyword, nil},
|
||||
{`0x[0-9a-fA-F]+\.[0-9a-fA-F]+([pP][\-+]?[0-9a-fA-F]+)?`, LiteralNumberFloat, nil},
|
||||
{`0x[0-9a-fA-F]+\.?[pP][\-+]?[0-9a-fA-F]+`, LiteralNumberFloat, nil},
|
||||
{`[0-9]+\.[0-9]+([eE][-+]?[0-9]+)?`, LiteralNumberFloat, nil},
|
||||
{`[0-9]+\.?[eE][-+]?[0-9]+`, LiteralNumberFloat, nil},
|
||||
{`0b[01]+`, LiteralNumberBin, nil},
|
||||
{`0o[0-7]+`, LiteralNumberOct, nil},
|
||||
{`0x[0-9a-fA-F]+`, LiteralNumberHex, nil},
|
||||
{`[0-9]+`, LiteralNumberInteger, nil},
|
||||
{`@[a-zA-Z_]\w*`, NameBuiltin, nil},
|
||||
{`[a-zA-Z_]\w*`, Name, nil},
|
||||
{`\'\\\'\'`, LiteralStringEscape, nil},
|
||||
{`\'\\(|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{6}|[nr\\t\'"])\'`, LiteralStringEscape, nil},
|
||||
{`\'[^\\\']\'`, LiteralString, nil},
|
||||
{`\\\\[^\n]*`, LiteralStringHeredoc, nil},
|
||||
{`c\\\\[^\n]*`, LiteralStringHeredoc, nil},
|
||||
{`c?"`, LiteralString, Push("string")},
|
||||
{`[+%=><|^!?/\-*&~:]`, Operator, nil},
|
||||
{`[{}()\[\],.;]`, Punctuation, nil},
|
||||
},
|
||||
"string": {
|
||||
{`\\(x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{6}|[nr\\t\'"])`, LiteralStringEscape, nil},
|
||||
{`[^\\"\n]+`, LiteralString, nil},
|
||||
{`"`, LiteralString, Pop(1)},
|
||||
},
|
||||
},
|
||||
))
|
@ -1,60 +0,0 @@
|
||||
package css
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Declaration represents a parsed style property
|
||||
type Declaration struct {
|
||||
Property string
|
||||
Value string
|
||||
Important bool
|
||||
}
|
||||
|
||||
// NewDeclaration instanciates a new Declaration
|
||||
func NewDeclaration() *Declaration {
|
||||
return &Declaration{}
|
||||
}
|
||||
|
||||
// Returns string representation of the Declaration
|
||||
func (decl *Declaration) String() string {
|
||||
return decl.StringWithImportant(true)
|
||||
}
|
||||
|
||||
// StringWithImportant returns string representation with optional !important part
|
||||
func (decl *Declaration) StringWithImportant(option bool) string {
|
||||
result := fmt.Sprintf("%s: %s", decl.Property, decl.Value)
|
||||
|
||||
if option && decl.Important {
|
||||
result += " !important"
|
||||
}
|
||||
|
||||
result += ";"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Equal returns true if both Declarations are equals
|
||||
func (decl *Declaration) Equal(other *Declaration) bool {
|
||||
return (decl.Property == other.Property) && (decl.Value == other.Value) && (decl.Important == other.Important)
|
||||
}
|
||||
|
||||
//
|
||||
// DeclarationsByProperty
|
||||
//
|
||||
|
||||
// DeclarationsByProperty represents sortable style declarations
|
||||
type DeclarationsByProperty []*Declaration
|
||||
|
||||
// Implements sort.Interface
|
||||
func (declarations DeclarationsByProperty) Len() int {
|
||||
return len(declarations)
|
||||
}
|
||||
|
||||
// Implements sort.Interface
|
||||
func (declarations DeclarationsByProperty) Swap(i, j int) {
|
||||
declarations[i], declarations[j] = declarations[j], declarations[i]
|
||||
}
|
||||
|
||||
// Implements sort.Interface
|
||||
func (declarations DeclarationsByProperty) Less(i, j int) bool {
|
||||
return declarations[i].Property < declarations[j].Property
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
package css
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
indentSpace = 2
|
||||
)
|
||||
|
||||
// RuleKind represents a Rule kind
|
||||
type RuleKind int
|
||||
|
||||
// Rule kinds
|
||||
const (
|
||||
QualifiedRule RuleKind = iota
|
||||
AtRule
|
||||
)
|
||||
|
||||
// At Rules than have Rules inside their block instead of Declarations
|
||||
var atRulesWithRulesBlock = []string{
|
||||
"@document", "@font-feature-values", "@keyframes", "@media", "@supports",
|
||||
}
|
||||
|
||||
// Rule represents a parsed CSS rule
|
||||
type Rule struct {
|
||||
Kind RuleKind
|
||||
|
||||
// At Rule name (eg: "@media")
|
||||
Name string
|
||||
|
||||
// Raw prelude
|
||||
Prelude string
|
||||
|
||||
// Qualified Rule selectors parsed from prelude
|
||||
Selectors []string
|
||||
|
||||
// Style properties
|
||||
Declarations []*Declaration
|
||||
|
||||
// At Rule embedded rules
|
||||
Rules []*Rule
|
||||
|
||||
// Current rule embedding level
|
||||
EmbedLevel int
|
||||
}
|
||||
|
||||
// NewRule instanciates a new Rule
|
||||
func NewRule(kind RuleKind) *Rule {
|
||||
return &Rule{
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns string representation of rule kind
|
||||
func (kind RuleKind) String() string {
|
||||
switch kind {
|
||||
case QualifiedRule:
|
||||
return "Qualified Rule"
|
||||
case AtRule:
|
||||
return "At Rule"
|
||||
default:
|
||||
return "WAT"
|
||||
}
|
||||
}
|
||||
|
||||
// EmbedsRules returns true if this rule embeds another rules
|
||||
func (rule *Rule) EmbedsRules() bool {
|
||||
if rule.Kind == AtRule {
|
||||
for _, atRuleName := range atRulesWithRulesBlock {
|
||||
if rule.Name == atRuleName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Equal returns true if both rules are equals
|
||||
func (rule *Rule) Equal(other *Rule) bool {
|
||||
if (rule.Kind != other.Kind) ||
|
||||
(rule.Prelude != other.Prelude) ||
|
||||
(rule.Name != other.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (len(rule.Selectors) != len(other.Selectors)) ||
|
||||
(len(rule.Declarations) != len(other.Declarations)) ||
|
||||
(len(rule.Rules) != len(other.Rules)) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, sel := range rule.Selectors {
|
||||
if sel != other.Selectors[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i, decl := range rule.Declarations {
|
||||
if !decl.Equal(other.Declarations[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i, rule := range rule.Rules {
|
||||
if !rule.Equal(other.Rules[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Diff returns a string representation of rules differences
|
||||
func (rule *Rule) Diff(other *Rule) []string {
|
||||
result := []string{}
|
||||
|
||||
if rule.Kind != other.Kind {
|
||||
result = append(result, fmt.Sprintf("Kind: %s | %s", rule.Kind.String(), other.Kind.String()))
|
||||
}
|
||||
|
||||
if rule.Prelude != other.Prelude {
|
||||
result = append(result, fmt.Sprintf("Prelude: \"%s\" | \"%s\"", rule.Prelude, other.Prelude))
|
||||
}
|
||||
|
||||
if rule.Name != other.Name {
|
||||
result = append(result, fmt.Sprintf("Name: \"%s\" | \"%s\"", rule.Name, other.Name))
|
||||
}
|
||||
|
||||
if len(rule.Selectors) != len(other.Selectors) {
|
||||
result = append(result, fmt.Sprintf("Selectors: %v | %v", strings.Join(rule.Selectors, ", "), strings.Join(other.Selectors, ", ")))
|
||||
} else {
|
||||
for i, sel := range rule.Selectors {
|
||||
if sel != other.Selectors[i] {
|
||||
result = append(result, fmt.Sprintf("Selector: \"%s\" | \"%s\"", sel, other.Selectors[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Declarations) != len(other.Declarations) {
|
||||
result = append(result, fmt.Sprintf("Declarations Nb: %d | %d", len(rule.Declarations), len(other.Declarations)))
|
||||
} else {
|
||||
for i, decl := range rule.Declarations {
|
||||
if !decl.Equal(other.Declarations[i]) {
|
||||
result = append(result, fmt.Sprintf("Declaration: \"%s\" | \"%s\"", decl.String(), other.Declarations[i].String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Rules) != len(other.Rules) {
|
||||
result = append(result, fmt.Sprintf("Rules Nb: %d | %d", len(rule.Rules), len(other.Rules)))
|
||||
} else {
|
||||
|
||||
for i, rule := range rule.Rules {
|
||||
if !rule.Equal(other.Rules[i]) {
|
||||
result = append(result, fmt.Sprintf("Rule: \"%s\" | \"%s\"", rule.String(), other.Rules[i].String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the string representation of a rule
|
||||
func (rule *Rule) String() string {
|
||||
result := ""
|
||||
|
||||
if rule.Kind == QualifiedRule {
|
||||
for i, sel := range rule.Selectors {
|
||||
if i != 0 {
|
||||
result += ", "
|
||||
}
|
||||
result += sel
|
||||
}
|
||||
} else {
|
||||
// AtRule
|
||||
result += fmt.Sprintf("%s", rule.Name)
|
||||
|
||||
if rule.Prelude != "" {
|
||||
if result != "" {
|
||||
result += " "
|
||||
}
|
||||
result += fmt.Sprintf("%s", rule.Prelude)
|
||||
}
|
||||
}
|
||||
|
||||
if (len(rule.Declarations) == 0) && (len(rule.Rules) == 0) {
|
||||
result += ";"
|
||||
} else {
|
||||
result += " {\n"
|
||||
|
||||
if rule.EmbedsRules() {
|
||||
for _, subRule := range rule.Rules {
|
||||
result += fmt.Sprintf("%s%s\n", rule.indent(), subRule.String())
|
||||
}
|
||||
} else {
|
||||
for _, decl := range rule.Declarations {
|
||||
result += fmt.Sprintf("%s%s\n", rule.indent(), decl.String())
|
||||
}
|
||||
}
|
||||
|
||||
result += fmt.Sprintf("%s}", rule.indentEndBlock())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns identation spaces for declarations and rules
|
||||
func (rule *Rule) indent() string {
|
||||
result := ""
|
||||
|
||||
for i := 0; i < ((rule.EmbedLevel + 1) * indentSpace); i++ {
|
||||
result += " "
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns identation spaces for end of block character
|
||||
func (rule *Rule) indentEndBlock() string {
|
||||
result := ""
|
||||
|
||||
for i := 0; i < (rule.EmbedLevel * indentSpace); i++ {
|
||||
result += " "
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package css
|
||||
|
||||
// Stylesheet represents a parsed stylesheet
|
||||
type Stylesheet struct {
|
||||
Rules []*Rule
|
||||
}
|
||||
|
||||
// NewStylesheet instanciate a new Stylesheet
|
||||
func NewStylesheet() *Stylesheet {
|
||||
return &Stylesheet{}
|
||||
}
|
||||
|
||||
// Returns string representation of the Stylesheet
|
||||
func (sheet *Stylesheet) String() string {
|
||||
result := ""
|
||||
|
||||
for _, rule := range sheet.Rules {
|
||||
if result != "" {
|
||||
result += "\n"
|
||||
}
|
||||
result += rule.String()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Aymerick JEHANNE
|
||||
|
||||
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.
|
||||
|
@ -1,409 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/css/scanner"
|
||||
|
||||
"github.com/aymerick/douceur/css"
|
||||
)
|
||||
|
||||
const (
|
||||
importantSuffixRegexp = `(?i)\s*!important\s*$`
|
||||
)
|
||||
|
||||
var (
|
||||
importantRegexp *regexp.Regexp
|
||||
)
|
||||
|
||||
// Parser represents a CSS parser
|
||||
type Parser struct {
|
||||
scan *scanner.Scanner // Tokenizer
|
||||
|
||||
// Tokens parsed but not consumed yet
|
||||
tokens []*scanner.Token
|
||||
|
||||
// Rule embedding level
|
||||
embedLevel int
|
||||
}
|
||||
|
||||
func init() {
|
||||
importantRegexp = regexp.MustCompile(importantSuffixRegexp)
|
||||
}
|
||||
|
||||
// NewParser instanciates a new parser
|
||||
func NewParser(txt string) *Parser {
|
||||
return &Parser{
|
||||
scan: scanner.New(txt),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses a whole stylesheet
|
||||
func Parse(text string) (*css.Stylesheet, error) {
|
||||
result, err := NewParser(text).ParseStylesheet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseDeclarations parses CSS declarations
|
||||
func ParseDeclarations(text string) ([]*css.Declaration, error) {
|
||||
result, err := NewParser(text).ParseDeclarations()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseStylesheet parses a stylesheet
|
||||
func (parser *Parser) ParseStylesheet() (*css.Stylesheet, error) {
|
||||
result := css.NewStylesheet()
|
||||
|
||||
// Parse BOM
|
||||
if _, err := parser.parseBOM(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Parse list of rules
|
||||
rules, err := parser.ParseRules()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Rules = rules
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseRules parses a list of rules
|
||||
func (parser *Parser) ParseRules() ([]*css.Rule, error) {
|
||||
result := []*css.Rule{}
|
||||
|
||||
inBlock := false
|
||||
if parser.tokenChar("{") {
|
||||
// parsing a block of rules
|
||||
inBlock = true
|
||||
parser.embedLevel++
|
||||
|
||||
parser.shiftToken()
|
||||
}
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenIgnorable() {
|
||||
parser.shiftToken()
|
||||
} else if parser.tokenChar("}") {
|
||||
if !inBlock {
|
||||
errMsg := fmt.Sprintf("Unexpected } character: %s", parser.nextToken().String())
|
||||
return result, errors.New(errMsg)
|
||||
}
|
||||
|
||||
parser.shiftToken()
|
||||
parser.embedLevel--
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
rule, err := parser.ParseRule()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
rule.EmbedLevel = parser.embedLevel
|
||||
result = append(result, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// ParseRule parses a rule
|
||||
func (parser *Parser) ParseRule() (*css.Rule, error) {
|
||||
if parser.tokenAtKeyword() {
|
||||
return parser.parseAtRule()
|
||||
}
|
||||
|
||||
return parser.parseQualifiedRule()
|
||||
}
|
||||
|
||||
// ParseDeclarations parses a list of declarations
|
||||
func (parser *Parser) ParseDeclarations() ([]*css.Declaration, error) {
|
||||
result := []*css.Declaration{}
|
||||
|
||||
if parser.tokenChar("{") {
|
||||
parser.shiftToken()
|
||||
}
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenIgnorable() {
|
||||
parser.shiftToken()
|
||||
} else if parser.tokenChar("}") {
|
||||
// end of block
|
||||
parser.shiftToken()
|
||||
break
|
||||
} else {
|
||||
declaration, err := parser.ParseDeclaration()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result = append(result, declaration)
|
||||
}
|
||||
}
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// ParseDeclaration parses a declaration
|
||||
func (parser *Parser) ParseDeclaration() (*css.Declaration, error) {
|
||||
result := css.NewDeclaration()
|
||||
curValue := ""
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenChar(":") {
|
||||
result.Property = strings.TrimSpace(curValue)
|
||||
curValue = ""
|
||||
|
||||
parser.shiftToken()
|
||||
} else if parser.tokenChar(";") || parser.tokenChar("}") {
|
||||
if result.Property == "" {
|
||||
errMsg := fmt.Sprintf("Unexpected ; character: %s", parser.nextToken().String())
|
||||
return result, errors.New(errMsg)
|
||||
}
|
||||
|
||||
if importantRegexp.MatchString(curValue) {
|
||||
result.Important = true
|
||||
curValue = importantRegexp.ReplaceAllString(curValue, "")
|
||||
}
|
||||
|
||||
result.Value = strings.TrimSpace(curValue)
|
||||
|
||||
if parser.tokenChar(";") {
|
||||
parser.shiftToken()
|
||||
}
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
token := parser.shiftToken()
|
||||
curValue += token.Value
|
||||
}
|
||||
}
|
||||
|
||||
// log.Printf("[parsed] Declaration: %s", result.String())
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse an At Rule
|
||||
func (parser *Parser) parseAtRule() (*css.Rule, error) {
|
||||
// parse rule name (eg: "@import")
|
||||
token := parser.shiftToken()
|
||||
|
||||
result := css.NewRule(css.AtRule)
|
||||
result.Name = token.Value
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenChar(";") {
|
||||
parser.shiftToken()
|
||||
|
||||
// finished
|
||||
break
|
||||
} else if parser.tokenChar("{") {
|
||||
if result.EmbedsRules() {
|
||||
// parse rules block
|
||||
rules, err := parser.ParseRules()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Rules = rules
|
||||
} else {
|
||||
// parse declarations block
|
||||
declarations, err := parser.ParseDeclarations()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Declarations = declarations
|
||||
}
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
// parse prelude
|
||||
prelude, err := parser.parsePrelude()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Prelude = prelude
|
||||
}
|
||||
}
|
||||
|
||||
// log.Printf("[parsed] Rule: %s", result.String())
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse a Qualified Rule
|
||||
func (parser *Parser) parseQualifiedRule() (*css.Rule, error) {
|
||||
result := css.NewRule(css.QualifiedRule)
|
||||
|
||||
for parser.tokenParsable() {
|
||||
if parser.tokenChar("{") {
|
||||
if result.Prelude == "" {
|
||||
errMsg := fmt.Sprintf("Unexpected { character: %s", parser.nextToken().String())
|
||||
return result, errors.New(errMsg)
|
||||
}
|
||||
|
||||
// parse declarations block
|
||||
declarations, err := parser.ParseDeclarations()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Declarations = declarations
|
||||
|
||||
// finished
|
||||
break
|
||||
} else {
|
||||
// parse prelude
|
||||
prelude, err := parser.parsePrelude()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Prelude = prelude
|
||||
}
|
||||
}
|
||||
|
||||
result.Selectors = strings.Split(result.Prelude, ",")
|
||||
for i, sel := range result.Selectors {
|
||||
result.Selectors[i] = strings.TrimSpace(sel)
|
||||
}
|
||||
|
||||
// log.Printf("[parsed] Rule: %s", result.String())
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse Rule prelude
|
||||
func (parser *Parser) parsePrelude() (string, error) {
|
||||
result := ""
|
||||
|
||||
for parser.tokenParsable() && !parser.tokenEndOfPrelude() {
|
||||
token := parser.shiftToken()
|
||||
result += token.Value
|
||||
}
|
||||
|
||||
result = strings.TrimSpace(result)
|
||||
|
||||
// log.Printf("[parsed] prelude: %s", result)
|
||||
|
||||
return result, parser.err()
|
||||
}
|
||||
|
||||
// Parse BOM
|
||||
func (parser *Parser) parseBOM() (bool, error) {
|
||||
if parser.nextToken().Type == scanner.TokenBOM {
|
||||
parser.shiftToken()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, parser.err()
|
||||
}
|
||||
|
||||
// Returns next token without removing it from tokens buffer
|
||||
func (parser *Parser) nextToken() *scanner.Token {
|
||||
if len(parser.tokens) == 0 {
|
||||
// fetch next token
|
||||
nextToken := parser.scan.Next()
|
||||
|
||||
// log.Printf("[token] %s => %v", nextToken.Type.String(), nextToken.Value)
|
||||
|
||||
// queue it
|
||||
parser.tokens = append(parser.tokens, nextToken)
|
||||
}
|
||||
|
||||
return parser.tokens[0]
|
||||
}
|
||||
|
||||
// Returns next token and remove it from the tokens buffer
|
||||
func (parser *Parser) shiftToken() *scanner.Token {
|
||||
var result *scanner.Token
|
||||
|
||||
result, parser.tokens = parser.tokens[0], parser.tokens[1:]
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns tokenizer error, or nil if no error
|
||||
func (parser *Parser) err() error {
|
||||
if parser.tokenError() {
|
||||
token := parser.nextToken()
|
||||
return fmt.Errorf("Tokenizer error: %s", token.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true if next token is Error
|
||||
func (parser *Parser) tokenError() bool {
|
||||
return parser.nextToken().Type == scanner.TokenError
|
||||
}
|
||||
|
||||
// Returns true if next token is EOF
|
||||
func (parser *Parser) tokenEOF() bool {
|
||||
return parser.nextToken().Type == scanner.TokenEOF
|
||||
}
|
||||
|
||||
// Returns true if next token is a whitespace
|
||||
func (parser *Parser) tokenWS() bool {
|
||||
return parser.nextToken().Type == scanner.TokenS
|
||||
}
|
||||
|
||||
// Returns true if next token is a comment
|
||||
func (parser *Parser) tokenComment() bool {
|
||||
return parser.nextToken().Type == scanner.TokenComment
|
||||
}
|
||||
|
||||
// Returns true if next token is a CDO or a CDC
|
||||
func (parser *Parser) tokenCDOorCDC() bool {
|
||||
switch parser.nextToken().Type {
|
||||
case scanner.TokenCDO, scanner.TokenCDC:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if next token is ignorable
|
||||
func (parser *Parser) tokenIgnorable() bool {
|
||||
return parser.tokenWS() || parser.tokenComment() || parser.tokenCDOorCDC()
|
||||
}
|
||||
|
||||
// Returns true if next token is parsable
|
||||
func (parser *Parser) tokenParsable() bool {
|
||||
return !parser.tokenEOF() && !parser.tokenError()
|
||||
}
|
||||
|
||||
// Returns true if next token is an At Rule keyword
|
||||
func (parser *Parser) tokenAtKeyword() bool {
|
||||
return parser.nextToken().Type == scanner.TokenAtKeyword
|
||||
}
|
||||
|
||||
// Returns true if next token is given character
|
||||
func (parser *Parser) tokenChar(value string) bool {
|
||||
token := parser.nextToken()
|
||||
return (token.Type == scanner.TokenChar) && (token.Value == value)
|
||||
}
|
||||
|
||||
// Returns true if next token marks the end of a prelude
|
||||
func (parser *Parser) tokenEndOfPrelude() bool {
|
||||
return parser.tokenChar(";") || parser.tokenChar("{")
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9
|
||||
- 1.5
|
||||
- tip
|
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2013, Gorilla web toolkit
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
Neither the name of the {organization} nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2012 The Gorilla 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 gorilla/css/scanner generates tokens for a CSS3 input.
|
||||
|
||||
It follows the CSS3 specification located at:
|
||||
|
||||
http://www.w3.org/TR/css3-syntax/
|
||||
|
||||
To use it, create a new scanner for a given CSS string and call Next() until
|
||||
the token returned has type TokenEOF or TokenError:
|
||||
|
||||
s := scanner.New(myCSS)
|
||||
for {
|
||||
token := s.Next()
|
||||
if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
|
||||
break
|
||||
}
|
||||
// Do something with the token...
|
||||
}
|
||||
|
||||
Following the CSS3 specification, an error can only occur when the scanner
|
||||
finds an unclosed quote or unclosed comment. In these cases the text becomes
|
||||
"untokenizable". Everything else is tokenizable and it is up to a parser
|
||||
to make sense of the token stream (or ignore nonsensical token sequences).
|
||||
|
||||
Note: the scanner doesn't perform lexical analysis or, in other words, it
|
||||
doesn't care about the token context. It is intended to be used by a
|
||||
lexer or parser.
|
||||
*/
|
||||
package scanner
|
@ -1,356 +0,0 @@
|
||||
// Copyright 2012 The Gorilla 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 scanner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// tokenType identifies the type of lexical tokens.
|
||||
type tokenType int
|
||||
|
||||
// String returns a string representation of the token type.
|
||||
func (t tokenType) String() string {
|
||||
return tokenNames[t]
|
||||
}
|
||||
|
||||
// Token represents a token and the corresponding string.
|
||||
type Token struct {
|
||||
Type tokenType
|
||||
Value string
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
// String returns a string representation of the token.
|
||||
func (t *Token) String() string {
|
||||
if len(t.Value) > 10 {
|
||||
return fmt.Sprintf("%s (line: %d, column: %d): %.10q...",
|
||||
t.Type, t.Line, t.Column, t.Value)
|
||||
}
|
||||
return fmt.Sprintf("%s (line: %d, column: %d): %q",
|
||||
t.Type, t.Line, t.Column, t.Value)
|
||||
}
|
||||
|
||||
// All tokens -----------------------------------------------------------------
|
||||
|
||||
// The complete list of tokens in CSS3.
|
||||
const (
|
||||
// Scanner flags.
|
||||
TokenError tokenType = iota
|
||||
TokenEOF
|
||||
// From now on, only tokens from the CSS specification.
|
||||
TokenIdent
|
||||
TokenAtKeyword
|
||||
TokenString
|
||||
TokenHash
|
||||
TokenNumber
|
||||
TokenPercentage
|
||||
TokenDimension
|
||||
TokenURI
|
||||
TokenUnicodeRange
|
||||
TokenCDO
|
||||
TokenCDC
|
||||
TokenS
|
||||
TokenComment
|
||||
TokenFunction
|
||||
TokenIncludes
|
||||
TokenDashMatch
|
||||
TokenPrefixMatch
|
||||
TokenSuffixMatch
|
||||
TokenSubstringMatch
|
||||
TokenChar
|
||||
TokenBOM
|
||||
)
|
||||
|
||||
// tokenNames maps tokenType's to their names. Used for conversion to string.
|
||||
var tokenNames = map[tokenType]string{
|
||||
TokenError: "error",
|
||||
TokenEOF: "EOF",
|
||||
TokenIdent: "IDENT",
|
||||
TokenAtKeyword: "ATKEYWORD",
|
||||
TokenString: "STRING",
|
||||
TokenHash: "HASH",
|
||||
TokenNumber: "NUMBER",
|
||||
TokenPercentage: "PERCENTAGE",
|
||||
TokenDimension: "DIMENSION",
|
||||
TokenURI: "URI",
|
||||
TokenUnicodeRange: "UNICODE-RANGE",
|
||||
TokenCDO: "CDO",
|
||||
TokenCDC: "CDC",
|
||||
TokenS: "S",
|
||||
TokenComment: "COMMENT",
|
||||
TokenFunction: "FUNCTION",
|
||||
TokenIncludes: "INCLUDES",
|
||||
TokenDashMatch: "DASHMATCH",
|
||||
TokenPrefixMatch: "PREFIXMATCH",
|
||||
TokenSuffixMatch: "SUFFIXMATCH",
|
||||
TokenSubstringMatch: "SUBSTRINGMATCH",
|
||||
TokenChar: "CHAR",
|
||||
TokenBOM: "BOM",
|
||||
}
|
||||
|
||||
// Macros and productions -----------------------------------------------------
|
||||
// http://www.w3.org/TR/css3-syntax/#tokenization
|
||||
|
||||
var macroRegexp = regexp.MustCompile(`\{[a-z]+\}`)
|
||||
|
||||
// macros maps macro names to patterns to be expanded.
|
||||
var macros = map[string]string{
|
||||
// must be escaped: `\.+*?()|[]{}^$`
|
||||
"ident": `-?{nmstart}{nmchar}*`,
|
||||
"name": `{nmchar}+`,
|
||||
"nmstart": `[a-zA-Z_]|{nonascii}|{escape}`,
|
||||
"nonascii": "[\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
|
||||
"unicode": `\\[0-9a-fA-F]{1,6}{wc}?`,
|
||||
"escape": "{unicode}|\\\\[\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
|
||||
"nmchar": `[a-zA-Z0-9_-]|{nonascii}|{escape}`,
|
||||
"num": `[0-9]*\.[0-9]+|[0-9]+`,
|
||||
"string": `"(?:{stringchar}|')*"|'(?:{stringchar}|")*'`,
|
||||
"stringchar": `{urlchar}|[ ]|\\{nl}`,
|
||||
"nl": `[\n\r\f]|\r\n`,
|
||||
"w": `{wc}*`,
|
||||
"wc": `[\t\n\f\r ]`,
|
||||
|
||||
// urlchar should accept [(ascii characters minus those that need escaping)|{nonascii}|{escape}]
|
||||
// ASCII characters range = `[\u0020-\u007e]`
|
||||
// Skip space \u0020 = `[\u0021-\u007e]`
|
||||
// Skip quotation mark \0022 = `[\u0021\u0023-\u007e]`
|
||||
// Skip apostrophe \u0027 = `[\u0021\u0023-\u0026\u0028-\u007e]`
|
||||
// Skip reverse solidus \u005c = `[\u0021\u0023-\u0026\u0028-\u005b\u005d\u007e]`
|
||||
// Finally, the left square bracket (\u005b) and right (\u005d) needs escaping themselves
|
||||
"urlchar": "[\u0021\u0023-\u0026\u0028-\\\u005b\\\u005d-\u007E]|{nonascii}|{escape}",
|
||||
}
|
||||
|
||||
// productions maps the list of tokens to patterns to be expanded.
|
||||
var productions = map[tokenType]string{
|
||||
// Unused regexps (matched using other methods) are commented out.
|
||||
TokenIdent: `{ident}`,
|
||||
TokenAtKeyword: `@{ident}`,
|
||||
TokenString: `{string}`,
|
||||
TokenHash: `#{name}`,
|
||||
TokenNumber: `{num}`,
|
||||
TokenPercentage: `{num}%`,
|
||||
TokenDimension: `{num}{ident}`,
|
||||
TokenURI: `url\({w}(?:{string}|{urlchar}*?){w}\)`,
|
||||
TokenUnicodeRange: `U\+[0-9A-F\?]{1,6}(?:-[0-9A-F]{1,6})?`,
|
||||
//TokenCDO: `<!--`,
|
||||
TokenCDC: `-->`,
|
||||
TokenS: `{wc}+`,
|
||||
TokenComment: `/\*[^\*]*[\*]+(?:[^/][^\*]*[\*]+)*/`,
|
||||
TokenFunction: `{ident}\(`,
|
||||
//TokenIncludes: `~=`,
|
||||
//TokenDashMatch: `\|=`,
|
||||
//TokenPrefixMatch: `\^=`,
|
||||
//TokenSuffixMatch: `\$=`,
|
||||
//TokenSubstringMatch: `\*=`,
|
||||
//TokenChar: `[^"']`,
|
||||
//TokenBOM: "\uFEFF",
|
||||
}
|
||||
|
||||
// matchers maps the list of tokens to compiled regular expressions.
|
||||
//
|
||||
// The map is filled on init() using the macros and productions defined in
|
||||
// the CSS specification.
|
||||
var matchers = map[tokenType]*regexp.Regexp{}
|
||||
|
||||
// matchOrder is the order to test regexps when first-char shortcuts
|
||||
// can't be used.
|
||||
var matchOrder = []tokenType{
|
||||
TokenURI,
|
||||
TokenFunction,
|
||||
TokenUnicodeRange,
|
||||
TokenIdent,
|
||||
TokenDimension,
|
||||
TokenPercentage,
|
||||
TokenNumber,
|
||||
TokenCDC,
|
||||
}
|
||||
|
||||
func init() {
|
||||
// replace macros and compile regexps for productions.
|
||||
replaceMacro := func(s string) string {
|
||||
return "(?:" + macros[s[1:len(s)-1]] + ")"
|
||||
}
|
||||
for t, s := range productions {
|
||||
for macroRegexp.MatchString(s) {
|
||||
s = macroRegexp.ReplaceAllStringFunc(s, replaceMacro)
|
||||
}
|
||||
matchers[t] = regexp.MustCompile("^(?:" + s + ")")
|
||||
}
|
||||
}
|
||||
|
||||
// Scanner --------------------------------------------------------------------
|
||||
|
||||
// New returns a new CSS scanner for the given input.
|
||||
func New(input string) *Scanner {
|
||||
// Normalize newlines.
|
||||
input = strings.Replace(input, "\r\n", "\n", -1)
|
||||
return &Scanner{
|
||||
input: input,
|
||||
row: 1,
|
||||
col: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Scanner scans an input and emits tokens following the CSS3 specification.
|
||||
type Scanner struct {
|
||||
input string
|
||||
pos int
|
||||
row int
|
||||
col int
|
||||
err *Token
|
||||
}
|
||||
|
||||
// Next returns the next token from the input.
|
||||
//
|
||||
// At the end of the input the token type is TokenEOF.
|
||||
//
|
||||
// If the input can't be tokenized the token type is TokenError. This occurs
|
||||
// in case of unclosed quotation marks or comments.
|
||||
func (s *Scanner) Next() *Token {
|
||||
if s.err != nil {
|
||||
return s.err
|
||||
}
|
||||
if s.pos >= len(s.input) {
|
||||
s.err = &Token{TokenEOF, "", s.row, s.col}
|
||||
return s.err
|
||||
}
|
||||
if s.pos == 0 {
|
||||
// Test BOM only once, at the beginning of the file.
|
||||
if strings.HasPrefix(s.input, "\uFEFF") {
|
||||
return s.emitSimple(TokenBOM, "\uFEFF")
|
||||
}
|
||||
}
|
||||
// There's a lot we can guess based on the first byte so we'll take a
|
||||
// shortcut before testing multiple regexps.
|
||||
input := s.input[s.pos:]
|
||||
switch input[0] {
|
||||
case '\t', '\n', '\f', '\r', ' ':
|
||||
// Whitespace.
|
||||
return s.emitToken(TokenS, matchers[TokenS].FindString(input))
|
||||
case '.':
|
||||
// Dot is too common to not have a quick check.
|
||||
// We'll test if this is a Char; if it is followed by a number it is a
|
||||
// dimension/percentage/number, and this will be matched later.
|
||||
if len(input) > 1 && !unicode.IsDigit(rune(input[1])) {
|
||||
return s.emitSimple(TokenChar, ".")
|
||||
}
|
||||
case '#':
|
||||
// Another common one: Hash or Char.
|
||||
if match := matchers[TokenHash].FindString(input); match != "" {
|
||||
return s.emitToken(TokenHash, match)
|
||||
}
|
||||
return s.emitSimple(TokenChar, "#")
|
||||
case '@':
|
||||
// Another common one: AtKeyword or Char.
|
||||
if match := matchers[TokenAtKeyword].FindString(input); match != "" {
|
||||
return s.emitSimple(TokenAtKeyword, match)
|
||||
}
|
||||
return s.emitSimple(TokenChar, "@")
|
||||
case ':', ',', ';', '%', '&', '+', '=', '>', '(', ')', '[', ']', '{', '}':
|
||||
// More common chars.
|
||||
return s.emitSimple(TokenChar, string(input[0]))
|
||||
case '"', '\'':
|
||||
// String or error.
|
||||
match := matchers[TokenString].FindString(input)
|
||||
if match != "" {
|
||||
return s.emitToken(TokenString, match)
|
||||
}
|
||||
|
||||
s.err = &Token{TokenError, "unclosed quotation mark", s.row, s.col}
|
||||
return s.err
|
||||
case '/':
|
||||
// Comment, error or Char.
|
||||
if len(input) > 1 && input[1] == '*' {
|
||||
match := matchers[TokenComment].FindString(input)
|
||||
if match != "" {
|
||||
return s.emitToken(TokenComment, match)
|
||||
} else {
|
||||
s.err = &Token{TokenError, "unclosed comment", s.row, s.col}
|
||||
return s.err
|
||||
}
|
||||
}
|
||||
return s.emitSimple(TokenChar, "/")
|
||||
case '~':
|
||||
// Includes or Char.
|
||||
return s.emitPrefixOrChar(TokenIncludes, "~=")
|
||||
case '|':
|
||||
// DashMatch or Char.
|
||||
return s.emitPrefixOrChar(TokenDashMatch, "|=")
|
||||
case '^':
|
||||
// PrefixMatch or Char.
|
||||
return s.emitPrefixOrChar(TokenPrefixMatch, "^=")
|
||||
case '$':
|
||||
// SuffixMatch or Char.
|
||||
return s.emitPrefixOrChar(TokenSuffixMatch, "$=")
|
||||
case '*':
|
||||
// SubstringMatch or Char.
|
||||
return s.emitPrefixOrChar(TokenSubstringMatch, "*=")
|
||||
case '<':
|
||||
// CDO or Char.
|
||||
return s.emitPrefixOrChar(TokenCDO, "<!--")
|
||||
}
|
||||
// Test all regexps, in order.
|
||||
for _, token := range matchOrder {
|
||||
if match := matchers[token].FindString(input); match != "" {
|
||||
return s.emitToken(token, match)
|
||||
}
|
||||
}
|
||||
// We already handled unclosed quotation marks and comments,
|
||||
// so this can only be a Char.
|
||||
r, width := utf8.DecodeRuneInString(input)
|
||||
token := &Token{TokenChar, string(r), s.row, s.col}
|
||||
s.col += width
|
||||
s.pos += width
|
||||
return token
|
||||
}
|
||||
|
||||
// updatePosition updates input coordinates based on the consumed text.
|
||||
func (s *Scanner) updatePosition(text string) {
|
||||
width := utf8.RuneCountInString(text)
|
||||
lines := strings.Count(text, "\n")
|
||||
s.row += lines
|
||||
if lines == 0 {
|
||||
s.col += width
|
||||
} else {
|
||||
s.col = utf8.RuneCountInString(text[strings.LastIndex(text, "\n"):])
|
||||
}
|
||||
s.pos += len(text) // while col is a rune index, pos is a byte index
|
||||
}
|
||||
|
||||
// emitToken returns a Token for the string v and updates the scanner position.
|
||||
func (s *Scanner) emitToken(t tokenType, v string) *Token {
|
||||
token := &Token{t, v, s.row, s.col}
|
||||
s.updatePosition(v)
|
||||
return token
|
||||
}
|
||||
|
||||
// emitSimple returns a Token for the string v and updates the scanner
|
||||
// position in a simplified manner.
|
||||
//
|
||||
// The string is known to have only ASCII characters and to not have a newline.
|
||||
func (s *Scanner) emitSimple(t tokenType, v string) *Token {
|
||||
token := &Token{t, v, s.row, s.col}
|
||||
s.col += len(v)
|
||||
s.pos += len(v)
|
||||
return token
|
||||
}
|
||||
|
||||
// emitPrefixOrChar returns a Token for type t if the current position
|
||||
// matches the given prefix. Otherwise it returns a Char token using the
|
||||
// first character from the prefix.
|
||||
//
|
||||
// The prefix is known to have only ASCII characters and to not have a newline.
|
||||
func (s *Scanner) emitPrefixOrChar(t tokenType, prefix string) *Token {
|
||||
if strings.HasPrefix(s.input[s.pos:], prefix) {
|
||||
return s.emitSimple(t, prefix)
|
||||
}
|
||||
return s.emitSimple(TokenChar, string(prefix[0]))
|
||||
}
|
@ -1,28 +1,101 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=code,go,linux,macos,windows
|
||||
|
||||
### Code ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### Go ###
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Vim swap files
|
||||
.*.sw?
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
_testmain.go
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
*.exe
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Code coverage stuff
|
||||
coverage.out
|
||||
# End of https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows
|
||||
|
@ -1,7 +0,0 @@
|
||||
language: go
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
script:
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
- if [[ "$TRAVIS_PULL_REQUEST" = "false" ]]; then $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
|
@ -0,0 +1,42 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
The format of this file is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
but only releases after v1.0.3 properly adhere to it.
|
||||
|
||||
|
||||
## [1.2.0] - 2021-01-27
|
||||
### Added
|
||||
- HSLuv and HPLuv color spaces (#41, #51)
|
||||
- CIE LCh(uv) color space, called `LuvLCh` in code (#51)
|
||||
- JSON and envconfig serialization support for `HexColor` (#42)
|
||||
- `DistanceLinearRGB` (#53)
|
||||
|
||||
### Fixed
|
||||
- RGB to/from XYZ conversion is more accurate (#51)
|
||||
- A bug in `XYZToLuvWhiteRef` that only applied to very small values was fixed (#51)
|
||||
- `BlendHCL` output is clamped so that it's not invalid (#46)
|
||||
- Properly documented `DistanceCIE76` (#40)
|
||||
- Some small godoc fixes
|
||||
|
||||
|
||||
## [1.0.3] - 2019-11-11
|
||||
- Remove SQLMock dependency
|
||||
|
||||
|
||||
## [1.0.2] - 2019-04-07
|
||||
- Fixes SQLMock dependency
|
||||
|
||||
|
||||
## [1.0.1] - 2019-03-24
|
||||
- Adds support for Go Modules
|
||||
|
||||
|
||||
## [1.0.0] - 2018-05-26
|
||||
- API Breaking change in `MakeColor`: instead of `panic`ing when alpha is zero, it now returns a secondary, boolean return value indicating success. See [the color.Color interface](#the-colorcolor-interface) section and [this FAQ entry](#q-why-would-makecolor-ever-fail) for details.
|
||||
|
||||
|
||||
## [0.9.0] - 2018-05-26
|
||||
- Initial version number after having ignored versioning for a long time :)
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,207 @@
|
||||
package colorful
|
||||
|
||||
import "math"
|
||||
|
||||
// Source: https://github.com/hsluv/hsluv-go
|
||||
// Under MIT License
|
||||
// Modified so that Saturation and Luminance are in [0..1] instead of [0..100].
|
||||
|
||||
// HSLuv uses a rounded version of the D65. This has no impact on the final RGB
|
||||
// values, but to keep high levels of accuracy for internal operations and when
|
||||
// comparing to the test values, this modified white reference is used internally.
|
||||
//
|
||||
// See this GitHub thread for details on these values:
|
||||
// https://github.com/hsluv/hsluv/issues/79
|
||||
var hSLuvD65 = [3]float64{0.95045592705167, 1.0, 1.089057750759878}
|
||||
|
||||
func LuvLChToHSLuv(l, c, h float64) (float64, float64, float64) {
|
||||
// [-1..1] but the code expects it to be [-100..100]
|
||||
c *= 100.0
|
||||
l *= 100.0
|
||||
|
||||
var s, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
s = 0.0
|
||||
} else {
|
||||
max = maxChromaForLH(l, h)
|
||||
s = c / max * 100.0
|
||||
}
|
||||
return h, clamp01(s / 100.0), clamp01(l / 100.0)
|
||||
}
|
||||
|
||||
func HSLuvToLuvLCh(h, s, l float64) (float64, float64, float64) {
|
||||
l *= 100.0
|
||||
s *= 100.0
|
||||
|
||||
var c, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
c = 0.0
|
||||
} else {
|
||||
max = maxChromaForLH(l, h)
|
||||
c = max / 100.0 * s
|
||||
}
|
||||
|
||||
// c is [-100..100], but for LCh it's supposed to be almost [-1..1]
|
||||
return clamp01(l / 100.0), c / 100.0, h
|
||||
}
|
||||
|
||||
func LuvLChToHPLuv(l, c, h float64) (float64, float64, float64) {
|
||||
// [-1..1] but the code expects it to be [-100..100]
|
||||
c *= 100.0
|
||||
l *= 100.0
|
||||
|
||||
var s, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
s = 0.0
|
||||
} else {
|
||||
max = maxSafeChromaForL(l)
|
||||
s = c / max * 100.0
|
||||
}
|
||||
return h, s / 100.0, l / 100.0
|
||||
}
|
||||
|
||||
func HPLuvToLuvLCh(h, s, l float64) (float64, float64, float64) {
|
||||
// [-1..1] but the code expects it to be [-100..100]
|
||||
l *= 100.0
|
||||
s *= 100.0
|
||||
|
||||
var c, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
c = 0.0
|
||||
} else {
|
||||
max = maxSafeChromaForL(l)
|
||||
c = max / 100.0 * s
|
||||
}
|
||||
return l / 100.0, c / 100.0, h
|
||||
}
|
||||
|
||||
// HSLuv creates a new Color from values in the HSLuv color space.
|
||||
// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1].
|
||||
//
|
||||
// The returned color values are clamped (using .Clamped), so this will never output
|
||||
// an invalid color.
|
||||
func HSLuv(h, s, l float64) Color {
|
||||
// HSLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB
|
||||
l, u, v := LuvLChToLuv(HSLuvToLuvLCh(h, s, l))
|
||||
return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped()
|
||||
}
|
||||
|
||||
// HPLuv creates a new Color from values in the HPLuv color space.
|
||||
// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1].
|
||||
//
|
||||
// The returned color values are clamped (using .Clamped), so this will never output
|
||||
// an invalid color.
|
||||
func HPLuv(h, s, l float64) Color {
|
||||
// HPLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB
|
||||
l, u, v := LuvLChToLuv(HPLuvToLuvLCh(h, s, l))
|
||||
return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped()
|
||||
}
|
||||
|
||||
// HSLuv returns the Hue, Saturation and Luminance of the color in the HSLuv
|
||||
// color space. Hue in [0..360], a Saturation [0..1], and a Luminance
|
||||
// (lightness) in [0..1].
|
||||
func (col Color) HSLuv() (h, s, l float64) {
|
||||
// sRGB -> Linear RGB -> CIEXYZ -> CIELUV -> LuvLCh -> HSLuv
|
||||
return LuvLChToHSLuv(col.LuvLChWhiteRef(hSLuvD65))
|
||||
}
|
||||
|
||||
// HPLuv returns the Hue, Saturation and Luminance of the color in the HSLuv
|
||||
// color space. Hue in [0..360], a Saturation [0..1], and a Luminance
|
||||
// (lightness) in [0..1].
|
||||
//
|
||||
// Note that HPLuv can only represent pastel colors, and so the Saturation
|
||||
// value could be much larger than 1 for colors it can't represent.
|
||||
func (col Color) HPLuv() (h, s, l float64) {
|
||||
return LuvLChToHPLuv(col.LuvLChWhiteRef(hSLuvD65))
|
||||
}
|
||||
|
||||
// DistanceHSLuv calculates Euclidan distance in the HSLuv colorspace. No idea
|
||||
// how useful this is.
|
||||
//
|
||||
// The Hue value is divided by 100 before the calculation, so that H, S, and L
|
||||
// have the same relative ranges.
|
||||
func (c1 Color) DistanceHSLuv(c2 Color) float64 {
|
||||
h1, s1, l1 := c1.HSLuv()
|
||||
h2, s2, l2 := c2.HSLuv()
|
||||
return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2))
|
||||
}
|
||||
|
||||
// DistanceHPLuv calculates Euclidean distance in the HPLuv colorspace. No idea
|
||||
// how useful this is.
|
||||
//
|
||||
// The Hue value is divided by 100 before the calculation, so that H, S, and L
|
||||
// have the same relative ranges.
|
||||
func (c1 Color) DistanceHPLuv(c2 Color) float64 {
|
||||
h1, s1, l1 := c1.HPLuv()
|
||||
h2, s2, l2 := c2.HPLuv()
|
||||
return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2))
|
||||
}
|
||||
|
||||
var m = [3][3]float64{
|
||||
{3.2409699419045214, -1.5373831775700935, -0.49861076029300328},
|
||||
{-0.96924363628087983, 1.8759675015077207, 0.041555057407175613},
|
||||
{0.055630079696993609, -0.20397695888897657, 1.0569715142428786},
|
||||
}
|
||||
|
||||
const kappa = 903.2962962962963
|
||||
const epsilon = 0.0088564516790356308
|
||||
|
||||
func maxChromaForLH(l, h float64) float64 {
|
||||
hRad := h / 360.0 * math.Pi * 2.0
|
||||
minLength := math.MaxFloat64
|
||||
for _, line := range getBounds(l) {
|
||||
length := lengthOfRayUntilIntersect(hRad, line[0], line[1])
|
||||
if length > 0.0 && length < minLength {
|
||||
minLength = length
|
||||
}
|
||||
}
|
||||
return minLength
|
||||
}
|
||||
|
||||
func getBounds(l float64) [6][2]float64 {
|
||||
var sub2 float64
|
||||
var ret [6][2]float64
|
||||
sub1 := math.Pow(l+16.0, 3.0) / 1560896.0
|
||||
if sub1 > epsilon {
|
||||
sub2 = sub1
|
||||
} else {
|
||||
sub2 = l / kappa
|
||||
}
|
||||
for i := range m {
|
||||
for k := 0; k < 2; k++ {
|
||||
top1 := (284517.0*m[i][0] - 94839.0*m[i][2]) * sub2
|
||||
top2 := (838422.0*m[i][2]+769860.0*m[i][1]+731718.0*m[i][0])*l*sub2 - 769860.0*float64(k)*l
|
||||
bottom := (632260.0*m[i][2]-126452.0*m[i][1])*sub2 + 126452.0*float64(k)
|
||||
ret[i*2+k][0] = top1 / bottom
|
||||
ret[i*2+k][1] = top2 / bottom
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func lengthOfRayUntilIntersect(theta, x, y float64) (length float64) {
|
||||
length = y / (math.Sin(theta) - x*math.Cos(theta))
|
||||
return
|
||||
}
|
||||
|
||||
func maxSafeChromaForL(l float64) float64 {
|
||||
minLength := math.MaxFloat64
|
||||
for _, line := range getBounds(l) {
|
||||
m1 := line[0]
|
||||
b1 := line[1]
|
||||
x := intersectLineLine(m1, b1, -1.0/m1, 0.0)
|
||||
dist := distanceFromPole(x, b1+x*m1)
|
||||
if dist < minLength {
|
||||
minLength = dist
|
||||
}
|
||||
}
|
||||
return minLength
|
||||
}
|
||||
|
||||
func intersectLineLine(x1, y1, x2, y2 float64) float64 {
|
||||
return (y1 - y2) / (x2 - x1)
|
||||
}
|
||||
|
||||
func distanceFromPole(x, y float64) float64 {
|
||||
return math.Sqrt(math.Pow(x, 2.0) + math.Pow(y, 2.0))
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
module github.com/mattn/go-runewidth
|
||||
|
||||
go 1.9
|
||||
|
||||
require github.com/rivo/uniseg v0.1.0
|
||||
|
@ -0,0 +1,2 @@
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
@ -1,15 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# goland idea folder
|
||||
*.idea
|
@ -1,7 +1,6 @@
|
||||
1. John Graham-Cumming http://jgc.org/
|
||||
1. Mohammad Gufran https://github.com/Gufran
|
||||
1. Steven Gutzwiller https://github.com/StevenGutzwiller
|
||||
1. Andrew Krasichkov @buglloc https://github.com/buglloc
|
||||
1. John Graham-Cumming http://jgc.org/
|
||||
1. Mike Samuel mikesamuel@gmail.com
|
||||
1. Dmitri Shuralyov shurcooL@gmail.com
|
||||
1. https://github.com/opennota
|
||||
1. https://github.com/opennota
|
||||
1. https://github.com/Gufran
|
@ -1,8 +1,2 @@
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
|
||||
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue