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:
Felix Kling 2024-01-31 00:57:48 +01:00 committed by GitHub
parent afe49d23e2
commit bb2b7b3b64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 269 additions and 100 deletions

View File

@ -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()

View File

@ -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"
}
]
`)
})
})

View File

@ -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]}`
}

View File

@ -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)

View File

@ -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":"/"}]}'
)
})
})

View File

@ -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
}

View File

@ -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,
})

View File

@ -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

View File

@ -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",

View File

@ -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: