From 47e76c749b830b18f23f417c58c1ae7b9c0fe70d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 11:03:51 -0400 Subject: [PATCH 01/12] flatten --- packages/editor/src/lib/Editor.svelte | 34 +++++++++++++-------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/editor/src/lib/Editor.svelte b/packages/editor/src/lib/Editor.svelte index a162e957ef..a744b93b7b 100644 --- a/packages/editor/src/lib/Editor.svelte +++ b/packages/editor/src/lib/Editor.svelte @@ -166,25 +166,23 @@ }); $effect(() => { - if (editor_view) { - if (workspace.selected_name) { - const current_warnings = warnings[workspace.selected_name] || []; - const diagnostics = current_warnings.map((warning) => { - /** @type {import('@codemirror/lint').Diagnostic} */ - const diagnostic: Diagnostic = { - from: warning.start!.character, - to: warning.end!.character, - severity: 'warning', - message: warning.message - }; - - return diagnostic; - }); + if (!editor_view || !workspace.selected_name) return; + + const current_warnings = warnings[workspace.selected_name] || []; + const diagnostics = current_warnings.map((warning) => { + /** @type {import('@codemirror/lint').Diagnostic} */ + const diagnostic: Diagnostic = { + from: warning.start!.character, + to: warning.end!.character, + severity: 'warning', + message: warning.message + }; + + return diagnostic; + }); - const transaction = setDiagnostics(editor_view.state, diagnostics); - editor_view.dispatch(transaction); - } - } + const transaction = setDiagnostics(editor_view.state, diagnostics); + editor_view.dispatch(transaction); }); From 837e5a7cd1c710717c4081b4c9001e5597ce0339 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 11:05:41 -0400 Subject: [PATCH 02/12] tidy up --- .../src/lib/tutorial/adapters/rollup/index.svelte.ts | 5 +++-- .../tutorial/adapters/webcontainer/index.svelte.ts | 5 +++-- apps/svelte.dev/src/lib/tutorial/index.d.ts | 11 ----------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts b/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts index 7daf5dccd5..e5c8e88773 100644 --- a/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts +++ b/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts @@ -1,8 +1,9 @@ -import type { Adapter, Warning } from '$lib/tutorial'; -import type { File, Item } from 'editor'; import Bundler from '@sveltejs/repl/bundler'; // @ts-ignore package exports don't have types import * as yootils from 'yootils'; +import type { Adapter } from '$lib/tutorial'; +import type { File, Item } from 'editor'; +import type { Warning } from 'svelte/compiler'; /** Rollup bundler singleton */ let bundler: Bundler; diff --git a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts index 31f0b712dc..abe2f8d54e 100644 --- a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts +++ b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts @@ -6,8 +6,9 @@ import * as yootils from 'yootils'; import { get_depth } from '../../../utils/path.js'; import { escape_html } from '../../../utils/escape.js'; import { ready } from '../common/index.js'; -import type { Adapter, Warning } from '$lib/tutorial'; +import type { Adapter } from '$lib/tutorial'; import type { Item, File } from 'editor'; +import type { Warning } from 'svelte/compiler'; const converter = new AnsiToHtml({ fg: 'var(--sk-text-3)' @@ -47,7 +48,7 @@ export async function create(): Promise { } }); - let warnings: Record = {}; + let warnings: Record = {}; let timeout: any; function schedule_to_update_warning(msec: number) { diff --git a/apps/svelte.dev/src/lib/tutorial/index.d.ts b/apps/svelte.dev/src/lib/tutorial/index.d.ts index 900356b80b..efc4f9a43c 100644 --- a/apps/svelte.dev/src/lib/tutorial/index.d.ts +++ b/apps/svelte.dev/src/lib/tutorial/index.d.ts @@ -64,17 +64,6 @@ export interface EditingConstraints { remove: Set; } -// TODO replace with `Warning` from `svelte/compiler` -export interface Warning { - code: string; - start: { line: number; column: number; character: number }; - end: { line: number; column: number; character: number }; - pos: number; - filename: string; - frame: string; - message: string; -} - export interface MenuItem { icon: string; label: string; From 0198c84d7506079138774f30c3bb7478ec5ffcc0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 11:55:13 -0400 Subject: [PATCH 03/12] start fixing diagnostics --- .../tutorial/adapters/rollup/index.svelte.ts | 19 +- .../routes/tutorial/[...slug]/+page.svelte | 5 + .../tutorial/[...slug]/adapter.svelte.ts | 4 +- packages/editor/src/lib/Editor.svelte | 12 +- packages/editor/src/lib/codemirror.css | 305 +++++++++++------- .../repl/src/lib/workers/bundler/index.ts | 14 +- 6 files changed, 224 insertions(+), 135 deletions(-) diff --git a/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts b/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts index e5c8e88773..a527605c67 100644 --- a/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts +++ b/apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts @@ -3,7 +3,7 @@ import Bundler from '@sveltejs/repl/bundler'; import * as yootils from 'yootils'; import type { Adapter } from '$lib/tutorial'; import type { File, Item } from 'editor'; -import type { Warning } from 'svelte/compiler'; +import type { CompileError, Warning } from 'svelte/compiler'; /** Rollup bundler singleton */ let bundler: Bundler; @@ -11,6 +11,7 @@ let bundler: Bundler; export const state = new (class RollupState { progress = $state.raw({ value: 0, text: 'initialising' }); bundle = $state.raw(null); + errors = $state.raw>(); warnings = $state.raw>({}); })(); @@ -52,15 +53,23 @@ export async function create(): Promise { type: f.name.split('.').pop() ?? 'svelte' })) ); + state.bundle = result; - const _warnings: Record = {}; + // TODO this approach is insufficient — we need to get diagnostics for + // individual files, not just the bundle as a whole + state.errors = {}; + state.warnings = {}; + + if (result.error) { + const file = '/src/lib/' + result.error.filename; + state.errors[file] = result.error; + } + for (const warning of result?.warnings ?? []) { const file = '/src/lib/' + warning.filename; - _warnings[file] = _warnings[file] || []; - _warnings[file].push(warning); + (state.warnings[file] ??= []).push(warning); } - state.warnings = _warnings; } const q = yootils.queue(1); diff --git a/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte b/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte index 6c0c9523d3..261a32e5c0 100644 --- a/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte +++ b/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte @@ -226,6 +226,11 @@ }); let completed = $derived(is_completed(workspace.files, b)); + + $inspect({ + errors: adapter.adapter_state.errors, + warnings: adapter.adapter_state.warnings + }); diff --git a/apps/svelte.dev/src/routes/tutorial/[...slug]/adapter.svelte.ts b/apps/svelte.dev/src/routes/tutorial/[...slug]/adapter.svelte.ts index 86982e2af9..ab2d84ad77 100644 --- a/apps/svelte.dev/src/routes/tutorial/[...slug]/adapter.svelte.ts +++ b/apps/svelte.dev/src/routes/tutorial/[...slug]/adapter.svelte.ts @@ -31,7 +31,9 @@ export const adapter_state = new (class { status: 'initialising' } ); - /** Compiler warnings */ + + /** Diagnostics */ + errors = $derived((use_rollup ? rollup_state.errors : wc_state.errors) || {}); warnings = $derived((use_rollup ? rollup_state.warnings : wc_state.warnings) || {}); })(); diff --git a/packages/editor/src/lib/Editor.svelte b/packages/editor/src/lib/Editor.svelte index a744b93b7b..44fab39f33 100644 --- a/packages/editor/src/lib/Editor.svelte +++ b/packages/editor/src/lib/Editor.svelte @@ -172,10 +172,18 @@ const diagnostics = current_warnings.map((warning) => { /** @type {import('@codemirror/lint').Diagnostic} */ const diagnostic: Diagnostic = { + severity: 'warning', from: warning.start!.character, to: warning.end!.character, - severity: 'warning', - message: warning.message + message: warning.message, + renderMessage: () => { + const span = document.createElement('span'); + span.innerHTML = `${warning.message + .replace(/&/g, '&') + .replace(/$1`)} (${warning.code})`; + return span; + } }; return diagnostic; diff --git a/packages/editor/src/lib/codemirror.css b/packages/editor/src/lib/codemirror.css index d9932c1d01..85721f0c04 100644 --- a/packages/editor/src/lib/codemirror.css +++ b/packages/editor/src/lib/codemirror.css @@ -4,121 +4,194 @@ .cm-editor { height: 100%; -} - -.cm-editor .cm-scroller { - font: var(--sk-font-mono); -} - -.cm-editor .cm-gutters { - background: var(--sk-back-3); - border-right: none; - padding: 0; - width: 5rem; -} - -.cm-editor .cm-activeLine { - background: var(--sk-back-translucent); -} - -.cm-editor .cm-activeLineGutter { - background-color: var(--sk-back-3); -} - -.cm-editor .cm-lineNumbers { - flex: 1; - color: #237893; -} - -.cm-editor .cm-foldGutter { - width: 1rem; -} - -.cm-focused .cm-cursor { - border-left-color: var(--sk-text-3); -} - -.cm-editor .cm-content { - padding: 1rem 0; -} - -.cm-editor .cm-line { - padding: 0 1rem; -} - -.cm-editor .cm-selectionBackground { - border-radius: 2px; -} - -.cm-editor .cm-selectionMatch { - background: var(--selection-color); - color: var(--sk-text-2); -} - -.cm-editor .cm-tooltip { - background: var(--sk-back-2); - color: var(--sk-text-1); - border: none; - border-radius: 2px; - overflow: hidden; - margin: 0.4rem 0; - filter: drop-shadow(1px 2px 5px rgba(0, 0, 0, 0.1)); -} - -.cm-editor .cm-tooltip-hover { - border: none; -} - -.cm-editor .cm-tooltip-below { -} -.cm-editor .cm-tooltip-lint { -} -.cm-editor .cm-tooltip-section { -} -.cm-editor .cm-diagnostic { - border: none; - padding: 0.2rem 0.8rem; -} - -.cm-editor .cm-diagnostic-warning { - background: var(--sk-theme-1-variant); - color: white; -} - -.cm-editor .cm-diagnosticText { -} - -.cm-editor .cm-tooltip.cm-tooltip-autocomplete > ul { - font: var(--sk-font-mono); -} - -.cm-editor .cm-panels { - background: var(--sk-back-4); - color: var(--sk-text-1); -} - -.cm-editor .cm-panels.cm-panels-top, -.cm-editor .cm-panels.cm-panels-bottom { - border-top: 1px solid var(--sk-back-5); - border-bottom: 1px solid var(--sk-back-5); -} - -.cm-editor .cm-button { - background: var(--sk-back-5); - border: 2px solid transparent; -} - -.cm-editor .cm-textfield { - background: var(--sk-back-1); - color: var(--sk-text-1); - border: 2px solid transparent; -} - -.cm-editor .cm-search button:focus-visible, -.cm-editor .cm-search input:focus-visible { - border: 2px solid var(--flash); -} -.cm-editor .cm-search input[type='checkbox']:focus-visible { - outline: 2px solid var(--flash); + .cm-scroller { + font: var(--sk-font-mono); + } + + .cm-gutters { + background: var(--sk-back-3); + border-right: none; + padding: 0; + width: 5rem; + } + + .cm-activeLine { + background: var(--sk-back-translucent); + } + + .cm-activeLineGutter { + background-color: var(--sk-back-3); + } + + .cm-lineNumbers { + flex: 1; + color: #237893; + } + + .cm-foldGutter { + width: 1rem; + } + + &.cm-focused .cm-cursor { + border-left-color: var(--sk-text-3); + } + + .cm-content { + padding: 1rem 0; + } + + .cm-line { + padding: 0 1rem; + } + + .cm-selectionBackground { + border-radius: 2px; + } + + .cm-selectionMatch { + background: var(--selection-color); + color: var(--sk-text-2); + } + + .cm-tooltip.cm-tooltip-autocomplete > ul { + font: var(--sk-font-mono); + } + + .cm-panels { + background: var(--sk-back-4); + color: var(--sk-text-1); + } + + .cm-panels.cm-panels-top, + .cm-panels.cm-panels-bottom { + border-top: 1px solid var(--sk-back-5); + border-bottom: 1px solid var(--sk-back-5); + } + + .cm-button { + background: var(--sk-back-5); + border: 2px solid transparent; + } + + .cm-textfield { + background: var(--sk-back-1); + color: var(--sk-text-1); + border: 2px solid transparent; + } + + .cm-search button:focus-visible, + .cm-search input:focus-visible { + border: 2px solid var(--flash); + } + + .cm-search input[type='checkbox']:focus-visible { + outline: 2px solid var(--flash); + } + + .cm-tooltip { + --warning: hsl(40 100% 70%); + --error: hsl(0 100% 90%); + border: none; + background: var(--sk-back-3); + font: var(--sk-font-ui-small); + max-width: calc(100vw - 10em); + position: relative; + filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.1)); + + :root.dark { + --warning: hsl(40 100% 50%); + --error: hsl(0 100% 70%); + } + } + + .cm-tooltip-section { + position: relative; + padding: 0.5em; + left: -13px; + background: var(--bg); + border-radius: 2px; + max-width: 64em; + } + + .cm-tooltip-section::before { + content: ''; + position: absolute; + left: 10px; + width: 8px; + height: 8px; + transform: rotate(45deg); + background-color: var(--bg); + border-radius: 2px; + } + + .cm-tooltip-below .cm-tooltip-section { + top: 10px; + } + + .cm-tooltip-above .cm-tooltip-section { + bottom: 10px; + } + + .cm-tooltip-below .cm-tooltip-section::before { + top: -4px; + } + + .cm-tooltip-above .cm-tooltip-section::before { + bottom: -4px; + } + + .cm-tooltip:has(.cm-diagnostic) { + background: transparent; + } + + .cm-tooltip:has(.cm-diagnostic-warning) { + --bg: var(--warning); + --fg: #222; + } + + .cm-tooltip:has(.cm-diagnostic-error) { + --bg: var(--error); + --fg: #222; + } + + .cm-diagnostic { + padding: 0.2em 0.4em; + position: relative; + border: none; + border-radius: 2px; + } + + .cm-diagnostic:not(:last-child) { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } + + .cm-diagnostic-error { + border: none; + filter: drop-shadow(0px 0px 6px var(--error-bg)); + } + + .cm-diagnostic :not(code) { + font: var(--sk-font-ui-small); + } + + .cm-diagnosticText { + color: var(--fg); + position: relative; + z-index: 2; + } + + .cm-diagnosticText code { + color: inherit; + background-color: rgba(0, 0, 0, 0.05); + border-radius: 2px; + top: 0; + padding: 0.2em; + font-size: 1em; + } + + .cm-diagnosticText strong { + font: var(--sk-font-mono); + opacity: 0.7; + } } diff --git a/packages/repl/src/lib/workers/bundler/index.ts b/packages/repl/src/lib/workers/bundler/index.ts index b21afe2599..f670797f78 100644 --- a/packages/repl/src/lib/workers/bundler/index.ts +++ b/packages/repl/src/lib/workers/bundler/index.ts @@ -14,7 +14,7 @@ import loop_protect from './plugins/loop-protect'; import type { Plugin, TransformResult } from '@rollup/browser'; import type { BundleMessageData } from '../workers'; import type { File, Warning } from '../../types'; -import type { CompileResult } from 'svelte/compiler'; +import type { CompileError, CompileResult } from 'svelte/compiler'; let packages_url: string; let svelte_url: string; @@ -502,7 +502,6 @@ async function bundle({ uid, files }: { uid: number; files: File[] }) { cached.client, lookup ); - let error; try { if (client.error) { @@ -553,11 +552,7 @@ async function bundle({ uid, files }: { uid: number; files: File[] }) { } catch (err) { console.error(err); - // @ts-ignore - const e: Error = error || err; - - // @ts-ignore - delete e.toString; + const e = err as CompileError; return { uid, @@ -565,10 +560,7 @@ async function bundle({ uid, files }: { uid: number; files: File[] }) { server: null, imports: null, warnings: client.warnings, - error: Object.assign({}, e, { - message: e.message, - stack: e.stack - }) + error: { ...e } }; } } From f4bc09b89933e049be3e31c42b26cd7d5030e5f0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 12:14:54 -0400 Subject: [PATCH 04/12] move styles into .css --- packages/editor/src/lib/codemirror.css | 98 +++++++++++++++++--- packages/repl/src/lib/theme.ts | 119 +------------------------ 2 files changed, 88 insertions(+), 129 deletions(-) diff --git a/packages/editor/src/lib/codemirror.css b/packages/editor/src/lib/codemirror.css index 85721f0c04..f30c280cd1 100644 --- a/packages/editor/src/lib/codemirror.css +++ b/packages/editor/src/lib/codemirror.css @@ -3,15 +3,34 @@ } .cm-editor { + color: var(--sk-code-base); + background-color: transparent; height: 100%; + &.cm-focused { + .cm-cursor { + border-left-color: var(--sk-text-3); + } + + > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, + .cm-selectionBackground, + .cm-content ::selection { + background-color: var(--sk-selection-color); + } + + .cm-matchingBracket, + .cm-nonmatchingBracket { + background-color: #bad0f847; + } + } + .cm-scroller { font: var(--sk-font-mono); } .cm-gutters { background: var(--sk-back-3); - border-right: none; + border: none; padding: 0; width: 5rem; } @@ -26,19 +45,50 @@ .cm-lineNumbers { flex: 1; - color: #237893; + + .cm-gutterElement:not(:last-child) { + display: flex; + justify-content: end; + align-items: end; + } } .cm-foldGutter { width: 1rem; } - &.cm-focused .cm-cursor { - border-left-color: var(--sk-text-3); + .cm-foldPlaceholder { + background-color: transparent; + border: none; + color: #ddd; + } + + .cm-lintRange { + background-position: left bottom; + background-repeat: repeat-x; + padding-bottom: 4px; + + &.cm-lintRange-error { + /* TODO */ + } + + &.cm-lintRange-warning { + /* TODO */ + } } .cm-content { - padding: 1rem 0; + /* ensure no gap between top of editor and highlighted first/last line */ + padding-top: 0; + padding-bottom: 0; + + .cm-line:first-child { + padding-top: 4px; + } + + .cm-line:last-child { + padding-bottom: 4px; + } } .cm-line { @@ -54,19 +104,29 @@ color: var(--sk-text-2); } - .cm-tooltip.cm-tooltip-autocomplete > ul { - font: var(--sk-font-mono); + .cm-tooltip.cm-tooltip-autocomplete { + color: var(--sk-text-2) !important; + perspective: 1px; + + & > ul > li[aria-selected] { + background-color: var(--sk-back-4); + color: var(--sk-text-1) !important; + } + + & > ul { + font: var(--sk-font-mono); + } } .cm-panels { background: var(--sk-back-4); color: var(--sk-text-1); - } - .cm-panels.cm-panels-top, - .cm-panels.cm-panels-bottom { - border-top: 1px solid var(--sk-back-5); - border-bottom: 1px solid var(--sk-back-5); + &.cm-panels-top, + &.cm-panels-bottom { + border-top: 1px solid var(--sk-back-5); + border-bottom: 1px solid var(--sk-back-5); + } } .cm-button { @@ -89,6 +149,10 @@ outline: 2px solid var(--flash); } + .cm-searchMatch.cm-searchMatch-selected { + background-color: #6199ff2f; + } + .cm-tooltip { --warning: hsl(40 100% 70%); --error: hsl(0 100% 90%); @@ -103,6 +167,16 @@ --warning: hsl(40 100% 50%); --error: hsl(0 100% 70%); } + + .cm-tooltip-arrow::before { + border-top-color: transparent; + border-bottom-color: transparent; + } + + .cm-tooltip-arrow::after { + border-top-color: var(--sk-back-3); + border-bottom-color: var(--sk-back-3); + } } .cm-tooltip-section { diff --git a/packages/repl/src/lib/theme.ts b/packages/repl/src/lib/theme.ts index 6bf7b0c973..86e9a6f06b 100644 --- a/packages/repl/src/lib/theme.ts +++ b/packages/repl/src/lib/theme.ts @@ -1,122 +1,7 @@ import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'; -import { EditorView } from '@codemirror/view'; import { tags as t } from '@lezer/highlight'; -const ERROR_HUE = 0; -const WARNING_HUE = 40; - -const WARNING_FG = `hsl(${WARNING_HUE} 100% 60%)`; -const WARNING_BG = `hsl(${WARNING_HUE} 100% 40% / 0.5)`; - -const ERROR_FG = `hsl(${ERROR_HUE} 100% 40%)`; -const ERROR_BG = `hsl(${ERROR_HUE} 100% 40% / 0.5)`; - -function svg(content: string, attrs = `viewBox="0 0 40 40"`) { - return `url('data:image/svg+xml,${encodeURIComponent( - content - )}')`; -} - -function underline(color: string) { - return svg( - ``, - `width="6" height="4"` - ); -} - -const svelteThemeStyles = EditorView.theme( - { - '&': { - color: 'var(--sk-code-base)', - backgroundColor: 'transparent' - }, - - '.cm-content': { - // ensure no gap between top of editor and highlighted first/last line - paddingTop: '0px', - paddingBottom: '0px' - }, - - '.cm-content .cm-line:first-child': { - paddingTop: '4px' - }, - - '.cm-content .cm-line:last-child': { - paddingBottom: '4px' - }, - - '.cm-lineNumbers .cm-gutterElement:not(:last-child)': { - display: 'flex', - justifyContent: 'end', - alignItems: 'end' - }, - - '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': - { backgroundColor: 'var(--sk-selection-color)' }, - - '.cm-panels': { backgroundColor: 'var(--sk-back-2)', color: 'var(--sk-text-2)' }, - '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' }, - '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' }, - - '.cm-searchMatch.cm-searchMatch-selected': { - backgroundColor: '#6199ff2f' - }, - - '.cm-activeLine': { backgroundColor: '#6699ff0b' }, - '.cm-selectionMatch': { backgroundColor: '#aafe661a' }, - - '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': { - backgroundColor: '#bad0f847' - }, - - '.cm-gutters': { - backgroundColor: 'var(--sk-back-3)', - border: 'none' - }, - - '.cm-activeLineGutter': { - backgroundColor: 'var(--sk-back-4)' - }, - - '.cm-foldPlaceholder': { - backgroundColor: 'transparent', - border: 'none', - color: '#ddd' - }, - - // https://github.com/codemirror/lint/blob/271b35f5d31a7e3645eaccbfec608474022098e1/src/lint.ts#L620 - '.cm-lintRange': { - backgroundPosition: 'left bottom', - backgroundRepeat: 'repeat-x', - paddingBottom: '4px' - }, - '.cm-lintRange-error': { - backgroundImage: underline(ERROR_FG) - }, - '.cm-lintRange-warning': { - backgroundImage: underline(WARNING_FG) - }, - '.cm-tooltip .cm-tooltip-arrow:before': { - borderTopColor: 'transparent', - borderBottomColor: 'transparent' - }, - '.cm-tooltip .cm-tooltip-arrow:after': { - borderTopColor: 'var(--sk-back-3)', - borderBottomColor: 'var(--sk-back-3)' - }, - '.cm-tooltip-autocomplete': { - color: 'var(--sk-text-2) !important', - perspective: '1px', - '& > ul > li[aria-selected]': { - backgroundColor: 'var(--sk-back-4)', - color: 'var(--sk-text-1) !important' - } - } - }, - { dark: true } -); - -/// The highlighting style for code in the One Dark theme. +// TODO move this into the `editor` package const svelteHighlightStyle = HighlightStyle.define([ { tag: t.keyword, color: 'var(--sk-code-keyword)' }, { @@ -154,4 +39,4 @@ const svelteHighlightStyle = HighlightStyle.define([ { tag: t.invalid, color: '#ff008c' } ]); -export const svelteTheme = [svelteThemeStyles, syntaxHighlighting(svelteHighlightStyle)]; +export const svelteTheme = syntaxHighlighting(svelteHighlightStyle); From b07075f3688be41a8c9e19d39d9b21261a813382 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 12:25:29 -0400 Subject: [PATCH 05/12] attempt to wire up errors. doesnt work yet because we need to compile files individually --- .../routes/tutorial/[...slug]/+page.svelte | 1 + packages/editor/src/lib/Editor.svelte | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte b/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte index 261a32e5c0..b6665d9d96 100644 --- a/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte +++ b/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte @@ -321,6 +321,7 @@
{ diff --git a/packages/editor/src/lib/Editor.svelte b/packages/editor/src/lib/Editor.svelte index 44fab39f33..a91df5bf27 100644 --- a/packages/editor/src/lib/Editor.svelte +++ b/packages/editor/src/lib/Editor.svelte @@ -14,17 +14,18 @@ import { autocomplete_for_svelte } from '@sveltejs/site-kit/codemirror'; import type { Diagnostic } from '@codemirror/lint'; import { Workspace, type Item, type File } from './Workspace.svelte.js'; - import type { Warning } from 'svelte/compiler'; + import type { CompileError, Warning } from 'svelte/compiler'; import './codemirror.css'; interface Props { - warnings: Record; // TODO this should include errors as well + errors: Record; + warnings: Record; workspace: Workspace; onchange: (file: File, contents: string) => void; autocomplete_filter?: (file: File) => boolean; } - let { warnings, workspace, onchange, autocomplete_filter = () => true }: Props = $props(); + let { errors, warnings, workspace, onchange, autocomplete_filter = () => true }: Props = $props(); let container: HTMLDivElement; @@ -168,10 +169,31 @@ $effect(() => { if (!editor_view || !workspace.selected_name) return; + const diagnostics: Diagnostic[] = []; + + const error = null; // TODO should be `errors[workspace.selected_name]` but it's currently a Rollup plugin error... const current_warnings = warnings[workspace.selected_name] || []; - const diagnostics = current_warnings.map((warning) => { - /** @type {import('@codemirror/lint').Diagnostic} */ - const diagnostic: Diagnostic = { + + if (error) { + diagnostics.push({ + severity: 'error', + from: error.position![0], + to: error.position![1], + message: error.message, + renderMessage: () => { + // TODO expose error codes, so we can link to docs in future + const span = document.createElement('span'); + span.innerHTML = `${error.message + .replace(/&/g, '&') + .replace(/$1`)}`; + return span; + } + }); + } + + for (const warning of current_warnings) { + diagnostics.push({ severity: 'warning', from: warning.start!.character, to: warning.end!.character, @@ -184,10 +206,8 @@ .replace(/`(.+?)`/g, `$1`)} (${warning.code})`; return span; } - }; - - return diagnostic; - }); + }); + } const transaction = setDiagnostics(editor_view.state, diagnostics); editor_view.dispatch(transaction); From 04c17e36133f52732f7213777993cc9ba11eaf76 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 12:38:27 -0400 Subject: [PATCH 06/12] fix some css --- packages/editor/src/lib/codemirror.css | 132 ++++++++++--------------- 1 file changed, 51 insertions(+), 81 deletions(-) diff --git a/packages/editor/src/lib/codemirror.css b/packages/editor/src/lib/codemirror.css index f30c280cd1..9e36fbbe78 100644 --- a/packages/editor/src/lib/codemirror.css +++ b/packages/editor/src/lib/codemirror.css @@ -168,65 +168,35 @@ --error: hsl(0 100% 70%); } - .cm-tooltip-arrow::before { - border-top-color: transparent; - border-bottom-color: transparent; + &:has(.cm-diagnostic) { + background: transparent; } - .cm-tooltip-arrow::after { - border-top-color: var(--sk-back-3); - border-bottom-color: var(--sk-back-3); + &:has(.cm-diagnostic-warning) { + --bg: var(--warning); + --fg: #222; } - } - - .cm-tooltip-section { - position: relative; - padding: 0.5em; - left: -13px; - background: var(--bg); - border-radius: 2px; - max-width: 64em; - } - - .cm-tooltip-section::before { - content: ''; - position: absolute; - left: 10px; - width: 8px; - height: 8px; - transform: rotate(45deg); - background-color: var(--bg); - border-radius: 2px; - } - - .cm-tooltip-below .cm-tooltip-section { - top: 10px; - } - .cm-tooltip-above .cm-tooltip-section { - bottom: 10px; - } - - .cm-tooltip-below .cm-tooltip-section::before { - top: -4px; - } - - .cm-tooltip-above .cm-tooltip-section::before { - bottom: -4px; - } + &:has(.cm-diagnostic-error) { + --bg: var(--error); + --fg: #222; + } - .cm-tooltip:has(.cm-diagnostic) { - background: transparent; - } + .cm-tooltip-section { + position: relative; + left: -1rem; + background: var(--bg); + border-radius: 2px; + max-width: 64em; + } - .cm-tooltip:has(.cm-diagnostic-warning) { - --bg: var(--warning); - --fg: #222; - } + &.cm-tooltip-below .cm-tooltip-section { + top: 1rem; + } - .cm-tooltip:has(.cm-diagnostic-error) { - --bg: var(--error); - --fg: #222; + &.cm-tooltip-above .cm-tooltip-section { + bottom: 1rem; + } } .cm-diagnostic { @@ -234,38 +204,38 @@ position: relative; border: none; border-radius: 2px; - } - - .cm-diagnostic:not(:last-child) { - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - } - .cm-diagnostic-error { - border: none; - filter: drop-shadow(0px 0px 6px var(--error-bg)); - } - - .cm-diagnostic :not(code) { - font: var(--sk-font-ui-small); - } + &:not(:last-child) { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } - .cm-diagnosticText { - color: var(--fg); - position: relative; - z-index: 2; - } + &.cm-diagnostic-error { + border: none; + filter: drop-shadow(0px 0px 6px var(--error-bg)); + } - .cm-diagnosticText code { - color: inherit; - background-color: rgba(0, 0, 0, 0.05); - border-radius: 2px; - top: 0; - padding: 0.2em; - font-size: 1em; - } + :not(code) { + font: var(--sk-font-ui-small); + } - .cm-diagnosticText strong { - font: var(--sk-font-mono); - opacity: 0.7; + .cm-diagnosticText { + color: var(--fg); + position: relative; + z-index: 2; + + code { + color: inherit; + background-color: rgba(0, 0, 0, 0.05); + border-radius: 2px; + top: 0; + padding: 0.2em; + font-size: 1em; + } + + strong { + font: var(--sk-font-mono); + opacity: 0.7; + } + } } } From c007951bd082203390fcb8aa103da19b34added6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 12:52:05 -0400 Subject: [PATCH 07/12] fix --- packages/editor/src/lib/codemirror.css | 89 +++++++++++++------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/packages/editor/src/lib/codemirror.css b/packages/editor/src/lib/codemirror.css index 9e36fbbe78..f47639ec1a 100644 --- a/packages/editor/src/lib/codemirror.css +++ b/packages/editor/src/lib/codemirror.css @@ -161,7 +161,8 @@ font: var(--sk-font-ui-small); max-width: calc(100vw - 10em); position: relative; - filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.1)); + padding: 1rem; + filter: var(--sk-shadow); :root.dark { --warning: hsl(40 100% 50%); @@ -184,58 +185,58 @@ .cm-tooltip-section { position: relative; - left: -1rem; + /* left: -1rem; */ + padding: 1rem; background: var(--bg); border-radius: 2px; max-width: 64em; + + .cm-diagnostic { + padding: 0; + margin: 0; + position: relative; + border: none; + border-radius: var(--sk-border-radius); + + &:not(:last-child) { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + margin-bottom: 1rem; + padding-bottom: 1rem; + } + + :not(code) { + font: var(--sk-font-ui-small); + } + + .cm-diagnosticText { + position: relative; + display: flex; + color: var(--fg); + z-index: 2; + + code { + color: inherit; + background-color: rgba(0, 0, 0, 0.05); + font-size: 1em; + margin: 0; /* TODO this counteracts base styles that should probably be deleted? */ + padding: 0.2rem 0.4rem; + } + + strong { + font: var(--sk-font-mono); + font-size: 1em; + opacity: 0.7; + } + } + } } &.cm-tooltip-below .cm-tooltip-section { - top: 1rem; + /* top: 1rem; */ } &.cm-tooltip-above .cm-tooltip-section { - bottom: 1rem; - } - } - - .cm-diagnostic { - padding: 0.2em 0.4em; - position: relative; - border: none; - border-radius: 2px; - - &:not(:last-child) { - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - } - - &.cm-diagnostic-error { - border: none; - filter: drop-shadow(0px 0px 6px var(--error-bg)); - } - - :not(code) { - font: var(--sk-font-ui-small); - } - - .cm-diagnosticText { - color: var(--fg); - position: relative; - z-index: 2; - - code { - color: inherit; - background-color: rgba(0, 0, 0, 0.05); - border-radius: 2px; - top: 0; - padding: 0.2em; - font-size: 1em; - } - - strong { - font: var(--sk-font-mono); - opacity: 0.7; - } + /* bottom: 1rem; */ } } } From 70f8ad96713e2d4435670c2bd7a8c3fe188e7e17 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 12:53:04 -0400 Subject: [PATCH 08/12] tidy --- apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte | 5 ----- packages/editor/src/lib/Editor.svelte | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte b/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte index b6665d9d96..a1c9d71b95 100644 --- a/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte +++ b/apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte @@ -226,11 +226,6 @@ }); let completed = $derived(is_completed(workspace.files, b)); - - $inspect({ - errors: adapter.adapter_state.errors, - warnings: adapter.adapter_state.warnings - }); diff --git a/packages/editor/src/lib/Editor.svelte b/packages/editor/src/lib/Editor.svelte index a91df5bf27..00e165695d 100644 --- a/packages/editor/src/lib/Editor.svelte +++ b/packages/editor/src/lib/Editor.svelte @@ -204,6 +204,7 @@ .replace(/&/g, '&') .replace(/$1`)} (${warning.code})`; + return span; } }); From f390dda85f9d1fb5da816a6ed40427b94146842a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 13:00:11 -0400 Subject: [PATCH 09/12] comment out for now --- packages/editor/src/lib/Editor.svelte | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/editor/src/lib/Editor.svelte b/packages/editor/src/lib/Editor.svelte index 00e165695d..25f0d56510 100644 --- a/packages/editor/src/lib/Editor.svelte +++ b/packages/editor/src/lib/Editor.svelte @@ -175,21 +175,21 @@ const current_warnings = warnings[workspace.selected_name] || []; if (error) { - diagnostics.push({ - severity: 'error', - from: error.position![0], - to: error.position![1], - message: error.message, - renderMessage: () => { - // TODO expose error codes, so we can link to docs in future - const span = document.createElement('span'); - span.innerHTML = `${error.message - .replace(/&/g, '&') - .replace(/$1`)}`; - return span; - } - }); + // diagnostics.push({ + // severity: 'error', + // from: error.position![0], + // to: error.position![1], + // message: error.message, + // renderMessage: () => { + // // TODO expose error codes, so we can link to docs in future + // const span = document.createElement('span'); + // span.innerHTML = `${error.message + // .replace(/&/g, '&') + // .replace(/$1`)}`; + // return span; + // } + // }); } for (const warning of current_warnings) { From ef1afedc604efa0e48010a948f124204fc461bfe Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 13:04:50 -0400 Subject: [PATCH 10/12] fix type errors --- .../tutorial/adapters/webcontainer/index.svelte.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts index abe2f8d54e..7ebb6bd70d 100644 --- a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts +++ b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts @@ -64,13 +64,20 @@ export async function create(): Promise { state.logs = []; } else if (chunk?.startsWith('svelte:warnings:')) { const warn: Warning = JSON.parse(chunk.slice(16)); - const filename = warn.filename.startsWith('/') ? warn.filename : '/' + warn.filename; + const filename = (warn.filename!.startsWith('/') ? warn.filename : '/' + warn.filename)!; const current = warnings[filename]; if (!current) { warnings[filename] = [warn]; // the exact same warning may be given multiple times in a row - } else if (!current.some((s) => s.code === warn.code && s.pos === warn.pos)) { + } else if ( + !current.some( + (s) => + s.code === warn.code && + s.position![0] === warn.position![0] && + s.position![1] === warn.position![1] + ) + ) { current.push(warn); } From e3c2af7c093ebf64aabf60e22f8c2c2c6d826732 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 13:08:02 -0400 Subject: [PATCH 11/12] quick fix --- .../src/lib/tutorial/adapters/webcontainer/index.svelte.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts index 7ebb6bd70d..9393cca96a 100644 --- a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts +++ b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts @@ -8,7 +8,7 @@ import { escape_html } from '../../../utils/escape.js'; import { ready } from '../common/index.js'; import type { Adapter } from '$lib/tutorial'; import type { Item, File } from 'editor'; -import type { Warning } from 'svelte/compiler'; +import type { CompileError, Warning } from 'svelte/compiler'; const converter = new AnsiToHtml({ fg: 'var(--sk-text-3)' @@ -22,6 +22,7 @@ export const state = new (class WCState { base = $state.raw(null); error = $state.raw(null); logs = $state.raw([]); + errors = $state.raw>({}); warnings = $state.raw>({}); })(); From 0224ad191a59d8b9e2934ed93ce619c8624eb2ba Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Oct 2024 13:11:18 -0400 Subject: [PATCH 12/12] goddammit --- .../src/lib/tutorial/adapters/webcontainer/index.svelte.ts | 2 +- packages/editor/src/lib/Editor.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts index 9393cca96a..29e13709e1 100644 --- a/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts +++ b/apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts @@ -22,7 +22,7 @@ export const state = new (class WCState { base = $state.raw(null); error = $state.raw(null); logs = $state.raw([]); - errors = $state.raw>({}); + errors = $state.raw>({}); warnings = $state.raw>({}); })(); diff --git a/packages/editor/src/lib/Editor.svelte b/packages/editor/src/lib/Editor.svelte index 25f0d56510..f955e03365 100644 --- a/packages/editor/src/lib/Editor.svelte +++ b/packages/editor/src/lib/Editor.svelte @@ -18,7 +18,7 @@ import './codemirror.css'; interface Props { - errors: Record; + errors: Record; warnings: Record; workspace: Workspace; onchange: (file: File, contents: string) => void;