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

feat: present meta.defaultOptions in the config inspector #110

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/components/RuleItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { RuleConfigStates, RuleInfo, RuleLevel } from '~~/shared/types'
import { useClipboard } from '@vueuse/core'
import { getRuleLevel, getRuleOptions } from '~~/shared/rules'
import { vTooltip } from 'floating-vue'
import { deepCompareOptions } from '~/composables/options'
import { getRuleDefaultOptions } from '~/composables/payload'

const props = defineProps<{
rule: RuleInfo
Expand All @@ -17,6 +19,11 @@ const emit = defineEmits<{
stateClick: [RuleLevel]
}>()

function redundantOptions(options: any) {
const { hasRedundantOptions } = deepCompareOptions(options ?? [], getRuleDefaultOptions(props.rule.name))
return hasRedundantOptions
}

const { copy } = useClipboard()

function capitalize(str?: string) {
Expand All @@ -38,6 +45,7 @@ function capitalize(str?: string) {
:level="s.level"
:config-index="s.configIndex"
:has-options="!!s.options?.length"
:has-redundant-options="redundantOptions(s.options)"
/>
<template #popper="{ shown }">
<RuleStateItem v-if="shown" :state="s" />
Expand All @@ -53,6 +61,7 @@ function capitalize(str?: string) {
<RuleLevelIcon
:level="getRuleLevel(value)!"
:has-options="!!getRuleOptions(value)?.length"
:has-redundant-options="redundantOptions(getRuleOptions(value))"
/>
</div>

Expand Down
3 changes: 2 additions & 1 deletion app/components/RuleLevelIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { nth } from '~/composables/strings'
const props = defineProps<{
level: RuleLevel
hasOptions?: boolean
hasRedundantOptions?: boolean
configIndex?: number
class?: string
}>()
Expand All @@ -32,6 +33,6 @@ const icon = computed(() => ({
<template>
<div relative :class="[color, props.class]" :title="title">
<div :class="icon" />
<div v-if="hasOptions" absolute right--2px top--2px h-6px w-6px rounded-full bg-current op75 />
<div v-if="hasOptions" absolute right--2px top--2px h-6px w-6px rounded-full bg-current op75 :class="hasRedundantOptions ? 'text-blue5' : ''" />
</div>
</template>
73 changes: 58 additions & 15 deletions app/components/RuleStateItem.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script setup lang="ts">
import type { RuleConfigState } from '~~/shared/types'
import { useRouter } from '#app/composables/router'
import { computed } from 'vue'
import { payload } from '~/composables/payload'
import { computed, reactive } from 'vue'
import { deepCompareOptions } from '~/composables/options'
import { getRuleDefaultOptions, payload } from '~/composables/payload'
import { filtersConfigs } from '~/composables/state'
import { nth, stringifyUnquoted } from '~/composables/strings'
import { nth, stringifyOptions } from '~/composables/strings'

const props = defineProps<{
state: RuleConfigState
Expand All @@ -19,6 +20,16 @@ const colors = {

const config = computed(() => payload.value.configs[props.state.configIndex])

const defaultOptions = computed(() => getRuleDefaultOptions(props.state.name))

const comparedOptions = computed(() => deepCompareOptions(props.state.options ?? [], defaultOptions.value))

const initialRuleOptionsView = computed(() => !props.state.options?.length && defaultOptions.value?.length ? 'default' : 'state')

const ruleOptions = reactive({
viewType: initialRuleOptionsView.value as 'state' | 'default',
})

const router = useRouter()
function goto() {
filtersConfigs.rule = props.state.name
Expand Down Expand Up @@ -71,20 +82,52 @@ function goto() {
</div>
</template>
</div>
<template v-if="state.options?.length">
<div flex="~ gap-2 items-center">
<div i-ph-sliders-duotone my1 flex-none op75 />
<div op50>
Rule options
<template v-if="state.options?.length || defaultOptions?.length">
<div items-center justify-between md:flex>
<div flex="~ gap-1" op50>
<button
v-if="state.options?.length"
btn-action
:class="{ 'btn-action-active': ruleOptions.viewType === 'state' }"
@click="ruleOptions.viewType = 'state'"
>
<div i-ph-sliders-duotone my1 flex-none op75 />
Rule options
</button>
<button
v-if="defaultOptions?.length"
btn-action
:class="{ 'btn-action-active': ruleOptions.viewType === 'default' }"
@click="ruleOptions.viewType = 'default'"
>
<div i-ph-faders-duotone my1 flex-none op75 />
Option defaults
</button>
</div>
</div>
<Shiki
v-for="options, idx of state.options"
:key="idx"
lang="ts"
:code="stringifyUnquoted(options)"
rounded bg-code p2 text-sm
/>
<template v-if="ruleOptions.viewType === 'state'">
<Shiki
v-for="options, idx of comparedOptions.options"
:key="idx"
lang="ts"
:code="stringifyOptions(options)"
rounded bg-code p2 text-sm
/>
</template>
<template v-if="ruleOptions.viewType === 'default'">
<Shiki
v-for="options, idx of defaultOptions"
:key="idx"
lang="ts"
:code="stringifyOptions(options)"
rounded bg-code p2 text-sm
/>
</template>
</template>
<template v-if="ruleOptions.viewType === 'state' && comparedOptions.hasRedundantOptions">
<div op50>
Options <span italic op75>italicized</span> match the default for the rule
</div>
</template>
</div>
</template>
10 changes: 10 additions & 0 deletions app/components/Shiki.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { transformerNotationMap } from '@shikijs/transformers'

// @unocss-include
export default defineComponent({
name: 'Shiki',
Expand All @@ -24,6 +26,14 @@ export default defineComponent({
node.properties.style = ''
},
},
transformerNotationMap(
{
classMap: {
muted: 'muted',
},
},
'@shikijs/transformers:notation-muted',
),
],
})
})
Expand Down
53 changes: 53 additions & 0 deletions app/composables/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Indicates if any user-supplied option values match the default value for that option
*/
let hasRedundantOptions: boolean

/**
* Wraps an option value in a 3-part array, while still preserving the original data type and value.
*
* The '--' markers provide something that a regex replace can easily later match on.
* (See transformDiff() in ./strings.ts)
*/
function redundantOption(option: any) {
hasRedundantOptions = true
return ['--', option, '--']
}

function deepCompareOption(option: any, defaultOption: any) {
if (defaultOption === void 0)
return option
if (typeof option !== typeof defaultOption)
return option

if (option === defaultOption)
return redundantOption(option)

if (typeof option === 'object' && option !== null && defaultOption !== null) {
if (Array.isArray(option) && Array.isArray(defaultOption) && option.length === defaultOption.length) {
if (option.length === 0)
return redundantOption(option)
return option.map((value: any, index: number): any[] => deepCompareOption(value, defaultOption[index]))
}
else if (!Array.isArray(option) && !Array.isArray(defaultOption)) {
const optionKeys = Object.keys(option)

return optionKeys.reduce((comparedKeys: Record<string, any>, key) => {
comparedKeys[key] = deepCompareOption(option[key], defaultOption[key])
return comparedKeys
}, {})
}
}

return option
}

export function deepCompareOptions(options: any[], defaultOptions: any[]) {
hasRedundantOptions = false
const comparedOptions = options.map((value, index) => deepCompareOption(value, index < defaultOptions.length ? defaultOptions[index] : void 0))

return {
options: comparedOptions,
hasRedundantOptions,
}
}
4 changes: 4 additions & 0 deletions app/composables/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ export function getRuleFromName(name: string): RuleInfo {
}
}

export function getRuleDefaultOptions(name: string): any[] {
return payload.value.rules[name]?.defaultOptions ?? []
}

export function getRuleStates(name: string): RuleConfigStates | undefined {
return payload.value.ruleToState.get(name)
}
Expand Down
15 changes: 15 additions & 0 deletions app/composables/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ export function nth(n: number) {
return `${n}th`
}

export function stringifyOptions(object: any) {
/**
* Replaces all occurrences of the pattern:
* `['--', value, '--']`
*
* with:
* `value, // [!code muted]
*
* Lines with the [!code muted] comment will be processed by Shiki's diff
* notation transformer and have the `.line.muted` classes applied
*/
return stringifyUnquoted(object)
.replace(/\[\s*'--',\s*(\S.+),\s*'--'\s*\],?/g, '$1, // [!code muted]')
}

export function stringifyUnquoted(obj: any) {
return JSON.stringify(obj, null, 2)
.replace(/"(\w+)":/g, '$1:')
Expand Down
5 changes: 5 additions & 0 deletions app/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ html.dark {
font-size: 15px;
}

.shiki span.line.muted {
font-style: italic;
opacity: 75%;
}

.font-mono, [font-mono=""] {
font-variant-ligatures: none;
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@iconify-json/twemoji": "catalog:",
"@iconify-json/vscode-icons": "catalog:",
"@nuxt/eslint": "catalog:",
"@shikijs/transformers": "catalog:",
"@types/connect": "catalog:",
"@types/ws": "catalog:",
"@typescript-eslint/utils": "catalog:",
Expand Down
Loading
Loading