mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 12:51:55 +00:00
search: Render patterns and filters as "chips" when keyword search is enabled (#59973)
This commit makes the following changes: - When the search pattern type is set to `keyword`, filters and patterns are decorated with a background to more easily identify keywords and sequences of keywords. - Escaped quotation marks are decorated differently (same as regex escape sequences). This takes into account the delimiter with which the pattern was quoted (i.e. `"\""` searches for `"` but `'\"'` searches for `\"` and thus `\"` is not an escape sequence). - Hover tooltips are tweaked to show the realy value that a pattern is searching for when it contains escaped delimiters. Co-authored-by: rob rhyne <rob@sourcegraph.com> Co-authored-by: Camden Cheek <camden@ccheek.com>
This commit is contained in:
parent
afe49d23e2
commit
bb2b7b3b64
@ -1,30 +1,67 @@
|
||||
import { RangeSetBuilder } from '@codemirror/state'
|
||||
import { Decoration, EditorView } from '@codemirror/view'
|
||||
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { Token } from '@sourcegraph/shared/src/search/query/token'
|
||||
import { isFilterOfType } from '@sourcegraph/shared/src/search/query/utils'
|
||||
|
||||
import { decoratedTokens, queryTokens } from '../../codemirror/parsedQuery'
|
||||
import { queryTokens } from '../../codemirror/parsedQuery'
|
||||
|
||||
const contextFilter = Decoration.mark({ class: 'sg-query-token-filter-context', inclusiveEnd: false })
|
||||
const filter = Decoration.mark({ class: 'sg-query-token sg-query-token-filter' })
|
||||
const pattern = Decoration.mark({ class: 'sg-query-token sg-query-token-pattern' })
|
||||
|
||||
function getDecorationForToken(token: Token): Decoration | null {
|
||||
switch (token.type) {
|
||||
case 'filter': {
|
||||
return filter
|
||||
}
|
||||
case 'pattern': {
|
||||
return pattern
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const filterDecoration = [
|
||||
EditorView.baseTheme({
|
||||
'.sg-query-token-filter-context': {
|
||||
'.sg-query-token': {
|
||||
borderRadius: '3px',
|
||||
padding: '1px 0',
|
||||
backgroundColor: '#eff2f5a0', // --gray-02 with transparency to make selection visible
|
||||
// We only apply little horizontal padding because it appears that
|
||||
// the padding interferes with the cursor position (CodeMirror will
|
||||
// place the cursor after the padding, not after the last character,
|
||||
// which is surprising to the user).
|
||||
padding: '1px 3px',
|
||||
},
|
||||
'.theme-dark & .sg-query-token-filter-context': {
|
||||
backgroundColor: '#343a4da0', // --gray-08 with transparency to make selection visible
|
||||
'.sg-query-token-pattern': {
|
||||
backgroundColor: '#E6EBF295', // --gray-03 with transparency to make text selection visible
|
||||
},
|
||||
'.sg-query-token-filter': {
|
||||
backgroundColor: '#CCEDFFa0', // --oc-blue-1 with transparency to make text selection visible
|
||||
},
|
||||
|
||||
'.theme-dark & .sg-query-token-pattern': {
|
||||
backgroundColor: '#5E6E8C80', // --gray-08 with transparency to make text selection visible
|
||||
},
|
||||
|
||||
'.theme-dark & .sg-query-token-filter': {
|
||||
backgroundColor: '#074884a0', // --oc-blue-9 with transparency to make text selection visible
|
||||
},
|
||||
}),
|
||||
EditorView.decorations.compute([decoratedTokens, 'selection'], state => {
|
||||
EditorView.decorations.compute([queryTokens, 'selection'], state => {
|
||||
const query = state.facet(queryTokens)
|
||||
const builder = new RangeSetBuilder<Decoration>()
|
||||
for (const token of query.tokens) {
|
||||
if (token.type === 'filter' && isFilterOfType(token, FilterType.context)) {
|
||||
builder.add(token.range.start, token.range.end, contextFilter)
|
||||
let decoration: Decoration | null = null
|
||||
if (query.patternType === SearchPatternType.keyword) {
|
||||
decoration = getDecorationForToken(token)
|
||||
} else if (token.type === 'filter' && isFilterOfType(token, FilterType.context)) {
|
||||
// In non-keyword mode, the context filter is styled the same
|
||||
// way as regular patterns.
|
||||
decoration = pattern
|
||||
}
|
||||
if (decoration) {
|
||||
builder.add(token.range.start, token.range.end, decoration)
|
||||
}
|
||||
}
|
||||
return builder.finish()
|
||||
|
||||
@ -31,6 +31,7 @@ const getTokens = (tokens: Token[]): { startIndex: number; scopes: string }[] =>
|
||||
scopes: token.type,
|
||||
}
|
||||
}
|
||||
case 'metaKeyword':
|
||||
case 'metaPath':
|
||||
case 'metaRevision':
|
||||
case 'metaRegexp':
|
||||
@ -1825,31 +1826,88 @@ describe('scanSearchQuery() and decorate()', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
test('Do not highlight keywords inside quotes for keyword pattern type', () => {
|
||||
test('do not decorate keywords inside quotes for keyword pattern type', () => {
|
||||
expect(getTokens(toSuccess(scanSearchQuery('"foo and bar" and bas', false, SearchPatternType.keyword))))
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"startIndex": 0,
|
||||
"scopes": "identifier"
|
||||
},
|
||||
{
|
||||
"startIndex": 13,
|
||||
"scopes": "whitespace"
|
||||
},
|
||||
{
|
||||
"startIndex": 14,
|
||||
"scopes": "keyword"
|
||||
},
|
||||
{
|
||||
"startIndex": 17,
|
||||
"scopes": "whitespace"
|
||||
},
|
||||
{
|
||||
"startIndex": 18,
|
||||
"scopes": "identifier"
|
||||
}
|
||||
]
|
||||
`)
|
||||
[
|
||||
{
|
||||
"startIndex": 0,
|
||||
"scopes": "identifier"
|
||||
},
|
||||
{
|
||||
"startIndex": 13,
|
||||
"scopes": "whitespace"
|
||||
},
|
||||
{
|
||||
"startIndex": 14,
|
||||
"scopes": "keyword"
|
||||
},
|
||||
{
|
||||
"startIndex": 17,
|
||||
"scopes": "whitespace"
|
||||
},
|
||||
{
|
||||
"startIndex": 18,
|
||||
"scopes": "identifier"
|
||||
}
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
test('decorate escaped quotes inside quoted patterns', () => {
|
||||
expect(getTokens(toSuccess(scanSearchQuery(String.raw`"foo\"\'bar\""`, false, SearchPatternType.keyword))))
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"startIndex": 0,
|
||||
"scopes": "identifier"
|
||||
},
|
||||
{
|
||||
"startIndex": 4,
|
||||
"scopes": "metaKeywordEscapedCharacter"
|
||||
},
|
||||
{
|
||||
"startIndex": 11,
|
||||
"scopes": "metaKeywordEscapedCharacter"
|
||||
}
|
||||
]
|
||||
`)
|
||||
expect(getTokens(toSuccess(scanSearchQuery(String.raw`'foo\"\'bar\''`, false, SearchPatternType.keyword))))
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"startIndex": 0,
|
||||
"scopes": "identifier"
|
||||
},
|
||||
{
|
||||
"startIndex": 6,
|
||||
"scopes": "metaKeywordEscapedCharacter"
|
||||
},
|
||||
{
|
||||
"startIndex": 11,
|
||||
"scopes": "metaKeywordEscapedCharacter"
|
||||
}
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
test('do not decorate quotes inside quoted filter values', () => {
|
||||
expect(getTokens(toSuccess(scanSearchQuery(String.raw`file:"foo\"bar"`, false, SearchPatternType.keyword))))
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"startIndex": 0,
|
||||
"scopes": "field"
|
||||
},
|
||||
{
|
||||
"startIndex": 4,
|
||||
"scopes": "metaFilterSeparator"
|
||||
},
|
||||
{
|
||||
"startIndex": 5,
|
||||
"scopes": "identifier"
|
||||
}
|
||||
]
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
@ -41,6 +41,7 @@ type MetaToken =
|
||||
| MetaSelector
|
||||
| MetaPath
|
||||
| MetaPredicate
|
||||
| MetaKeyword
|
||||
|
||||
/**
|
||||
* Defines common properties for meta tokens.
|
||||
@ -202,6 +203,18 @@ export interface MetaPredicate {
|
||||
value: Predicate
|
||||
}
|
||||
|
||||
enum MetaKeywordKind {
|
||||
EscapedCharacter = 'EscapedCharacter',
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyword tokens, which can be quoted and contain escape sequences.
|
||||
*/
|
||||
interface MetaKeyword extends BaseMetaToken {
|
||||
type: 'metaKeyword'
|
||||
kind: MetaKeywordKind
|
||||
}
|
||||
|
||||
/**
|
||||
* Coalesces consecutive pattern tokens. Used, for example, when parsing
|
||||
* literal characters like 'f', 'o', 'o' in regular expressions, which are
|
||||
@ -799,6 +812,41 @@ const mapStructuralMeta = (pattern: Pattern): DecoratedToken[] => {
|
||||
return decorated
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds decorations for a quoted pattern, like "foo" and "foo \" bar" for
|
||||
* escaped quotes inside the pattern. The result always contains the
|
||||
* original token so that hover tooltips can show information about the
|
||||
* whole pattern.
|
||||
*/
|
||||
const mapQuotedPattern = (token: Pattern): DecoratedToken[] => {
|
||||
// We always include the original token so that hover tooltips can show
|
||||
// information about the whole pattern.
|
||||
const tokens: DecoratedToken[] = [token]
|
||||
const { value, delimiter } = token
|
||||
|
||||
if (delimiter) {
|
||||
// + 1 because `value` is the value without delimiters, but token.range.start is the position of the first delimiter
|
||||
const startOffset = token.range.start + 1
|
||||
let i = 0
|
||||
|
||||
while (i < value.length) {
|
||||
if (value[i] === '\\' && value[i + 1] === delimiter) {
|
||||
tokens.push({
|
||||
type: 'metaKeyword',
|
||||
kind: MetaKeywordKind.EscapedCharacter,
|
||||
value: value.slice(i, i + 2),
|
||||
range: {
|
||||
start: startOffset + i,
|
||||
end: startOffset + i + 2,
|
||||
},
|
||||
})
|
||||
i += 1
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
/**
|
||||
* Returns true for filter values that have regexp values, e.g., repo, file.
|
||||
* Excludes FilterType.content because that depends on the pattern kind.
|
||||
@ -1108,7 +1156,11 @@ export const decorate = (token: Token): DecoratedToken[] => {
|
||||
break
|
||||
}
|
||||
case PatternKind.Literal: {
|
||||
decorated.push(token)
|
||||
if (token.delimited) {
|
||||
decorated.push(...mapQuotedPattern(token))
|
||||
} else {
|
||||
decorated.push(token)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -1162,7 +1214,10 @@ export const decorate = (token: Token): DecoratedToken[] => {
|
||||
return decorated
|
||||
}
|
||||
|
||||
const tokenKindToCSSName: Record<MetaRevisionKind | MetaRegexpKind | MetaPredicateKind | MetaStructuralKind, string> = {
|
||||
const tokenKindToCSSName: Record<
|
||||
MetaRevisionKind | MetaRegexpKind | MetaPredicateKind | MetaStructuralKind | MetaKeywordKind,
|
||||
string
|
||||
> = {
|
||||
Separator: 'separator',
|
||||
IncludeGlobMarker: 'include-glob-marker',
|
||||
ExcludeGlobMarker: 'exclude-glob-marker',
|
||||
@ -1217,6 +1272,7 @@ export const toCSSClassName = (token: DecoratedToken): string => {
|
||||
return `search-revision-${tokenKindToCSSName[token.kind]}`
|
||||
}
|
||||
|
||||
case 'metaKeyword':
|
||||
case 'metaRegexp': {
|
||||
return `search-regexp-meta-${tokenKindToCSSName[token.kind]}`
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
MetaSelectorKind,
|
||||
type MetaPredicate,
|
||||
} from './decoratedToken'
|
||||
import { Pattern } from './token'
|
||||
|
||||
const toRegexpHover = (token: MetaRegexp): string => {
|
||||
switch (token.kind) {
|
||||
@ -238,11 +239,20 @@ const toPredicateHover = (token: MetaPredicate): string => {
|
||||
return ''
|
||||
}
|
||||
|
||||
const toPatternHover = (token: Pattern): string => {
|
||||
let value = token.value
|
||||
if (token.delimited && token.delimiter) {
|
||||
// Replace escaped delimiters with the delimiter itself.
|
||||
value = value.replaceAll(new RegExp(`\\\\${token.delimiter}`, 'g'), token.delimiter)
|
||||
}
|
||||
const quantity = value.length > 1 ? 'string' : 'character'
|
||||
return `Matches the ${quantity} \`${value}\`.`
|
||||
}
|
||||
|
||||
export const toHover = (token: DecoratedToken): string => {
|
||||
switch (token.type) {
|
||||
case 'pattern': {
|
||||
const quantity = token.value.length > 1 ? 'string' : 'character'
|
||||
return `Matches the ${quantity} \`${token.value}\`.`
|
||||
return toPatternHover(token)
|
||||
}
|
||||
case 'metaRegexp': {
|
||||
return toRegexpHover(token)
|
||||
|
||||
@ -13,25 +13,25 @@ expect.addSnapshotSerializer({
|
||||
describe('keyword()', () => {
|
||||
test('double quoted patterns are interpreted as literal', () => {
|
||||
expect(scanSearchQuery('"foo and bar"', false, SearchPatternType.keyword)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":13},"kind":1,"value":"foo and bar","delimited":true}]}'
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":13},"kind":1,"value":"foo and bar","delimited":true,"delimiter":"\\""}]}'
|
||||
)
|
||||
})
|
||||
|
||||
test('single quoted patterns are interpreted as literal', () => {
|
||||
expect(scanSearchQuery("'foo or bar'", false, SearchPatternType.keyword)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":12},"kind":1,"value":"foo or bar","delimited":true}]}'
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":12},"kind":1,"value":"foo or bar","delimited":true,"delimiter":"\'"}]}'
|
||||
)
|
||||
})
|
||||
|
||||
test('recognize keywords outside quoted patterns', () => {
|
||||
expect(scanSearchQuery('"foo or bar" or bas', false, SearchPatternType.keyword)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":12},"kind":1,"value":"foo or bar","delimited":true},{"type":"whitespace","range":{"start":12,"end":13}},{"type":"keyword","value":"or","range":{"start":13,"end":15},"kind":"or"},{"type":"whitespace","range":{"start":15,"end":16}},{"type":"pattern","range":{"start":16,"end":19},"kind":1,"value":"bas","delimited":false}]}'
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":12},"kind":1,"value":"foo or bar","delimited":true,"delimiter":"\\""},{"type":"whitespace","range":{"start":12,"end":13}},{"type":"keyword","value":"or","range":{"start":13,"end":15},"kind":"or"},{"type":"whitespace","range":{"start":15,"end":16}},{"type":"pattern","range":{"start":16,"end":19},"kind":1,"value":"bas","delimited":false}]}'
|
||||
)
|
||||
})
|
||||
|
||||
test('scan literal and regexp patterns', () => {
|
||||
expect(scanSearchQuery('pfalz /mosel/', false, SearchPatternType.keyword)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":5},"kind":1,"value":"pfalz","delimited":false},{"type":"whitespace","range":{"start":5,"end":6}},{"type":"pattern","range":{"start":6,"end":13},"kind":2,"value":"mosel","delimited":true}]}'
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":5},"kind":1,"value":"pfalz","delimited":false},{"type":"whitespace","range":{"start":5,"end":6}},{"type":"pattern","range":{"start":6,"end":13},"kind":2,"value":"mosel","delimited":true,"delimiter":"/"}]}'
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -136,7 +136,7 @@ describe('scanSearchQuery() for literal search', () => {
|
||||
|
||||
test('filter with quoted value', () => {
|
||||
expect(scanSearchQuery('f:"b"')).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"filter","range":{"start":0,"end":5},"field":{"type":"literal","value":"f","range":{"start":0,"end":1}},"value":{"type":"literal","value":"b","range":{"start":2,"end":5},"quoted":true},"negated":false}]}'
|
||||
'{"type":"success","term":[{"type":"filter","range":{"start":0,"end":5},"field":{"type":"literal","value":"f","range":{"start":0,"end":1}},"value":{"type":"literal","value":"b","range":{"start":2,"end":5},"quoted":true,"quotes":"\\""},"negated":false}]}'
|
||||
)
|
||||
})
|
||||
|
||||
@ -159,7 +159,7 @@ describe('scanSearchQuery() for literal search', () => {
|
||||
|
||||
test('quoted, double quotes, regexp', () =>
|
||||
expect(scanSearchQuery('"a:b"', false, SearchPatternType.regexp)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"literal","value":"a:b","range":{"start":0,"end":5},"quoted":true}]}'
|
||||
'{"type":"success","term":[{"type":"literal","value":"a:b","range":{"start":0,"end":5},"quoted":true,"quotes":"\\""}]}'
|
||||
))
|
||||
|
||||
test('quoted, single quotes, literal', () =>
|
||||
@ -169,7 +169,7 @@ describe('scanSearchQuery() for literal search', () => {
|
||||
|
||||
test('quoted, single quotes, regexp', () =>
|
||||
expect(scanSearchQuery("'a:b'", false, SearchPatternType.regexp)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"literal","value":"a:b","range":{"start":0,"end":5},"quoted":true}]}'
|
||||
'{"type":"success","term":[{"type":"literal","value":"a:b","range":{"start":0,"end":5},"quoted":true,"quotes":"\'"}]}'
|
||||
))
|
||||
|
||||
test('quoted (do not escape quotes in literal mode)', () =>
|
||||
@ -179,7 +179,7 @@ describe('scanSearchQuery() for literal search', () => {
|
||||
|
||||
test('quoted (escape quotes in regex mode)', () =>
|
||||
expect(scanSearchQuery('"-\\"a\\":b"', false, SearchPatternType.regexp)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"literal","value":"-\\\\\\"a\\\\\\":b","range":{"start":0,"end":10},"quoted":true}]}'
|
||||
'{"type":"success","term":[{"type":"literal","value":"-\\\\\\"a\\\\\\":b","range":{"start":0,"end":10},"quoted":true,"quotes":"\\""}]}'
|
||||
))
|
||||
|
||||
test('complex query', () =>
|
||||
@ -225,7 +225,7 @@ describe('scanSearchQuery() for regexp', () => {
|
||||
|
||||
test('interpret regexp slash quotes', () => {
|
||||
expect(scanSearchQuery('r:a /a regexp \\ pattern/', false, SearchPatternType.regexp)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"filter","range":{"start":0,"end":3},"field":{"type":"literal","value":"r","range":{"start":0,"end":1}},"value":{"type":"literal","value":"a","range":{"start":2,"end":3},"quoted":false},"negated":false},{"type":"whitespace","range":{"start":3,"end":4}},{"type":"literal","value":"a regexp \\\\ pattern","range":{"start":4,"end":24},"quoted":true}]}'
|
||||
'{"type":"success","term":[{"type":"filter","range":{"start":0,"end":3},"field":{"type":"literal","value":"r","range":{"start":0,"end":1}},"value":{"type":"literal","value":"a","range":{"start":2,"end":3},"quoted":false},"negated":false},{"type":"whitespace","range":{"start":3,"end":4}},{"type":"literal","value":"a regexp \\\\ pattern","range":{"start":4,"end":24},"quoted":true,"quotes":"/"}]}'
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -233,7 +233,7 @@ describe('scanSearchQuery() for regexp', () => {
|
||||
describe('scanSearchQuery() for standard', () => {
|
||||
test('scan literal and regexp patterns', () => {
|
||||
expect(scanSearchQuery('pfalz /mosel/', false, SearchPatternType.standard)).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":5},"kind":1,"value":"pfalz","delimited":false},{"type":"whitespace","range":{"start":5,"end":6}},{"type":"pattern","range":{"start":6,"end":13},"kind":2,"value":"mosel","delimited":true}]}'
|
||||
'{"type":"success","term":[{"type":"pattern","range":{"start":0,"end":5},"kind":1,"value":"pfalz","delimited":false},{"type":"whitespace","range":{"start":5,"end":6}},{"type":"pattern","range":{"start":6,"end":13},"kind":2,"value":"mosel","delimited":true,"delimiter":"/"}]}'
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -283,7 +283,7 @@ describe('scanSearchQuery() with predicate', () => {
|
||||
|
||||
test('detect patterntype inside query', () => {
|
||||
expect(scanSearchQuery('patterntype:standard /test/')).toMatchInlineSnapshot(
|
||||
'{"type":"success","term":[{"type":"filter","range":{"start":0,"end":20},"field":{"type":"literal","value":"patterntype","range":{"start":0,"end":11}},"value":{"type":"literal","value":"standard","range":{"start":12,"end":20},"quoted":false},"negated":false},{"type":"whitespace","range":{"start":20,"end":21}},{"type":"pattern","range":{"start":21,"end":27},"kind":2,"value":"test","delimited":true}]}'
|
||||
'{"type":"success","term":[{"type":"filter","range":{"start":0,"end":20},"field":{"type":"literal","value":"patterntype","range":{"start":0,"end":11}},"value":{"type":"literal","value":"standard","range":{"start":12,"end":20},"quoted":false},"negated":false},{"type":"whitespace","range":{"start":20,"end":21}},{"type":"pattern","range":{"start":21,"end":27},"kind":2,"value":"test","delimited":true,"delimiter":"/"}]}'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -131,7 +131,7 @@ const quoted =
|
||||
return {
|
||||
type: 'success',
|
||||
// end + 1 as `end` is currently the index of the quote in the string.
|
||||
term: createLiteral(input.slice(start + 1, end), { start, end: end + 1 }, true),
|
||||
term: createLiteral(input.slice(start + 1, end), { start, end: end + 1 }, true, delimiter),
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,7 +424,8 @@ const createPattern = (
|
||||
value: string,
|
||||
range: CharacterRange,
|
||||
kind: PatternKind,
|
||||
delimited?: boolean
|
||||
delimited?: boolean,
|
||||
delimiter?: string
|
||||
): ScanSuccess<Pattern> => ({
|
||||
type: 'success',
|
||||
term: {
|
||||
@ -433,6 +434,7 @@ const createPattern = (
|
||||
kind,
|
||||
value,
|
||||
delimited,
|
||||
delimiter,
|
||||
},
|
||||
})
|
||||
|
||||
@ -446,11 +448,11 @@ const keepScanning = (input: string, start: number): boolean => scanFilterOrKeyw
|
||||
* @param kind The {@link PatternKind} label to apply to the resulting pattern scanner.
|
||||
*/
|
||||
export const toPatternResult =
|
||||
(scanner: Scanner<Literal>, kind: PatternKind, delimited = false): Scanner<Pattern> =>
|
||||
(scanner: Scanner<Literal>, kind: PatternKind): Scanner<Pattern> =>
|
||||
(input, start) => {
|
||||
const result = scanner(input, start)
|
||||
if (result.type === 'success') {
|
||||
return createPattern(result.term.value, result.term.range, kind, result.term.quoted)
|
||||
return createPattern(result.term.value, result.term.range, kind, result.term.quoted, result.term.quotes)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ export interface Pattern extends BaseToken {
|
||||
kind: PatternKind
|
||||
value: string
|
||||
delimited?: boolean
|
||||
delimiter?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,6 +61,7 @@ export interface Literal extends BaseToken {
|
||||
type: 'literal'
|
||||
value: string
|
||||
quoted: boolean
|
||||
quotes?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,9 +122,10 @@ export interface ClosingParen extends BaseToken {
|
||||
type: 'closingParen'
|
||||
}
|
||||
|
||||
export const createLiteral = (value: string, range: CharacterRange, quoted = false): Literal => ({
|
||||
export const createLiteral = (value: string, range: CharacterRange, quoted = false, quotes?: string): Literal => ({
|
||||
type: 'literal',
|
||||
value,
|
||||
range,
|
||||
quoted,
|
||||
quotes,
|
||||
})
|
||||
|
||||
@ -97,7 +97,10 @@ export const documentHighlightsSource = Facet.define<DocumentHighlightsSource>({
|
||||
* Together with {@link DocumentHighlightsManager} provides an extenion that
|
||||
* fetches document higlights from Sourcegraph extensions.
|
||||
*/
|
||||
function documentHighlights(sources: Facet<DocumentHighlightsSource>, sink: Facet<DocumentHighlight[]>): Extension {
|
||||
function documentHighlights(
|
||||
sources: Facet<DocumentHighlightsSource>,
|
||||
sink: Facet<DocumentHighlight[], DocumentHighlight[]>
|
||||
): Extension {
|
||||
// This field is used to provide inputs for the documentHighlights facet.
|
||||
// The facet gets updated whenever the field changes. The view plugin
|
||||
// listens to mouse events, sents queries to the extensions host and
|
||||
|
||||
@ -275,8 +275,8 @@
|
||||
"@codemirror/legacy-modes": "^6.3.1",
|
||||
"@codemirror/lint": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.1",
|
||||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/view": "^6.7.2",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.23.1",
|
||||
"@graphiql/react": "^0.10.0",
|
||||
"@lezer/common": "^1.0.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
|
||||
@ -21,7 +21,7 @@ importers:
|
||||
version: 3.8.0-alpha.7(graphql-ws@5.14.1)(graphql@15.4.0)(react-dom@18.1.0)(react@18.1.0)
|
||||
'@codemirror/autocomplete':
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(@lezer/common@1.0.0)
|
||||
version: 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(@lezer/common@1.0.0)
|
||||
'@codemirror/commands':
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
@ -44,11 +44,11 @@ importers:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
'@codemirror/state':
|
||||
specifier: ^6.2.0
|
||||
version: 6.2.0
|
||||
specifier: ^6.4.0
|
||||
version: 6.4.0
|
||||
'@codemirror/view':
|
||||
specifier: ^6.7.2
|
||||
version: 6.7.3
|
||||
specifier: ^6.23.1
|
||||
version: 6.23.1
|
||||
'@graphiql/react':
|
||||
specifier: ^0.10.0
|
||||
version: 0.10.0(@codemirror/language@6.2.0)(@types/node@20.8.0)(graphql-ws@5.14.1)(graphql@15.4.0)(react-dom@18.1.0)(react@18.1.0)
|
||||
@ -72,7 +72,7 @@ importers:
|
||||
version: 0.0.1
|
||||
'@opencodegraph/codemirror-extension':
|
||||
specifier: ^0.0.1
|
||||
version: 0.0.1(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(react-dom@18.1.0)(react@18.1.0)
|
||||
version: 0.0.1(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(react-dom@18.1.0)(react@18.1.0)
|
||||
'@opentelemetry/api':
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
@ -3108,7 +3108,7 @@ packages:
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
dev: true
|
||||
|
||||
/@codemirror/autocomplete@6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(@lezer/common@1.0.0):
|
||||
/@codemirror/autocomplete@6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(@lezer/common@1.0.0):
|
||||
resolution: {integrity: sha512-wtO4O5WDyXhhCd4q4utDIDZxnQfmJ++3dGBCG9LMtI79+92OcA1DVk/n7BEupKmjIr8AzvptDz7YQ9ud6OkU+A==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
@ -3117,8 +3117,8 @@ packages:
|
||||
'@lezer/common': ^1.0.0
|
||||
dependencies:
|
||||
'@codemirror/language': 6.2.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
'@lezer/common': 1.0.0
|
||||
dev: false
|
||||
|
||||
@ -3126,31 +3126,31 @@ packages:
|
||||
resolution: {integrity: sha512-iNHDByicYqQjs0Wo1MKGfqNbMYMyhS9WV6EwMVwsHXImlFemgEUC+c5X22bXKBStN3qnwg4fArNZM+gkv22baQ==}
|
||||
dependencies:
|
||||
'@codemirror/language': 6.2.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
'@lezer/common': 1.0.0
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-css@6.0.0(@codemirror/view@6.7.3)(@lezer/common@1.0.0):
|
||||
/@codemirror/lang-css@6.0.0(@codemirror/view@6.23.1)(@lezer/common@1.0.0):
|
||||
resolution: {integrity: sha512-jBqc+BTuwhNOTlrimFghLlSrN6iFuE44HULKWoR4qKYObhOIl9Lci1iYj6zMIte1XTQmZguNvjXMyr43LUKwSw==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(@lezer/common@1.0.0)
|
||||
'@codemirror/autocomplete': 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(@lezer/common@1.0.0)
|
||||
'@codemirror/language': 6.2.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/state': 6.4.0
|
||||
'@lezer/css': 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/view'
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-html@6.1.0(@codemirror/view@6.7.3):
|
||||
/@codemirror/lang-html@6.1.0(@codemirror/view@6.23.1):
|
||||
resolution: {integrity: sha512-gA7NmJxqvnhwza05CvR7W/39Ap9r/4Vs9uiC0IeFYo1hSlJzc/8N6Evviz6vTW1x8SpHcRYyqKOf6rpl6LfWtg==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(@lezer/common@1.0.0)
|
||||
'@codemirror/lang-css': 6.0.0(@codemirror/view@6.7.3)(@lezer/common@1.0.0)
|
||||
'@codemirror/autocomplete': 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(@lezer/common@1.0.0)
|
||||
'@codemirror/lang-css': 6.0.0(@codemirror/view@6.23.1)(@lezer/common@1.0.0)
|
||||
'@codemirror/lang-javascript': 6.0.1
|
||||
'@codemirror/language': 6.2.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/state': 6.4.0
|
||||
'@lezer/common': 1.0.0
|
||||
'@lezer/html': 1.0.0
|
||||
transitivePeerDependencies:
|
||||
@ -3160,11 +3160,11 @@ packages:
|
||||
/@codemirror/lang-javascript@6.0.1:
|
||||
resolution: {integrity: sha512-kjGbBEosl+ozDU5ruDV48w4v3H6KECTFiDjqMLT0KhVwESPfv3wOvnDrTT0uaMOg3YRGnBWsyiIoKHl/tNWWDg==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(@lezer/common@1.0.0)
|
||||
'@codemirror/autocomplete': 6.1.0(@codemirror/language@6.2.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(@lezer/common@1.0.0)
|
||||
'@codemirror/language': 6.2.0
|
||||
'@codemirror/lint': 6.0.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
'@lezer/common': 1.0.0
|
||||
'@lezer/javascript': 1.0.1
|
||||
dev: false
|
||||
@ -3179,10 +3179,10 @@ packages:
|
||||
/@codemirror/lang-markdown@6.0.0:
|
||||
resolution: {integrity: sha512-ozJaO1W4WgGlwWOoYCSYzbVhhM0YM/4lAWLrNsBbmhh5Ztpl0qm4CgEQRl3t8/YcylTZYBIXiskui8sHNGd4dg==}
|
||||
dependencies:
|
||||
'@codemirror/lang-html': 6.1.0(@codemirror/view@6.7.3)
|
||||
'@codemirror/lang-html': 6.1.0(@codemirror/view@6.23.1)
|
||||
'@codemirror/language': 6.2.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
'@lezer/common': 1.0.0
|
||||
'@lezer/markdown': 1.0.1
|
||||
dev: false
|
||||
@ -3190,12 +3190,12 @@ packages:
|
||||
/@codemirror/language@6.2.0:
|
||||
resolution: {integrity: sha512-tabB0Ef/BflwoEmTB4a//WZ9P90UQyne9qWB9YFsmeS4bnEqSys7UpGk/da1URMXhyfuzWCwp+AQNMhvu8SfnA==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
'@lezer/common': 1.0.0
|
||||
'@lezer/highlight': 1.0.0
|
||||
'@lezer/lr': 1.2.0
|
||||
style-mod: 4.0.0
|
||||
style-mod: 4.1.0
|
||||
dev: false
|
||||
|
||||
/@codemirror/legacy-modes@6.3.1:
|
||||
@ -3207,28 +3207,28 @@ packages:
|
||||
/@codemirror/lint@6.0.0:
|
||||
resolution: {integrity: sha512-nUUXcJW1Xp54kNs+a1ToPLK8MadO0rMTnJB8Zk4Z8gBdrN0kqV7uvUraU/T2yqg+grDNR38Vmy/MrhQN/RgwiA==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
crelt: 1.0.5
|
||||
dev: false
|
||||
|
||||
/@codemirror/search@6.0.1:
|
||||
resolution: {integrity: sha512-uOinkOrM+daMduCgMPomDfKLr7drGHB4jHl3Vq6xY2WRlL7MkNsBE0b+XHYa/Mee2npsJOgwvkW4n1lMFeBW2Q==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
crelt: 1.0.5
|
||||
dev: false
|
||||
|
||||
/@codemirror/state@6.2.0:
|
||||
resolution: {integrity: sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==}
|
||||
/@codemirror/state@6.4.0:
|
||||
resolution: {integrity: sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==}
|
||||
dev: false
|
||||
|
||||
/@codemirror/view@6.7.3:
|
||||
resolution: {integrity: sha512-Lt+4POnhXrZFfHOdPzXEHxrzwdy7cjqYlMkOWvoFGi6/bAsjzlFfr0NY3B15B/PGx+cDFgM1hlc12wvYeZbGLw==}
|
||||
/@codemirror/view@6.23.1:
|
||||
resolution: {integrity: sha512-J2Xnn5lFYT1ZN/5ewEoMBCmLlL71lZ3mBdb7cUEuHhX2ESoSrNEucpsDXpX22EuTGm9LOgC9v4Z0wx+Ez8QmGA==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
style-mod: 4.0.0
|
||||
'@codemirror/state': 6.4.0
|
||||
style-mod: 4.1.0
|
||||
w3c-keyname: 2.2.4
|
||||
dev: false
|
||||
|
||||
@ -5996,14 +5996,14 @@ packages:
|
||||
rxjs: 7.8.1
|
||||
dev: false
|
||||
|
||||
/@opencodegraph/codemirror-extension@0.0.1(@codemirror/state@6.2.0)(@codemirror/view@6.7.3)(react-dom@18.1.0)(react@18.1.0):
|
||||
/@opencodegraph/codemirror-extension@0.0.1(@codemirror/state@6.4.0)(@codemirror/view@6.23.1)(react-dom@18.1.0)(react@18.1.0):
|
||||
resolution: {integrity: sha512-n3WP/88qcgeJboqGI7HOfPujkhnTAJ5TGV9IIs03MAyY/V4/+/J8TclhUzjCX5Pu27eoG4RuZlY7379ejUvYkw==}
|
||||
peerDependencies:
|
||||
'@codemirror/state': ^6.2.0
|
||||
'@codemirror/view': ^6.7.2
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@codemirror/state': 6.4.0
|
||||
'@codemirror/view': 6.23.1
|
||||
'@opencodegraph/client': 0.0.1
|
||||
'@opencodegraph/ui-react': 0.0.1(react-dom@18.1.0)(react@18.1.0)
|
||||
deep-equal: 2.2.3
|
||||
@ -21302,7 +21302,7 @@ packages:
|
||||
/puppeteer@13.7.0:
|
||||
resolution: {integrity: sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==}
|
||||
engines: {node: '>=10.18.1'}
|
||||
deprecated: < 21.4.0 is no longer supported
|
||||
deprecated: < 21.5.0 is no longer supported
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
cross-fetch: 3.1.5
|
||||
@ -23449,8 +23449,8 @@ packages:
|
||||
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
|
||||
dev: false
|
||||
|
||||
/style-mod@4.0.0:
|
||||
resolution: {integrity: sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==}
|
||||
/style-mod@4.1.0:
|
||||
resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==}
|
||||
dev: false
|
||||
|
||||
/style-search@0.1.0:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user