Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alpha token to autocomplete suggestion of bezier-vscode #2496

Merged
Merged
5 changes: 5 additions & 0 deletions .changeset/selfish-ladybugs-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'bezier-vscode': minor
---

Include `alpha` token for autocomplete suggestion.
119 changes: 75 additions & 44 deletions packages/bezier-vscode/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { tokens as _tokens } from '@channel.io/bezier-tokens'
import { tokens } from '@channel.io/bezier-tokens'
import { tokens as alphaTokens } from '@channel.io/bezier-tokens/alpha'
import {
type CompletionItem,
CompletionItemKind,
Expand All @@ -11,21 +12,56 @@ import {
} from 'vscode-languageserver/node'
import { TextDocument } from 'vscode-languageserver-textdocument'

import { hexToRGBA } from './utils'

const tokens = {
..._tokens.lightTheme,
..._tokens.global,
color: {
..._tokens.lightTheme.color,
..._tokens.global.color,
},
import { deepMerge, hexToRGBA } from './utils'

type TokenValue = string | number
type AlphaTokenValue = Record<string, string | number>
type TokenMap = Record<string, Record<string, TokenValue | AlphaTokenValue>>

const assignToTokenMap = (
target: TokenMap,
source: TokenMap,
transformValue: (v: TokenValue | AlphaTokenValue) => TokenValue = (v) =>
v as TokenValue
) => {
Object.entries(source).forEach(([category, tokenObject]) => {
if (target[category] === undefined) {
target[category] = {}
}
Object.entries(tokenObject).forEach(([name, value]) => {
target[category][name] = transformValue(value)
})
})
}

type TokenGroup = keyof typeof tokens
const alphaTokenMap = {} as TokenMap
const tokenMap = {} as TokenMap

assignToTokenMap(
alphaTokenMap,
alphaTokens.lightTheme,
(v) => (v as AlphaTokenValue).value
)
assignToTokenMap(
alphaTokenMap,
alphaTokens.global,
(v) => (v as AlphaTokenValue).value
)
assignToTokenMap(tokenMap, tokens.lightTheme)
assignToTokenMap(tokenMap, tokens.global)

const allTokenMap = deepMerge(alphaTokenMap, tokenMap) as Record<
| keyof typeof alphaTokens.global
| keyof typeof alphaTokens.lightTheme
| keyof typeof tokens.global
| keyof typeof tokens.lightTheme,
Record<string, string>
>

type TokenGroup = keyof typeof allTokenMap

const completionItemsByTokenGroup = Object.fromEntries(
Object.entries(tokens).map(([groupName, tokenKeyValues]) => {
Object.entries(allTokenMap).map(([groupName, tokenKeyValues]) => {
const completionItems: CompletionItem[] = Object.entries(
tokenKeyValues
).map(([key, value]) => ({
Expand All @@ -51,8 +87,11 @@ const tokenGroupPatterns = {
font: /font:|letter-spacing:|line-height:/,
transition: /transition:/,
opacity: /opacity:/,
shadow: /box-shadow:/,
gradient: /background:|background-image:/,
'z-index': /z-index:/,
} satisfies Record<TokenGroup, RegExp>
// FIXME: delete Exclude when dimension token is removed
} satisfies Record<Exclude<TokenGroup, 'dimension'>, RegExp>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마이너) FIXME 주석 추가해주시면 좋을 거 같습니다

const allCompletionItems = Object.values(completionItemsByTokenGroup).flat()

Expand All @@ -77,6 +116,27 @@ connection.onInitialize(() => {
return result
})

const hasMatchingPattern = (currentText: string): boolean => {
return Object.values(tokenGroupPatterns).some((pattern) =>
pattern.test(currentText)
)
}

const getMatchedCompletionItems = (currentText: string): CompletionItem[] => {
if (!hasMatchingPattern(currentText)) {
return []
}

return Object.entries(tokenGroupPatterns)
.filter(([_, pattern]) => pattern.test(currentText))
.flatMap(
([tokenGroupName]) =>
completionItemsByTokenGroup[
tokenGroupName as keyof typeof tokenGroupPatterns
] ?? []
)
}

// // This handler provides the initial list of the completion items.
connection.onCompletion(
(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
Expand All @@ -85,8 +145,6 @@ connection.onCompletion(
// info and always provide the same completion items.
const doc = documents.get(_textDocumentPosition.textDocument.uri)

let matchedCompletionItems: CompletionItem[] = []

// if the doc can't be found, return nothing
if (!doc) {
return []
Expand All @@ -97,36 +155,9 @@ connection.onCompletion(
end: { line: _textDocumentPosition.position.line, character: 1000 },
})

if (
Object.values(tokenGroupPatterns).every(
(pattern) => !pattern.test(currentText)
)
) {
return []
}

for (const [tokenGroupName, pattern] of Object.entries(
tokenGroupPatterns
)) {
if (pattern.test(currentText)) {
const currentCompletionItems =
completionItemsByTokenGroup[
tokenGroupName as keyof typeof tokenGroupPatterns
]

matchedCompletionItems = matchedCompletionItems.concat(
currentCompletionItems
)
}
}

// if there were matches above, send them
if (matchedCompletionItems.length > 0) {
return matchedCompletionItems
}
const matchedItems = getMatchedCompletionItems(currentText)

// if there were no matches, send everything
return allCompletionItems
return matchedItems.length > 0 ? matchedItems : allCompletionItems
}
)

Expand Down
23 changes: 23 additions & 0 deletions packages/bezier-vscode/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,26 @@ export const hexToRGBA = (hex: string) => {
}
return `rgba(${r}, ${g}, ${b}, 1)`
}

export const deepMerge = (target: any, source: any) => {
if (!source || !isObject(source)) return target

Object.keys(source).forEach((key) => {
const targetValue = target[key]
const sourceValue = source[key]

if (isObject(targetValue) && isObject(sourceValue)) {
target[key] = deepMerge(Object.assign({}, targetValue), sourceValue)
} else if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
target[key] = Array.from(new Set([...targetValue, ...sourceValue]))
} else {
target[key] = sourceValue
}
})

return target
}

const isObject = (obj: Object) => {
return obj && typeof obj === 'object' && !Array.isArray(obj)
}
Loading