mirror of
https://github.com/grafana/grafana.git
synced 2025-12-20 19:44:55 +08:00
Compare commits
44 Commits
docs/updat
...
6.1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf9cb45fe8 | ||
|
|
44c5c0e0a6 | ||
|
|
b658b3e13e | ||
|
|
62bd94949b | ||
|
|
02222a753a | ||
|
|
c9e67140cc | ||
|
|
e097fb1166 | ||
|
|
7b86c393c0 | ||
|
|
fef1733b0e | ||
|
|
7219633952 | ||
|
|
2926f9b0b1 | ||
|
|
db39c105ed | ||
|
|
2985098f8e | ||
|
|
bd42db2743 | ||
|
|
c4222bb65a | ||
|
|
9504db83bf | ||
|
|
0992fbc4be | ||
|
|
1f35ce691c | ||
|
|
be84bffa57 | ||
|
|
a2236de67f | ||
|
|
de8f6ac4b1 | ||
|
|
0c7d43b999 | ||
|
|
0974663079 | ||
|
|
9eba279b8d | ||
|
|
da0302f15e | ||
|
|
eff01d2b54 | ||
|
|
cbc515b22c | ||
|
|
638d49dd6e | ||
|
|
9d452256bf | ||
|
|
f941f25831 | ||
|
|
b585fdec6f | ||
|
|
e6c639f6f0 | ||
|
|
03346b6f6f | ||
|
|
9a575f93ad | ||
|
|
21957ad515 | ||
|
|
bd93aad63d | ||
|
|
8ae5980c23 | ||
|
|
eb38581dc1 | ||
|
|
be217d8c0e | ||
|
|
dfbc3bfb1f | ||
|
|
a6c8cd7f3a | ||
|
|
56e4032db3 | ||
|
|
944e526eb9 | ||
|
|
0d6db7e6b8 |
4
.browserslistrc
Normal file
4
.browserslistrc
Normal file
@@ -0,0 +1,4 @@
|
||||
>1%,
|
||||
Chrome > 20
|
||||
last 4 versions,
|
||||
Firefox ESR
|
||||
@@ -322,7 +322,7 @@ jobs:
|
||||
|
||||
deploy-enterprise-master:
|
||||
docker:
|
||||
- image: grafana/grafana-ci-deploy:1.2.1
|
||||
- image: grafana/grafana-ci-deploy:1.2.2
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
@@ -347,7 +347,7 @@ jobs:
|
||||
|
||||
deploy-enterprise-release:
|
||||
docker:
|
||||
- image: grafana/grafana-ci-deploy:1.2.1
|
||||
- image: grafana/grafana-ci-deploy:1.2.2
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@@ -380,7 +380,7 @@ jobs:
|
||||
|
||||
deploy-master:
|
||||
docker:
|
||||
- image: grafana/grafana-ci-deploy:1.2.1
|
||||
- image: grafana/grafana-ci-deploy:1.2.2
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
@@ -411,7 +411,7 @@ jobs:
|
||||
|
||||
deploy-release:
|
||||
docker:
|
||||
- image: grafana/grafana-ci-deploy:1.2.1
|
||||
- image: grafana/grafana-ci-deploy:1.2.2
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
|
||||
21
Gopkg.lock
generated
21
Gopkg.lock
generated
@@ -250,6 +250,23 @@
|
||||
revision = "1f39c590c64924f358c0d89016ac9b2bb84e9125"
|
||||
version = "v0.7.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6a7159c5f8f8826207545407a458b43ab6599c1c8a2271465c2e979ecea1dcd4"
|
||||
name = "github.com/gobwas/glob"
|
||||
packages = [
|
||||
".",
|
||||
"compiler",
|
||||
"match",
|
||||
"syntax",
|
||||
"syntax/ast",
|
||||
"syntax/lexer",
|
||||
"util/runes",
|
||||
"util/strings",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "5ccd90ef52e1e632236f7326478d4faa74f99438"
|
||||
version = "v0.2.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ffbb19fb66f140b5ea059428d1f84246a055d1bc3d9456c1e5c3d143611f03d0"
|
||||
@@ -885,6 +902,7 @@
|
||||
"github.com/aws/aws-sdk-go/service/sts",
|
||||
"github.com/benbjohnson/clock",
|
||||
"github.com/bmizerany/assert",
|
||||
"github.com/bradfitz/gomemcache/memcache",
|
||||
"github.com/codegangsta/cli",
|
||||
"github.com/davecgh/go-spew/spew",
|
||||
"github.com/denisenkom/go-mssqldb",
|
||||
@@ -900,6 +918,7 @@
|
||||
"github.com/go-stack/stack",
|
||||
"github.com/go-xorm/core",
|
||||
"github.com/go-xorm/xorm",
|
||||
"github.com/gobwas/glob",
|
||||
"github.com/gorilla/websocket",
|
||||
"github.com/gosimple/slug",
|
||||
"github.com/grafana/grafana-plugin-model/go/datasource",
|
||||
@@ -915,7 +934,6 @@
|
||||
"github.com/opentracing/opentracing-go/ext",
|
||||
"github.com/opentracing/opentracing-go/log",
|
||||
"github.com/patrickmn/go-cache",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/prometheus/client_golang/api",
|
||||
"github.com/prometheus/client_golang/api/prometheus/v1",
|
||||
"github.com/prometheus/client_golang/prometheus",
|
||||
@@ -937,6 +955,7 @@
|
||||
"gopkg.in/ldap.v3",
|
||||
"gopkg.in/macaron.v1",
|
||||
"gopkg.in/mail.v2",
|
||||
"gopkg.in/redis.v2",
|
||||
"gopkg.in/square/go-jose.v2",
|
||||
"gopkg.in/yaml.v2",
|
||||
]
|
||||
|
||||
1
build.go
1
build.go
@@ -361,6 +361,7 @@ func createPackage(options linuxPackageOptions) {
|
||||
fmt.Printf("pkgArch is set to '%s', generated arch is '%s'\n", pkgArch, options.packageArch)
|
||||
if pkgArch == "armv6" {
|
||||
name += "-rpi"
|
||||
args = append(args, "--replaces", "grafana")
|
||||
}
|
||||
args = append(args, "--name", name)
|
||||
|
||||
|
||||
55
docs/sources/guides/whats-new-in-v6-1.md
Normal file
55
docs/sources/guides/whats-new-in-v6-1.md
Normal file
@@ -0,0 +1,55 @@
|
||||
+++
|
||||
title = "What's New in Grafana v6.1"
|
||||
description = "Feature & improvement highlights for Grafana v6.1"
|
||||
keywords = ["grafana", "new", "documentation", "6.1"]
|
||||
type = "docs"
|
||||
[menu.docs]
|
||||
name = "Version 6.1"
|
||||
identifier = "v6.1"
|
||||
parent = "whatsnew"
|
||||
weight = -12
|
||||
+++
|
||||
|
||||
# What's New in Grafana v6.1
|
||||
|
||||
## Highlights
|
||||
|
||||
### Ad hoc Filtering for Prometheus
|
||||
|
||||
{{< imgbox max-width="30%" img="/img/docs/v61/prometheus-ad-hoc.gif" caption="Ad-hoc filters variable for Prometheus" >}}
|
||||
|
||||
The ad hoc filter feature allows you to create new key/value filters on the fly with autocomplete for both key and values. The filter condition is then automatically applied to all queries on the dashboard. This makes it easier to explore your data in a dashboard without changing queries and without having to add new template variables.
|
||||
|
||||
Other timeseries databases with label-based query languages have had this feature for a while. Recently Prometheus added support for fetching label names from their API and thanks to [Mitsuhiro Tanda](https://github.com/mtanda) implementing it in Grafana, the Prometheus datasource finally supports ad hoc filtering.
|
||||
|
||||
Support for fetching a list of label names was released in Prometheus v2.6.0 so that is a requirement for this feature to work in Grafana.
|
||||
|
||||
### Permissions: Editors can own dashboards, folders and teams they create
|
||||
|
||||
When the dashboard folders feature and permissions system was released in Grafana 5.0, users with the editor role were not allowed to administrate dashboards, folders or teams. In the 6.1 release, we have added a config option so that by default editors are admins for any Dashboard, Folder or Team they create.
|
||||
|
||||
This feature also adds a new Team permission that can be assigned to any user with the editor or viewer role and lets that user add other users to the Team.
|
||||
|
||||
We believe that this is more in line with the Grafana philosophy, as it will allow teams to be more self-organizing. This option will be made permanent if it gets positive feedback from the community so let us know what you think in the [issue on GitHub](https://github.com/grafana/grafana/issues/15590).
|
||||
|
||||
To turn this feature on add the following [config option](/installation/configuration/#editors-can-admin) to your Grafana ini file in the `users` section and then restart the Grafana server:
|
||||
|
||||
```ini
|
||||
[users]
|
||||
editors_can_admin = true
|
||||
```
|
||||
|
||||
### Minor Features and Fixes
|
||||
|
||||
This release contains a lot of small features and fixes:
|
||||
|
||||
- A new keyboard shortcut `d l` toggles all Graph legends in a dashboard.
|
||||
- A small bug fix for Elasticsearch - template variables in the alias field now work properly.
|
||||
- Some new capabilities have been added for datasource plugins that will be of interest to plugin authors:
|
||||
- a new oauth pass-through option.
|
||||
- it is now possible to add user details to requests sent to the dataproxy.
|
||||
- Heatmap and Explore fixes.
|
||||
|
||||
Checkout the [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md) file for a complete list of new features, changes, and bug fixes.
|
||||
|
||||
A huge thanks to our community for all the reported issues, bug fixes and feedback.
|
||||
@@ -152,6 +152,7 @@ Content-Type: application/json
|
||||
PUT /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
@@ -170,7 +171,7 @@ Content-Type: application/json
|
||||
Deletes an existing notification channel identified by uid.
|
||||
|
||||
**Example Request**:
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /api/alert-notifications/uid/team-a-email-notifier HTTP/1.1
|
||||
Accept: application/json
|
||||
@@ -198,6 +199,7 @@ Content-Type: application/json
|
||||
DELETE /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -217,7 +219,7 @@ Content-Type: application/json
|
||||
|
||||
**Example Request**:
|
||||
|
||||
**Example Request**:
|
||||
```http
|
||||
POST /api/alert-notifications/test HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
@@ -247,7 +249,7 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"uid": "cIBgcSjkk",
|
||||
"uid": "new-alert-notification", // optional
|
||||
"name": "new alert notification", //Required
|
||||
"type": "email", //Required
|
||||
"isDefault": false,
|
||||
@@ -267,7 +269,7 @@ Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"uid": "cIBgcSjkk",
|
||||
"uid": "new-alert-notification",
|
||||
"name": "new alert notification",
|
||||
"type": "email",
|
||||
"isDefault": false,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"company": "Grafana Labs"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "6.1.0-pre",
|
||||
"version": "6.1.6",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git"
|
||||
@@ -185,7 +185,7 @@
|
||||
"eventemitter3": "^2.0.3",
|
||||
"file-saver": "^1.3.3",
|
||||
"immutable": "^3.8.2",
|
||||
"jquery": "^3.2.1",
|
||||
"jquery": "3.4.0",
|
||||
"lodash": "^4.17.10",
|
||||
"moment": "^2.22.2",
|
||||
"mousetrap": "^1.6.0",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@types/react-color": "^2.14.0",
|
||||
"classnames": "^2.2.5",
|
||||
"d3": "^5.7.0",
|
||||
"jquery": "^3.2.1",
|
||||
"jquery": "3.4.0",
|
||||
"lodash": "^4.17.10",
|
||||
"moment": "^2.22.2",
|
||||
"papaparse": "^4.6.3",
|
||||
|
||||
@@ -72,7 +72,7 @@ export class Input extends PureComponent<Props> {
|
||||
const inputElementProps = this.populateEventPropsWithStatus(restProps, validationEvents);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<input {...inputElementProps} className={inputClassName} />
|
||||
{error && !hideErrorMessage && <span>{error}</span>}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Input renders correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="gf-form-input"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import uniqueId from 'lodash/uniqueId';
|
||||
import { Input } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
label: string;
|
||||
@@ -39,7 +38,7 @@ export class Switch extends PureComponent<Props, State> {
|
||||
<label htmlFor={labelId} className={`gf-form gf-form-switch-container ${className || ''}`}>
|
||||
{label && <div className={labelClassName}>{label}</div>}
|
||||
<div className={switchClassName}>
|
||||
<Input id={labelId} type="checkbox" checked={checked} onChange={this.internalOnChange} />
|
||||
<input id={labelId} type="checkbox" checked={checked} onChange={this.internalOnChange} />
|
||||
<span className="gf-form-switch__slider" />
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -148,10 +148,10 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"grayBlue": "#212327",
|
||||
"greenBase": "#299c46",
|
||||
"greenShade": "#23843b",
|
||||
"headingColor": "#e3e3e3",
|
||||
"headingColor": "#d8d9da",
|
||||
"inputBlack": "#09090b",
|
||||
"link": "#e3e3e3",
|
||||
"linkDisabled": "#e3e3e3",
|
||||
"link": "#d8d9da",
|
||||
"linkDisabled": "#8e8e8e",
|
||||
"linkExternal": "#33b5e5",
|
||||
"linkHover": "#ffffff",
|
||||
"online": "#299c46",
|
||||
@@ -178,8 +178,8 @@ exports[`Render should render with base threshold 1`] = `
|
||||
},
|
||||
"name": "Grafana Dark",
|
||||
"panelPadding": Object {
|
||||
"horizontal": 10,
|
||||
"vertical": 5,
|
||||
"horizontal": 16,
|
||||
"vertical": 8,
|
||||
},
|
||||
"spacing": Object {
|
||||
"d": "14px",
|
||||
@@ -305,10 +305,10 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"grayBlue": "#212327",
|
||||
"greenBase": "#299c46",
|
||||
"greenShade": "#23843b",
|
||||
"headingColor": "#e3e3e3",
|
||||
"headingColor": "#d8d9da",
|
||||
"inputBlack": "#09090b",
|
||||
"link": "#e3e3e3",
|
||||
"linkDisabled": "#e3e3e3",
|
||||
"link": "#d8d9da",
|
||||
"linkDisabled": "#8e8e8e",
|
||||
"linkExternal": "#33b5e5",
|
||||
"linkHover": "#ffffff",
|
||||
"online": "#299c46",
|
||||
@@ -335,8 +335,8 @@ exports[`Render should render with base threshold 1`] = `
|
||||
},
|
||||
"name": "Grafana Dark",
|
||||
"panelPadding": Object {
|
||||
"horizontal": 10,
|
||||
"vertical": 5,
|
||||
"horizontal": 16,
|
||||
"vertical": 8,
|
||||
},
|
||||
"spacing": Object {
|
||||
"d": "14px",
|
||||
@@ -465,7 +465,13 @@ exports[`Render should render with base threshold 1`] = `
|
||||
type="text"
|
||||
value="Base"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="gf-form-input"
|
||||
readOnly={true}
|
||||
|
||||
@@ -197,7 +197,9 @@ $side-menu-width: 60px;
|
||||
|
||||
// dashboard
|
||||
$dashboard-padding: $space-md;
|
||||
$panel-padding: 0 $space-md $space-sm $space-md;
|
||||
$panel-padding: 0 ${theme.panelPadding.horizontal}px ${theme.panelPadding.vertical}px ${
|
||||
theme.panelPadding.horizontal
|
||||
}px;
|
||||
|
||||
// tabs
|
||||
$tabs-padding: 10px 15px 9px;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import tinycolor from 'tinycolor2';
|
||||
import defaultTheme from './default';
|
||||
import { GrafanaTheme, GrafanaThemeType } from '../types/theme';
|
||||
|
||||
@@ -66,11 +65,11 @@ const darkTheme: GrafanaTheme = {
|
||||
textWeak: basicColors.gray2,
|
||||
textEmphasis: basicColors.gray5,
|
||||
textFaint: basicColors.dark5,
|
||||
link: new tinycolor(basicColors.white).darken(11).toString(),
|
||||
linkDisabled: new tinycolor(basicColors.white).darken(11).toString(),
|
||||
link: basicColors.gray4,
|
||||
linkDisabled: basicColors.gray2,
|
||||
linkHover: basicColors.white,
|
||||
linkExternal: basicColors.blue,
|
||||
headingColor: new tinycolor(basicColors.white).darken(11).toString(),
|
||||
headingColor: basicColors.gray4,
|
||||
},
|
||||
background: {
|
||||
dropdown: basicColors.dark3,
|
||||
|
||||
@@ -67,8 +67,8 @@ const theme: GrafanaThemeCommons = {
|
||||
},
|
||||
},
|
||||
panelPadding: {
|
||||
horizontal: 10,
|
||||
vertical: 5,
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
},
|
||||
zIndex: {
|
||||
dropdown: '1000',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import tinycolor from 'tinycolor2';
|
||||
import defaultTheme from './default';
|
||||
import { GrafanaTheme, GrafanaThemeType } from '../types/theme';
|
||||
|
||||
@@ -65,11 +64,11 @@ const lightTheme: GrafanaTheme = {
|
||||
text: basicColors.gray1,
|
||||
textStrong: basicColors.dark2,
|
||||
textWeak: basicColors.gray2,
|
||||
textEmphasis: basicColors.gray5,
|
||||
textEmphasis: basicColors.dark5,
|
||||
textFaint: basicColors.dark4,
|
||||
link: basicColors.gray1,
|
||||
linkDisabled: new tinycolor(basicColors.gray1).lighten(30).toString(),
|
||||
linkHover: new tinycolor(basicColors.gray1).darken(20).toString(),
|
||||
linkDisabled: basicColors.gray3,
|
||||
linkHover: basicColors.dark1,
|
||||
linkExternal: basicColors.blueLight,
|
||||
headingColor: basicColors.gray1,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,9 @@ export interface DisplayValue {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export type DecimalCount = number | null | undefined;
|
||||
|
||||
export interface DecimalInfo {
|
||||
decimals: number;
|
||||
scaledDecimals: number;
|
||||
decimals: DecimalCount;
|
||||
scaledDecimals: DecimalCount;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,12 @@ describe('Format value', () => {
|
||||
expect(instance(value).text).toEqual('0.02');
|
||||
});
|
||||
|
||||
it('should use override decimals', () => {
|
||||
const value = 100030303;
|
||||
const instance = getDisplayProcessor({ decimals: 2, unit: 'bytes' });
|
||||
expect(instance(value).text).toEqual('95.40 MiB');
|
||||
});
|
||||
|
||||
it('should return mapped value if there are matching value mappings', () => {
|
||||
const valueMappings: ValueMapping[] = [
|
||||
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
||||
@@ -182,5 +188,6 @@ describe('getDecimalsForValue()', () => {
|
||||
expect(getDecimalsForValue(20000)).toEqual({ decimals: 0, scaledDecimals: -2 });
|
||||
expect(getDecimalsForValue(200000)).toEqual({ decimals: 0, scaledDecimals: -3 });
|
||||
expect(getDecimalsForValue(200000000)).toEqual({ decimals: 0, scaledDecimals: -6 });
|
||||
expect(getDecimalsForValue(100, 2)).toEqual({ decimals: 2, scaledDecimals: null });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,8 +8,15 @@ import { getMappedValue } from './valueMappings';
|
||||
import { getColorFromHexRgbOrName } from './namedColorsPalette';
|
||||
|
||||
// Types
|
||||
import { Threshold, ValueMapping, DecimalInfo, DisplayValue, GrafanaTheme, GrafanaThemeType } from '../types';
|
||||
import { DecimalCount } from './valueFormats/valueFormats';
|
||||
import {
|
||||
Threshold,
|
||||
ValueMapping,
|
||||
DecimalInfo,
|
||||
DisplayValue,
|
||||
GrafanaTheme,
|
||||
GrafanaThemeType,
|
||||
DecimalCount,
|
||||
} from '../types';
|
||||
|
||||
export type DisplayProcessor = (value: any) => DisplayValue;
|
||||
|
||||
@@ -69,18 +76,7 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
|
||||
|
||||
if (!isNaN(numeric)) {
|
||||
if (shouldFormat && !_.isBoolean(value)) {
|
||||
let decimals;
|
||||
let scaledDecimals = 0;
|
||||
|
||||
if (!options.decimals) {
|
||||
const decimalInfo = getDecimalsForValue(value);
|
||||
|
||||
decimals = decimalInfo.decimals;
|
||||
scaledDecimals = decimalInfo.scaledDecimals;
|
||||
} else {
|
||||
decimals = options.decimals;
|
||||
}
|
||||
|
||||
const { decimals, scaledDecimals } = getDecimalsForValue(value, options.decimals);
|
||||
text = formatFunc(numeric, decimals, scaledDecimals, options.isUtc);
|
||||
}
|
||||
if (thresholds && thresholds.length > 0) {
|
||||
@@ -159,7 +155,12 @@ export function getColorFromThreshold(value: number, thresholds: Threshold[], th
|
||||
return getColorFromHexRgbOrName(thresholds[0].color, themeType);
|
||||
}
|
||||
|
||||
export function getDecimalsForValue(value: number): DecimalInfo {
|
||||
export function getDecimalsForValue(value: number, decimalOverride?: DecimalCount): DecimalInfo {
|
||||
if (_.isNumber(decimalOverride)) {
|
||||
// It's important that scaledDecimals is null here
|
||||
return { decimals: decimalOverride, scaledDecimals: null };
|
||||
}
|
||||
|
||||
const delta = value / 2;
|
||||
let dec = -Math.floor(Math.log(delta) / Math.LN10);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { toFixed, DecimalCount } from './valueFormats';
|
||||
import { toFixed } from './valueFormats';
|
||||
import { DecimalCount } from '../../types';
|
||||
|
||||
export function toPercent(size: number, decimals: DecimalCount) {
|
||||
if (size === null) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { toFixed, toFixedScaled, DecimalCount } from './valueFormats';
|
||||
import { toFixed, toFixedScaled } from './valueFormats';
|
||||
import { DecimalCount } from '../../types';
|
||||
import moment from 'moment';
|
||||
|
||||
interface IntervalsInSeconds {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { scaledUnits, DecimalCount } from './valueFormats';
|
||||
import { scaledUnits } from './valueFormats';
|
||||
import { DecimalCount } from '../../types';
|
||||
|
||||
export function currency(symbol: string) {
|
||||
const units = ['', 'K', 'M', 'B', 'T'];
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { toFixed, getValueFormat } from './valueFormats';
|
||||
|
||||
describe('valueFormats', () => {
|
||||
describe('toFixed and negative decimals', () => {
|
||||
it('should treat as zero decimals', () => {
|
||||
const str = toFixed(186.123, -2);
|
||||
expect(str).toBe('186');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ms format when scaled decimals is null do not use it', () => {
|
||||
it('should use specified decimals', () => {
|
||||
const str = getValueFormat('ms')(10000086.123, 1, null);
|
||||
expect(str).toBe('2.8 hour');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbytes format when scaled decimals is null do not use it', () => {
|
||||
it('should use specified decimals', () => {
|
||||
const str = getValueFormat('kbytes')(10000000, 3, null);
|
||||
expect(str).toBe('9.537 GiB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deckbytes format when scaled decimals is null do not use it', () => {
|
||||
it('should use specified decimals', () => {
|
||||
const str = getValueFormat('deckbytes')(10000000, 3, null);
|
||||
expect(str).toBe('10.000 GB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ms format when scaled decimals is 0', () => {
|
||||
it('should use scaledDecimals and add 3', () => {
|
||||
const str = getValueFormat('ms')(1200, 0, 0);
|
||||
expect(str).toBe('1.200 s');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import { getCategories } from './categories';
|
||||
|
||||
export type DecimalCount = number | null | undefined;
|
||||
import { DecimalCount } from '../../types';
|
||||
|
||||
export type ValueFormatter = (
|
||||
value: number,
|
||||
@@ -57,17 +56,15 @@ export function toFixed(value: number, decimals?: DecimalCount): string {
|
||||
|
||||
export function toFixedScaled(
|
||||
value: number,
|
||||
decimals?: DecimalCount,
|
||||
scaledDecimals?: DecimalCount,
|
||||
additionalDecimals?: DecimalCount,
|
||||
decimals: DecimalCount,
|
||||
scaledDecimals: DecimalCount,
|
||||
additionalDecimals: number,
|
||||
ext?: string
|
||||
) {
|
||||
if (scaledDecimals) {
|
||||
if (additionalDecimals) {
|
||||
return toFixed(value, scaledDecimals + additionalDecimals) + ext;
|
||||
} else {
|
||||
return toFixed(value, scaledDecimals) + ext;
|
||||
}
|
||||
if (scaledDecimals === null || scaledDecimals === undefined) {
|
||||
return toFixed(value, decimals) + ext;
|
||||
} else {
|
||||
return toFixed(value, scaledDecimals + additionalDecimals) + ext;
|
||||
}
|
||||
|
||||
return toFixed(value, decimals) + ext;
|
||||
|
||||
@@ -261,6 +261,10 @@ func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationComma
|
||||
return Error(500, "Failed to update alert notification", err)
|
||||
}
|
||||
|
||||
if cmd.Result == nil {
|
||||
return Error(404, "Alert notification not found", nil)
|
||||
}
|
||||
|
||||
return JSON(200, dtos.NewAlertNotification(cmd.Result))
|
||||
}
|
||||
|
||||
@@ -272,6 +276,10 @@ func UpdateAlertNotificationByUID(c *m.ReqContext, cmd m.UpdateAlertNotification
|
||||
return Error(500, "Failed to update alert notification", err)
|
||||
}
|
||||
|
||||
if cmd.Result == nil {
|
||||
return Error(404, "Alert notification not found", nil)
|
||||
}
|
||||
|
||||
return JSON(200, dtos.NewAlertNotification(cmd.Result))
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ func populateDashboardsByTag(orgID int64, signedInUser *m.SignedInUser, dashboar
|
||||
Slug: item.Slug,
|
||||
Title: item.Title,
|
||||
Uri: item.Uri,
|
||||
Url: m.GetDashboardUrl(item.Uid, item.Slug),
|
||||
Url: item.Url,
|
||||
Order: dashboardTagOrder[tag],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
_ "github.com/gobwas/glob"
|
||||
_ "gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ type AlertNotification struct {
|
||||
}
|
||||
|
||||
type CreateAlertNotificationCommand struct {
|
||||
Uid string `json:"-"`
|
||||
Uid string `json:"uid"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Type string `json:"type" binding:"Required"`
|
||||
SendReminder bool `json:"sendReminder"`
|
||||
@@ -54,6 +54,7 @@ type CreateAlertNotificationCommand struct {
|
||||
|
||||
type UpdateAlertNotificationCommand struct {
|
||||
Id int64 `json:"id" binding:"Required"`
|
||||
Uid string `json:"uid"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Type string `json:"type" binding:"Required"`
|
||||
SendReminder bool `json:"sendReminder"`
|
||||
@@ -68,6 +69,7 @@ type UpdateAlertNotificationCommand struct {
|
||||
|
||||
type UpdateAlertNotificationWithUidCommand struct {
|
||||
Uid string `json:"-"`
|
||||
NewUid string `json:"uid"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Type string `json:"type" binding:"Required"`
|
||||
SendReminder bool `json:"sendReminder"`
|
||||
|
||||
@@ -42,7 +42,8 @@ func (rs *RenderingService) renderViaPhantomJS(ctx context.Context, opts Opts) (
|
||||
|
||||
cmdArgs := []string{
|
||||
"--ignore-ssl-errors=true",
|
||||
"--web-security=false",
|
||||
"--web-security=true",
|
||||
"--local-url-access=false",
|
||||
phantomDebugArg,
|
||||
scriptPath,
|
||||
fmt.Sprintf("url=%v", url),
|
||||
|
||||
@@ -317,6 +317,10 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error {
|
||||
current.SendReminder = cmd.SendReminder
|
||||
current.DisableResolveMessage = cmd.DisableResolveMessage
|
||||
|
||||
if cmd.Uid != "" {
|
||||
current.Uid = cmd.Uid
|
||||
}
|
||||
|
||||
if current.SendReminder {
|
||||
if cmd.Frequency == "" {
|
||||
return m.ErrNotificationFrequencyNotFound
|
||||
@@ -356,8 +360,13 @@ func UpdateAlertNotificationWithUid(cmd *m.UpdateAlertNotificationWithUidCommand
|
||||
return fmt.Errorf("Cannot update, alert notification uid %s doesn't exist", cmd.Uid)
|
||||
}
|
||||
|
||||
if cmd.NewUid == "" {
|
||||
cmd.NewUid = cmd.Uid
|
||||
}
|
||||
|
||||
updateNotification := &m.UpdateAlertNotificationCommand{
|
||||
Id: current.Id,
|
||||
Uid: cmd.NewUid,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
SendReminder: cmd.SendReminder,
|
||||
@@ -373,6 +382,8 @@ func UpdateAlertNotificationWithUid(cmd *m.UpdateAlertNotificationWithUidCommand
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = updateNotification.Result
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
legendFormat = regexp.MustCompile(`\[\[(\w+?)*\]\]*|\$\s*(\w+?)*`)
|
||||
legendFormat = regexp.MustCompile(`\[\[(\w+)(\.\w+)*\]\]*|\$\s*(\w+?)*`)
|
||||
}
|
||||
|
||||
func (rp *ResponseParser) Parse(response *Response, query *Query) *tsdb.QueryResult {
|
||||
|
||||
@@ -75,7 +75,10 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
{
|
||||
Name: "cpu.upc",
|
||||
Columns: []string{"time", "mean", "sum"},
|
||||
Tags: map[string]string{"datacenter": "America"},
|
||||
Tags: map[string]string{
|
||||
"datacenter": "America",
|
||||
"dc.region.name": "Northeast",
|
||||
},
|
||||
Values: [][]interface{}{
|
||||
{json.Number("111"), json.Number("222"), json.Number("333")},
|
||||
},
|
||||
@@ -159,6 +162,13 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
|
||||
So(result.Series[0].Name, ShouldEqual, "alias America")
|
||||
})
|
||||
|
||||
Convey("tag alias with periods", func() {
|
||||
query := &Query{Alias: "alias [[tag_dc.region.name]]"}
|
||||
result := parser.Parse(response, query)
|
||||
|
||||
So(result.Series[0].Name, ShouldEqual, "alias Northeast")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,4 +22,13 @@ describe('ticks', () => {
|
||||
expect(dec.scaledDecimals).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStringPrecision()', () => {
|
||||
it('"3.12" should return 2', () => {
|
||||
expect(ticks.getStringPrecision('3.12')).toBe(2);
|
||||
});
|
||||
it('"asd" should return 0', () => {
|
||||
expect(ticks.getStringPrecision('asd.asd')).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -141,6 +141,7 @@ export function buildQueryTransaction(
|
||||
__interval: { text: interval, value: interval },
|
||||
__interval_ms: { text: intervalMs, value: intervalMs },
|
||||
},
|
||||
maxDataPoints: queryOptions.maxDataPoints,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -201,6 +201,10 @@ export function getPrecision(num: number): number {
|
||||
* Get decimal precision of number stored as a string ("3.14" => 2)
|
||||
*/
|
||||
export function getStringPrecision(num: string): number {
|
||||
if (isNaN((num as unknown) as number)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const dotIndex = num.indexOf('.');
|
||||
if (dotIndex === -1) {
|
||||
return 0;
|
||||
|
||||
@@ -136,10 +136,16 @@ export class DataPanel extends Component<Props, State> {
|
||||
try {
|
||||
const ds = await this.dataSourceSrv.get(datasource, scopedVars);
|
||||
|
||||
// TODO interpolate variables
|
||||
const minInterval = this.props.minInterval || ds.interval;
|
||||
const intervalRes = kbn.calculateInterval(timeRange, widthPixels, minInterval);
|
||||
|
||||
// make shallow copy of scoped vars,
|
||||
// and add built in variables interval and interval_ms
|
||||
const scopedVarsWithInterval = Object.assign({}, scopedVars, {
|
||||
__interval: { text: intervalRes.interval, value: intervalRes.interval },
|
||||
__interval_ms: { text: intervalRes.intervalMs.toString(), value: intervalRes.intervalMs },
|
||||
});
|
||||
|
||||
const queryOptions: DataQueryOptions = {
|
||||
timezone: 'browser',
|
||||
panelId: panelId,
|
||||
@@ -150,7 +156,7 @@ export class DataPanel extends Component<Props, State> {
|
||||
intervalMs: intervalRes.intervalMs,
|
||||
targets: queries,
|
||||
maxDataPoints: maxDataPoints || widthPixels,
|
||||
scopedVars: scopedVars || {},
|
||||
scopedVars: scopedVarsWithInterval,
|
||||
cacheTimeout: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
||||
// give angular time to compile
|
||||
setTimeout(() => {
|
||||
this.setState({ hasTextEditMode: !!this.angularScope.toggleEditorMode });
|
||||
}, 10);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
onToggleCollapse = () => {
|
||||
|
||||
@@ -39,14 +39,20 @@ export class QueryInspector extends PureComponent<Props, State> {
|
||||
|
||||
componentDidMount() {
|
||||
const { panel } = this.props;
|
||||
panel.events.on('refresh', this.onPanelRefresh);
|
||||
|
||||
appEvents.on('ds-request-response', this.onDataSourceResponse);
|
||||
appEvents.on('ds-request-error', this.onRequestError);
|
||||
|
||||
panel.events.on('refresh', this.onPanelRefresh);
|
||||
panel.refresh();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { panel } = this.props;
|
||||
|
||||
appEvents.off('ds-request-response', this.onDataSourceResponse);
|
||||
appEvents.on('ds-request-error', this.onRequestError);
|
||||
|
||||
panel.events.off('refresh', this.onPanelRefresh);
|
||||
}
|
||||
|
||||
@@ -73,6 +79,10 @@ export class QueryInspector extends PureComponent<Props, State> {
|
||||
}));
|
||||
};
|
||||
|
||||
onRequestError = (err: any) => {
|
||||
this.onDataSourceResponse(err);
|
||||
};
|
||||
|
||||
onDataSourceResponse = (response: any = {}) => {
|
||||
if (this.state.isMocking) {
|
||||
this.handleMocking(response);
|
||||
|
||||
@@ -199,6 +199,15 @@ export interface QueriesImportedPayload {
|
||||
queries: DataQuery[];
|
||||
}
|
||||
|
||||
export interface LoadExploreDataSourcesPayload {
|
||||
exploreId: ExploreId;
|
||||
exploreDatasources: DataSourceSelectItem[];
|
||||
}
|
||||
|
||||
export interface RunQueriesPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a query row after the row with the given index.
|
||||
*/
|
||||
@@ -323,7 +332,8 @@ export const queryTransactionSuccessAction = actionCreatorFactory<QueryTransacti
|
||||
* Remove query row of the given index, as well as associated query results.
|
||||
*/
|
||||
export const removeQueryRowAction = actionCreatorFactory<RemoveQueryRowPayload>('explore/REMOVE_QUERY_ROW').create();
|
||||
export const runQueriesAction = noPayloadActionCreatorFactory('explore/RUN_QUERIES').create();
|
||||
|
||||
export const runQueriesAction = actionCreatorFactory<RunQueriesPayload>('explore/RUN_QUERIES').create();
|
||||
|
||||
/**
|
||||
* Start a scan for more results using the given scanner.
|
||||
|
||||
@@ -513,6 +513,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
|
||||
supportsGraph,
|
||||
supportsLogs,
|
||||
supportsTable,
|
||||
containerWidth,
|
||||
} = getState().explore[exploreId];
|
||||
|
||||
if (!hasNonEmptyQuery(queries)) {
|
||||
@@ -525,7 +526,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
|
||||
// but we're using the datasource interval limit for now
|
||||
const interval = datasourceInstance.interval;
|
||||
|
||||
dispatch(runQueriesAction());
|
||||
dispatch(runQueriesAction({ exploreId }));
|
||||
// Keep table queries first since they need to return quickly
|
||||
if ((ignoreUIState || showingTable) && supportsTable) {
|
||||
dispatch(
|
||||
@@ -551,6 +552,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
|
||||
interval,
|
||||
format: 'time_series',
|
||||
instant: false,
|
||||
maxDataPoints: containerWidth,
|
||||
},
|
||||
makeTimeSeriesList
|
||||
)
|
||||
|
||||
@@ -12,7 +12,14 @@ import {
|
||||
import { ExploreItemState, ExploreState, QueryTransaction, ExploreId, ExploreUpdateState } from 'app/types/explore';
|
||||
import { DataQuery } from '@grafana/ui/src/types';
|
||||
|
||||
import { HigherOrderAction, ActionTypes, SplitCloseActionPayload, splitCloseAction } from './actionTypes';
|
||||
import {
|
||||
HigherOrderAction,
|
||||
ActionTypes,
|
||||
SplitCloseActionPayload,
|
||||
splitCloseAction,
|
||||
runQueriesAction,
|
||||
} from './actionTypes';
|
||||
|
||||
import { reducerFactory } from 'app/core/redux';
|
||||
import {
|
||||
addQueryRowAction,
|
||||
@@ -158,14 +165,8 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
.addMapper({
|
||||
filter: changeSizeAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
const { range, datasourceInstance } = state;
|
||||
let interval = '1s';
|
||||
if (datasourceInstance && datasourceInstance.interval) {
|
||||
interval = datasourceInstance.interval;
|
||||
}
|
||||
const containerWidth = action.payload.width;
|
||||
const queryIntervals = getIntervals(range, interval, containerWidth);
|
||||
return { ...state, containerWidth, queryIntervals };
|
||||
return { ...state, containerWidth };
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
@@ -250,7 +251,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
.addMapper({
|
||||
filter: loadDatasourceSuccessAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
const { containerWidth, range } = state;
|
||||
const {
|
||||
StartPage,
|
||||
datasourceInstance,
|
||||
@@ -260,11 +260,9 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
supportsLogs,
|
||||
supportsTable,
|
||||
} = action.payload;
|
||||
const queryIntervals = getIntervals(range, datasourceInstance.interval, containerWidth);
|
||||
|
||||
return {
|
||||
...state,
|
||||
queryIntervals,
|
||||
StartPage,
|
||||
datasourceInstance,
|
||||
history,
|
||||
@@ -517,6 +515,21 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: runQueriesAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
const { range, datasourceInstance, containerWidth } = state;
|
||||
let interval = '1s';
|
||||
if (datasourceInstance && datasourceInstance.interval) {
|
||||
interval = datasourceInstance.interval;
|
||||
}
|
||||
const queryIntervals = getIntervals(range, interval, containerWidth);
|
||||
return {
|
||||
...state,
|
||||
queryIntervals,
|
||||
};
|
||||
},
|
||||
})
|
||||
.create();
|
||||
|
||||
export const updateChildRefreshState = (
|
||||
|
||||
@@ -9,6 +9,12 @@ import locationUtil from 'app/core/utils/location_util';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import { store } from 'app/store/store';
|
||||
|
||||
export const queryParamsToPreserve: { [key: string]: boolean } = {
|
||||
kiosk: true,
|
||||
autofitpanels: true,
|
||||
orgId: true,
|
||||
};
|
||||
|
||||
export class PlaylistSrv {
|
||||
private cancelPromise: any;
|
||||
private dashboards: Array<{ url: string }>;
|
||||
@@ -41,9 +47,7 @@ export class PlaylistSrv {
|
||||
|
||||
const dash = this.dashboards[this.index];
|
||||
const queryParams = this.$location.search();
|
||||
const filteredParams = _.pickBy(queryParams, key => {
|
||||
return key === 'kiosk' || key === 'autofitpanels' || key === 'orgId';
|
||||
});
|
||||
const filteredParams = _.pickBy(queryParams, (value: any, key: string) => queryParamsToPreserve[key]);
|
||||
const nextDashboardUrl = locationUtil.stripBaseFromUrl(dash.url);
|
||||
|
||||
// this is done inside timeout to make sure digest happens after
|
||||
|
||||
@@ -8,13 +8,13 @@ export default class DefaultVariableQueryEditor extends PureComponent<VariableQu
|
||||
this.state = { value: props.query };
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
this.setState({ value: event.target.value });
|
||||
}
|
||||
onChange = (event: React.FormEvent<HTMLInputElement>) => {
|
||||
this.setState({ value: event.currentTarget.value });
|
||||
};
|
||||
|
||||
handleBlur(event) {
|
||||
this.props.onChange(event.target.value, event.target.value);
|
||||
}
|
||||
onBlur = (event: React.FormEvent<HTMLInputElement>) => {
|
||||
this.props.onChange(event.currentTarget.value, event.currentTarget.value);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
@@ -24,8 +24,8 @@ export default class DefaultVariableQueryEditor extends PureComponent<VariableQu
|
||||
type="text"
|
||||
className="gf-form-input"
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
onChange={this.onChange}
|
||||
onBlur={this.onBlur}
|
||||
placeholder="metric name or tags query"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -112,6 +112,7 @@ export class CloudWatchQueryParameterCtrl {
|
||||
query = $scope.datasource.getDimensionKeys($scope.target.namespace, $scope.target.region);
|
||||
} else if (segment.type === 'value') {
|
||||
const dimensionKey = $scope.dimSegments[$index - 2].value;
|
||||
delete target.dimensions[dimensionKey];
|
||||
query = $scope.datasource.getDimensionValues(
|
||||
target.region,
|
||||
target.namespace,
|
||||
|
||||
@@ -966,7 +966,6 @@ export class FuncInstance {
|
||||
const str = this.def.name + '(';
|
||||
|
||||
const parameters = _.map(this.params, (value, index) => {
|
||||
const valueInterpolated = replaceVariables(value);
|
||||
let paramType;
|
||||
|
||||
if (index < this.def.params.length) {
|
||||
@@ -980,6 +979,8 @@ export class FuncInstance {
|
||||
return value;
|
||||
}
|
||||
|
||||
const valueInterpolated = _.isString(value) ? replaceVariables(value) : value;
|
||||
|
||||
// param types that might be quoted
|
||||
// To quote variables correctly we need to interpolate it to check if it contains a numeric or string value
|
||||
if (_.includes(['int_or_interval', 'node_or_tag'], paramType) && _.isFinite(+valueInterpolated)) {
|
||||
|
||||
@@ -19,6 +19,7 @@ export default class GraphiteQuery {
|
||||
this.datasource = datasource;
|
||||
this.target = target;
|
||||
this.templateSrv = templateSrv;
|
||||
this.scopedVars = scopedVars;
|
||||
this.parseTarget();
|
||||
|
||||
this.removeTagValue = '-- remove tag --';
|
||||
@@ -162,7 +163,9 @@ export default class GraphiteQuery {
|
||||
|
||||
updateModelTarget(targets) {
|
||||
const wrapFunction = (target: string, func: any) => {
|
||||
return func.render(target, this.templateSrv.replace);
|
||||
return func.render(target, (value: string) => {
|
||||
return this.templateSrv.replace(value, this.scopedVars);
|
||||
});
|
||||
};
|
||||
|
||||
if (!this.target.textEditor) {
|
||||
|
||||
@@ -31,7 +31,8 @@ describe('when creating func instance from func names', () => {
|
||||
});
|
||||
|
||||
function replaceVariablesDummy(str: string) {
|
||||
return str;
|
||||
// important that this does replace
|
||||
return str.replace('asdasdas', 'asdsad');
|
||||
}
|
||||
|
||||
describe('when rendering func instance', () => {
|
||||
|
||||
@@ -398,7 +398,13 @@ Array [
|
||||
>
|
||||
Alias By
|
||||
</label>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="gf-form-input gf-form-input width-24"
|
||||
onChange={[Function]}
|
||||
@@ -426,7 +432,13 @@ Array [
|
||||
>
|
||||
Project
|
||||
</span>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="gf-form-input gf-form-input width-15"
|
||||
disabled={true}
|
||||
|
||||
@@ -594,7 +594,8 @@ export class HeatmapRenderer {
|
||||
yGridSize = Math.floor((this.yScale(1) - this.yScale(base)) / splitFactor);
|
||||
}
|
||||
|
||||
this.cardWidth = xGridSize - this.cardPadding * 2;
|
||||
const cardWidth = xGridSize - this.cardPadding * 2;
|
||||
this.cardWidth = Math.max(cardWidth, MIN_CARD_SIZE);
|
||||
this.cardHeight = yGridSize ? yGridSize - this.cardPadding * 2 : 0;
|
||||
}
|
||||
|
||||
@@ -611,16 +612,13 @@ export class HeatmapRenderer {
|
||||
}
|
||||
|
||||
getCardWidth(d) {
|
||||
let w;
|
||||
let w = this.cardWidth;
|
||||
if (this.xScale(d.x) < 0) {
|
||||
// Cut card left to prevent overlay
|
||||
const cuttedWidth = this.xScale(d.x) + this.cardWidth;
|
||||
w = cuttedWidth > 0 ? cuttedWidth : 0;
|
||||
w = this.xScale(d.x) + this.cardWidth;
|
||||
} else if (this.xScale(d.x) + this.cardWidth > this.chartWidth) {
|
||||
// Cut card right to prevent overlay
|
||||
w = this.chartWidth - this.xScale(d.x) - this.cardPadding;
|
||||
} else {
|
||||
w = this.cardWidth;
|
||||
}
|
||||
|
||||
// Card width should be MIN_CARD_SIZE at least, but cut cards shouldn't be displayed
|
||||
|
||||
@@ -191,7 +191,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
data.value = 0;
|
||||
data.valueRounded = 0;
|
||||
} else {
|
||||
const decimalInfo = getDecimalsForValue(data.value);
|
||||
const decimalInfo = getDecimalsForValue(data.value, this.panel.decimals);
|
||||
const formatFunc = getValueFormat(this.panel.format);
|
||||
|
||||
data.valueFormatted = formatFunc(
|
||||
@@ -199,7 +199,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
decimalInfo.decimals,
|
||||
decimalInfo.scaledDecimals
|
||||
);
|
||||
data.valueRounded = kbn.roundValue(data.value, this.panel.decimals || 0);
|
||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
||||
}
|
||||
|
||||
this.setValueMapping(data);
|
||||
@@ -279,17 +279,15 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
data.value = this.series[0].stats[this.panel.valueName];
|
||||
data.flotpairs = this.series[0].flotpairs;
|
||||
|
||||
let decimals = this.panel.decimals;
|
||||
let scaledDecimals = 0;
|
||||
const decimalInfo = getDecimalsForValue(data.value, this.panel.decimals);
|
||||
|
||||
if (!this.panel.decimals) {
|
||||
const decimalInfo = getDecimalsForValue(data.value);
|
||||
decimals = decimalInfo.decimals;
|
||||
scaledDecimals = decimalInfo.scaledDecimals;
|
||||
}
|
||||
|
||||
data.valueFormatted = formatFunc(data.value, decimals, scaledDecimals, this.dashboard.isTimezoneUtc());
|
||||
data.valueRounded = kbn.roundValue(data.value, decimals);
|
||||
data.valueFormatted = formatFunc(
|
||||
data.value,
|
||||
decimalInfo.decimals,
|
||||
decimalInfo.scaledDecimals,
|
||||
this.dashboard.isTimezoneUtc()
|
||||
);
|
||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
||||
}
|
||||
|
||||
// Add $__name variable for using in prefix or postfix
|
||||
|
||||
@@ -316,6 +316,7 @@ export interface QueryOptions {
|
||||
hinting?: boolean;
|
||||
instant?: boolean;
|
||||
valueWithRefId?: boolean;
|
||||
maxDataPoints?: number;
|
||||
}
|
||||
|
||||
export interface QueryTransaction {
|
||||
|
||||
@@ -102,14 +102,14 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black);
|
||||
|
||||
// Links
|
||||
// -------------------------
|
||||
$link-color: #e3e3e3;
|
||||
$link-color-disabled: #e3e3e3;
|
||||
$link-color: #d8d9da;
|
||||
$link-color-disabled: #8e8e8e;
|
||||
$link-hover-color: #ffffff;
|
||||
$external-link-color: #33b5e5;
|
||||
|
||||
// Typography
|
||||
// -------------------------
|
||||
$headings-color: #e3e3e3;
|
||||
$headings-color: #d8d9da;
|
||||
$abbr-border-color: $gray-2 !default;
|
||||
$text-muted: $text-color-weak;
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ $side-menu-width: 60px;
|
||||
|
||||
// dashboard
|
||||
$dashboard-padding: $space-md;
|
||||
$panel-padding: 0 $space-md $space-sm $space-md;
|
||||
$panel-padding: 0 16px 8px 16px;
|
||||
|
||||
// tabs
|
||||
$tabs-padding: 10px 15px 9px;
|
||||
|
||||
@@ -76,7 +76,7 @@ $text-color: #52545c;
|
||||
$text-color-strong: #41444b;
|
||||
$text-color-weak: #767980;
|
||||
$text-color-faint: #35373f;
|
||||
$text-color-emphasis: #dde4ed;
|
||||
$text-color-emphasis: #41444b;
|
||||
|
||||
$text-shadow-faint: none;
|
||||
|
||||
@@ -89,8 +89,8 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%);
|
||||
// Links
|
||||
// -------------------------
|
||||
$link-color: #52545c;
|
||||
$link-color-disabled: #9ea0a9;
|
||||
$link-hover-color: #222326;
|
||||
$link-color-disabled: #acb6bf;
|
||||
$link-hover-color: #1e2028;
|
||||
$external-link-color: #5794f2;
|
||||
|
||||
// Typography
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
.share-modal-info-text {
|
||||
margin-top: 5px;
|
||||
strong {
|
||||
color: $headings-color;
|
||||
color: $text-color-emphasis;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,10 @@
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs {
|
||||
.graph-panel {
|
||||
display: -webkit-box;
|
||||
}
|
||||
|
||||
.graph-panel--legend-right {
|
||||
.graph-legend {
|
||||
display: block;
|
||||
|
||||
@@ -335,7 +335,6 @@
|
||||
}
|
||||
|
||||
@mixin left-brand-border-gradient() {
|
||||
border: none;
|
||||
border-image: linear-gradient(rgba(255, 213, 0, 1) 0%, rgba(255, 68, 0, 1) 99%, rgba(255, 68, 0, 1) 100%);
|
||||
border-image-slice: 1;
|
||||
border-style: solid;
|
||||
|
||||
@@ -18,7 +18,9 @@ RUN pip install -U awscli crcmod && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
ln -s /opt/google-cloud-sdk/bin/gsutil /usr/bin/gsutil && \
|
||||
ln -s /opt/google-cloud-sdk/bin/gcloud /usr/bin/gcloud
|
||||
ln -s /opt/google-cloud-sdk/bin/gcloud /usr/bin/gcloud && \
|
||||
mkdir -p /deb-repo /rpm-repo && \
|
||||
chown circleci:circleci /deb-repo /rpm-repo
|
||||
|
||||
COPY --from=0 /go/bin/aptly /usr/local/bin/aptly
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
_version="1.2.1"
|
||||
_version="1.2.2"
|
||||
_tag="grafana/grafana-ci-deploy:${_version}"
|
||||
|
||||
docker build -t $_tag .
|
||||
|
||||
@@ -6,8 +6,8 @@ EXTRA_OPTS="$@"
|
||||
|
||||
# Right now we hack this in into the publish script.
|
||||
# Eventually we might want to keep a list of all previous releases somewhere.
|
||||
_releaseNoteUrl="https://community.grafana.com/t/release-notes-v6-0-x/14010"
|
||||
_whatsNewUrl="http://docs.grafana.org/guides/whats-new-in-v6-0/"
|
||||
_releaseNoteUrl="https://community.grafana.com/t/release-notes-v6-1-x/15772"
|
||||
_whatsNewUrl="http://docs.grafana.org/guides/whats-new-in-v6-1/"
|
||||
|
||||
./scripts/build/release_publisher/release_publisher \
|
||||
--wn ${_whatsNewUrl} \
|
||||
|
||||
@@ -14,21 +14,21 @@ const changelogTaskRunner: TaskRunner<ChangelogOptions> = async ({ milestone })
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
if (!/^\d+$/.test(milestone)) {
|
||||
console.log('Use milestone number not title, find number in milestone url');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await client.get('/issues', {
|
||||
params: {
|
||||
state: 'closed',
|
||||
per_page: 100,
|
||||
labels: 'add to changelog',
|
||||
milestone: milestone,
|
||||
},
|
||||
});
|
||||
|
||||
const issues = res.data.filter(item => {
|
||||
if (!item.milestone) {
|
||||
console.log('Item missing milestone', item.number);
|
||||
return false;
|
||||
}
|
||||
return item.milestone.title === milestone;
|
||||
});
|
||||
const issues = res.data;
|
||||
|
||||
const bugs = _.sortBy(
|
||||
issues.filter(item => {
|
||||
|
||||
@@ -27,13 +27,10 @@ const cherryPickRunner: TaskRunner<CherryPickOptions> = async () => {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(item.number + ' closed_at ' + item.closed_at + ' ' + item.html_url);
|
||||
console.log(`${item.title} (${item.number}) closed_at ${item.closed_at}`);
|
||||
console.log(`\tURL: ${item.closed_at} ${item.html_url}`);
|
||||
const issueDetails = await client.get(item.pull_request.url);
|
||||
const commits = await client.get(issueDetails.data.commits_url);
|
||||
|
||||
for (const commit of commits.data) {
|
||||
console.log(commit.commit.message + ' sha: ' + commit.sha);
|
||||
}
|
||||
console.log(`\tMerge sha: ${issueDetails.data.merge_commit_sha}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'autoprefixer': {},
|
||||
'postcss-reporter': {},
|
||||
'postcss-browser-reporter': {},
|
||||
}
|
||||
}
|
||||
module.exports = () => {
|
||||
return {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
'postcss-reporter': {},
|
||||
'postcss-browser-reporter': {},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ module.exports = function(options) {
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
sourceMap: options.sourceMap,
|
||||
config: { path: __dirname + '/postcss.config.js' },
|
||||
config: { path: __dirname },
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
21
vendor/github.com/gobwas/glob/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gobwas/glob/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Sergey Kamardin
|
||||
|
||||
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.
|
||||
525
vendor/github.com/gobwas/glob/compiler/compiler.go
generated
vendored
Normal file
525
vendor/github.com/gobwas/glob/compiler/compiler.go
generated
vendored
Normal file
@@ -0,0 +1,525 @@
|
||||
package compiler
|
||||
|
||||
// TODO use constructor with all matchers, and to their structs private
|
||||
// TODO glue multiple Text nodes (like after QuoteMeta)
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gobwas/glob/match"
|
||||
"github.com/gobwas/glob/syntax/ast"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
)
|
||||
|
||||
func optimizeMatcher(matcher match.Matcher) match.Matcher {
|
||||
switch m := matcher.(type) {
|
||||
|
||||
case match.Any:
|
||||
if len(m.Separators) == 0 {
|
||||
return match.NewSuper()
|
||||
}
|
||||
|
||||
case match.AnyOf:
|
||||
if len(m.Matchers) == 1 {
|
||||
return m.Matchers[0]
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
case match.List:
|
||||
if m.Not == false && len(m.List) == 1 {
|
||||
return match.NewText(string(m.List))
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
case match.BTree:
|
||||
m.Left = optimizeMatcher(m.Left)
|
||||
m.Right = optimizeMatcher(m.Right)
|
||||
|
||||
r, ok := m.Value.(match.Text)
|
||||
if !ok {
|
||||
return m
|
||||
}
|
||||
|
||||
var (
|
||||
leftNil = m.Left == nil
|
||||
rightNil = m.Right == nil
|
||||
)
|
||||
if leftNil && rightNil {
|
||||
return match.NewText(r.Str)
|
||||
}
|
||||
|
||||
_, leftSuper := m.Left.(match.Super)
|
||||
lp, leftPrefix := m.Left.(match.Prefix)
|
||||
la, leftAny := m.Left.(match.Any)
|
||||
|
||||
_, rightSuper := m.Right.(match.Super)
|
||||
rs, rightSuffix := m.Right.(match.Suffix)
|
||||
ra, rightAny := m.Right.(match.Any)
|
||||
|
||||
switch {
|
||||
case leftSuper && rightSuper:
|
||||
return match.NewContains(r.Str, false)
|
||||
|
||||
case leftSuper && rightNil:
|
||||
return match.NewSuffix(r.Str)
|
||||
|
||||
case rightSuper && leftNil:
|
||||
return match.NewPrefix(r.Str)
|
||||
|
||||
case leftNil && rightSuffix:
|
||||
return match.NewPrefixSuffix(r.Str, rs.Suffix)
|
||||
|
||||
case rightNil && leftPrefix:
|
||||
return match.NewPrefixSuffix(lp.Prefix, r.Str)
|
||||
|
||||
case rightNil && leftAny:
|
||||
return match.NewSuffixAny(r.Str, la.Separators)
|
||||
|
||||
case leftNil && rightAny:
|
||||
return match.NewPrefixAny(r.Str, ra.Separators)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
return matcher
|
||||
}
|
||||
|
||||
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
|
||||
if len(matchers) == 0 {
|
||||
return nil, fmt.Errorf("compile error: need at least one matcher")
|
||||
}
|
||||
if len(matchers) == 1 {
|
||||
return matchers[0], nil
|
||||
}
|
||||
if m := glueMatchers(matchers); m != nil {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
idx := -1
|
||||
maxLen := -1
|
||||
var val match.Matcher
|
||||
for i, matcher := range matchers {
|
||||
if l := matcher.Len(); l != -1 && l >= maxLen {
|
||||
maxLen = l
|
||||
idx = i
|
||||
val = matcher
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil { // not found matcher with static length
|
||||
r, err := compileMatchers(matchers[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return match.NewBTree(matchers[0], nil, r), nil
|
||||
}
|
||||
|
||||
left := matchers[:idx]
|
||||
var right []match.Matcher
|
||||
if len(matchers) > idx+1 {
|
||||
right = matchers[idx+1:]
|
||||
}
|
||||
|
||||
var l, r match.Matcher
|
||||
var err error
|
||||
if len(left) > 0 {
|
||||
l, err = compileMatchers(left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(right) > 0 {
|
||||
r, err = compileMatchers(right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return match.NewBTree(val, l, r), nil
|
||||
}
|
||||
|
||||
func glueMatchers(matchers []match.Matcher) match.Matcher {
|
||||
if m := glueMatchersAsEvery(matchers); m != nil {
|
||||
return m
|
||||
}
|
||||
if m := glueMatchersAsRow(matchers); m != nil {
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func glueMatchersAsRow(matchers []match.Matcher) match.Matcher {
|
||||
if len(matchers) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
c []match.Matcher
|
||||
l int
|
||||
)
|
||||
for _, matcher := range matchers {
|
||||
if ml := matcher.Len(); ml == -1 {
|
||||
return nil
|
||||
} else {
|
||||
c = append(c, matcher)
|
||||
l += ml
|
||||
}
|
||||
}
|
||||
return match.NewRow(l, c...)
|
||||
}
|
||||
|
||||
func glueMatchersAsEvery(matchers []match.Matcher) match.Matcher {
|
||||
if len(matchers) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
hasAny bool
|
||||
hasSuper bool
|
||||
hasSingle bool
|
||||
min int
|
||||
separator []rune
|
||||
)
|
||||
|
||||
for i, matcher := range matchers {
|
||||
var sep []rune
|
||||
|
||||
switch m := matcher.(type) {
|
||||
case match.Super:
|
||||
sep = []rune{}
|
||||
hasSuper = true
|
||||
|
||||
case match.Any:
|
||||
sep = m.Separators
|
||||
hasAny = true
|
||||
|
||||
case match.Single:
|
||||
sep = m.Separators
|
||||
hasSingle = true
|
||||
min++
|
||||
|
||||
case match.List:
|
||||
if !m.Not {
|
||||
return nil
|
||||
}
|
||||
sep = m.List
|
||||
hasSingle = true
|
||||
min++
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// initialize
|
||||
if i == 0 {
|
||||
separator = sep
|
||||
}
|
||||
|
||||
if runes.Equal(sep, separator) {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if hasSuper && !hasAny && !hasSingle {
|
||||
return match.NewSuper()
|
||||
}
|
||||
|
||||
if hasAny && !hasSuper && !hasSingle {
|
||||
return match.NewAny(separator)
|
||||
}
|
||||
|
||||
if (hasAny || hasSuper) && min > 0 && len(separator) == 0 {
|
||||
return match.NewMin(min)
|
||||
}
|
||||
|
||||
every := match.NewEveryOf()
|
||||
|
||||
if min > 0 {
|
||||
every.Add(match.NewMin(min))
|
||||
|
||||
if !hasAny && !hasSuper {
|
||||
every.Add(match.NewMax(min))
|
||||
}
|
||||
}
|
||||
|
||||
if len(separator) > 0 {
|
||||
every.Add(match.NewContains(string(separator), true))
|
||||
}
|
||||
|
||||
return every
|
||||
}
|
||||
|
||||
func minimizeMatchers(matchers []match.Matcher) []match.Matcher {
|
||||
var done match.Matcher
|
||||
var left, right, count int
|
||||
|
||||
for l := 0; l < len(matchers); l++ {
|
||||
for r := len(matchers); r > l; r-- {
|
||||
if glued := glueMatchers(matchers[l:r]); glued != nil {
|
||||
var swap bool
|
||||
|
||||
if done == nil {
|
||||
swap = true
|
||||
} else {
|
||||
cl, gl := done.Len(), glued.Len()
|
||||
swap = cl > -1 && gl > -1 && gl > cl
|
||||
swap = swap || count < r-l
|
||||
}
|
||||
|
||||
if swap {
|
||||
done = glued
|
||||
left = l
|
||||
right = r
|
||||
count = r - l
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if done == nil {
|
||||
return matchers
|
||||
}
|
||||
|
||||
next := append(append([]match.Matcher{}, matchers[:left]...), done)
|
||||
if right < len(matchers) {
|
||||
next = append(next, matchers[right:]...)
|
||||
}
|
||||
|
||||
if len(next) == len(matchers) {
|
||||
return next
|
||||
}
|
||||
|
||||
return minimizeMatchers(next)
|
||||
}
|
||||
|
||||
// minimizeAnyOf tries to apply some heuristics to minimize number of nodes in given tree
|
||||
func minimizeTree(tree *ast.Node) *ast.Node {
|
||||
switch tree.Kind {
|
||||
case ast.KindAnyOf:
|
||||
return minimizeTreeAnyOf(tree)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// minimizeAnyOf tries to find common children of given node of AnyOf pattern
|
||||
// it searches for common children from left and from right
|
||||
// if any common children are found – then it returns new optimized ast tree
|
||||
// else it returns nil
|
||||
func minimizeTreeAnyOf(tree *ast.Node) *ast.Node {
|
||||
if !areOfSameKind(tree.Children, ast.KindPattern) {
|
||||
return nil
|
||||
}
|
||||
|
||||
commonLeft, commonRight := commonChildren(tree.Children)
|
||||
commonLeftCount, commonRightCount := len(commonLeft), len(commonRight)
|
||||
if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts
|
||||
return nil
|
||||
}
|
||||
|
||||
var result []*ast.Node
|
||||
if commonLeftCount > 0 {
|
||||
result = append(result, ast.NewNode(ast.KindPattern, nil, commonLeft...))
|
||||
}
|
||||
|
||||
var anyOf []*ast.Node
|
||||
for _, child := range tree.Children {
|
||||
reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount]
|
||||
var node *ast.Node
|
||||
if len(reuse) == 0 {
|
||||
// this pattern is completely reduced by commonLeft and commonRight patterns
|
||||
// so it become nothing
|
||||
node = ast.NewNode(ast.KindNothing, nil)
|
||||
} else {
|
||||
node = ast.NewNode(ast.KindPattern, nil, reuse...)
|
||||
}
|
||||
anyOf = appendIfUnique(anyOf, node)
|
||||
}
|
||||
switch {
|
||||
case len(anyOf) == 1 && anyOf[0].Kind != ast.KindNothing:
|
||||
result = append(result, anyOf[0])
|
||||
case len(anyOf) > 1:
|
||||
result = append(result, ast.NewNode(ast.KindAnyOf, nil, anyOf...))
|
||||
}
|
||||
|
||||
if commonRightCount > 0 {
|
||||
result = append(result, ast.NewNode(ast.KindPattern, nil, commonRight...))
|
||||
}
|
||||
|
||||
return ast.NewNode(ast.KindPattern, nil, result...)
|
||||
}
|
||||
|
||||
func commonChildren(nodes []*ast.Node) (commonLeft, commonRight []*ast.Node) {
|
||||
if len(nodes) <= 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// find node that has least number of children
|
||||
idx := leastChildren(nodes)
|
||||
if idx == -1 {
|
||||
return
|
||||
}
|
||||
tree := nodes[idx]
|
||||
treeLength := len(tree.Children)
|
||||
|
||||
// allocate max able size for rightCommon slice
|
||||
// to get ability insert elements in reverse order (from end to start)
|
||||
// without sorting
|
||||
commonRight = make([]*ast.Node, treeLength)
|
||||
lastRight := treeLength // will use this to get results as commonRight[lastRight:]
|
||||
|
||||
var (
|
||||
breakLeft bool
|
||||
breakRight bool
|
||||
commonTotal int
|
||||
)
|
||||
for i, j := 0, treeLength-1; commonTotal < treeLength && j >= 0 && !(breakLeft && breakRight); i, j = i+1, j-1 {
|
||||
treeLeft := tree.Children[i]
|
||||
treeRight := tree.Children[j]
|
||||
|
||||
for k := 0; k < len(nodes) && !(breakLeft && breakRight); k++ {
|
||||
// skip least children node
|
||||
if k == idx {
|
||||
continue
|
||||
}
|
||||
|
||||
restLeft := nodes[k].Children[i]
|
||||
restRight := nodes[k].Children[j+len(nodes[k].Children)-treeLength]
|
||||
|
||||
breakLeft = breakLeft || !treeLeft.Equal(restLeft)
|
||||
|
||||
// disable searching for right common parts, if left part is already overlapping
|
||||
breakRight = breakRight || (!breakLeft && j <= i)
|
||||
breakRight = breakRight || !treeRight.Equal(restRight)
|
||||
}
|
||||
|
||||
if !breakLeft {
|
||||
commonTotal++
|
||||
commonLeft = append(commonLeft, treeLeft)
|
||||
}
|
||||
if !breakRight {
|
||||
commonTotal++
|
||||
lastRight = j
|
||||
commonRight[j] = treeRight
|
||||
}
|
||||
}
|
||||
|
||||
commonRight = commonRight[lastRight:]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func appendIfUnique(target []*ast.Node, val *ast.Node) []*ast.Node {
|
||||
for _, n := range target {
|
||||
if reflect.DeepEqual(n, val) {
|
||||
return target
|
||||
}
|
||||
}
|
||||
return append(target, val)
|
||||
}
|
||||
|
||||
func areOfSameKind(nodes []*ast.Node, kind ast.Kind) bool {
|
||||
for _, n := range nodes {
|
||||
if n.Kind != kind {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func leastChildren(nodes []*ast.Node) int {
|
||||
min := -1
|
||||
idx := -1
|
||||
for i, n := range nodes {
|
||||
if idx == -1 || (len(n.Children) < min) {
|
||||
min = len(n.Children)
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
func compileTreeChildren(tree *ast.Node, sep []rune) ([]match.Matcher, error) {
|
||||
var matchers []match.Matcher
|
||||
for _, desc := range tree.Children {
|
||||
m, err := compile(desc, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, optimizeMatcher(m))
|
||||
}
|
||||
return matchers, nil
|
||||
}
|
||||
|
||||
func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) {
|
||||
switch tree.Kind {
|
||||
case ast.KindAnyOf:
|
||||
// todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go)
|
||||
if n := minimizeTree(tree); n != nil {
|
||||
return compile(n, sep)
|
||||
}
|
||||
matchers, err := compileTreeChildren(tree, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return match.NewAnyOf(matchers...), nil
|
||||
|
||||
case ast.KindPattern:
|
||||
if len(tree.Children) == 0 {
|
||||
return match.NewNothing(), nil
|
||||
}
|
||||
matchers, err := compileTreeChildren(tree, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err = compileMatchers(minimizeMatchers(matchers))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case ast.KindAny:
|
||||
m = match.NewAny(sep)
|
||||
|
||||
case ast.KindSuper:
|
||||
m = match.NewSuper()
|
||||
|
||||
case ast.KindSingle:
|
||||
m = match.NewSingle(sep)
|
||||
|
||||
case ast.KindNothing:
|
||||
m = match.NewNothing()
|
||||
|
||||
case ast.KindList:
|
||||
l := tree.Value.(ast.List)
|
||||
m = match.NewList([]rune(l.Chars), l.Not)
|
||||
|
||||
case ast.KindRange:
|
||||
r := tree.Value.(ast.Range)
|
||||
m = match.NewRange(r.Lo, r.Hi, r.Not)
|
||||
|
||||
case ast.KindText:
|
||||
t := tree.Value.(ast.Text)
|
||||
m = match.NewText(t.Text)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("could not compile tree: unknown node type")
|
||||
}
|
||||
|
||||
return optimizeMatcher(m), nil
|
||||
}
|
||||
|
||||
func Compile(tree *ast.Node, sep []rune) (match.Matcher, error) {
|
||||
m, err := compile(tree, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
80
vendor/github.com/gobwas/glob/glob.go
generated
vendored
Normal file
80
vendor/github.com/gobwas/glob/glob.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
package glob
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob/compiler"
|
||||
"github.com/gobwas/glob/syntax"
|
||||
)
|
||||
|
||||
// Glob represents compiled glob pattern.
|
||||
type Glob interface {
|
||||
Match(string) bool
|
||||
}
|
||||
|
||||
// Compile creates Glob for given pattern and strings (if any present after pattern) as separators.
|
||||
// The pattern syntax is:
|
||||
//
|
||||
// pattern:
|
||||
// { term }
|
||||
//
|
||||
// term:
|
||||
// `*` matches any sequence of non-separator characters
|
||||
// `**` matches any sequence of characters
|
||||
// `?` matches any single non-separator character
|
||||
// `[` [ `!` ] { character-range } `]`
|
||||
// character class (must be non-empty)
|
||||
// `{` pattern-list `}`
|
||||
// pattern alternatives
|
||||
// c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`)
|
||||
// `\` c matches character c
|
||||
//
|
||||
// character-range:
|
||||
// c matches character c (c != `\\`, `-`, `]`)
|
||||
// `\` c matches character c
|
||||
// lo `-` hi matches character c for lo <= c <= hi
|
||||
//
|
||||
// pattern-list:
|
||||
// pattern { `,` pattern }
|
||||
// comma-separated (without spaces) patterns
|
||||
//
|
||||
func Compile(pattern string, separators ...rune) (Glob, error) {
|
||||
ast, err := syntax.Parse(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matcher, err := compiler.Compile(ast, separators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return matcher, nil
|
||||
}
|
||||
|
||||
// MustCompile is the same as Compile, except that if Compile returns error, this will panic
|
||||
func MustCompile(pattern string, separators ...rune) Glob {
|
||||
g, err := Compile(pattern, separators...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// QuoteMeta returns a string that quotes all glob pattern meta characters
|
||||
// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`.
|
||||
func QuoteMeta(s string) string {
|
||||
b := make([]byte, 2*len(s))
|
||||
|
||||
// a byte loop is correct because all meta characters are ASCII
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if syntax.Special(s[i]) {
|
||||
b[j] = '\\'
|
||||
j++
|
||||
}
|
||||
b[j] = s[i]
|
||||
j++
|
||||
}
|
||||
|
||||
return string(b[0:j])
|
||||
}
|
||||
45
vendor/github.com/gobwas/glob/match/any.go
generated
vendored
Normal file
45
vendor/github.com/gobwas/glob/match/any.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/strings"
|
||||
)
|
||||
|
||||
type Any struct {
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewAny(s []rune) Any {
|
||||
return Any{s}
|
||||
}
|
||||
|
||||
func (self Any) Match(s string) bool {
|
||||
return strings.IndexAnyRunes(s, self.Separators) == -1
|
||||
}
|
||||
|
||||
func (self Any) Index(s string) (int, []int) {
|
||||
found := strings.IndexAnyRunes(s, self.Separators)
|
||||
switch found {
|
||||
case -1:
|
||||
case 0:
|
||||
return 0, segments0
|
||||
default:
|
||||
s = s[:found]
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(s))
|
||||
for i := range s {
|
||||
segments = append(segments, i)
|
||||
}
|
||||
segments = append(segments, len(s))
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Any) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Any) String() string {
|
||||
return fmt.Sprintf("<any:![%s]>", string(self.Separators))
|
||||
}
|
||||
82
vendor/github.com/gobwas/glob/match/any_of.go
generated
vendored
Normal file
82
vendor/github.com/gobwas/glob/match/any_of.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package match
|
||||
|
||||
import "fmt"
|
||||
|
||||
type AnyOf struct {
|
||||
Matchers Matchers
|
||||
}
|
||||
|
||||
func NewAnyOf(m ...Matcher) AnyOf {
|
||||
return AnyOf{Matchers(m)}
|
||||
}
|
||||
|
||||
func (self *AnyOf) Add(m Matcher) error {
|
||||
self.Matchers = append(self.Matchers, m)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self AnyOf) Match(s string) bool {
|
||||
for _, m := range self.Matchers {
|
||||
if m.Match(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self AnyOf) Index(s string) (int, []int) {
|
||||
index := -1
|
||||
|
||||
segments := acquireSegments(len(s))
|
||||
for _, m := range self.Matchers {
|
||||
idx, seg := m.Index(s)
|
||||
if idx == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if index == -1 || idx < index {
|
||||
index = idx
|
||||
segments = append(segments[:0], seg...)
|
||||
continue
|
||||
}
|
||||
|
||||
if idx > index {
|
||||
continue
|
||||
}
|
||||
|
||||
// here idx == index
|
||||
segments = appendMerge(segments, seg)
|
||||
}
|
||||
|
||||
if index == -1 {
|
||||
releaseSegments(segments)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return index, segments
|
||||
}
|
||||
|
||||
func (self AnyOf) Len() (l int) {
|
||||
l = -1
|
||||
for _, m := range self.Matchers {
|
||||
ml := m.Len()
|
||||
switch {
|
||||
case l == -1:
|
||||
l = ml
|
||||
continue
|
||||
|
||||
case ml == -1:
|
||||
return -1
|
||||
|
||||
case l != ml:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self AnyOf) String() string {
|
||||
return fmt.Sprintf("<any_of:[%s]>", self.Matchers)
|
||||
}
|
||||
146
vendor/github.com/gobwas/glob/match/btree.go
generated
vendored
Normal file
146
vendor/github.com/gobwas/glob/match/btree.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type BTree struct {
|
||||
Value Matcher
|
||||
Left Matcher
|
||||
Right Matcher
|
||||
ValueLengthRunes int
|
||||
LeftLengthRunes int
|
||||
RightLengthRunes int
|
||||
LengthRunes int
|
||||
}
|
||||
|
||||
func NewBTree(Value, Left, Right Matcher) (tree BTree) {
|
||||
tree.Value = Value
|
||||
tree.Left = Left
|
||||
tree.Right = Right
|
||||
|
||||
lenOk := true
|
||||
if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 {
|
||||
lenOk = false
|
||||
}
|
||||
|
||||
if Left != nil {
|
||||
if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 {
|
||||
lenOk = false
|
||||
}
|
||||
}
|
||||
|
||||
if Right != nil {
|
||||
if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 {
|
||||
lenOk = false
|
||||
}
|
||||
}
|
||||
|
||||
if lenOk {
|
||||
tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes
|
||||
} else {
|
||||
tree.LengthRunes = -1
|
||||
}
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
func (self BTree) Len() int {
|
||||
return self.LengthRunes
|
||||
}
|
||||
|
||||
// todo?
|
||||
func (self BTree) Index(s string) (int, []int) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self BTree) Match(s string) bool {
|
||||
inputLen := len(s)
|
||||
|
||||
// self.Length, self.RLen and self.LLen are values meaning the length of runes for each part
|
||||
// here we manipulating byte length for better optimizations
|
||||
// but these checks still works, cause minLen of 1-rune string is 1 byte.
|
||||
if self.LengthRunes != -1 && self.LengthRunes > inputLen {
|
||||
return false
|
||||
}
|
||||
|
||||
// try to cut unnecessary parts
|
||||
// by knowledge of length of right and left part
|
||||
var offset, limit int
|
||||
if self.LeftLengthRunes >= 0 {
|
||||
offset = self.LeftLengthRunes
|
||||
}
|
||||
if self.RightLengthRunes >= 0 {
|
||||
limit = inputLen - self.RightLengthRunes
|
||||
} else {
|
||||
limit = inputLen
|
||||
}
|
||||
|
||||
for offset < limit {
|
||||
// search for matching part in substring
|
||||
index, segments := self.Value.Index(s[offset:limit])
|
||||
if index == -1 {
|
||||
releaseSegments(segments)
|
||||
return false
|
||||
}
|
||||
|
||||
l := s[:offset+index]
|
||||
var left bool
|
||||
if self.Left != nil {
|
||||
left = self.Left.Match(l)
|
||||
} else {
|
||||
left = l == ""
|
||||
}
|
||||
|
||||
if left {
|
||||
for i := len(segments) - 1; i >= 0; i-- {
|
||||
length := segments[i]
|
||||
|
||||
var right bool
|
||||
var r string
|
||||
// if there is no string for the right branch
|
||||
if inputLen <= offset+index+length {
|
||||
r = ""
|
||||
} else {
|
||||
r = s[offset+index+length:]
|
||||
}
|
||||
|
||||
if self.Right != nil {
|
||||
right = self.Right.Match(r)
|
||||
} else {
|
||||
right = r == ""
|
||||
}
|
||||
|
||||
if right {
|
||||
releaseSegments(segments)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, step := utf8.DecodeRuneInString(s[offset+index:])
|
||||
offset += index + step
|
||||
|
||||
releaseSegments(segments)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self BTree) String() string {
|
||||
const n string = "<nil>"
|
||||
var l, r string
|
||||
if self.Left == nil {
|
||||
l = n
|
||||
} else {
|
||||
l = self.Left.String()
|
||||
}
|
||||
if self.Right == nil {
|
||||
r = n
|
||||
} else {
|
||||
r = self.Right.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<btree:[%s<-%s->%s]>", l, self.Value, r)
|
||||
}
|
||||
58
vendor/github.com/gobwas/glob/match/contains.go
generated
vendored
Normal file
58
vendor/github.com/gobwas/glob/match/contains.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Contains struct {
|
||||
Needle string
|
||||
Not bool
|
||||
}
|
||||
|
||||
func NewContains(needle string, not bool) Contains {
|
||||
return Contains{needle, not}
|
||||
}
|
||||
|
||||
func (self Contains) Match(s string) bool {
|
||||
return strings.Contains(s, self.Needle) != self.Not
|
||||
}
|
||||
|
||||
func (self Contains) Index(s string) (int, []int) {
|
||||
var offset int
|
||||
|
||||
idx := strings.Index(s, self.Needle)
|
||||
|
||||
if !self.Not {
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
offset = idx + len(self.Needle)
|
||||
if len(s) <= offset {
|
||||
return 0, []int{offset}
|
||||
}
|
||||
s = s[offset:]
|
||||
} else if idx != -1 {
|
||||
s = s[:idx]
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(s) + 1)
|
||||
for i := range s {
|
||||
segments = append(segments, offset+i)
|
||||
}
|
||||
|
||||
return 0, append(segments, offset+len(s))
|
||||
}
|
||||
|
||||
func (self Contains) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Contains) String() string {
|
||||
var not string
|
||||
if self.Not {
|
||||
not = "!"
|
||||
}
|
||||
return fmt.Sprintf("<contains:%s[%s]>", not, self.Needle)
|
||||
}
|
||||
99
vendor/github.com/gobwas/glob/match/every_of.go
generated
vendored
Normal file
99
vendor/github.com/gobwas/glob/match/every_of.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type EveryOf struct {
|
||||
Matchers Matchers
|
||||
}
|
||||
|
||||
func NewEveryOf(m ...Matcher) EveryOf {
|
||||
return EveryOf{Matchers(m)}
|
||||
}
|
||||
|
||||
func (self *EveryOf) Add(m Matcher) error {
|
||||
self.Matchers = append(self.Matchers, m)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self EveryOf) Len() (l int) {
|
||||
for _, m := range self.Matchers {
|
||||
if ml := m.Len(); l > 0 {
|
||||
l += ml
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self EveryOf) Index(s string) (int, []int) {
|
||||
var index int
|
||||
var offset int
|
||||
|
||||
// make `in` with cap as len(s),
|
||||
// cause it is the maximum size of output segments values
|
||||
next := acquireSegments(len(s))
|
||||
current := acquireSegments(len(s))
|
||||
|
||||
sub := s
|
||||
for i, m := range self.Matchers {
|
||||
idx, seg := m.Index(sub)
|
||||
if idx == -1 {
|
||||
releaseSegments(next)
|
||||
releaseSegments(current)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// we use copy here instead of `current = seg`
|
||||
// cause seg is a slice from reusable buffer `in`
|
||||
// and it could be overwritten in next iteration
|
||||
current = append(current, seg...)
|
||||
} else {
|
||||
// clear the next
|
||||
next = next[:0]
|
||||
|
||||
delta := index - (idx + offset)
|
||||
for _, ex := range current {
|
||||
for _, n := range seg {
|
||||
if ex+delta == n {
|
||||
next = append(next, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(next) == 0 {
|
||||
releaseSegments(next)
|
||||
releaseSegments(current)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
current = append(current[:0], next...)
|
||||
}
|
||||
|
||||
index = idx + offset
|
||||
sub = s[index:]
|
||||
offset += idx
|
||||
}
|
||||
|
||||
releaseSegments(next)
|
||||
|
||||
return index, current
|
||||
}
|
||||
|
||||
func (self EveryOf) Match(s string) bool {
|
||||
for _, m := range self.Matchers {
|
||||
if !m.Match(s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self EveryOf) String() string {
|
||||
return fmt.Sprintf("<every_of:[%s]>", self.Matchers)
|
||||
}
|
||||
49
vendor/github.com/gobwas/glob/match/list.go
generated
vendored
Normal file
49
vendor/github.com/gobwas/glob/match/list.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type List struct {
|
||||
List []rune
|
||||
Not bool
|
||||
}
|
||||
|
||||
func NewList(list []rune, not bool) List {
|
||||
return List{list, not}
|
||||
}
|
||||
|
||||
func (self List) Match(s string) bool {
|
||||
r, w := utf8.DecodeRuneInString(s)
|
||||
if len(s) > w {
|
||||
return false
|
||||
}
|
||||
|
||||
inList := runes.IndexRune(self.List, r) != -1
|
||||
return inList == !self.Not
|
||||
}
|
||||
|
||||
func (self List) Len() int {
|
||||
return lenOne
|
||||
}
|
||||
|
||||
func (self List) Index(s string) (int, []int) {
|
||||
for i, r := range s {
|
||||
if self.Not == (runes.IndexRune(self.List, r) == -1) {
|
||||
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self List) String() string {
|
||||
var not string
|
||||
if self.Not {
|
||||
not = "!"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<list:%s[%s]>", not, string(self.List))
|
||||
}
|
||||
81
vendor/github.com/gobwas/glob/match/match.go
generated
vendored
Normal file
81
vendor/github.com/gobwas/glob/match/match.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package match
|
||||
|
||||
// todo common table of rune's length
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const lenOne = 1
|
||||
const lenZero = 0
|
||||
const lenNo = -1
|
||||
|
||||
type Matcher interface {
|
||||
Match(string) bool
|
||||
Index(string) (int, []int)
|
||||
Len() int
|
||||
String() string
|
||||
}
|
||||
|
||||
type Matchers []Matcher
|
||||
|
||||
func (m Matchers) String() string {
|
||||
var s []string
|
||||
for _, matcher := range m {
|
||||
s = append(s, fmt.Sprint(matcher))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", strings.Join(s, ","))
|
||||
}
|
||||
|
||||
// appendMerge merges and sorts given already SORTED and UNIQUE segments.
|
||||
func appendMerge(target, sub []int) []int {
|
||||
lt, ls := len(target), len(sub)
|
||||
out := make([]int, 0, lt+ls)
|
||||
|
||||
for x, y := 0, 0; x < lt || y < ls; {
|
||||
if x >= lt {
|
||||
out = append(out, sub[y:]...)
|
||||
break
|
||||
}
|
||||
|
||||
if y >= ls {
|
||||
out = append(out, target[x:]...)
|
||||
break
|
||||
}
|
||||
|
||||
xValue := target[x]
|
||||
yValue := sub[y]
|
||||
|
||||
switch {
|
||||
|
||||
case xValue == yValue:
|
||||
out = append(out, xValue)
|
||||
x++
|
||||
y++
|
||||
|
||||
case xValue < yValue:
|
||||
out = append(out, xValue)
|
||||
x++
|
||||
|
||||
case yValue < xValue:
|
||||
out = append(out, yValue)
|
||||
y++
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
target = append(target[:0], out...)
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
func reverseSegments(input []int) {
|
||||
l := len(input)
|
||||
m := l / 2
|
||||
|
||||
for i := 0; i < m; i++ {
|
||||
input[i], input[l-i-1] = input[l-i-1], input[i]
|
||||
}
|
||||
}
|
||||
49
vendor/github.com/gobwas/glob/match/max.go
generated
vendored
Normal file
49
vendor/github.com/gobwas/glob/match/max.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Max struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
func NewMax(l int) Max {
|
||||
return Max{l}
|
||||
}
|
||||
|
||||
func (self Max) Match(s string) bool {
|
||||
var l int
|
||||
for range s {
|
||||
l += 1
|
||||
if l > self.Limit {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self Max) Index(s string) (int, []int) {
|
||||
segments := acquireSegments(self.Limit + 1)
|
||||
segments = append(segments, 0)
|
||||
var count int
|
||||
for i, r := range s {
|
||||
count++
|
||||
if count > self.Limit {
|
||||
break
|
||||
}
|
||||
segments = append(segments, i+utf8.RuneLen(r))
|
||||
}
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Max) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Max) String() string {
|
||||
return fmt.Sprintf("<max:%d>", self.Limit)
|
||||
}
|
||||
57
vendor/github.com/gobwas/glob/match/min.go
generated
vendored
Normal file
57
vendor/github.com/gobwas/glob/match/min.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Min struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
func NewMin(l int) Min {
|
||||
return Min{l}
|
||||
}
|
||||
|
||||
func (self Min) Match(s string) bool {
|
||||
var l int
|
||||
for range s {
|
||||
l += 1
|
||||
if l >= self.Limit {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self Min) Index(s string) (int, []int) {
|
||||
var count int
|
||||
|
||||
c := len(s) - self.Limit + 1
|
||||
if c <= 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
segments := acquireSegments(c)
|
||||
for i, r := range s {
|
||||
count++
|
||||
if count >= self.Limit {
|
||||
segments = append(segments, i+utf8.RuneLen(r))
|
||||
}
|
||||
}
|
||||
|
||||
if len(segments) == 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Min) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Min) String() string {
|
||||
return fmt.Sprintf("<min:%d>", self.Limit)
|
||||
}
|
||||
27
vendor/github.com/gobwas/glob/match/nothing.go
generated
vendored
Normal file
27
vendor/github.com/gobwas/glob/match/nothing.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Nothing struct{}
|
||||
|
||||
func NewNothing() Nothing {
|
||||
return Nothing{}
|
||||
}
|
||||
|
||||
func (self Nothing) Match(s string) bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
func (self Nothing) Index(s string) (int, []int) {
|
||||
return 0, segments0
|
||||
}
|
||||
|
||||
func (self Nothing) Len() int {
|
||||
return lenZero
|
||||
}
|
||||
|
||||
func (self Nothing) String() string {
|
||||
return fmt.Sprintf("<nothing>")
|
||||
}
|
||||
50
vendor/github.com/gobwas/glob/match/prefix.go
generated
vendored
Normal file
50
vendor/github.com/gobwas/glob/match/prefix.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Prefix struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func NewPrefix(p string) Prefix {
|
||||
return Prefix{p}
|
||||
}
|
||||
|
||||
func (self Prefix) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Prefix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
length := len(self.Prefix)
|
||||
var sub string
|
||||
if len(s) > idx+length {
|
||||
sub = s[idx+length:]
|
||||
} else {
|
||||
sub = ""
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(sub) + 1)
|
||||
segments = append(segments, length)
|
||||
for i, r := range sub {
|
||||
segments = append(segments, length+i+utf8.RuneLen(r))
|
||||
}
|
||||
|
||||
return idx, segments
|
||||
}
|
||||
|
||||
func (self Prefix) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Prefix) Match(s string) bool {
|
||||
return strings.HasPrefix(s, self.Prefix)
|
||||
}
|
||||
|
||||
func (self Prefix) String() string {
|
||||
return fmt.Sprintf("<prefix:%s>", self.Prefix)
|
||||
}
|
||||
55
vendor/github.com/gobwas/glob/match/prefix_any.go
generated
vendored
Normal file
55
vendor/github.com/gobwas/glob/match/prefix_any.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
sutil "github.com/gobwas/glob/util/strings"
|
||||
)
|
||||
|
||||
type PrefixAny struct {
|
||||
Prefix string
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewPrefixAny(s string, sep []rune) PrefixAny {
|
||||
return PrefixAny{s, sep}
|
||||
}
|
||||
|
||||
func (self PrefixAny) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Prefix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
n := len(self.Prefix)
|
||||
sub := s[idx+n:]
|
||||
i := sutil.IndexAnyRunes(sub, self.Separators)
|
||||
if i > -1 {
|
||||
sub = sub[:i]
|
||||
}
|
||||
|
||||
seg := acquireSegments(len(sub) + 1)
|
||||
seg = append(seg, n)
|
||||
for i, r := range sub {
|
||||
seg = append(seg, n+i+utf8.RuneLen(r))
|
||||
}
|
||||
|
||||
return idx, seg
|
||||
}
|
||||
|
||||
func (self PrefixAny) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self PrefixAny) Match(s string) bool {
|
||||
if !strings.HasPrefix(s, self.Prefix) {
|
||||
return false
|
||||
}
|
||||
return sutil.IndexAnyRunes(s[len(self.Prefix):], self.Separators) == -1
|
||||
}
|
||||
|
||||
func (self PrefixAny) String() string {
|
||||
return fmt.Sprintf("<prefix_any:%s![%s]>", self.Prefix, string(self.Separators))
|
||||
}
|
||||
62
vendor/github.com/gobwas/glob/match/prefix_suffix.go
generated
vendored
Normal file
62
vendor/github.com/gobwas/glob/match/prefix_suffix.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PrefixSuffix struct {
|
||||
Prefix, Suffix string
|
||||
}
|
||||
|
||||
func NewPrefixSuffix(p, s string) PrefixSuffix {
|
||||
return PrefixSuffix{p, s}
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) Index(s string) (int, []int) {
|
||||
prefixIdx := strings.Index(s, self.Prefix)
|
||||
if prefixIdx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
suffixLen := len(self.Suffix)
|
||||
if suffixLen <= 0 {
|
||||
return prefixIdx, []int{len(s) - prefixIdx}
|
||||
}
|
||||
|
||||
if (len(s) - prefixIdx) <= 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(s) - prefixIdx)
|
||||
for sub := s[prefixIdx:]; ; {
|
||||
suffixIdx := strings.LastIndex(sub, self.Suffix)
|
||||
if suffixIdx == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
segments = append(segments, suffixIdx+suffixLen)
|
||||
sub = sub[:suffixIdx]
|
||||
}
|
||||
|
||||
if len(segments) == 0 {
|
||||
releaseSegments(segments)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
reverseSegments(segments)
|
||||
|
||||
return prefixIdx, segments
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) Match(s string) bool {
|
||||
return strings.HasPrefix(s, self.Prefix) && strings.HasSuffix(s, self.Suffix)
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) String() string {
|
||||
return fmt.Sprintf("<prefix_suffix:[%s,%s]>", self.Prefix, self.Suffix)
|
||||
}
|
||||
48
vendor/github.com/gobwas/glob/match/range.go
generated
vendored
Normal file
48
vendor/github.com/gobwas/glob/match/range.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Range struct {
|
||||
Lo, Hi rune
|
||||
Not bool
|
||||
}
|
||||
|
||||
func NewRange(lo, hi rune, not bool) Range {
|
||||
return Range{lo, hi, not}
|
||||
}
|
||||
|
||||
func (self Range) Len() int {
|
||||
return lenOne
|
||||
}
|
||||
|
||||
func (self Range) Match(s string) bool {
|
||||
r, w := utf8.DecodeRuneInString(s)
|
||||
if len(s) > w {
|
||||
return false
|
||||
}
|
||||
|
||||
inRange := r >= self.Lo && r <= self.Hi
|
||||
|
||||
return inRange == !self.Not
|
||||
}
|
||||
|
||||
func (self Range) Index(s string) (int, []int) {
|
||||
for i, r := range s {
|
||||
if self.Not != (r >= self.Lo && r <= self.Hi) {
|
||||
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self Range) String() string {
|
||||
var not string
|
||||
if self.Not {
|
||||
not = "!"
|
||||
}
|
||||
return fmt.Sprintf("<range:%s[%s,%s]>", not, string(self.Lo), string(self.Hi))
|
||||
}
|
||||
77
vendor/github.com/gobwas/glob/match/row.go
generated
vendored
Normal file
77
vendor/github.com/gobwas/glob/match/row.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Row struct {
|
||||
Matchers Matchers
|
||||
RunesLength int
|
||||
Segments []int
|
||||
}
|
||||
|
||||
func NewRow(len int, m ...Matcher) Row {
|
||||
return Row{
|
||||
Matchers: Matchers(m),
|
||||
RunesLength: len,
|
||||
Segments: []int{len},
|
||||
}
|
||||
}
|
||||
|
||||
func (self Row) matchAll(s string) bool {
|
||||
var idx int
|
||||
for _, m := range self.Matchers {
|
||||
length := m.Len()
|
||||
|
||||
var next, i int
|
||||
for next = range s[idx:] {
|
||||
i++
|
||||
if i == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i < length || !m.Match(s[idx:idx+next+1]) {
|
||||
return false
|
||||
}
|
||||
|
||||
idx += next + 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self Row) lenOk(s string) bool {
|
||||
var i int
|
||||
for range s {
|
||||
i++
|
||||
if i > self.RunesLength {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return self.RunesLength == i
|
||||
}
|
||||
|
||||
func (self Row) Match(s string) bool {
|
||||
return self.lenOk(s) && self.matchAll(s)
|
||||
}
|
||||
|
||||
func (self Row) Len() (l int) {
|
||||
return self.RunesLength
|
||||
}
|
||||
|
||||
func (self Row) Index(s string) (int, []int) {
|
||||
for i := range s {
|
||||
if len(s[i:]) < self.RunesLength {
|
||||
break
|
||||
}
|
||||
if self.matchAll(s[i:]) {
|
||||
return i, self.Segments
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self Row) String() string {
|
||||
return fmt.Sprintf("<row_%d:[%s]>", self.RunesLength, self.Matchers)
|
||||
}
|
||||
91
vendor/github.com/gobwas/glob/match/segments.go
generated
vendored
Normal file
91
vendor/github.com/gobwas/glob/match/segments.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type SomePool interface {
|
||||
Get() []int
|
||||
Put([]int)
|
||||
}
|
||||
|
||||
var segmentsPools [1024]sync.Pool
|
||||
|
||||
func toPowerOfTwo(v int) int {
|
||||
v--
|
||||
v |= v >> 1
|
||||
v |= v >> 2
|
||||
v |= v >> 4
|
||||
v |= v >> 8
|
||||
v |= v >> 16
|
||||
v++
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
const (
|
||||
cacheFrom = 16
|
||||
cacheToAndHigher = 1024
|
||||
cacheFromIndex = 15
|
||||
cacheToAndHigherIndex = 1023
|
||||
)
|
||||
|
||||
var (
|
||||
segments0 = []int{0}
|
||||
segments1 = []int{1}
|
||||
segments2 = []int{2}
|
||||
segments3 = []int{3}
|
||||
segments4 = []int{4}
|
||||
)
|
||||
|
||||
var segmentsByRuneLength [5][]int = [5][]int{
|
||||
0: segments0,
|
||||
1: segments1,
|
||||
2: segments2,
|
||||
3: segments3,
|
||||
4: segments4,
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := cacheToAndHigher; i >= cacheFrom; i >>= 1 {
|
||||
func(i int) {
|
||||
segmentsPools[i-1] = sync.Pool{New: func() interface{} {
|
||||
return make([]int, 0, i)
|
||||
}}
|
||||
}(i)
|
||||
}
|
||||
}
|
||||
|
||||
func getTableIndex(c int) int {
|
||||
p := toPowerOfTwo(c)
|
||||
switch {
|
||||
case p >= cacheToAndHigher:
|
||||
return cacheToAndHigherIndex
|
||||
case p <= cacheFrom:
|
||||
return cacheFromIndex
|
||||
default:
|
||||
return p - 1
|
||||
}
|
||||
}
|
||||
|
||||
func acquireSegments(c int) []int {
|
||||
// make []int with less capacity than cacheFrom
|
||||
// is faster than acquiring it from pool
|
||||
if c < cacheFrom {
|
||||
return make([]int, 0, c)
|
||||
}
|
||||
|
||||
return segmentsPools[getTableIndex(c)].Get().([]int)[:0]
|
||||
}
|
||||
|
||||
func releaseSegments(s []int) {
|
||||
c := cap(s)
|
||||
|
||||
// make []int with less capacity than cacheFrom
|
||||
// is faster than acquiring it from pool
|
||||
if c < cacheFrom {
|
||||
return
|
||||
}
|
||||
|
||||
segmentsPools[getTableIndex(c)].Put(s)
|
||||
}
|
||||
43
vendor/github.com/gobwas/glob/match/single.go
generated
vendored
Normal file
43
vendor/github.com/gobwas/glob/match/single.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// single represents ?
|
||||
type Single struct {
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewSingle(s []rune) Single {
|
||||
return Single{s}
|
||||
}
|
||||
|
||||
func (self Single) Match(s string) bool {
|
||||
r, w := utf8.DecodeRuneInString(s)
|
||||
if len(s) > w {
|
||||
return false
|
||||
}
|
||||
|
||||
return runes.IndexRune(self.Separators, r) == -1
|
||||
}
|
||||
|
||||
func (self Single) Len() int {
|
||||
return lenOne
|
||||
}
|
||||
|
||||
func (self Single) Index(s string) (int, []int) {
|
||||
for i, r := range s {
|
||||
if runes.IndexRune(self.Separators, r) == -1 {
|
||||
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self Single) String() string {
|
||||
return fmt.Sprintf("<single:![%s]>", string(self.Separators))
|
||||
}
|
||||
35
vendor/github.com/gobwas/glob/match/suffix.go
generated
vendored
Normal file
35
vendor/github.com/gobwas/glob/match/suffix.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Suffix struct {
|
||||
Suffix string
|
||||
}
|
||||
|
||||
func NewSuffix(s string) Suffix {
|
||||
return Suffix{s}
|
||||
}
|
||||
|
||||
func (self Suffix) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Suffix) Match(s string) bool {
|
||||
return strings.HasSuffix(s, self.Suffix)
|
||||
}
|
||||
|
||||
func (self Suffix) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Suffix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return 0, []int{idx + len(self.Suffix)}
|
||||
}
|
||||
|
||||
func (self Suffix) String() string {
|
||||
return fmt.Sprintf("<suffix:%s>", self.Suffix)
|
||||
}
|
||||
43
vendor/github.com/gobwas/glob/match/suffix_any.go
generated
vendored
Normal file
43
vendor/github.com/gobwas/glob/match/suffix_any.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sutil "github.com/gobwas/glob/util/strings"
|
||||
)
|
||||
|
||||
type SuffixAny struct {
|
||||
Suffix string
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewSuffixAny(s string, sep []rune) SuffixAny {
|
||||
return SuffixAny{s, sep}
|
||||
}
|
||||
|
||||
func (self SuffixAny) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Suffix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
i := sutil.LastIndexAnyRunes(s[:idx], self.Separators) + 1
|
||||
|
||||
return i, []int{idx + len(self.Suffix) - i}
|
||||
}
|
||||
|
||||
func (self SuffixAny) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self SuffixAny) Match(s string) bool {
|
||||
if !strings.HasSuffix(s, self.Suffix) {
|
||||
return false
|
||||
}
|
||||
return sutil.IndexAnyRunes(s[:len(s)-len(self.Suffix)], self.Separators) == -1
|
||||
}
|
||||
|
||||
func (self SuffixAny) String() string {
|
||||
return fmt.Sprintf("<suffix_any:![%s]%s>", string(self.Separators), self.Suffix)
|
||||
}
|
||||
33
vendor/github.com/gobwas/glob/match/super.go
generated
vendored
Normal file
33
vendor/github.com/gobwas/glob/match/super.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Super struct{}
|
||||
|
||||
func NewSuper() Super {
|
||||
return Super{}
|
||||
}
|
||||
|
||||
func (self Super) Match(s string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self Super) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Super) Index(s string) (int, []int) {
|
||||
segments := acquireSegments(len(s) + 1)
|
||||
for i := range s {
|
||||
segments = append(segments, i)
|
||||
}
|
||||
segments = append(segments, len(s))
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Super) String() string {
|
||||
return fmt.Sprintf("<super>")
|
||||
}
|
||||
45
vendor/github.com/gobwas/glob/match/text.go
generated
vendored
Normal file
45
vendor/github.com/gobwas/glob/match/text.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// raw represents raw string to match
|
||||
type Text struct {
|
||||
Str string
|
||||
RunesLength int
|
||||
BytesLength int
|
||||
Segments []int
|
||||
}
|
||||
|
||||
func NewText(s string) Text {
|
||||
return Text{
|
||||
Str: s,
|
||||
RunesLength: utf8.RuneCountInString(s),
|
||||
BytesLength: len(s),
|
||||
Segments: []int{len(s)},
|
||||
}
|
||||
}
|
||||
|
||||
func (self Text) Match(s string) bool {
|
||||
return self.Str == s
|
||||
}
|
||||
|
||||
func (self Text) Len() int {
|
||||
return self.RunesLength
|
||||
}
|
||||
|
||||
func (self Text) Index(s string) (int, []int) {
|
||||
index := strings.Index(s, self.Str)
|
||||
if index == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return index, self.Segments
|
||||
}
|
||||
|
||||
func (self Text) String() string {
|
||||
return fmt.Sprintf("<text:`%v`>", self.Str)
|
||||
}
|
||||
122
vendor/github.com/gobwas/glob/syntax/ast/ast.go
generated
vendored
Normal file
122
vendor/github.com/gobwas/glob/syntax/ast/ast.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Parent *Node
|
||||
Children []*Node
|
||||
Value interface{}
|
||||
Kind Kind
|
||||
}
|
||||
|
||||
func NewNode(k Kind, v interface{}, ch ...*Node) *Node {
|
||||
n := &Node{
|
||||
Kind: k,
|
||||
Value: v,
|
||||
}
|
||||
for _, c := range ch {
|
||||
Insert(n, c)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (a *Node) Equal(b *Node) bool {
|
||||
if a.Kind != b.Kind {
|
||||
return false
|
||||
}
|
||||
if a.Value != b.Value {
|
||||
return false
|
||||
}
|
||||
if len(a.Children) != len(b.Children) {
|
||||
return false
|
||||
}
|
||||
for i, c := range a.Children {
|
||||
if !c.Equal(b.Children[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Node) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(a.Kind.String())
|
||||
if a.Value != nil {
|
||||
buf.WriteString(" =")
|
||||
buf.WriteString(fmt.Sprintf("%v", a.Value))
|
||||
}
|
||||
if len(a.Children) > 0 {
|
||||
buf.WriteString(" [")
|
||||
for i, c := range a.Children {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(c.String())
|
||||
}
|
||||
buf.WriteString("]")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func Insert(parent *Node, children ...*Node) {
|
||||
parent.Children = append(parent.Children, children...)
|
||||
for _, ch := range children {
|
||||
ch.Parent = parent
|
||||
}
|
||||
}
|
||||
|
||||
type List struct {
|
||||
Not bool
|
||||
Chars string
|
||||
}
|
||||
|
||||
type Range struct {
|
||||
Not bool
|
||||
Lo, Hi rune
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Text string
|
||||
}
|
||||
|
||||
type Kind int
|
||||
|
||||
const (
|
||||
KindNothing Kind = iota
|
||||
KindPattern
|
||||
KindList
|
||||
KindRange
|
||||
KindText
|
||||
KindAny
|
||||
KindSuper
|
||||
KindSingle
|
||||
KindAnyOf
|
||||
)
|
||||
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case KindNothing:
|
||||
return "Nothing"
|
||||
case KindPattern:
|
||||
return "Pattern"
|
||||
case KindList:
|
||||
return "List"
|
||||
case KindRange:
|
||||
return "Range"
|
||||
case KindText:
|
||||
return "Text"
|
||||
case KindAny:
|
||||
return "Any"
|
||||
case KindSuper:
|
||||
return "Super"
|
||||
case KindSingle:
|
||||
return "Single"
|
||||
case KindAnyOf:
|
||||
return "AnyOf"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
157
vendor/github.com/gobwas/glob/syntax/ast/parser.go
generated
vendored
Normal file
157
vendor/github.com/gobwas/glob/syntax/ast/parser.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/syntax/lexer"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Lexer interface {
|
||||
Next() lexer.Token
|
||||
}
|
||||
|
||||
type parseFn func(*Node, Lexer) (parseFn, *Node, error)
|
||||
|
||||
func Parse(lexer Lexer) (*Node, error) {
|
||||
var parser parseFn
|
||||
|
||||
root := NewNode(KindPattern, nil)
|
||||
|
||||
var (
|
||||
tree *Node
|
||||
err error
|
||||
)
|
||||
for parser, tree = parserMain, root; parser != nil; {
|
||||
parser, tree, err = parser(tree, lexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func parserMain(tree *Node, lex Lexer) (parseFn, *Node, error) {
|
||||
for {
|
||||
token := lex.Next()
|
||||
switch token.Type {
|
||||
case lexer.EOF:
|
||||
return nil, tree, nil
|
||||
|
||||
case lexer.Error:
|
||||
return nil, tree, errors.New(token.Raw)
|
||||
|
||||
case lexer.Text:
|
||||
Insert(tree, NewNode(KindText, Text{token.Raw}))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.Any:
|
||||
Insert(tree, NewNode(KindAny, nil))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.Super:
|
||||
Insert(tree, NewNode(KindSuper, nil))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.Single:
|
||||
Insert(tree, NewNode(KindSingle, nil))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.RangeOpen:
|
||||
return parserRange, tree, nil
|
||||
|
||||
case lexer.TermsOpen:
|
||||
a := NewNode(KindAnyOf, nil)
|
||||
Insert(tree, a)
|
||||
|
||||
p := NewNode(KindPattern, nil)
|
||||
Insert(a, p)
|
||||
|
||||
return parserMain, p, nil
|
||||
|
||||
case lexer.Separator:
|
||||
p := NewNode(KindPattern, nil)
|
||||
Insert(tree.Parent, p)
|
||||
|
||||
return parserMain, p, nil
|
||||
|
||||
case lexer.TermsClose:
|
||||
return parserMain, tree.Parent.Parent, nil
|
||||
|
||||
default:
|
||||
return nil, tree, fmt.Errorf("unexpected token: %s", token)
|
||||
}
|
||||
}
|
||||
return nil, tree, fmt.Errorf("unknown error")
|
||||
}
|
||||
|
||||
func parserRange(tree *Node, lex Lexer) (parseFn, *Node, error) {
|
||||
var (
|
||||
not bool
|
||||
lo rune
|
||||
hi rune
|
||||
chars string
|
||||
)
|
||||
for {
|
||||
token := lex.Next()
|
||||
switch token.Type {
|
||||
case lexer.EOF:
|
||||
return nil, tree, errors.New("unexpected end")
|
||||
|
||||
case lexer.Error:
|
||||
return nil, tree, errors.New(token.Raw)
|
||||
|
||||
case lexer.Not:
|
||||
not = true
|
||||
|
||||
case lexer.RangeLo:
|
||||
r, w := utf8.DecodeRuneInString(token.Raw)
|
||||
if len(token.Raw) > w {
|
||||
return nil, tree, fmt.Errorf("unexpected length of lo character")
|
||||
}
|
||||
lo = r
|
||||
|
||||
case lexer.RangeBetween:
|
||||
//
|
||||
|
||||
case lexer.RangeHi:
|
||||
r, w := utf8.DecodeRuneInString(token.Raw)
|
||||
if len(token.Raw) > w {
|
||||
return nil, tree, fmt.Errorf("unexpected length of lo character")
|
||||
}
|
||||
|
||||
hi = r
|
||||
|
||||
if hi < lo {
|
||||
return nil, tree, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo))
|
||||
}
|
||||
|
||||
case lexer.Text:
|
||||
chars = token.Raw
|
||||
|
||||
case lexer.RangeClose:
|
||||
isRange := lo != 0 && hi != 0
|
||||
isChars := chars != ""
|
||||
|
||||
if isChars == isRange {
|
||||
return nil, tree, fmt.Errorf("could not parse range")
|
||||
}
|
||||
|
||||
if isRange {
|
||||
Insert(tree, NewNode(KindRange, Range{
|
||||
Lo: lo,
|
||||
Hi: hi,
|
||||
Not: not,
|
||||
}))
|
||||
} else {
|
||||
Insert(tree, NewNode(KindList, List{
|
||||
Chars: chars,
|
||||
Not: not,
|
||||
}))
|
||||
}
|
||||
|
||||
return parserMain, tree, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
273
vendor/github.com/gobwas/glob/syntax/lexer/lexer.go
generated
vendored
Normal file
273
vendor/github.com/gobwas/glob/syntax/lexer/lexer.go
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
char_any = '*'
|
||||
char_comma = ','
|
||||
char_single = '?'
|
||||
char_escape = '\\'
|
||||
char_range_open = '['
|
||||
char_range_close = ']'
|
||||
char_terms_open = '{'
|
||||
char_terms_close = '}'
|
||||
char_range_not = '!'
|
||||
char_range_between = '-'
|
||||
)
|
||||
|
||||
var specials = []byte{
|
||||
char_any,
|
||||
char_single,
|
||||
char_escape,
|
||||
char_range_open,
|
||||
char_range_close,
|
||||
char_terms_open,
|
||||
char_terms_close,
|
||||
}
|
||||
|
||||
func Special(c byte) bool {
|
||||
return bytes.IndexByte(specials, c) != -1
|
||||
}
|
||||
|
||||
type tokens []Token
|
||||
|
||||
func (i *tokens) shift() (ret Token) {
|
||||
ret = (*i)[0]
|
||||
copy(*i, (*i)[1:])
|
||||
*i = (*i)[:len(*i)-1]
|
||||
return
|
||||
}
|
||||
|
||||
func (i *tokens) push(v Token) {
|
||||
*i = append(*i, v)
|
||||
}
|
||||
|
||||
func (i *tokens) empty() bool {
|
||||
return len(*i) == 0
|
||||
}
|
||||
|
||||
var eof rune = 0
|
||||
|
||||
type lexer struct {
|
||||
data string
|
||||
pos int
|
||||
err error
|
||||
|
||||
tokens tokens
|
||||
termsLevel int
|
||||
|
||||
lastRune rune
|
||||
lastRuneSize int
|
||||
hasRune bool
|
||||
}
|
||||
|
||||
func NewLexer(source string) *lexer {
|
||||
l := &lexer{
|
||||
data: source,
|
||||
tokens: tokens(make([]Token, 0, 4)),
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *lexer) Next() Token {
|
||||
if l.err != nil {
|
||||
return Token{Error, l.err.Error()}
|
||||
}
|
||||
if !l.tokens.empty() {
|
||||
return l.tokens.shift()
|
||||
}
|
||||
|
||||
l.fetchItem()
|
||||
return l.Next()
|
||||
}
|
||||
|
||||
func (l *lexer) peek() (r rune, w int) {
|
||||
if l.pos == len(l.data) {
|
||||
return eof, 0
|
||||
}
|
||||
|
||||
r, w = utf8.DecodeRuneInString(l.data[l.pos:])
|
||||
if r == utf8.RuneError {
|
||||
l.errorf("could not read rune")
|
||||
r = eof
|
||||
w = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *lexer) read() rune {
|
||||
if l.hasRune {
|
||||
l.hasRune = false
|
||||
l.seek(l.lastRuneSize)
|
||||
return l.lastRune
|
||||
}
|
||||
|
||||
r, s := l.peek()
|
||||
l.seek(s)
|
||||
|
||||
l.lastRune = r
|
||||
l.lastRuneSize = s
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *lexer) seek(w int) {
|
||||
l.pos += w
|
||||
}
|
||||
|
||||
func (l *lexer) unread() {
|
||||
if l.hasRune {
|
||||
l.errorf("could not unread rune")
|
||||
return
|
||||
}
|
||||
l.seek(-l.lastRuneSize)
|
||||
l.hasRune = true
|
||||
}
|
||||
|
||||
func (l *lexer) errorf(f string, v ...interface{}) {
|
||||
l.err = fmt.Errorf(f, v...)
|
||||
}
|
||||
|
||||
func (l *lexer) inTerms() bool {
|
||||
return l.termsLevel > 0
|
||||
}
|
||||
|
||||
func (l *lexer) termsEnter() {
|
||||
l.termsLevel++
|
||||
}
|
||||
|
||||
func (l *lexer) termsLeave() {
|
||||
l.termsLevel--
|
||||
}
|
||||
|
||||
var inTextBreakers = []rune{char_single, char_any, char_range_open, char_terms_open}
|
||||
var inTermsBreakers = append(inTextBreakers, char_terms_close, char_comma)
|
||||
|
||||
func (l *lexer) fetchItem() {
|
||||
r := l.read()
|
||||
switch {
|
||||
case r == eof:
|
||||
l.tokens.push(Token{EOF, ""})
|
||||
|
||||
case r == char_terms_open:
|
||||
l.termsEnter()
|
||||
l.tokens.push(Token{TermsOpen, string(r)})
|
||||
|
||||
case r == char_comma && l.inTerms():
|
||||
l.tokens.push(Token{Separator, string(r)})
|
||||
|
||||
case r == char_terms_close && l.inTerms():
|
||||
l.tokens.push(Token{TermsClose, string(r)})
|
||||
l.termsLeave()
|
||||
|
||||
case r == char_range_open:
|
||||
l.tokens.push(Token{RangeOpen, string(r)})
|
||||
l.fetchRange()
|
||||
|
||||
case r == char_single:
|
||||
l.tokens.push(Token{Single, string(r)})
|
||||
|
||||
case r == char_any:
|
||||
if l.read() == char_any {
|
||||
l.tokens.push(Token{Super, string(r) + string(r)})
|
||||
} else {
|
||||
l.unread()
|
||||
l.tokens.push(Token{Any, string(r)})
|
||||
}
|
||||
|
||||
default:
|
||||
l.unread()
|
||||
|
||||
var breakers []rune
|
||||
if l.inTerms() {
|
||||
breakers = inTermsBreakers
|
||||
} else {
|
||||
breakers = inTextBreakers
|
||||
}
|
||||
l.fetchText(breakers)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) fetchRange() {
|
||||
var wantHi bool
|
||||
var wantClose bool
|
||||
var seenNot bool
|
||||
for {
|
||||
r := l.read()
|
||||
if r == eof {
|
||||
l.errorf("unexpected end of input")
|
||||
return
|
||||
}
|
||||
|
||||
if wantClose {
|
||||
if r != char_range_close {
|
||||
l.errorf("expected close range character")
|
||||
} else {
|
||||
l.tokens.push(Token{RangeClose, string(r)})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if wantHi {
|
||||
l.tokens.push(Token{RangeHi, string(r)})
|
||||
wantClose = true
|
||||
continue
|
||||
}
|
||||
|
||||
if !seenNot && r == char_range_not {
|
||||
l.tokens.push(Token{Not, string(r)})
|
||||
seenNot = true
|
||||
continue
|
||||
}
|
||||
|
||||
if n, w := l.peek(); n == char_range_between {
|
||||
l.seek(w)
|
||||
l.tokens.push(Token{RangeLo, string(r)})
|
||||
l.tokens.push(Token{RangeBetween, string(n)})
|
||||
wantHi = true
|
||||
continue
|
||||
}
|
||||
|
||||
l.unread() // unread first peek and fetch as text
|
||||
l.fetchText([]rune{char_range_close})
|
||||
wantClose = true
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) fetchText(breakers []rune) {
|
||||
var data []rune
|
||||
var escaped bool
|
||||
|
||||
reading:
|
||||
for {
|
||||
r := l.read()
|
||||
if r == eof {
|
||||
break
|
||||
}
|
||||
|
||||
if !escaped {
|
||||
if r == char_escape {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
|
||||
if runes.IndexRune(breakers, r) != -1 {
|
||||
l.unread()
|
||||
break reading
|
||||
}
|
||||
}
|
||||
|
||||
escaped = false
|
||||
data = append(data, r)
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
l.tokens.push(Token{Text, string(data)})
|
||||
}
|
||||
}
|
||||
88
vendor/github.com/gobwas/glob/syntax/lexer/token.go
generated
vendored
Normal file
88
vendor/github.com/gobwas/glob/syntax/lexer/token.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package lexer
|
||||
|
||||
import "fmt"
|
||||
|
||||
type TokenType int
|
||||
|
||||
const (
|
||||
EOF TokenType = iota
|
||||
Error
|
||||
Text
|
||||
Char
|
||||
Any
|
||||
Super
|
||||
Single
|
||||
Not
|
||||
Separator
|
||||
RangeOpen
|
||||
RangeClose
|
||||
RangeLo
|
||||
RangeHi
|
||||
RangeBetween
|
||||
TermsOpen
|
||||
TermsClose
|
||||
)
|
||||
|
||||
func (tt TokenType) String() string {
|
||||
switch tt {
|
||||
case EOF:
|
||||
return "eof"
|
||||
|
||||
case Error:
|
||||
return "error"
|
||||
|
||||
case Text:
|
||||
return "text"
|
||||
|
||||
case Char:
|
||||
return "char"
|
||||
|
||||
case Any:
|
||||
return "any"
|
||||
|
||||
case Super:
|
||||
return "super"
|
||||
|
||||
case Single:
|
||||
return "single"
|
||||
|
||||
case Not:
|
||||
return "not"
|
||||
|
||||
case Separator:
|
||||
return "separator"
|
||||
|
||||
case RangeOpen:
|
||||
return "range_open"
|
||||
|
||||
case RangeClose:
|
||||
return "range_close"
|
||||
|
||||
case RangeLo:
|
||||
return "range_lo"
|
||||
|
||||
case RangeHi:
|
||||
return "range_hi"
|
||||
|
||||
case RangeBetween:
|
||||
return "range_between"
|
||||
|
||||
case TermsOpen:
|
||||
return "terms_open"
|
||||
|
||||
case TermsClose:
|
||||
return "terms_close"
|
||||
|
||||
default:
|
||||
return "undef"
|
||||
}
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Type TokenType
|
||||
Raw string
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
return fmt.Sprintf("%v<%q>", t.Type, t.Raw)
|
||||
}
|
||||
14
vendor/github.com/gobwas/glob/syntax/syntax.go
generated
vendored
Normal file
14
vendor/github.com/gobwas/glob/syntax/syntax.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob/syntax/ast"
|
||||
"github.com/gobwas/glob/syntax/lexer"
|
||||
)
|
||||
|
||||
func Parse(s string) (*ast.Node, error) {
|
||||
return ast.Parse(lexer.NewLexer(s))
|
||||
}
|
||||
|
||||
func Special(b byte) bool {
|
||||
return lexer.Special(b)
|
||||
}
|
||||
154
vendor/github.com/gobwas/glob/util/runes/runes.go
generated
vendored
Normal file
154
vendor/github.com/gobwas/glob/util/runes/runes.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package runes
|
||||
|
||||
func Index(s, needle []rune) int {
|
||||
ls, ln := len(s), len(needle)
|
||||
|
||||
switch {
|
||||
case ln == 0:
|
||||
return 0
|
||||
case ln == 1:
|
||||
return IndexRune(s, needle[0])
|
||||
case ln == ls:
|
||||
if Equal(s, needle) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case ln > ls:
|
||||
return -1
|
||||
}
|
||||
|
||||
head:
|
||||
for i := 0; i < ls && ls-i >= ln; i++ {
|
||||
for y := 0; y < ln; y++ {
|
||||
if s[i+y] != needle[y] {
|
||||
continue head
|
||||
}
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func LastIndex(s, needle []rune) int {
|
||||
ls, ln := len(s), len(needle)
|
||||
|
||||
switch {
|
||||
case ln == 0:
|
||||
if ls == 0 {
|
||||
return 0
|
||||
}
|
||||
return ls
|
||||
case ln == 1:
|
||||
return IndexLastRune(s, needle[0])
|
||||
case ln == ls:
|
||||
if Equal(s, needle) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case ln > ls:
|
||||
return -1
|
||||
}
|
||||
|
||||
head:
|
||||
for i := ls - 1; i >= 0 && i >= ln; i-- {
|
||||
for y := ln - 1; y >= 0; y-- {
|
||||
if s[i-(ln-y-1)] != needle[y] {
|
||||
continue head
|
||||
}
|
||||
}
|
||||
|
||||
return i - ln + 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// IndexAny returns the index of the first instance of any Unicode code point
|
||||
// from chars in s, or -1 if no Unicode code point from chars is present in s.
|
||||
func IndexAny(s, chars []rune) int {
|
||||
if len(chars) > 0 {
|
||||
for i, c := range s {
|
||||
for _, m := range chars {
|
||||
if c == m {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func Contains(s, needle []rune) bool {
|
||||
return Index(s, needle) >= 0
|
||||
}
|
||||
|
||||
func Max(s []rune) (max rune) {
|
||||
for _, r := range s {
|
||||
if r > max {
|
||||
max = r
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Min(s []rune) rune {
|
||||
min := rune(-1)
|
||||
for _, r := range s {
|
||||
if min == -1 {
|
||||
min = r
|
||||
continue
|
||||
}
|
||||
|
||||
if r < min {
|
||||
min = r
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
func IndexRune(s []rune, r rune) int {
|
||||
for i, c := range s {
|
||||
if c == r {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func IndexLastRune(s []rune, r rune) int {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == r {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func Equal(a, b []rune) bool {
|
||||
if len(a) == len(b) {
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPrefix tests whether the string s begins with prefix.
|
||||
func HasPrefix(s, prefix []rune) bool {
|
||||
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
// HasSuffix tests whether the string s ends with suffix.
|
||||
func HasSuffix(s, suffix []rune) bool {
|
||||
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
|
||||
}
|
||||
39
vendor/github.com/gobwas/glob/util/strings/strings.go
generated
vendored
Normal file
39
vendor/github.com/gobwas/glob/util/strings/strings.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package strings
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func IndexAnyRunes(s string, rs []rune) int {
|
||||
for _, r := range rs {
|
||||
if i := strings.IndexRune(s, r); i != -1 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func LastIndexAnyRunes(s string, rs []rune) int {
|
||||
for _, r := range rs {
|
||||
i := -1
|
||||
if 0 <= r && r < utf8.RuneSelf {
|
||||
i = strings.LastIndexByte(s, byte(r))
|
||||
} else {
|
||||
sub := s
|
||||
for len(sub) > 0 {
|
||||
j := strings.IndexRune(s, r)
|
||||
if j == -1 {
|
||||
break
|
||||
}
|
||||
i = j
|
||||
sub = sub[i+1:]
|
||||
}
|
||||
}
|
||||
if i != -1 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
Reference in New Issue
Block a user