Skip to content

Commit

Permalink
fix: refactored default language
Browse files Browse the repository at this point in the history
  • Loading branch information
snorrees committed Jan 16, 2023
1 parent 301640e commit 2111776
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 143 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Code input for [Sanity](https://sanity.io/).

Currently only a subset of languages and features are exposed, over time we will implement a richer set of options.
Currently only a subset of languages and features are exposed, but more can be added via plugin options.

![Code input](assets/basic-input.png)

Expand Down Expand Up @@ -58,7 +58,7 @@ Now you can use the `code` type in your schema types:

## Options

- `language` - Default language for this code field. If none is provided, code-input will use the most recently selected language (stored in localstorage)
- `language` - Default language for this code field.
- `languageAlternatives` - Array of languages that should be available (se its format in the example below)
- `withFilename` - Boolean option to display input field for filename

Expand Down
84 changes: 14 additions & 70 deletions src/CodeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
MemberField,
ObjectInputProps,
ObjectMember,
ObjectSchemaType,
RenderInputCallback,
set,
setIfMissing,
Expand All @@ -15,9 +14,10 @@ import {
} from 'sanity'
import {Card, Select, Stack, ThemeColorSchemeKey} from '@sanity/ui'
import styled from 'styled-components'
import {CodeInputLanguage, CodeInputValue} from './types'
import {LANGUAGE_ALIASES, PATH_CODE, SUPPORTED_LANGUAGES} from './config'
import {CodeInputLanguage, CodeInputValue, CodeSchemaType} from './types'
import {PATH_CODE} from './config'
import {useCodeMirror} from './codemirror/useCodeMirror'
import {useLanguageMode} from './codemirror/useLanguageMode'

export type {CodeInputLanguage, CodeInputValue} from './types'

Expand All @@ -31,25 +31,6 @@ const EditorContainer = styled(Card)`
height: 250px;
overflow-y: auto;
`
const defaultMode = 'text'

/**
* @public
*/
export interface CodeOptions {
theme?: string
darkTheme?: string
languageAlternatives?: CodeInputLanguage[]
language?: string
withFilename?: boolean
}

/**
* @public
*/
export type CodeSchemaType = Omit<ObjectSchemaType, 'options'> & {
options?: CodeOptions
}

/**
* @public
Expand All @@ -59,10 +40,6 @@ export type CodeInputProps = ObjectInputProps<CodeInputValue, CodeSchemaType> &
colorScheme?: ThemeColorSchemeKey
}

function resolveAliasedLanguage(lang?: string) {
return (lang && LANGUAGE_ALIASES[lang]) ?? lang
}

export function CodeInput(props: CodeInputProps) {
const {
members,
Expand Down Expand Up @@ -103,14 +80,7 @@ export function CodeInput(props: CodeInputProps) {
},
[onChange, type]
)
const languages = useLanguageAlternatives(props.schemaType)
const fixedLanguage = type.options?.language
const language = value?.language || fixedLanguage

// the language config from the schema
const configured = languages.find((entry) => entry.value === language)

const languageMode = configured?.mode ?? resolveAliasedLanguage(language) ?? defaultMode
const {languages, language, languageMode} = useLanguageMode(props.schemaType, props.value)

const CodeMirror = useCodeMirror()

Expand Down Expand Up @@ -150,7 +120,12 @@ export function CodeInput(props: CodeInputProps) {
return (
<Stack space={4}>
{languageFieldMember && (
<LanguageField {...props} member={languageFieldMember} languages={languages} />
<LanguageField
{...props}
member={languageFieldMember}
language={language}
languages={languages}
/>
)}

{type.options?.withFilename && filenameMember && (
Expand All @@ -177,15 +152,15 @@ export function CodeInput(props: CodeInputProps) {
}

function LanguageField(
props: CodeInputProps & {member: FieldMember; languages: CodeInputLanguage[]}
props: CodeInputProps & {member: FieldMember; languages: CodeInputLanguage[]; language: string}
) {
const {member, languages, renderItem, renderField, renderPreview} = props
const {member, languages, language, renderItem, renderField, renderPreview} = props
const renderLanguageInput = useCallback(
(inputProps: Omit<InputProps, 'renderDefault'>) => {
return (
<Select
{...(inputProps as StringInputProps)}
value={(inputProps as StringInputProps).value ?? defaultMode}
value={language}
onChange={(e) => {
const newValue = e.currentTarget.value
inputProps.onChange(newValue ? set(newValue) : unset())
Expand All @@ -199,7 +174,7 @@ function LanguageField(
</Select>
)
},
[languages]
[languages, language]
)

return (
Expand All @@ -222,34 +197,3 @@ function useFieldMember(members: ObjectMember[], fieldName: string) {
[members, fieldName]
)
}

function useLanguageAlternatives(type: CodeSchemaType) {
return useMemo((): CodeInputLanguage[] => {
const languageAlternatives = type.options?.languageAlternatives
if (!languageAlternatives) {
return SUPPORTED_LANGUAGES
}

if (!Array.isArray(languageAlternatives)) {
throw new Error(
`'options.languageAlternatives' should be an array, got ${typeof languageAlternatives}`
)
}

return languageAlternatives.reduce((acc: CodeInputLanguage[], {title, value: val, mode}) => {
const alias = LANGUAGE_ALIASES[val]
if (alias) {
// eslint-disable-next-line no-console
console.warn(
`'options.languageAlternatives' lists a language with value "%s", which is an alias of "%s" - please replace the value to read "%s"`,
val,
alias,
alias
)

return acc.concat({title, value: alias, mode: mode})
}
return acc.concat({title, value: val, mode})
}, [])
}, [type])
}
32 changes: 5 additions & 27 deletions src/PreviewCode.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, {Suspense, useEffect, useRef} from 'react'
import React, {Suspense} from 'react'
import styled from 'styled-components'
import {Box, Card} from '@sanity/ui'
import {CodeInputValue} from './types'
import {CodeInputValue, CodeSchemaType} from './types'
import {PreviewProps} from 'sanity'
import {useCodeMirror} from './codemirror/useCodeMirror'
import {useLanguageMode} from './codemirror/useLanguageMode'

const PreviewContainer = styled(Box)`
position: relative;
Expand All @@ -20,24 +21,8 @@ export interface PreviewCodeProps extends PreviewProps {
* @public
*/
export default function PreviewCode(props: PreviewCodeProps) {
const aceEditorRef = useRef<any>()

useEffect(() => {
if (!aceEditorRef?.current) return

const editor = aceEditorRef.current?.editor

if (editor) {
// Avoid cursor and focus tracking by Ace
editor.renderer.$cursorLayer.element.style.opacity = 0
editor.textInput.getElement().disabled = true
}
}, [])

const {selection, schemaType: type} = props
const fixedLanguage = type?.options?.language

const language = selection?.language || fixedLanguage || 'text'
const {languageMode} = useLanguageMode(type as CodeSchemaType, props.selection)

const CodeMirror = useCodeMirror()
return (
Expand All @@ -46,7 +31,6 @@ export default function PreviewCode(props: PreviewCodeProps) {
{CodeMirror && (
<Suspense fallback={<Card padding={2}>Loading code preview...</Card>}>
<CodeMirror
ref={aceEditorRef}
readOnly
editable={false}
value={selection?.code || ''}
Expand All @@ -57,13 +41,7 @@ export default function PreviewCode(props: PreviewCodeProps) {
highlightActiveLineGutter: false,
highlightActiveLine: false,
}}
languageMode={language}

/* markers={
selection?.highlightedLines
? createHighlightMarkers(selection.highlightedLines)
: undefined
}*/
languageMode={languageMode}
/>
</Suspense>
)}
Expand Down
21 changes: 16 additions & 5 deletions src/codemirror/CodeMirrorProxy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ export interface CodeMirrorProps extends ReactCodeMirrorProps {
* It is also responsible for integrating any CodeMirror extensions.
*/
const CodeMirrorProxy = forwardRef<ReactCodeMirrorRef, CodeMirrorProps>((props, ref) => {
const {value, languageMode, onHighlightChange, highlightLines, ...codeMirrorProps} = props
const {
value,
readOnly,
basicSetup,
languageMode,
onHighlightChange,
highlightLines,
...codeMirrorProps
} = props
const theme = useCodeMirrorTheme()
const [editorView, setEditorView] = useState<EditorView | undefined>(undefined)

Expand All @@ -29,14 +37,15 @@ const CodeMirrorProxy = forwardRef<ReactCodeMirrorRef, CodeMirrorProps>((props,
const baseExtensions = [
highlightLine({
onHighlightChange,
readOnly,
}),
EditorView.lineWrapping,
]
if (languageExtension) {
return [...baseExtensions, languageExtension]
}
return baseExtensions
}, [onHighlightChange, languageExtension])
}, [onHighlightChange, languageExtension, readOnly])

useEffect(() => {
if (editorView) {
Expand Down Expand Up @@ -71,9 +80,11 @@ const CodeMirrorProxy = forwardRef<ReactCodeMirrorRef, CodeMirrorProps>((props,
setEditorView(view)
}}
initialState={initialState}
basicSetup={{
highlightActiveLine: false,
}}
basicSetup={
basicSetup ?? {
highlightActiveLine: false,
}
}
/>
)
})
Expand Down
57 changes: 30 additions & 27 deletions src/codemirror/highlightLineExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const highlightState: {

export interface HighlightLineConfig {
onHighlightChange?: (lines: number[]) => void
readOnly?: boolean
}

const highlightTheme = EditorView.baseTheme({
Expand Down Expand Up @@ -97,34 +98,36 @@ const highlightTheme = EditorView.baseTheme({
export const highlightLine = (config: HighlightLineConfig): Extension => {
return [
lineHighlightField,
lineNumbers({
domEventHandlers: {
mousedown: (editorView, lineInfo) => {
// Determine if the line for the clicked gutter line number has highlighted state or not
const line = editorView.state.doc.lineAt(lineInfo.from)
let isHighlighted = false
editorView.state
.field(lineHighlightField)
.between(line.from, line.to, (from, to, value) => {
if (value) {
isHighlighted = true
return false // stop iteration
}
return undefined
})
config.readOnly
? []
: lineNumbers({
domEventHandlers: {
mousedown: (editorView, lineInfo) => {
// Determine if the line for the clicked gutter line number has highlighted state or not
const line = editorView.state.doc.lineAt(lineInfo.from)
let isHighlighted = false
editorView.state
.field(lineHighlightField)
.between(line.from, line.to, (from, to, value) => {
if (value) {
isHighlighted = true
return false // stop iteration
}
return undefined
})

if (isHighlighted) {
editorView.dispatch({effects: removeLineHighlight.of(line.from)})
} else {
editorView.dispatch({effects: addLineHighlight.of(line.from)})
}
if (config?.onHighlightChange) {
config.onHighlightChange(editorView.state.toJSON(highlightState).highlight)
}
return true
},
},
}),
if (isHighlighted) {
editorView.dispatch({effects: removeLineHighlight.of(line.from)})
} else {
editorView.dispatch({effects: addLineHighlight.of(line.from)})
}
if (config?.onHighlightChange) {
config.onHighlightChange(editorView.state.toJSON(highlightState).highlight)
}
return true
},
},
}),
highlightTheme,
]
}
Expand Down
Loading

0 comments on commit 2111776

Please sign in to comment.