Skip to content

Commit

Permalink
feat: improve diagnostics and add preview state indicator
Browse files Browse the repository at this point in the history
  • Loading branch information
Cubxity committed Sep 16, 2023
1 parent 1a023c8 commit df1084e
Show file tree
Hide file tree
Showing 18 changed files with 138 additions and 76 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@tauri-apps/api": "^1.3.0",
"clsx": "^1.2.1",
"lodash": "^4.17.21",
"lucide-svelte": "^0.279.0",
"monaco-editor": "^0.38.0",
"vscode-oniguruma": "^1.7.0",
"vscode-textmate": "^9.0.0"
Expand Down
12 changes: 11 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 23 additions & 12 deletions src-tauri/src/ipc/commands/typst.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::{Error, Result};
use crate::ipc::commands::project;
use crate::ipc::model::TypstRenderResponse;
use crate::ipc::{TypstCompileEvent, TypstDocument, TypstSourceError};
use crate::ipc::{
TypstCompileEvent, TypstDiagnosticSeverity, TypstDocument, TypstSourceDiagnostic,
};
use crate::project::ProjectManager;
use base64::Engine;
use log::debug;
Expand All @@ -13,6 +15,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
use tauri::Runtime;
use typst::diag::Severity;
use typst::eval::Tracer;
use typst::geom::Color;
use typst::ide::{Completion, CompletionKind};
Expand Down Expand Up @@ -118,28 +121,36 @@ pub async fn typst_compile<R: Runtime>(
width: width.to_pt(),
height: height.to_pt(),
}),
errors: None,
diagnostics: None,
},
);
}
Err(errors) => {
debug!("compilation failed with {:?} errors", errors.len());
Err(diagnostics) => {
debug!(
"compilation failed with {:?} diagnostics",
diagnostics.len()
);

let source = world.source(source_id);
let errors: Vec<TypstSourceError> = match source {
Ok(source) => errors
let diagnostics: Vec<TypstSourceDiagnostic> = match source {
Ok(source) => diagnostics
.iter()
.filter(|e| e.span.id() == Some(source_id))
.filter_map(|e| {
let span = source.find(e.span)?;
.filter(|d| d.span.id() == Some(source_id))
.filter_map(|d| {
let span = source.find(d.span)?;
let range = span.range();
let start = content[..range.start].chars().count();
let size = content[range.start..range.end].chars().count();

let message = e.message.to_string();
Some(TypstSourceError {
let message = d.message.to_string();
Some(TypstSourceDiagnostic {
range: start..start + size,
severity: match d.severity {
Severity::Error => TypstDiagnosticSeverity::Error,
Severity::Warning => TypstDiagnosticSeverity::Warning,
},
message,
hints: d.hints.iter().map(|hint| hint.to_string()).collect(),
})
})
.collect(),
Expand All @@ -150,7 +161,7 @@ pub async fn typst_compile<R: Runtime>(
"typst_compile",
TypstCompileEvent {
document: None,
errors: Some(errors),
diagnostics: Some(diagnostics),
},
);
}
Expand Down
13 changes: 11 additions & 2 deletions src-tauri/src/ipc/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::PathBuf;
#[derive(Serialize, Clone, Debug)]
pub struct TypstCompileEvent {
pub document: Option<TypstDocument>,
pub errors: Option<Vec<TypstSourceError>>,
pub diagnostics: Option<Vec<TypstSourceDiagnostic>>,
}

#[derive(Serialize, Clone, Debug)]
Expand All @@ -17,9 +17,18 @@ pub struct TypstDocument {
}

#[derive(Serialize, Clone, Debug)]
pub struct TypstSourceError {
#[serde(rename_all = "snake_case")]
pub enum TypstDiagnosticSeverity {
Error,
Warning,
}

#[derive(Serialize, Clone, Debug)]
pub struct TypstSourceDiagnostic {
pub range: Range<usize>,
pub severity: TypstDiagnosticSeverity,
pub message: String,
pub hints: Vec<String>,
}

#[derive(Serialize, Clone, Debug)]
Expand Down
60 changes: 37 additions & 23 deletions src/components/Editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import IMarkerData = editorType.IMarkerData;
import { paste } from "$lib/ipc/clipboard";
import { throttle } from "$lib/fn";
import { PreviewState, shell } from "$lib/stores";
let divEl: HTMLDivElement;
let editor: ICodeEditor;
Expand All @@ -23,6 +24,8 @@
const handleCompile = async () => {
const model = editor.getModel();
if (model) {
shell.setPreviewState(PreviewState.Compiling);
// Removing the preceding slash
await compile(model.uri.path, model.getValue());
}
Expand All @@ -44,9 +47,9 @@
// @ts-ignore
self.MonacoEnvironment = {
getWorker: function(_moduleId: any, label: string) {
getWorker: function (_moduleId: any, label: string) {
return new EditorWorker.default();
}
},
};
editor = (await monacoImport).editor.create(divEl, {
Expand All @@ -56,7 +59,7 @@
folding: true,
quickSuggestions: false,
wordWrap: "on",
unicodeHighlight: { ambiguousCharacters: false }
unicodeHighlight: { ambiguousCharacters: false },
});
editor.onDidChangeModel((e: IModelChangedEvent) => {
Expand All @@ -79,24 +82,31 @@
// Returns an unlisten function
return appWindow.listen<TypstCompileEvent>("typst_compile", ({ event, payload }) => {
const { errors } = payload;
const { document, diagnostics } = payload;
const model = editor.getModel();
if (model) {
const markers: IMarkerData[] = errors?.map(({ range, message }) => {
const start = model.getPositionAt(range.start);
const end = model.getPositionAt(range.end);
return {
startLineNumber: start.lineNumber,
startColumn: start.column,
endLineNumber: end.lineNumber,
endColumn: end.column,
message,
severity: monaco.MarkerSeverity.Error
};
}) ?? [];
const markers: IMarkerData[] =
diagnostics?.map(({ range, severity, message, hints }) => {
const start = model.getPositionAt(range.start);
const end = model.getPositionAt(range.end);
return {
startLineNumber: start.lineNumber,
startColumn: start.column,
endLineNumber: end.lineNumber,
endColumn: end.column,
message: message + "\n" + hints.map((hint) => `hint: ${hint}`).join("\n"),
severity:
severity === "error" ? monaco.MarkerSeverity.Error : monaco.MarkerSeverity.Warning,
};
}) ?? [];
monaco.editor.setModelMarkers(model, "owner", markers);
}
if (document) {
shell.setPreviewState(PreviewState.Idle);
} else {
shell.setPreviewState(PreviewState.CompileError);
}
});
});
Expand Down Expand Up @@ -140,17 +150,21 @@
const range = editor.getSelection();
const model = editor.getModel();
if (range && model) {
model.pushEditOperations([], [
{
range: range,
text: `\n#figure(\n image("${res.path}"),\n caption: []\n)\n`
}
], () => null);
model.pushEditOperations(
[],
[
{
range: range,
text: `\n#figure(\n image("${res.path}"),\n caption: []\n)\n`,
},
],
() => null
);
}
}
};
$: fetchContent(editor, path);
</script>

<div bind:this={divEl} on:paste={handlePaste} class={$$props.class}></div>
<div bind:this={divEl} on:paste={handlePaste} class={$$props.class} />
13 changes: 5 additions & 8 deletions src/components/ExplorerNode.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
<script lang="ts">
import clsx from "clsx";
import FileIcon from "./icons/FileIcon.svelte";
import FolderIcon from "./icons/FolderIcon.svelte";
import ArrowDropDownIcon from "./icons/ArrowDropDownIcon.svelte";
import ArrowRightIcon from "./icons/ArrowRightIcon.svelte";
import type { FileItem, FileType, FSRefreshEvent } from "../lib/ipc";
import { project, shell } from "../lib/stores";
import { listDir } from "../lib/ipc";
import { onMount } from "svelte";
import { ChevronDownIcon, ChevronRightIcon, FileIcon, FolderIcon } from "lucide-svelte";
import { appWindow } from "@tauri-apps/api/window";
export let type: FileType;
Expand Down Expand Up @@ -57,17 +54,17 @@
>
{#if type === "directory"}
<svelte:component
this={expanded ? ArrowDropDownIcon : ArrowRightIcon}
this={expanded ? ChevronDownIcon : ChevronRightIcon}
class="w-4 h-4 inline fill-neutral-500 mr-1"
/>
{/if}
<svelte:component
this={type === "file" ? FileIcon : FolderIcon}
class={clsx("w-4 h-4 inline fill-neutral-500 mr-2", type === "file" && "ml-5")}
class={clsx("w-4 h-4 inline stroke-neutral-400 mr-2", type === "file" && "ml-5")}
/>
<span class="flex-1 truncate">
{path === "/" ? "root" : path.slice(path.lastIndexOf("/") + 1)}
</span>
{path === "/" ? "root" : path.slice(path.lastIndexOf("/") + 1)}
</span>
</div>
{/if}
{#if expanded}
Expand Down
7 changes: 3 additions & 4 deletions src/components/ExplorerTree.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<script lang="ts">
import clsx from "clsx";
import ExplorerNode from "./ExplorerNode.svelte";
import AddIcon from "./icons/AddIcon.svelte";
import { project, shell } from "$lib/stores";
import { createFile } from "$lib/ipc";
import { PlusIcon } from "lucide-svelte";
const handleCreate = () => {
shell.createModal({
Expand All @@ -14,7 +13,7 @@
if (path) {
createFile(path);
}
}
},
});
};
</script>
Expand All @@ -25,7 +24,7 @@
<span class="text-lg font-bold block flex-1">Project</span>
<div class="flex flex-row rounded-md border border-neutral-700 overflow-clip">
<button class="p-1 transition-colors hover:bg-neutral-700" on:click={handleCreate}>
<AddIcon class="w-4 h-4" />
<PlusIcon class="w-4 h-4" />
</button>
</div>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/components/ShellModal.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { Modal } from "../lib/stores";
import { shell } from "../lib/stores";
import CloseIcon from "./icons/CloseIcon.svelte";
import { XIcon } from "lucide-svelte";
let modal: Modal | undefined;
$: modal = $shell.modals[0];
Expand Down Expand Up @@ -36,17 +36,17 @@
</span>
<button
class="rounded-md border border-neutral-700 p-1 transition-colors hover:bg-neutral-700"
on:click={handleClose}
on:click={() => handleClose()}
>
<CloseIcon class="w-4 h-4" />
<XIcon class="w-4 h-4" />
</button>
</div>
{#if modal.type === "input"}
<input
class="w-full rounded-md bg-neutral-600 px-2 py-1 mt-2 text-sm"
on:keyup={handleInputKeyUp}
autofocus
>
/>
{/if}
</div>
</div>
Expand Down
8 changes: 5 additions & 3 deletions src/components/SidePanel.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script>
import FolderIcon from "./icons/FolderIcon.svelte";
import ExplorerTree from "./ExplorerTree.svelte";
import { FolderIcon } from "lucide-svelte";
import clsx from "clsx";
// TODO: Implement a modular side panel system?
Expand All @@ -13,7 +13,10 @@

<div class="p-1.5 border-r border-neutral-700">
<button
class={clsx("rounded-md p-1 transition-colors hover:bg-neutral-700/60", explorerVisible && "bg-neutral-700/80")}
class={clsx(
"rounded-md p-1 transition-colors hover:bg-neutral-700/60",
explorerVisible && "bg-neutral-700/80"
)}
on:click={toggleExplorer}
>
<FolderIcon class="w-5 h-5 text-neutral-300" />
Expand All @@ -22,4 +25,3 @@
{#if explorerVisible}
<ExplorerTree class="w-80" />
{/if}

Loading

0 comments on commit df1084e

Please sign in to comment.